msbuild icon indicating copy to clipboard operation
msbuild copied to clipboard

Loss of Executable Bit When Extracting Archive with Unzip Task

Open pepone opened this issue 1 year ago • 4 comments

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 -x retained the executable bit (-r-xr-xr-x permissions).
  • Files extracted with the Unzip MSBuild 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.

pepone avatar Mar 28 '24 16:03 pepone

Team triage: What version of the dotnet sdk are you using (you can use dotnet --version to see the version)?

AR-May avatar Apr 02 '24 13:04 AR-May

I tested with 8.0.101

pepone avatar Apr 02 '24 14:04 pepone

@JaynieBai can you please take a look at this, try to repro/investigate this?

AR-May avatar Apr 02 '24 15:04 AR-May

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.

KalleOlaviNiemitalo avatar Apr 11 '24 15:04 KalleOlaviNiemitalo