using System; using System.IO; using System.Linq; using System.Net.Http; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using Semmle.Util.Logging; namespace Semmle.Util { public static class FileUtils { public const string NugetExeUrl = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"; public static string ConvertToWindows(string path) { return path.Replace('/', '\\'); } public static string ConvertToUnix(string path) { return path.Replace('\\', '/'); } public static string ConvertToNative(string path) { return Path.DirectorySeparatorChar == '/' ? path.Replace('\\', '/') : path.Replace('/', '\\'); } /// /// Moves the source file to the destination, overwriting the destination file if /// it exists already. /// /// Source file. /// Target file. public static void MoveOrReplace(string src, string dest) { File.Move(src, dest, overwrite: true); } /// /// Attempt to delete the given file (ignoring I/O exceptions). /// /// The file to delete. public static void TryDelete(string file) { try { File.Delete(file); } catch (IOException) { // Ignore } } /// /// Finds the path for the program based on the /// PATH environment variable, and in the case of Windows the /// PATHEXT environment variable. /// /// Returns null of no path can be found. /// public static string? FindProgramOnPath(string prog) { var paths = Environment.GetEnvironmentVariable("PATH")?.Split(Path.PathSeparator); string[] exes; if (Win32.IsWindows()) { var extensions = Environment.GetEnvironmentVariable("PATHEXT")?.Split(';')?.ToArray(); exes = extensions is null || extensions.Any(prog.EndsWith) ? new[] { prog } : extensions.Select(ext => prog + ext).ToArray(); } else { exes = new[] { prog }; } var candidates = paths?.Where(path => exes.Any(exe0 => File.Exists(Path.Combine(path, exe0)))); return candidates?.FirstOrDefault(); } /// /// Computes the hash of . /// public static string ComputeFileHash(string filePath) { using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); using var shaAlg = SHA256.Create(); var sha = shaAlg.ComputeHash(fileStream); var hex = new StringBuilder(sha.Length * 2); foreach (var b in sha) hex.AppendFormat("{0:x2}", b); return hex.ToString(); } private static async Task DownloadFileAsync(string address, string filename) { using var httpClient = new HttpClient(); using var request = new HttpRequestMessage(HttpMethod.Get, address); using var contentStream = await (await httpClient.SendAsync(request)).Content.ReadAsStreamAsync(); using var stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true); await contentStream.CopyToAsync(stream); } /// /// Downloads the file at to . /// public static void DownloadFile(string address, string fileName) => DownloadFileAsync(address, fileName).Wait(); public static string NestPaths(ILogger logger, string? outerpath, string innerpath) { var nested = innerpath; if (!string.IsNullOrEmpty(outerpath)) { // Remove all leading path separators / or \ // For example, UNC paths have two leading \\ innerpath = innerpath.TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); if (innerpath.Length > 1 && innerpath[1] == ':') innerpath = innerpath[0] + "_" + innerpath.Substring(2); nested = Path.Combine(outerpath, innerpath); } try { var directoryName = Path.GetDirectoryName(nested); if (directoryName is null) { logger.Log(Severity.Warning, "Failed to get directory name from path '" + nested + "'."); throw new InvalidOperationException(); } Directory.CreateDirectory(directoryName); } catch (PathTooLongException) { logger.Log(Severity.Warning, "Failed to create parent directory of '" + nested + "': Path too long."); throw; } return nested; } private static readonly Lazy tempFolderPath = new Lazy(() => { var tempPath = Path.GetTempPath(); var name = Guid.NewGuid().ToString("N").ToUpper(); var tempFolder = Path.Combine(tempPath, "GitHub", name); Directory.CreateDirectory(tempFolder); return tempFolder; }); public static string GetTemporaryWorkingDirectory(Func getEnvironmentVariable, string lang, out bool shouldCleanUp) { var tempFolder = getEnvironmentVariable($"CODEQL_EXTRACTOR_{lang}_SCRATCH_DIR"); if (!string.IsNullOrEmpty(tempFolder)) { shouldCleanUp = false; return tempFolder; } shouldCleanUp = true; return tempFolderPath.Value; } public static string GetTemporaryWorkingDirectory(out bool shouldCleanUp) => GetTemporaryWorkingDirectory(Environment.GetEnvironmentVariable, "CSHARP", out shouldCleanUp); public static FileInfo CreateTemporaryFile(string extension, out bool shouldCleanUpContainingFolder) { var tempFolder = GetTemporaryWorkingDirectory(out shouldCleanUpContainingFolder); Directory.CreateDirectory(tempFolder); string outputPath; do { outputPath = Path.Combine(tempFolder, Path.GetRandomFileName() + extension); } while (File.Exists(outputPath)); File.Create(outputPath); return new FileInfo(outputPath); } } }