Skip to content

mutate.Extract can fail for images which create a hard-link and later remove it #977

Open

Description

Hi there! My project has code to unpack an OCI image into a flat local rootfs directory, and I just noticed mutate.Extract exists for doing the same thing. I hoped to switch to it, but one of my tests started failing.

The current implementation of mutate.Extract unpacks the layers in reverse-order to avoid creating files which are later removed. I took this same approach initially and ran into the bug I'm raising here: if a target for a hard-link is created in one layer and then removed in a later layer, it fails to create any hard-links to it present in the earlier layers. (Something like that; sorry, it's been a while.)

My fix (concourse/registry-image-resource@ecf520b) was to un-optimize it and just unpack in the original order, removing files whenever it sees a whiteout. It was kind of a bummer.

This is low priority for me, just raising the issue in case someone else runs into the same error. This is a bit of an edge case so I'd understand if the performance trade-offs make it not worth fixing, but in my case I had to be compatible with everything; I'm happy to just keep maintaining my code for this. Feel free to just close this if you don't want to fix it.

Here's a Dockerfile that can demonstrate the issue:

FROM alpine
RUN apk --no-cache add git
RUN rm /usr/bin/git
A patch and test to run (in my project) to reproduce the error

Apply to registry-image resource:

diff --git a/commands/in.go b/commands/in.go
index b085c42..7fcc553 100644
--- a/commands/in.go
+++ b/commands/in.go
@@ -8,10 +8,12 @@ import (
        "os"
        "path/filepath"

+       "github.com/concourse/go-archive/tarfs"
        resource "github.com/concourse/registry-image-resource"
        "github.com/fatih/color"
        "github.com/google/go-containerregistry/pkg/name"
        v1 "github.com/google/go-containerregistry/pkg/v1"
+       "github.com/google/go-containerregistry/pkg/v1/mutate"
        "github.com/google/go-containerregistry/pkg/v1/remote"
        "github.com/google/go-containerregistry/pkg/v1/tarball"
        "github.com/sirupsen/logrus"
@@ -194,7 +196,7 @@ func ociFormat(dest string, tag name.Tag, image v1.Image) error {
 }

 func rootfsFormat(dest string, image v1.Image, debug bool, stderr io.Writer) error {
-       err := unpackImage(filepath.Join(dest, "rootfs"), image, debug, stderr)
+       err := tarfs.Extract(mutate.Extract(image), filepath.Join(dest, "rootfs"))
        if err != nil {
                return fmt.Errorf("extract image: %w", err)
        }

Test failure:

fetching concourse/test-image-removed-hardlinks@sha256:ee6f9ea0fe639ba0f30b3d6085f6c7813660a4de3e84bbec87c32e521d200dc9
ERRO[0000] download failed: save image: write rootfs: extract image: link /tmp/docker-image-in-dir081144662/rootfs/usr/bin/git /tmp/docker-image-in-dir081144662/rootfs/usr/bin/git-receive-pack: no such file or directory

• Failure in Spec Setup (JustBeforeEach) [0.827 seconds]
In
/home/vito/src/registry-image-resource/in_test.go:25
  a hardlink that is later removed [JustBeforeEach]
  /home/vito/src/registry-image-resource/in_test.go:213
    works
    /home/vito/src/registry-image-resource/in_test.go:222

    Unexpected error:
        <*exec.ExitError | 0xc000031060>: {
            ProcessState: {
                pid: 22284,
                status: 256,
                rusage: {
                    Utime: {Sec: 0, Usec: 104605},
                    Stime: {Sec: 0, Usec: 58114},
                    Maxrss: 28472,
                    Ixrss: 0,
                    Idrss: 0,
                    Isrss: 0,
                    Minflt: 1264,
                    Majflt: 0,
                    Nswap: 0,
                    Inblock: 0,
                    Oublock: 640,
                    Msgsnd: 0,
                    Msgrcv: 0,
                    Nsignals: 0,
                    Nvcsw: 1344,
                    Nivcsw: 40,
                },
            },
            Stderr: nil,
        }
        exit status 1
    occurred

    /home/vito/src/registry-image-resource/in_test.go:74
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions