Skip to content

Commit 07890f8

Browse files
authored
Merge pull request #64 from AdityaNPL/fix/backup_chain/is_valid_file_path
fix(IsValidFilePath): Can now validate Windows UNC paths on Unix runtime
2 parents babf28a + 6307f34 commit 07890f8

4 files changed

Lines changed: 81 additions & 45 deletions

File tree

src/BackupChain.cs

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -98,22 +98,8 @@ private static IEnumerable<BackupMetadata> NextLogBackup(IEnumerable<BackupMetad
9898

9999
private static bool IsValidFilePath(BackupMetadata meta)
100100
{
101-
if(BackupFileTools.IsUrl(meta.PhysicalDeviceName))
102-
return true;
103-
104-
// A quick check before leaning on exceptions
105-
if(Path.GetInvalidPathChars().Any(meta.PhysicalDeviceName.Contains))
106-
return false;
107-
108-
try {
109-
// This will throw an argument exception if the path is invalid
110-
Path.GetFullPath(meta.PhysicalDeviceName);
111-
// A relative path won't help us much if the destination is another server. It needs to be rooted.
112-
return Path.IsPathRooted(meta.PhysicalDeviceName);
113-
}
114-
catch(Exception) {
115-
return false;
116-
}
101+
var path = meta.PhysicalDeviceName;
102+
return BackupFileTools.IsValidFileUrl(path) || BackupFileTools.IsValidFilePath(path);
117103
}
118104
}
119105
}

src/SmoFacade/BackupFileTools.cs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
namespace AgDatabaseMove.SmoFacade
22
{
33
using System;
4+
using System.IO;
5+
using System.Linq;
46
using System.Text.RegularExpressions;
57

68

@@ -13,9 +15,11 @@ public enum BackupType
1315
Log // trn / L
1416
}
1517

16-
public static bool IsUrl(string path)
18+
public static bool IsValidFileUrl(string path)
1719
{
18-
return Regex.IsMatch(path, @"(http(|s):\/)(\/[^\s]+)+\.([a-zA-Z]+)$");
20+
return Uri.TryCreate(path, UriKind.Absolute, out var uriResult)
21+
&& (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps || uriResult.IsUnc)
22+
&& Path.HasExtension(path);
1923
}
2024

2125
public static string BackupTypeToExtension(BackupType type)
@@ -45,5 +49,23 @@ public static BackupType BackupTypeAbbrevToType(string type)
4549
throw new ArgumentException("Invalid backup type");
4650
}
4751
}
52+
53+
public static bool IsValidFilePath(string path)
54+
{
55+
// A quick check before leaning on exceptions
56+
if(Path.GetInvalidPathChars().Any(path.Contains)) {
57+
return false;
58+
}
59+
60+
try {
61+
// This will throw an argument exception if the path is invalid
62+
Path.GetFullPath(path);
63+
// A relative path won't help us much if the destination is another server. It needs to be rooted.
64+
return Path.IsPathRooted(path) && Path.HasExtension(path);
65+
}
66+
catch(Exception) {
67+
return false;
68+
}
69+
}
4870
}
4971
}

