-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Tar: restore directory permissions while extracting. (#72078)
* Tar: restore directory permissions while extracting. * PR feedback. * On Windows, as on Unix: don't set group/other write permission by default. * Fix Windows compilation. * Fix Windows compilation II. * Update test for Windows. * Fix test WindowsFileMode value. * Remove branch. * Apply suggestions from code review Co-authored-by: Eric Erhardt <[email protected]> * Add back DefaultWindowsMode. * Fix build failure in TarWriter due to DefaultWindowsMode usage. * Fix DefaultWindowsMode. * Apply suggestions from code review Co-authored-by: Carlos Sanchez <[email protected]> Co-authored-by: Eric Erhardt <[email protected]> Co-authored-by: Carlos Sanchez <[email protected]>
- Loading branch information
1 parent
1ae7c5f
commit 820ffd4
Showing
16 changed files
with
646 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
149 changes: 149 additions & 0 deletions
149
src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHelpers.Unix.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
// 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.IO; | ||
using System.Diagnostics; | ||
|
||
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; | ||
|
||
/* | ||
Tar files are usually ordered: parent directories come before their child entries. | ||
They may be unordered. In that case we need to create parent directories before | ||
we know the proper permissions for these directories. | ||
We create these directories with restrictive permissions. If we encounter an entry for | ||
the directory later, we store the mode to apply it later. | ||
If the archive doesn't have an entry for the parent directory, we use the default mask. | ||
The pending modes to be applied are tracked through a reverse-sorted dictionary. | ||
The reverse order is needed to apply permissions to children before their parent. | ||
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, bool overwriteMetadata, SortedDictionary<string, UnixFileMode>? pendingModes) | ||
{ | ||
// Restrictive mask for creating the missing parent directories while 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 when we're overwriting metadata | ||
// or the directory was created as a missing parent (stored in pendingModes). | ||
if (mode.HasValue) | ||
{ | ||
bool hasExtractPermissions = (mode.Value & ExtractPermissions) == ExtractPermissions; | ||
if (hasExtractPermissions) | ||
{ | ||
bool removed = pendingModes.Remove(fullPath); | ||
if (overwriteMetadata || removed) | ||
{ | ||
UnixFileMode umask = UMask; | ||
File.SetUnixFileMode(fullPath, mode.Value & ~umask); | ||
} | ||
} | ||
else if (overwriteMetadata || pendingModes.ContainsKey(fullPath)) | ||
{ | ||
pendingModes[fullPath] = mode.Value; | ||
} | ||
} | ||
return; | ||
} | ||
|
||
if (mode.HasValue) | ||
{ | ||
// Ensure we have sufficient permissions to extract in the directory. | ||
if ((mode.Value & ExtractPermissions) != ExtractPermissions) | ||
{ | ||
pendingModes[fullPath] = mode.Value; | ||
mode = ExtractPermissions; | ||
} | ||
} | ||
else | ||
{ | ||
pendingModes.Add(fullPath, DefaultDirectoryMode); | ||
mode = ExtractPermissions; | ||
} | ||
|
||
string parentDir = Path.GetDirectoryName(fullPath)!; | ||
string rootDir = Path.GetPathRoot(parentDir)!; | ||
bool hasMissingParents = false; | ||
for (string dir = parentDir; dir != rootDir && !Directory.Exists(dir); dir = Path.GetDirectoryName(dir)!) | ||
{ | ||
pendingModes.Add(dir, DefaultDirectoryMode); | ||
hasMissingParents = true; | ||
} | ||
|
||
if (hasMissingParents) | ||
{ | ||
Directory.CreateDirectory(parentDir, ExtractPermissions); | ||
} | ||
|
||
Directory.CreateDirectory(fullPath, mode.Value); | ||
} | ||
|
||
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); | ||
} | ||
} | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarHelpers.Windows.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// 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.IO; | ||
using System.Text; | ||
using System.Diagnostics; | ||
|
||
namespace System.Formats.Tar | ||
{ | ||
internal static partial class TarHelpers | ||
{ | ||
internal static SortedDictionary<string, UnixFileMode>? CreatePendingModesDictionary() | ||
=> null; | ||
|
||
internal static void CreateDirectory(string fullPath, UnixFileMode? mode, bool overwriteMetadata, SortedDictionary<string, UnixFileMode>? pendingModes) | ||
=> Directory.CreateDirectory(fullPath); | ||
|
||
internal static void SetPendingModes(SortedDictionary<string, UnixFileMode>? pendingModes) | ||
=> Debug.Assert(pendingModes is null); | ||
} | ||
} |
Oops, something went wrong.