Skip to content

Apply directory metadata only after the directory was populated - #378

Merged
folbricht merged 2 commits into
masterfrom
fix-untar-readonly-dirs
Aug 4, 2026
Merged

Apply directory metadata only after the directory was populated#378
folbricht merged 2 commits into
masterfrom
fix-untar-readonly-dirs

Conversation

@folbricht

Copy link
Copy Markdown
Owner

Fixes #376.

desync untar failed with Error: <dir>: mkdirat <dir>: permission denied (or openat <file>: permission denied) when the archive contained a read-only directory that holds further directories or files:

mkdir -p test/test
chmod -R 0555 test
desync tar test.catar test
desync untar test.catar test2

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 optional Finalize() method, so the other FilesystemWriter implementations (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 xp rather than dating them to the time of extraction.

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
folbricht force-pushed the fix-untar-readonly-dirs branch from f287f39 to 4e6cb04 Compare August 3, 2026 12:36
- 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.
@folbricht
folbricht marked this pull request as ready for review August 4, 2026 07:05
@folbricht
folbricht merged commit 71822ae into master Aug 4, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

desync cannot untar read-only directories with contents

2 participants