runtime: Invalid UnixFileMode value when using the TarFile API

Description

I’m trying to exact a tar file into a folder, and the tar file may contains folders and files, when I try to use the TarFile.ExtractToDirectory API, I got the error like follows, maybe I’m wrong on using the API

Exception when invoke command System.ArgumentException: Invalid UnixFileMode value. (Parameter 'unixCreateMode')
   at System.IO.Directory.CreateDirectoryCore(String path, UnixFileMode unixCreateMode)
   at System.Formats.Tar.TarHelpers.CreateDirectory(String fullPath, Nullable`1 mode, SortedDictionary`2 pendingModes)
   at System.Formats.Tar.TarEntry.ExtractRelativeToDirectoryAsync(String destinationDirectoryPath, Boolean overwrite, SortedDictionary`2 pendingModes, CancellationToken cancellationToken)
   at System.Formats.Tar.TarFile.ExtractToDirectoryInternalAsync(Stream source, String destinationDirectoryPath, Boolean overwriteFiles, Boolean leaveOpen, CancellationToken cancellationToken)
   at System.Formats.Tar.TarFile.ExtractToDirectoryInternalAsync(Stream source, String destinationDirectoryPath, Boolean overwriteFiles, Boolean leaveOpen, CancellationToken cancellationToken)

Reproduction Steps

var tmpDirPath = Path.Combine(TmpPath, Guid.NewGuid().ToString("N"));
await var fs = File.OpenRead(@"xx.tar.gz");
await using var decompressStream = new GZipStream(fs, CompressionMode.Decompress);
await TarFile.ExtractToDirectoryAsync(decompressStream, tmpDirPath, true);

Expected behavior

Work as normal

Actual behavior

Exception as before

Regression?

No response

Known Workarounds

No response

Configuration

.NET 7 RC2 CentOS 7

Other information

Works well on Windows but not on CentOS

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 19 (19 by maintainers)

Most upvoted comments

I am not sure if it belongs to producer of the package (https://github.com/golang/go) or its consumer (https://github.com/moby/moby).

The issue will be fixed in the next version of Docker: https://github.com/moby/moby/pull/44083.

The patch for UnixFileMode property looks good to me.

It seems like other tools are writing mode with higher values, but we are constraining it.

Probably the spec says this should store st_mode which is UnixFileMode + file type (similar to zip ExternalAttributes). We should update the TarWriter so it matches the spec.

I don’t see this. I get the same exception when doing

My bad. I had a modification in Directory.Unix.cs, which was skipping the exception. Yes it is there on the main without GZipStream.

I’m trying to pull the docker image with the docker registry API

Thanks. Perhaps it is archive/tar golang package which generates such a tarball? I was able to reproduce it with a 20K image:

$ docker pull hello-world
$ docker save hello-world -o hello-world.tar # uncompressed when saving a local image

# extract with your code without GZipStream step

I’ll open a PR in runtime-assets repo first so we can add test with the fix in this repo.

Here, the directory mode is 16877, but we should just discard the top four bits.

This patch fixes the issue (and passes all the existing tests):

--- a/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.cs
+++ b/src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarEntry.cs

         /// <remarks>The value in this field has no effect on Windows platforms.</remarks>
         public UnixFileMode Mode
         {
-            get => (UnixFileMode)_header._mode;
+            get => (UnixFileMode)(_header._mode  & 0xFFF); // mask to keep lower 12 bits and clear out the rest
             set
             {
-                if ((int)value is < 0 or > 4095) // 4095 in decimal is 7777 in octal
+                if ((int)value is < 0 or > 0xFFF) // 0xFFF (4095 in decimal) is 7777 in octal
                 {

No problem, with the repro file they can easily do it.