76 lines
3.5 KiB
C#
76 lines
3.5 KiB
C#
using System.Text.RegularExpressions;
|
||
|
||
namespace MatrixImageEditor;
|
||
|
||
internal static partial class ImagePathRules
|
||
{
|
||
public static string ValidateExistingImage(string imagePath)
|
||
{
|
||
var fullPath = ValidateImageNameAndDirectory(imagePath);
|
||
if (!File.Exists(fullPath)) throw new FileNotFoundException("镜像文件不存在", fullPath);
|
||
if (Directory.Exists(fullPath + ".sha256")) throw new IOException("同名 SHA-256 摘要路径被文件夹占用");
|
||
return fullPath;
|
||
}
|
||
|
||
public static string ValidateNewDestination(string sourcePath, string destinationPath)
|
||
{
|
||
var source = ValidateExistingImage(sourcePath);
|
||
var destination = ValidateImageNameAndDirectory(destinationPath);
|
||
if (destination.Equals(source, StringComparison.OrdinalIgnoreCase)) throw new ArgumentException("另存为必须使用与原镜像不同的文件名");
|
||
if (File.Exists(destination) || Directory.Exists(destination)) throw new IOException("目标镜像已经存在,请使用新的文件名");
|
||
|
||
var sidecar = destination + ".sha256";
|
||
if (File.Exists(sidecar) || Directory.Exists(sidecar)) throw new IOException("目标镜像的同名 SHA-256 摘要已经存在,请使用新的文件名");
|
||
return destination;
|
||
}
|
||
|
||
public static string? CleanupNewDestination(string destinationPath)
|
||
{
|
||
List<string>? failures = null;
|
||
foreach (var target in new[] { destinationPath + ".sha256", destinationPath })
|
||
{
|
||
try
|
||
{
|
||
if (File.Exists(target)) File.Delete(target);
|
||
}
|
||
catch (Exception exception)
|
||
{
|
||
failures ??= new List<string>();
|
||
failures.Add($"{Path.GetFileName(target)}:{exception.Message}");
|
||
}
|
||
}
|
||
return failures is null ? null : string.Join(";", failures);
|
||
}
|
||
|
||
private static string ValidateImageNameAndDirectory(string imagePath)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(imagePath)) throw new ArgumentException("镜像路径不能为空");
|
||
|
||
string fullPath;
|
||
try
|
||
{
|
||
fullPath = Path.GetFullPath(imagePath);
|
||
}
|
||
catch (Exception exception) when (exception is ArgumentException or NotSupportedException or PathTooLongException)
|
||
{
|
||
throw new ArgumentException("镜像路径格式无效", exception);
|
||
}
|
||
|
||
var fileName = Path.GetFileName(fullPath);
|
||
if (string.IsNullOrWhiteSpace(fileName)) throw new ArgumentException("镜像文件名不能为空");
|
||
if (fileName.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0 || fileName != fileName.TrimEnd(' ', '.'))
|
||
{
|
||
throw new ArgumentException("镜像文件名包含 Windows 不允许的字符,或以空格、句点结尾");
|
||
}
|
||
if (!Path.GetExtension(fileName).Equals(".img", StringComparison.OrdinalIgnoreCase)) throw new ArgumentException("镜像文件名必须以 .img 结尾");
|
||
if (ReservedWindowsName().IsMatch(Path.GetFileNameWithoutExtension(fileName))) throw new ArgumentException("镜像文件名使用了 Windows 保留名称,请更换名称");
|
||
|
||
var directory = Path.GetDirectoryName(fullPath);
|
||
if (string.IsNullOrEmpty(directory) || !Directory.Exists(directory)) throw new DirectoryNotFoundException("镜像目标文件夹不存在");
|
||
return fullPath;
|
||
}
|
||
|
||
[GeneratedRegex("^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(?:\\..*)?$", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||
private static partial Regex ReservedWindowsName();
|
||
}
|