mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-20 15:44:25 +08:00
130 lines
4.6 KiB
C#
130 lines
4.6 KiB
C#
namespace LanDesktopPLONDS.Installer.Services;
|
|
|
|
public static class InstallerPathGuard
|
|
{
|
|
public static string GetDefaultInstallPath()
|
|
{
|
|
var programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
|
|
if (string.IsNullOrWhiteSpace(programFiles))
|
|
{
|
|
programFiles = Path.Combine(
|
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
|
"Programs");
|
|
}
|
|
|
|
return Path.Combine(programFiles, "LanMountainDesktop");
|
|
}
|
|
|
|
public static string NormalizeInstallPath(string path)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(path))
|
|
{
|
|
throw new ArgumentException("Installation path is required.", nameof(path));
|
|
}
|
|
|
|
var fullPath = Path.GetFullPath(path.Trim());
|
|
ValidateInstallPath(fullPath);
|
|
return fullPath;
|
|
}
|
|
|
|
public static void ValidateInstallPath(string path)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(path))
|
|
{
|
|
throw new InvalidOperationException("Installation path is required.");
|
|
}
|
|
|
|
var fullPath = Path.GetFullPath(path);
|
|
var root = Path.GetPathRoot(fullPath);
|
|
if (string.Equals(
|
|
fullPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar),
|
|
root?.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar),
|
|
StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
throw new InvalidOperationException("Choose a folder instead of a drive root.");
|
|
}
|
|
|
|
var blockedNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
"Windows",
|
|
"System32",
|
|
"SysWOW64",
|
|
"Program Files",
|
|
"Program Files (x86)",
|
|
"Users"
|
|
};
|
|
var name = Path.GetFileName(fullPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar));
|
|
if (blockedNames.Contains(name))
|
|
{
|
|
throw new InvalidOperationException("Choose a dedicated application folder.");
|
|
}
|
|
}
|
|
|
|
public static void EnsureUsableInstallPath(string path, long requiredBytes)
|
|
{
|
|
var fullPath = NormalizeInstallPath(path);
|
|
var directory = Directory.Exists(fullPath)
|
|
? new DirectoryInfo(fullPath)
|
|
: Directory.CreateDirectory(fullPath);
|
|
|
|
var testPath = Path.Combine(directory.FullName, $".write-test-{Guid.NewGuid():N}.tmp");
|
|
try
|
|
{
|
|
File.WriteAllText(testPath, string.Empty);
|
|
}
|
|
finally
|
|
{
|
|
if (File.Exists(testPath))
|
|
{
|
|
File.Delete(testPath);
|
|
}
|
|
}
|
|
|
|
var drive = new DriveInfo(directory.Root.FullName);
|
|
if (drive.AvailableFreeSpace > 0 && drive.AvailableFreeSpace < requiredBytes)
|
|
{
|
|
throw new InvalidOperationException("The selected drive does not have enough free space.");
|
|
}
|
|
}
|
|
|
|
public static void EnsureChildPath(string parent, string child)
|
|
{
|
|
if (!IsSameOrChildPath(parent, child))
|
|
{
|
|
throw new InvalidDataException($"Path escapes the expected root: {child}");
|
|
}
|
|
}
|
|
|
|
public static bool IsSameOrChildPath(string parent, string child)
|
|
{
|
|
var resolvedParent = Path.GetFullPath(parent)
|
|
.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
|
var resolvedChild = Path.GetFullPath(child);
|
|
return string.Equals(
|
|
resolvedParent,
|
|
resolvedChild.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar),
|
|
StringComparison.OrdinalIgnoreCase)
|
|
|| resolvedChild.StartsWith(resolvedParent + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)
|
|
|| resolvedChild.StartsWith(resolvedParent + Path.AltDirectorySeparatorChar, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
public static string NormalizeRelativePath(string relativePath)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(relativePath))
|
|
{
|
|
throw new InvalidDataException("Package entry path is empty.");
|
|
}
|
|
|
|
var normalized = relativePath
|
|
.Replace('\\', Path.DirectorySeparatorChar)
|
|
.Replace('/', Path.DirectorySeparatorChar)
|
|
.TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
|
if (Path.IsPathRooted(normalized) || normalized.Split(Path.DirectorySeparatorChar).Contains(".."))
|
|
{
|
|
throw new InvalidDataException($"Package entry path is invalid: {relativePath}");
|
|
}
|
|
|
|
return normalized;
|
|
}
|
|
}
|