using System; using System.Collections.Generic; using System.IO; using Robust.Shared.Utility; namespace Robust.Shared.ContentPack { /// /// Utility functions /// internal static class PathHelpers { /// /// Get the full directory path that the executable is located in. /// internal static string GetExecutableDirectory() { // Fallback in case the above doesn't work ig? var assembly = typeof(PathHelpers).Assembly; var location = assembly.Location; if (location == string.Empty) { // See https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assembly.location?view=net-5.0#remarks // This doesn't apply to us really because we don't do that kind of publishing, but whatever. throw new InvalidOperationException("Cannot find path of executable."); } return Path.GetDirectoryName(location)!; } /// /// Turns a relative path from the executable directory into a full path. /// public static string ExecutableRelativeFile(string file) { return Path.GetFullPath(Path.Combine(GetExecutableDirectory(), file)); } /// /// Recursively gets all files in a directory and all sub directories. /// /// Directory to start in. /// Enumerable of all file paths in that directory and sub directories. public static IEnumerable GetFiles(string path) { return Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories); } public static bool IsFileInUse(IOException exception) { var errorCode = exception.HResult & 0xFFFF; return errorCode switch { // TODO: verify works on non-win systems 32 => /* sharing not allowed */ true, 33 => /* file is locked */ true, _ => false }; } // TODO: gaf public static bool IsFileSystemCaseSensitive() => !OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS(); internal static string SafeGetResourcePath(string baseDir, ResPath path) { var relSysPath = path.ToRelativeSystemPath(); // This also blocks files like "..foo.yml". But whatever, I CBF fixing that. if (relSysPath.Contains("\\..") || relSysPath.Contains("/..") || relSysPath.StartsWith("..")) { // Hard cap on any exploit smuggling a .. in there. // Since that could allow leaving sandbox. throw new InvalidOperationException($"This branch should never be reached. Path: {path}"); } var retPath = Path.GetFullPath(Path.Join(baseDir, relSysPath)); // better safe than sorry check if (!retPath.StartsWith(baseDir)) { // Allow path to match if it's just missing the directory separator at the end. if (retPath != baseDir.TrimEnd(Path.DirectorySeparatorChar)) throw new InvalidOperationException($"This branch should never be reached. Path: {path}"); } return retPath; } } }