cake
cake copied to clipboard
Zip doesn't preserve file permissions on Linux
What You Are Seeing?
Using something like:
Zip(Directory("d"), File("test.zip"));
The zip file contents are correct, but linux file permissions (ugoa rwx) are not preserved within the zip file, when run on Linux.
What is Expected?
I should be able to list the zip file contents using unzip -Zl and see the linux file permissions that were set before zipping.
What version of Cake are you using?
dotnet core cake.tool version '0.34.1'
Are you running on a 32 or 64 bit system?
64 bit
What environment are you running on? Windows? Linux? Mac?
Linux
Are you running on a CI Server? If so, which one?
Yes, but I see the same results running locally.
How Did You Get This To Happen? (Steps to Reproduce)
- Create a script file on Linux:
mkdir d
echo '#!/bin/bash' > d/test.sh
echo "echo foo" >> d/test.sh
chmod a+x d/test.sh
- Write a build.cake script to zip the directory:
Zip(Directory("d"), File("test.zip"));
- Install the cake.tool
dotnet tool install -g cake.tool
- Run the cake script
dotnet cake
- Check the contents of the zipped file:
$ unzip -Zl test.zip
Archive: test.zip
Zip file size: 135 bytes, number of entries: 1
?--------- 2.0 unx 21 b- 23 defN 19-Aug-05 14:13 test.sh
1 file, 21 bytes uncompressed, 23 bytes compressed: -9.5%
In contrast, if the zip file is created with the linux zip command (infozip), the file contents are:
$ unzip -Zl linux-test.zip
Archive: linux-test.zip
Zip file size: 185 bytes, number of entries: 1
-rwxrwxrwx 3.0 unx 21 tx 21 stor 19-Aug-05 14:13 test.sh
1 file, 21 bytes uncompressed, 21 bytes compressed: 0.0%
which shows that the file permissions are preserved in the zip file.
AFAIK as I know this is partially a limitation of .NET ZipEntry ( see dotnet/corefx#17342 )
There's work arounds i.e.
const int rw_rw_r = (0x8000 + 0x0100 + 0x0080 + 0x0020 + 0x0010 + 0x0004) << 16;
entry.ExternalAttributes = rw_rw_r;
would set read write / read write / read,
Code for zipping in cake is located here https://github.com/cake-build/cake/blob/develop/src/Cake.Common/IO/Zipper.cs#L165-L192
Issue would be to in a clean way identify running on posix and identify correct file permissions for each file while guaranteeing to work with existing scripts.
Looks like you'd probably want to use the Mono.Posix.NETStandard NuGet package.
- Source: mono/mono/tree/master/mcs/class/Mono.Posix
- FilePermissions: search FilePermissions path:mcs/class/Mono.Posix
@gitfool yip probably, but unsure/need to evaluate if we want to take that dependency, probably a good use case to test in an addin first and promote to Cake itself once battle tested.
Link to the issue on the dotnet/runtime repo: Compression.ZipFile support for Unix Permissions.
It looks like Compression.ZipFile support for Unix Permissions made it into .NET 6.
https://github.com/dotnet/runtime/pull/55531
I tried running my Cake script on a dev container with only the .NET 6 SDK installed and my files still lose their Unix permissions when zipped. Any ideas here?
Which version of Cake?
Which version of Cake?
I'm using the dotnet cake tool 1.3.0.
Is .NET 6 active when you run dotnet --info on the same folder where you run dotnet cake?
Perhaps you have a global.json pining .NET to a prior version?
You might also want to try Cake.Tool 2.0.0-rc0001 which also targets net6.0 to see if there's any difference
This is the result when I run dotnet --info.
.NET SDK (reflecting any global.json):
Version: 6.0.100
Commit: 9e8b04bbff
Runtime Environment:
OS Name: debian
OS Version: 11
OS Platform: Linux
RID: debian.11-x64
Base Path: /usr/share/dotnet/sdk/6.0.100/
Host (useful for support):
Version: 6.0.0
Commit: 4822e3c3aa
.NET SDKs installed:
6.0.100 [/usr/share/dotnet/sdk]
.NET runtimes installed:
Microsoft.AspNetCore.App 6.0.0 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 6.0.0 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
To install additional .NET runtimes or SDKs:
https://aka.ms/dotnet-download
I have just tried running the exact steps posted above by @johncrim using both the Cake.Tool 1.3.0 and 2.0.0-rc0001 in my environment and it appears they still lose the permissions.
I haven't had the chance to look closely at the Cake code for the Zip function or the new/reworked API in .NET 6. I might try to do that after the weekend.
So I finally got a bit of time to look at this again and can confirm the changes for Unix Permissions did make it into .NET 6 however not for every API and this happens to be the one Cake is using, ZipArchive.CreateEntry().
From the associated pull request; https://github.com/dotnet/runtime/pull/55531/files, it looks like only the extension methods have been changed. This is the code to the extension method I think we would be interested in. It feels pretty similar to the code below that is in Cake.
https://github.com/cake-build/cake/blob/c0364c3894c44e792c1be81b684c2215cd838c90/src/Cake.Common/IO/Zipper.cs#L184-L195
I'm wondering if it would be better the CreateEntryFromFile() extension method was used instead?
@hannahchan I think you're right. Something like this (untested) change should capture the unix file permissions.
Thought I should share. I've been using System.IO.Compression.ZipFile.CreateFromDirectory() directly in my Cake script to get around this. We still might still want to update the wrapper for this in Cake.