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;
}
}
}