Skip to content

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

Open
@vito

Description

@vito

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

Metadata

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