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? failures = null; foreach (var target in new[] { destinationPath + ".sha256", destinationPath }) { try { if (File.Exists(target)) File.Delete(target); } catch (Exception exception) { failures ??= new List(); 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(); }