| File: System\Formats\Tar\TarHelpers.Unix.cs | Web Access |
| Project: src\runtime\src\libraries\System.Formats.Tar\src\System.Formats.Tar.csproj (System.Formats.Tar) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.Diagnostics; using System.IO; namespace System.Formats.Tar { internal static partial class TarHelpers { private static readonly Lazy<UnixFileMode> s_umask = new Lazy<UnixFileMode>(DetermineUMask); private static UnixFileMode DetermineUMask() { // To determine the umask, we'll create a file with full permissions and see // what gets filtered out. // note: only the owner of a file, and root can change file permissions. const UnixFileMode OwnershipPermissions = UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute | UnixFileMode.GroupRead | UnixFileMode.GroupWrite | UnixFileMode.GroupExecute | UnixFileMode.OtherRead | UnixFileMode.OtherWrite | UnixFileMode.OtherExecute; string filename = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); FileStreamOptions options = new() { Mode = FileMode.CreateNew, UnixCreateMode = OwnershipPermissions, Options = FileOptions.DeleteOnClose, Access = FileAccess.Write, BufferSize = 0 }; using var fs = new FileStream(filename, options); UnixFileMode actual = File.GetUnixFileMode(fs.SafeFileHandle); return OwnershipPermissions & ~actual; } private sealed class ReverseStringComparer : IComparer<string> { public int Compare(string? x, string? y) => StringComparer.Ordinal.Compare(y, x); } private static readonly ReverseStringComparer s_reverseStringComparer = new(); private static UnixFileMode UMask => s_umask.Value; // Use a reverse-sorted dictionary to apply permission to children before their parents. // Otherwise we may apply a restrictive mask to the parent, that prevents us from changing a child. internal static SortedDictionary<string, UnixFileMode>? CreatePendingModesDictionary() => new SortedDictionary<string, UnixFileMode>(s_reverseStringComparer); internal static void CreateDirectory(string fullPath, UnixFileMode? mode, SortedDictionary<string, UnixFileMode>? pendingModes) { // Minimal permissions required for extracting. const UnixFileMode ExtractPermissions = UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute; Debug.Assert(pendingModes is not null); if (Directory.Exists(fullPath)) { // Apply permissions to an existing directory. if (mode.HasValue) { // Ensure we have sufficient permissions to extract in the directory. bool hasExtractPermissions = (mode.Value & ExtractPermissions) == ExtractPermissions; if (hasExtractPermissions) { pendingModes.Remove(fullPath); UnixFileMode umask = UMask; File.SetUnixFileMode(fullPath, mode.Value & ~umask); } else { pendingModes[fullPath] = mode.Value; } } return; } // If there are missing parents, Directory.CreateDirectory will create them using default permissions. if (mode.HasValue) { // Ensure we have sufficient permissions to extract in the directory. if ((mode.Value & ExtractPermissions) != ExtractPermissions) { pendingModes[fullPath] = mode.Value; mode = ExtractPermissions; } Directory.CreateDirectory(fullPath, mode.Value); } else { Directory.CreateDirectory(fullPath); } } internal static void SetPendingModes(SortedDictionary<string, UnixFileMode>? pendingModes) { Debug.Assert(pendingModes is not null); if (pendingModes.Count == 0) { return; } UnixFileMode umask = UMask; foreach (KeyValuePair<string, UnixFileMode> dir in pendingModes) { File.SetUnixFileMode(dir.Key, dir.Value & ~umask); } } } }