src/SmoFacade/Server.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ public void Restore(IEnumerable<BackupMetadata> backupOrder, string databaseName
132132
var restore = new Restore { Database = databaseName, NoRecovery = true };
133133

134134
foreach(var backup in backupOrder) {
135-
var device = BackupFileTools.IsUrl(backup.PhysicalDeviceName) ? DeviceType.Url : DeviceType.File;
135+
var device = BackupFileTools.IsValidFileUrl(backup.PhysicalDeviceName) ? DeviceType.Url : DeviceType.File;
136136
var backupDeviceItem = new BackupDeviceItem(backup.PhysicalDeviceName, device);
137137
if(_credentialName != null && device == DeviceType.Url)
138138
backupDeviceItem.CredentialName = _credentialName;
@@ -207,7 +207,7 @@ private void Backup(Backup backup, string backupDirectoryPathQuery, string datab
207207
var backupDirectory = BackupDirectoryOrDefault(backupDirectoryPathQuery);
208208
var filePath =
209209
$"{backupDirectory}/{databaseName}_backup_{DateTime.Now.ToString("yyyy_MM_dd_hhmmss_fff")}.{BackupFileTools.BackupTypeToExtension(type)}";
210-
var deviceType = BackupFileTools.IsUrl(filePath) ? DeviceType.Url : DeviceType.File;
210+
var deviceType = BackupFileTools.IsValidFileUrl(filePath) ? DeviceType.Url : DeviceType.File;
211211

212212
var bdi = new BackupDeviceItem(filePath, deviceType);
213213
if(_credentialName != null && deviceType == DeviceType.Url)

tests/AgDatabaseMove.Unit/FileToolsTest.cs

Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,38 +7,39 @@ namespace AgDatabaseMove.Unit
77

88
public class BackupFileToolsTest
99
{
10-
public static IEnumerable<object[]> UrlFileExamples => new List<object[]> {
11-
new object[] { "https://hello/a.bak" },
12-
new object[] { "https://hello/a.full" },
13-
new object[] { "https://storage-account.blob.core.windows.net/container/file.trn" },
14-
new object[] { "https://hello/a.diff" },
15-
new object[] { "https://a.diff" },
16-
new object[] { "https://1/2/3/4/5/a.diff" },
17-
new object[] { "https://storage-account.blob.core.windows.net/container/file.bad" },
18-
new object[]
19-
{ "https://storage-account.blob.core.windows.net/container/sql/db_name/backup_2020_09_02_170003_697.trn" },
20-
new object[] { "http://hello/a.bak" }
21-
};
22-
23-
public static IEnumerable<object[]> NonUrlFileExamples => new List<object[]> {
24-
new object[] { @"c:\hello\a.bak" },
25-
new object[] { @"\\abc\hello/a.bak" },
26-
new object[] { "https://storage-account.blob.core.windows.net/container" },
27-
new object[] { "http://storage-account.blob.core.windows.net/container" }
28-
};
2910

3011
[Theory]
31-
[MemberData(nameof(UrlFileExamples))]
32-
public void UrlFilesAreUrl(string file)
12+
[InlineData(@"https://hello/a.bak")]
13+
[InlineData(@"https://hello/a.full")]
14+
[InlineData(@"https://storage-account.blob.core.windows.net/container/file.trn")]
15+
[InlineData(@"https://hello/a.diff")]
16+
[InlineData(@"https://1/2/3/4/5/a.diff")]
17+
[InlineData(@"https://storage-account.blob.core.windows.net/container/file.bad")]
18+
[InlineData(@"https://storage-account.blob.core.windows.net/container/sql/db_name/backup_2020_09_02_170003_697.trn")]
19+
[InlineData(@"http://a.bak")]
20+
[InlineData(@"\\UNC\syntax\path\file.ext")]
21+
[InlineData(@"\\server\file.ext")]
22+
[InlineData(@"//Unix/syntax/file.ext")]
23+
public void ValidUrlTests(string url)
3324
{
34-
Assert.True(BackupFileTools.IsUrl(file));
25+
Assert.True(BackupFileTools.IsValidFileUrl(url));
3526
}
3627

3728
[Theory]
38-
[MemberData(nameof(NonUrlFileExamples))]
39-
public void NonUrlFilesAreNotUrl(string file)
29+
[InlineData(@"")]
30+
[InlineData(@" ")]
31+
[InlineData(@"c:\hello\a.bak")]
32+
[InlineData(@"\\C:/")]
33+
[InlineData(@"\wrongUNC\file.txt")]
34+
[InlineData(@"/wrongUNC/file.txt")]
35+
[InlineData(@"\\server\dir")]
36+
[InlineData(@"\\server\dir\")]
37+
[InlineData(@"https://storage-account.blob.core.windows.net/dir")]
38+
[InlineData(@"http://storage-account.blob.core.windows.net/dir")]
39+
[InlineData(@"http://storage-account.blob.core.windows.net/dir/")]
40+
public void InvalidUrlTests(string url)
4041
{
41-
Assert.False(BackupFileTools.IsUrl(file));
42+
Assert.False(BackupFileTools.IsValidFileUrl(url));
4243
}
4344

4445
[Theory]
@@ -58,5 +59,32 @@ public void BackupTypeAbbrevToType(string abbrev, BackupFileTools.BackupType typ
5859
{
5960
Assert.Equal(type, BackupFileTools.BackupTypeAbbrevToType(abbrev));
6061
}
62+
63+
[Theory]
64+
[InlineData(@"C:\dir\file.ext")]
65+
[InlineData(@"/some/file.ext")]
66+
67+
public void ValidPathTests(string path)
68+
{
69+
Assert.True(BackupFileTools.IsValidFilePath(path));
70+
}
71+
72+
[Theory]
73+
[InlineData(@"")]
74+
[InlineData(@" ")]
75+
[InlineData(@"/dir")]
76+
[InlineData(@"/")]
77+
[InlineData(@"file.ext")]
78+
[InlineData(@"dir\file.ext")]
79+
[InlineData(@"C dir\file.ext")]
80+
[InlineData(@"dir")]
81+
[InlineData(@"C:\dir\")]
82+
[InlineData(@"C:\dir")]
83+
[InlineData(@"C:\")]
84+
[InlineData(@"C:\inval|d")]
85+
public void InValidPathTests(string path)
86+
{
87+
Assert.False(BackupFileTools.IsValidFilePath(path));
88+
}
6189
}
6290
}

0 commit comments

Comments
 (0)