Created
February 1, 2019 23:12
-
-
Save mjs3339/4662b4f46d32a1f9441e592d31b76d96 to your computer and use it in GitHub Desktop.
C# Pathhelper, Analyze Path Construction Without Touching Path
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class Pathhelper | |
{ | |
[Flags] | |
[Serializable] | |
public enum DeviceTypes | |
{ | |
None = 0, | |
CDRom = 1, | |
InvalidCDRom = 2, | |
PhysicalDrive = 4, | |
InvalidPhysicalDrive = 8, | |
GUID = 16, | |
InvalidGUID = 32, | |
HDC = 64, | |
InvalidHDC = 128, | |
USB = 256, | |
InvalidUSB = 512, | |
Volume = 1024, | |
InvalidVolume = 2048, | |
Drive = 4096, | |
InvalidDrive = 8192 | |
} | |
[Flags] | |
[Serializable] | |
public enum FSTypes | |
{ | |
None = 0, | |
InvalidDrive = 1, | |
Drive = 2, | |
InvalidDirectory = 4, | |
Directory = 8, | |
InvalidFile = 16, | |
File = 32, | |
Device = 64, | |
InvalidPath = 128 | |
} | |
private static readonly char[] InvalidPathChars = | |
{ | |
'\"', '<', '>', '|', '\0', (char) 1, (char) 2, (char) 3, (char) 4, (char) 5, | |
(char) 6, (char) 7, (char) 8, (char) 9, (char) 10, (char) 11, (char) 12, (char) 13, | |
(char) 14, (char) 15, (char) 16, (char) 17, (char) 18, (char) 19, (char) 20, | |
(char) 21, (char) 22, (char) 23, (char) 24, (char) 25, (char) 26, (char) 27, | |
(char) 28, (char) 29, (char) 30, (char) 31, '*', '?' | |
}; | |
private static readonly char[] InvalidFileChars = | |
{ | |
'\"', '<', '>', '|', '\0', (char) 1, (char) 2, (char) 3, (char) 4, (char) 5, | |
(char) 6, (char) 7, (char) 8, (char) 9, (char) 10, (char) 11, (char) 12, (char) 13, | |
(char) 14, (char) 15, (char) 16, (char) 17, (char) 18, (char) 19, (char) 20, | |
(char) 21, (char) 22, (char) 23, (char) 24, (char) 25, (char) 26, (char) 27, | |
(char) 28, (char) 29, (char) 30, (char) 31, ':', '*', '?', '\\', '/' | |
}; | |
public static List<string> Path_Reason = new List<string>(); | |
public static List<string> Device_Reason = new List<string>(); | |
public static FSTypes PathTypes {get; private set;} | |
public static DeviceTypes DeviceType{get; private set;} | |
[DllImport("kernel32.dll", SetLastError = true)] | |
private static extern bool GetVolumeNameForVolumeMountPoint(string | |
lpszVolumeMountPoint, [Out] StringBuilder lpszVolumeName, | |
int cchBufferLength); | |
private static string GetVolumeName(string drive) | |
{ | |
var sb = new StringBuilder(1024); | |
if(!GetVolumeNameForVolumeMountPoint(drive, sb, sb.Capacity)) | |
return null; | |
return sb.ToString(); | |
} | |
private static Dictionary<string, string> GetValidMountPoints() | |
{ | |
var vols = new Dictionary<string, string>(); | |
for(var c = 'c'; c <= 'z'; c++) | |
{ | |
var r = GetVolumeName(c + @":\"); | |
if(r != null) | |
vols.Add(c.ToString(), r); | |
} | |
return vols; | |
} | |
private static string[] GetValidMountPointsGUIDs() | |
{ | |
return GetValidMountPoints().Select(p => IsolateGuid(p.Value)).Where(s => !string.IsNullOrEmpty(s)).ToArray(); | |
} | |
private static string IsolateGuid(string str) | |
{ | |
var s = string.Empty; | |
var index = str.IndexOf("{", StringComparison.Ordinal); | |
if(index != -1) | |
{ | |
var eindex = str.IndexOf("}", index + 1, StringComparison.Ordinal); | |
if(eindex != -1) | |
s = str.Substring(index, eindex - index + 1); | |
} | |
return s; | |
} | |
private static bool IsValidDriveChar(char value) | |
{ | |
if(value >= 'A' && value <= 'Z') | |
return true; | |
return value >= 'a' && value <= 'z'; | |
} | |
private static bool IsPathRooted(string path) | |
{ | |
if(path == null) | |
return false; | |
var length = path.Length; | |
return length >= 1 && (path[0] == '\\' || path[0] == '/') || length >= 2 && path[1] == ':'; | |
} | |
private static bool IsPathDevice(string path) | |
{ | |
return path.Length >= 4 && path[0] == '\\' && (path[1] == '\\' || path[1] == '?') && path[2] == '?' && path[3] == '\\' || path.Length >= 4 && path[0] == '\\' && path[1] == '\\' && (path[2] == '.' || path[2] == '?') && path[3] == '\\'; | |
} | |
private static void ParseDevice(string path) | |
{ | |
if(path.Length == 6) | |
if(path[5] == ':') | |
{ | |
if(IsValidDriveChar(path[4])) | |
DeviceType = DeviceTypes.Drive; | |
else | |
Path_Reason.Add($"Invalid drive character in Device path:{path[4]}{path[5]}"); | |
return; | |
} | |
if(path.IndexOf("volume", StringComparison.OrdinalIgnoreCase) != -1) | |
{ | |
var lid = IsolateGuid(path); | |
var vmp = GetValidMountPointsGUIDs(); | |
var PathContainsValidGUID = false; | |
if(!string.IsNullOrEmpty(lid)) | |
if(lid.Length == 38) | |
foreach(var mp in vmp) | |
if(mp.IndexOf(lid, StringComparison.OrdinalIgnoreCase) != -1) | |
{ | |
PathContainsValidGUID = true; | |
break; | |
} | |
if(PathContainsValidGUID) | |
{ | |
DeviceType = DeviceTypes.Volume; | |
} | |
else | |
{ | |
Path_Reason.Add($"Invalid GUID in Device path GUID:{lid}"); | |
DeviceType = DeviceTypes.InvalidVolume; | |
} | |
return; | |
} | |
if(path.IndexOf("hdc", StringComparison.OrdinalIgnoreCase) != -1) | |
DeviceType = DeviceTypes.HDC; | |
if(path.IndexOf("cdrom", StringComparison.OrdinalIgnoreCase) != -1) | |
DeviceType = DeviceTypes.CDRom; | |
if(path.IndexOf("PhysicalDrive", StringComparison.OrdinalIgnoreCase) != -1) | |
DeviceType = DeviceTypes.PhysicalDrive; | |
if(path.IndexOf("usb", StringComparison.OrdinalIgnoreCase) != -1) | |
DeviceType = DeviceTypes.USB; | |
var guid = IsolateGuid(path); | |
if(!string.IsNullOrEmpty(guid) && guid.Length == 38) | |
DeviceType = DeviceTypes.GUID; | |
} | |
public static bool ContainsDirectory(string path) | |
{ | |
if(path == null) | |
return false; | |
if(!IsValidPath(path)) | |
return false; | |
var pos = path.LastIndexOf('\\'); | |
if(pos == -1 || pos + 1 > path.Length) | |
return false; | |
if(IsPathRooted(path)) | |
{ | |
var dcsp = path.IndexOf(':') + 1; | |
var dn = path.Substring(dcsp, pos + 1 - dcsp); | |
if(dn.Length >= 1) | |
return true; | |
} | |
else | |
{ | |
var dn = path.Substring(0, pos + 1); | |
if(dn.Length >= 1) | |
return true; | |
} | |
return false; | |
} | |
public static bool ContainsRoot(string path) | |
{ | |
if(path == null) | |
return false; | |
if(!IsValidPath(path)) | |
return false; | |
return IsPathRooted(path); | |
} | |
public static bool ContainsFile(string path) | |
{ | |
if(path == null) | |
return false; | |
if(!IsValidPath(path)) | |
return false; | |
return GetFileName(path) != null; | |
} | |
public static string GetDirectoryName(string path) | |
{ | |
if(path == null) | |
return null; | |
if(!IsValidPath(path)) | |
return null; | |
var pos = path.LastIndexOf('\\'); | |
if(pos == -1 || pos + 1 > path.Length) | |
return null; | |
return path.Substring(0, pos + 1); | |
} | |
public static string GetOnlyDirectoryName(string path) | |
{ | |
if(path == null) | |
return null; | |
if(!IsValidPath(path)) | |
return null; | |
if(path.Length <= 3) | |
return null; | |
var pos = path.LastIndexOf('\\'); | |
if(pos == -1 || pos + 1 > path.Length) | |
return null; | |
if(!IsPathRooted(path)) | |
{ | |
if(pos != 2) | |
return path.Substring(0, pos + 1); | |
return path.Substring(0) + "\\"; | |
} | |
if(pos != 2) | |
return path.Substring(2, pos - 1); | |
return path.Substring(2) + "\\"; | |
} | |
private static bool IsValidPath(string path) | |
{ | |
Path_Reason.Clear(); | |
PathTypes = FSTypes.None; | |
DeviceType = DeviceTypes.None; | |
if(string.IsNullOrEmpty(path)) | |
{ | |
Path_Reason.Add("Path name is NULL or Empty."); | |
return false; | |
} | |
if(path.Trim() == string.Empty) | |
{ | |
Path_Reason.Add("Path name contains only white space."); | |
return false; | |
} | |
if(IsPathDevice(path)) | |
return true; | |
if(path.Length < 2) | |
{ | |
Path_Reason.Add("Path name is to short."); | |
return false; | |
} | |
if(!IsValidDriveChar(path[0])) | |
{ | |
Path_Reason.Add($"Path names Drive character contains invalid character:{path[0]}"); | |
return false; | |
} | |
if(path.Length >= 3) | |
{ | |
if(path[1] != ':') | |
{ | |
Path_Reason.Add("Path name is missing either a volume separator."); | |
return false; | |
} | |
if(path[2] != '\\') | |
{ | |
Path_Reason.Add("Path name is missing either a Directory separator."); | |
return false; | |
} | |
} | |
else | |
{ | |
if(path.Length >= 2) | |
if(path[1] != ':') | |
{ | |
Path_Reason.Add("Path name is missing either a volume separator."); | |
return false; | |
} | |
} | |
if(!IsPathRooted(path)) | |
{ | |
Path_Reason.Add("Path name is not rooted."); | |
return false; | |
} | |
return true; | |
} | |
private static string GetFileName(string path) | |
{ | |
if(!IsValidPath(path)) | |
return null; | |
if(path == null) | |
return null; | |
if(path.Length <= 3) | |
return null; | |
var length = path.Length; | |
for(var i = length; --i >= 0;) | |
{ | |
var ch = path[i]; | |
if(ch == '\\' || ch == '/' || ch == ':') | |
return path.Substring(i + 1, length - i - 1); | |
} | |
return null; | |
} | |
public static bool GetPathType(string path) | |
{ | |
if(!IsValidPath(path)) | |
{ | |
PathTypes = FSTypes.InvalidPath; | |
return false; | |
} | |
if(IsPathDevice(path)) | |
{ | |
ParseDevice(path); | |
PathTypes |= FSTypes.Device; | |
return true; | |
} | |
if(path.Length >= 3) | |
if(IsValidDriveChar(path[0])) | |
PathTypes |= FSTypes.Drive; | |
var d = GetDirectoryName(path); | |
if(d != null) | |
if(d.IndexOfAny(InvalidPathChars) >= 0) | |
{ | |
Path_Reason.Add($"Path name contains a directory name which contains invalid characters:'{d}'"); | |
PathTypes |= FSTypes.InvalidDirectory; | |
} | |
else | |
{ | |
PathTypes |= FSTypes.Directory; | |
} | |
var f = GetFileName(path); | |
if(!string.IsNullOrEmpty(f)) | |
if(f.IndexOfAny(InvalidFileChars) >= 0) | |
{ | |
Path_Reason.Add($"Path name contains a file name which contains invalid characters:'{f}'"); | |
PathTypes |= FSTypes.InvalidFile; | |
} | |
else | |
{ | |
PathTypes |= FSTypes.File; | |
} | |
if(!((PathTypes & FSTypes.InvalidDirectory) != 0 && (PathTypes & FSTypes.InvalidDrive) != 0 && (PathTypes & FSTypes.InvalidFile) != 0 && (PathTypes & FSTypes.InvalidPath) != 0)) | |
{ | |
Path_Reason.Add("Path name is Valid."); | |
return true; | |
} | |
Path_Reason.Add("Path name is InValid for an unknown reason."); | |
return false; | |
} | |
public static bool ContainsDrive(string path) | |
{ | |
GetPathType(path); | |
return(PathTypes & FSTypes.Drive) != 0; | |
} | |
public static bool IsDevice(string path) | |
{ | |
GetPathType(path); | |
return(PathTypes & FSTypes.Device) != 0; | |
} | |
public static bool IsValid(string path) | |
{ | |
GetPathType(path); | |
return(PathTypes & FSTypes.InvalidPath) == 0; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment