cake icon indicating copy to clipboard operation
cake copied to clipboard

Zip doesn't preserve file permissions on Linux

Open johncrim opened this issue 6 years ago • 12 comments

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)

  1. 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
  1. Write a build.cake script to zip the directory:
Zip(Directory("d"), File("test.zip"));
  1. Install the cake.tool
dotnet tool install -g cake.tool
  1. Run the cake script
dotnet cake
  1. 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.

johncrim avatar Aug 05 '19 21:08 johncrim

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.

devlead avatar Aug 05 '19 21:08 devlead

Looks like you'd probably want to use the Mono.Posix.NETStandard NuGet package.

gitfool avatar Aug 11 '19 23:08 gitfool

@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.

devlead avatar Sep 04 '19 14:09 devlead

Link to the issue on the dotnet/runtime repo: Compression.ZipFile support for Unix Permissions.

augustoproiete avatar Oct 19 '20 01:10 augustoproiete

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?

hannahchan avatar Nov 19 '21 13:11 hannahchan

Which version of Cake?

devlead avatar Nov 19 '21 13:11 devlead

Which version of Cake?

I'm using the dotnet cake tool 1.3.0.

hannahchan avatar Nov 20 '21 00:11 hannahchan

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

augustoproiete avatar Nov 20 '21 16:11 augustoproiete

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.

hannahchan avatar Nov 21 '21 00:11 hannahchan

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 avatar Nov 28 '21 11:11 hannahchan

@hannahchan I think you're right. Something like this (untested) change should capture the unix file permissions.

gitfool avatar Nov 28 '21 18:11 gitfool

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.

hannahchan avatar Feb 07 '22 11:02 hannahchan