namespace dy.net.storage { public static class SafeLocalMigrationFile { public static bool TryResolve(string path, IEnumerable allowedRoots, out string fullPath, out string error) { fullPath = null; error = null; if (string.IsNullOrWhiteSpace(path)) { error = "本地路径为空"; return false; } try { fullPath = Path.GetFullPath(path); } catch (Exception ex) { error = "本地路径无效:" + ex.Message; return false; } var resolvedPath = fullPath; var root = (allowedRoots ?? Array.Empty()) .Select(Path.GetFullPath) .Where(candidate => IsWithin(resolvedPath, candidate)) .OrderByDescending(x => x.Length) .FirstOrDefault(); if (root == null) { error = "文件不在账号配置的旧存储根目录内"; return false; } if (!File.Exists(fullPath)) { error = "本地文件不存在"; return false; } try { for (var current = fullPath; !string.IsNullOrWhiteSpace(current); current = Path.GetDirectoryName(current)) { FileSystemInfo info = File.Exists(current) ? new FileInfo(current) : new DirectoryInfo(current); if (!string.IsNullOrWhiteSpace(info.LinkTarget)) { error = "旧文件路径包含符号链接,已拒绝迁移"; return false; } if (string.Equals(current, root, StringComparison.Ordinal)) break; } } catch (Exception ex) { error = "无法验证旧文件路径:" + ex.Message; return false; } return true; } public static bool IsWithin(string candidate, string root) { var fullCandidate = Path.GetFullPath(candidate).TrimEnd(Path.DirectorySeparatorChar); var fullRoot = Path.GetFullPath(root).TrimEnd(Path.DirectorySeparatorChar); return string.Equals(fullCandidate, fullRoot, StringComparison.Ordinal) || fullCandidate.StartsWith(fullRoot + Path.DirectorySeparatorChar, StringComparison.Ordinal); } } }