msbuild
msbuild copied to clipboard
Loss of Executable Bit When Extracting Archive with Unzip Task
I encountered an issue while using the Unzip task in the .NET MSBuild repository. I attempted to extract an archive containing executables for macOS and Linux, intending to use these executables in a subsequent task. However, the execution failed due to the loss of the executable bit during the extraction process.
Upon extracting the archive using the Unzip task, I observed that the permissions of the extracted files were not preserved correctly. Comparing the permissions of the extracted files with those extracted using the unzip -x command revealed a discrepancy:
- Files extracted with
unzip -xretained the executable bit (-r-xr-xr-xpermissions). - Files extracted with the
UnzipMSBuild task had incorrect permissions (-rw-r--r--).
I conducted further testing using .NET 8, specifically System.IO.Compression.ZipFile.ExtractToDirectory, which correctly preserved the executable bit.
I found a related fix in the .NET repository that addresses this issue: dotnet/runtime#55531.
Team triage: What version of the dotnet sdk are you using (you can use dotnet --version to see the version)?
I tested with 8.0.101
@JaynieBai can you please take a look at this, try to repro/investigate this?
The Unzip task does not use ZipFile.ExtractToDirectory; instead it enumerates the zip entries and creates the output files by calling File.Open: https://github.com/dotnet/msbuild/blob/e857a4e3330656ae5cd2f7f6fe18c511c7ae165a/src/Tasks/Unzip.cs#L231-L234
I presume this is to allow the task to be canceled, and to let the task log each extracted file to MSBuild.
System.IO.Compression.ZipFileExtensions.ExtractToFile uses the FileStream(string, FileStreamOptions) constructor that lets it specify FileStreamOptions.UnixCreateMode: https://github.com/dotnet/runtime/blob/5535e31a712343a63f5d7d796cd874e563e5ac14/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchiveEntry.Extract.cs#L81-L91
As ZipFileExtensions.ExtractToFile is a public API, I imagine the Unzip task could switch to using that. It's available on .NET Framework too. Because .NET Runtime does not restore execution permissions on Windows, it's OK that .NET Framework won't do that either. However, because there is no ZipFileExtensions.ExtractToFileAsync, this change would lose the ability to cancel the Unzip task during the extraction of a large file. Cancellation would still work between files.
Perhaps then, it's better to start using FileStream(string, FileStreamOptions) in the Unzip task and add #if to skip that on .NET Framework.