Apply directory metadata only after the directory was populated - #378
Merged
Conversation
Untar failed with a permission error when an archive contained a read-only directory holding further files or directories. The mode from the archive was applied right after the directory was created, so populating it afterwards was no longer possible. Directory metadata (owner, xattrs, permissions and timestamps) is now deferred until the directory is complete, which is when an entry outside of it arrives or the archive ends. Directories that aren't writable and searchable by us are temporarily given those permissions while their contents are written, same as GNU tar does. This also fixes directory timestamps, which were previously overwritten by the writes of the very contents of the directory. Fixes #376
folbricht
force-pushed
the
fix-untar-readonly-dirs
branch
from
August 3, 2026 12:36
f287f39 to
4e6cb04
Compare
- Create directories with the archive mode plus the bits needed to write their contents, rather than 0777. A restrictive directory is no longer world-accessible for the duration of the extraction, and an aborted extraction doesn't leave one behind. - Keep node names slash-separated in the archive decoder. filepath.Dir rewrote the separators on Windows when leaving a directory, which made the paths inconsistent with those of the entries themselves. - Relax the permissions of an existing directory that isn't writable even when the archive permissions are ignored, so --no-same-permissions can extract over a read-only tree. - Don't touch permissions on Windows, where a chmod would clear the read-only attribute that is never restored. - Carry the setuid/setgid/sticky bits while a directory is populated, so the contents of a setgid directory inherit its group. - Give the deferred work a documented FilesystemFinalizer interface and keep the error of the metadata applied by Close(). - Apply the metadata of a completed directory before dropping it, so a failure can still be retried by Close(). The tests for directory timestamps now run on all platforms, since the deferral has to work with either path separator.
matshch
approved these changes
Aug 3, 2026
folbricht
marked this pull request as ready for review
August 4, 2026 07:05
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #376.
desync untarfailed withError: <dir>: mkdirat <dir>: permission denied(oropenat <file>: permission denied) when the archive contained a read-only directory that holds further directories or files:LocalFS.CreateDir()applied the mode from the archive immediately after creating the directory, making it impossible to write the contents that follow.Directory metadata (owner, xattrs, permissions and timestamps) is now deferred until the directory has been fully populated, which is the case when an entry outside of it arrives, or when the archive ends. Directories that aren't writable and searchable by the current user are temporarily given those permissions while their contents are being written, the same way GNU tar handles this.
UnTar()signals the end of the archive through an optionalFinalize()method, so the otherFilesystemWriterimplementations (tar, mtree) are unaffected.LocalFS.Close()applies whatever is still outstanding on a best-effort basis, for the case an extraction is aborted.Deferring the timestamps fixes a second issue found while testing this: directory mtimes were not preserved since writing the contents of a directory updates its mtime. GNU tar delays this for the same reason. Extracting a tree with directories dated 2020 now gives the same result as
tar xprather than dating them to the time of extraction.