-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Compression.ZipFile support for Unix Permissions #55531
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
60c64ca
Compression.ZipFile support for Unix Permissions
eerhardt d52fce0
Add unit tests
eerhardt d4f1821
Fix build
eerhardt db4f43a
Fix windows build
eerhardt 1d012c5
Add Browser TFM
eerhardt f7f2078
Fix tests on wasm
eerhardt 04e929e
PR feedback. Add more ZipFile tests
eerhardt fe69ae7
Fix build
eerhardt 5b57040
Fix test on wasm
eerhardt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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
16 changes: 16 additions & 0 deletions
16
...Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchive.Create.Unix.cs
This file contains hidden or 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,16 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| namespace System.IO.Compression | ||
| { | ||
| public static partial class ZipFileExtensions | ||
| { | ||
| static partial void SetExternalAttributes(FileStream fs, ZipArchiveEntry entry) | ||
| { | ||
| Interop.Sys.FileStatus status; | ||
| Interop.CheckIo(Interop.Sys.FStat(fs.SafeFileHandle, out status), fs.Name); | ||
|
|
||
| entry.ExternalAttributes |= status.Mode << 16; | ||
eerhardt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
| } | ||
This file contains hidden or 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
29 changes: 29 additions & 0 deletions
29
...ssion.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchiveEntry.Extract.Unix.cs
This file contains hidden or 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,29 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| namespace System.IO.Compression | ||
| { | ||
| public static partial class ZipFileExtensions | ||
| { | ||
| static partial void ExtractExternalAttributes(FileStream fs, ZipArchiveEntry entry) | ||
| { | ||
| // Only extract USR, GRP, and OTH file permissions, and ignore | ||
| // S_ISUID, S_ISGID, and S_ISVTX bits. This matches unzip's default behavior. | ||
| // It is off by default because of this comment: | ||
|
|
||
| // "It's possible that a file in an archive could have one of these bits set | ||
| // and, unknown to the person unzipping, could allow others to execute the | ||
| // file as the user or group. The new option -K bypasses this check." | ||
| const int ExtractPermissionMask = 0x1FF; | ||
| int permissions = (entry.ExternalAttributes >> 16) & ExtractPermissionMask; | ||
|
|
||
| // If the permissions weren't set at all, don't write the file's permissions, | ||
| // since the .zip could have been made using a previous version of .NET, which didn't | ||
| // include the permissions, or was made on Windows. | ||
| if (permissions != 0) | ||
| { | ||
| Interop.CheckIo(Interop.Sys.FChMod(fs.SafeFileHandle, permissions), fs.Name); | ||
| } | ||
| } | ||
| } | ||
| } |
This file contains hidden or 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 hidden or 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 hidden or 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
159 changes: 159 additions & 0 deletions
159
src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Unix.cs
This file contains hidden or 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,159 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System.Text; | ||
| using Xunit; | ||
|
|
||
| namespace System.IO.Compression.Tests | ||
| { | ||
| public class ZipFile_Unix : ZipFileTestBase | ||
eerhardt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| { | ||
| [Fact] | ||
| public void UnixCreateSetsPermissionsInExternalAttributes() | ||
| { | ||
| // '7600' tests that S_ISUID, S_ISGID, and S_ISVTX bits get preserved in ExternalAttributes | ||
| string[] testPermissions = new[] { "777", "755", "644", "600", "7600" }; | ||
|
|
||
| using (var tempFolder = new TempDirectory(Path.Combine(GetTestFilePath(), "testFolder"))) | ||
| { | ||
| foreach (string permission in testPermissions) | ||
| { | ||
| CreateFile(tempFolder.Path, permission); | ||
| } | ||
|
|
||
| string archivePath = GetTestFilePath(); | ||
| ZipFile.CreateFromDirectory(tempFolder.Path, archivePath); | ||
|
|
||
| using (ZipArchive archive = ZipFile.OpenRead(archivePath)) | ||
| { | ||
| Assert.Equal(5, archive.Entries.Count); | ||
|
|
||
| foreach (ZipArchiveEntry entry in archive.Entries) | ||
| { | ||
| Assert.EndsWith(".txt", entry.Name, StringComparison.Ordinal); | ||
| EnsureExternalAttributes(entry.Name.Substring(0, entry.Name.Length - 4), entry); | ||
| } | ||
|
|
||
| void EnsureExternalAttributes(string permissions, ZipArchiveEntry entry) | ||
| { | ||
| Assert.Equal(Convert.ToInt32(permissions, 8), (entry.ExternalAttributes >> 16) & 0xFFF); | ||
| } | ||
| } | ||
|
|
||
| // test that round tripping the archive has the same file permissions | ||
| using (var extractFolder = new TempDirectory(Path.Combine(GetTestFilePath(), "extract"))) | ||
| { | ||
| ZipFile.ExtractToDirectory(archivePath, extractFolder.Path); | ||
|
|
||
| foreach (string permission in testPermissions) | ||
| { | ||
| string filename = Path.Combine(extractFolder.Path, permission + ".txt"); | ||
| Assert.True(File.Exists(filename)); | ||
|
|
||
| EnsureFilePermissions(filename, permission); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| [Fact] | ||
| public void UnixExtractSetsFilePermissionsFromExternalAttributes() | ||
| { | ||
| // '7600' tests that S_ISUID, S_ISGID, and S_ISVTX bits don't get extracted to file permissions | ||
| string[] testPermissions = new[] { "777", "755", "644", "754", "7600" }; | ||
| byte[] contents = Encoding.UTF8.GetBytes("contents"); | ||
|
|
||
| string archivePath = GetTestFilePath(); | ||
| using (FileStream fileStream = new FileStream(archivePath, FileMode.CreateNew)) | ||
| using (ZipArchive archive = new ZipArchive(fileStream, ZipArchiveMode.Create)) | ||
| { | ||
| foreach (string permission in testPermissions) | ||
| { | ||
| ZipArchiveEntry entry = archive.CreateEntry(permission + ".txt"); | ||
| entry.ExternalAttributes = Convert.ToInt32(permission, 8) << 16; | ||
| using Stream stream = entry.Open(); | ||
| stream.Write(contents); | ||
| stream.Flush(); | ||
| } | ||
| } | ||
|
|
||
| using (var tempFolder = new TempDirectory(GetTestFilePath())) | ||
| { | ||
| ZipFile.ExtractToDirectory(archivePath, tempFolder.Path); | ||
|
|
||
| foreach (string permission in testPermissions) | ||
| { | ||
| string filename = Path.Combine(tempFolder.Path, permission + ".txt"); | ||
| Assert.True(File.Exists(filename)); | ||
|
|
||
| EnsureFilePermissions(filename, permission); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private static void CreateFile(string folderPath, string permissions) | ||
| { | ||
| string filename = Path.Combine(folderPath, $"{permissions}.txt"); | ||
| File.WriteAllText(filename, "contents"); | ||
|
|
||
| Assert.Equal(0, Interop.Sys.ChMod(filename, Convert.ToInt32(permissions, 8))); | ||
| } | ||
|
|
||
| private static void EnsureFilePermissions(string filename, string permissions) | ||
| { | ||
| Interop.Sys.FileStatus status; | ||
| Assert.Equal(0, Interop.Sys.Stat(filename, out status)); | ||
|
|
||
| // note that we don't extract S_ISUID, S_ISGID, and S_ISVTX bits, | ||
| // so only use the last 3 numbers of permissions to verify the file permissions | ||
| permissions = permissions.Length > 3 ? permissions.Substring(permissions.Length - 3) : permissions; | ||
| Assert.Equal(Convert.ToInt32(permissions, 8), status.Mode & 0xFFF); | ||
| } | ||
|
|
||
| [Theory] | ||
| [InlineData("sharpziplib.zip", null)] // ExternalAttributes are not set in this .zip, use the system default | ||
| [InlineData("Linux_RW_RW_R__.zip", "664")] | ||
| [InlineData("Linux_RWXRW_R__.zip", "764")] | ||
| [InlineData("OSX_RWXRW_R__.zip", "764")] | ||
| public void UnixExtractFilePermissionsCompat(string zipName, string expectedPermissions) | ||
| { | ||
| expectedPermissions = GetExpectedPermissions(expectedPermissions); | ||
|
|
||
| string zipFileName = compat(zipName); | ||
| using (var tempFolder = new TempDirectory(GetTestFilePath())) | ||
| { | ||
| ZipFile.ExtractToDirectory(zipFileName, tempFolder.Path); | ||
|
|
||
| using ZipArchive archive = ZipFile.Open(zipFileName, ZipArchiveMode.Read); | ||
| foreach (ZipArchiveEntry entry in archive.Entries) | ||
| { | ||
| string filename = Path.Combine(tempFolder.Path, entry.FullName); | ||
| Assert.True(File.Exists(filename), $"File '{filename}' should exist"); | ||
|
|
||
| EnsureFilePermissions(filename, expectedPermissions); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private static string GetExpectedPermissions(string expectedPermissions) | ||
| { | ||
| if (string.IsNullOrEmpty(expectedPermissions)) | ||
| { | ||
| // Create a new file, and get its permissions to get the current system default permissions | ||
|
|
||
| using (var tempFolder = new TempDirectory()) | ||
| { | ||
| string filename = Path.Combine(tempFolder.Path, Path.GetRandomFileName()); | ||
| File.WriteAllText(filename, "contents"); | ||
|
|
||
| Interop.Sys.FileStatus status; | ||
| Assert.Equal(0, Interop.Sys.Stat(filename, out status)); | ||
|
|
||
| expectedPermissions = Convert.ToString(status.Mode & 0xFFF, 8); | ||
| } | ||
| } | ||
|
|
||
| return expectedPermissions; | ||
| } | ||
| } | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.