Skip to content

archive: Resolve hardlinks through absolute symlinks - #95

Closed
vvoland wants to merge 2 commits into
moby:mainfrom
vvoland:hardlinks
Closed

archive: Resolve hardlinks through absolute symlinks#95
vvoland wants to merge 2 commits into
moby:mainfrom
vvoland:hardlinks

Conversation

@vvoland

@vvoland vvoland commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

During #93 review this case was flagged by an AI review.
Adding this as a separate test.

The move to os.Root caused archive extraction to reject paths that traverse
absolute symlinks inside the destination. For example, given:

    var/run -> /run

os.Root interprets /run as an absolute host path and reports that the path
escapes the root. Archive extraction instead requires chroot-like semantics,
where absolute symlink targets are resolved relative to the extraction root.

When os.Root cannot traverse an entry's parent, resolve it with fsRootPath and
continue extraction using the resulting root-relative path. Leave the final
component unresolved because extraction may create or replace it.

Apply this handling to Unpack and UnpackLayer, including implied directories,
whiteouts, deferred directory timestamps, and opaque-whiteout path tracking.
The actual filesystem operations continue to use os.Root and remain confined
to the extraction destination.

This is a compatibility workaround that resolves paths separately from their
use. It should eventually be replaced with handle-relative operations that
provide resolve-in-root semantics.

A regression test was added, which fails before this patch;

    === RUN   TestUntarThroughAbsoluteSymlink
    === RUN   TestUntarThroughAbsoluteSymlink/existing_target
        archive_unix_test.go:558: assertion failed: error is not nil: statat var/run/existing/non-existing: path escapes from parent
    === RUN   TestUntarThroughAbsoluteSymlink/missing_target
        archive_unix_test.go:558: assertion failed: error is not nil: statat var/run/existing/non-existing: path escapes from parent
    --- FAIL: TestUntarThroughAbsoluteSymlink (0.00s)
        --- FAIL: TestUntarThroughAbsoluteSymlink/existing_target (0.00s)
        --- FAIL: TestUntarThroughAbsoluteSymlink/missing_target (0.00s)

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
@vvoland vvoland self-assigned this Jul 31, 2026
@vvoland vvoland changed the title archive: Test hardlinks through absolute symlinks archive: Resolve hardlinks through absolute symlinks Jul 31, 2026
Hardlink extraction failed when the source path crossed an absolute
symlink inside a container root because os.Root treated the target as
a host-rooted escape.

Resolve the validated source with extraction-root semantics before
linking, and reuse that bounded path when applying timestamps.

Signed-off-by: Paweł Gronowski <git@grono.dev>
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.

2 participants