Skip to content

Commit

Permalink
properly handle whiteout opaque dirs when unpacking
Browse files Browse the repository at this point in the history
#267

If a file in the layer tarball is labeled foo/.wh..wh..opq, that indicates that
the existing contents of the directory foo/ should be emptied. We were
previously treating this the same as normal whiteout files (e.g. foo/.wh.myfile
means delete foo/myfile), which doesn't work properly.

With this commit, we're now able to delete a directory and recreate it in the
same layer, and have it be unpacked properly.

Signed-off-by: Esteban Foronda <[email protected]>
Co-authored-by: Aidan Oldershaw <[email protected]>
  • Loading branch information
aoldershaw authored and EstebanFS committed Jun 22, 2021
1 parent 17186cf commit 753290b
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 3 deletions.
22 changes: 20 additions & 2 deletions commands/unpack.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ import (

"github.com/concourse/go-archive/tarfs"
"github.com/fatih/color"
"github.com/google/go-containerregistry/pkg/v1"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/sirupsen/logrus"
"github.com/vbauerster/mpb"
"github.com/vbauerster/mpb/decor"
)

const whiteoutPrefix = ".wh."
const whiteoutOpaqueDir = whiteoutPrefix + whiteoutPrefix + ".opq"

func unpackImage(dest string, img v1.Image, debug bool, out io.Writer) error {
layers, err := img.Layers()
Expand Down Expand Up @@ -102,7 +103,24 @@ func extractLayer(dest string, layer v1.Layer, bar *mpb.Bar, chown bool) error {

log.Debug("unpacking")

if strings.HasPrefix(base, whiteoutPrefix) {
if base == whiteoutOpaqueDir {
fi, err := os.Lstat(dir)
if err != nil && !os.IsNotExist(err) {
return err
}

log.Debugf("removing contents of %s", dir)

if err := os.RemoveAll(dir); err != nil {
return err

}
if err := os.Mkdir(dir, fi.Mode()&os.ModePerm); err != nil {
return err
}

continue
} else if strings.HasPrefix(base, whiteoutPrefix) {
// layer has marked a file as deleted
name := strings.TrimPrefix(base, whiteoutPrefix)
removedPath := filepath.Join(dir, name)
Expand Down
8 changes: 8 additions & 0 deletions in_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,14 @@ var _ = Describe("In", func() {

stat, err = os.Stat(rootfsPath("top-dir-2"))
Expect(err).To(HaveOccurred())

infos, err = ioutil.ReadDir(rootfsPath("top-dir-3"))
Expect(err).ToNot(HaveOccurred())
Expect(infos).To(HaveLen(1))

stat, err = os.Stat(rootfsPath("top-dir-3", "nested-dir"))
Expect(err).ToNot(HaveOccurred())
Expect(stat.IsDir()).To(BeTrue())
})
})

Expand Down
2 changes: 1 addition & 1 deletion suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const OLDER_LIBRARY_DIGEST = "sha256:2131f09e4044327fd101ca1fd4043e6f3ad921ae7ee

// see testdata/static/Dockerfile
const OLDER_STATIC_DIGEST = "sha256:7dabedca9d367a71d1cd646bd8d79f14de7b07327e4417ab691f5f13be5647a9"
const LATEST_STATIC_DIGEST = "sha256:fc484c7e21a5616c600778d7ee720b3adfe1373c896be4f068c3da4a205d4a2e"
const LATEST_STATIC_DIGEST = "sha256:29eddd288c312215a0af1070d26478b1f530a3137d4589314df1bc79e586860a"

// see testdata/static.tagged/Dockerfile
const LATEST_TAGGED_STATIC_DIGEST = "sha256:ecfdc2527b0a5d7d134be55234590336209e7feafc2ec364a930adf4a9c722e2"
Expand Down
3 changes: 3 additions & 0 deletions testdata/whiteout/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ RUN touch top-dir-2/file-gone
RUN mkdir top-dir-2/nested-dir-gone
RUN touch top-dir-2/nested-dir-gone/nested-file-gone
RUN rm -rf top-dir-2
RUN mkdir -p top-dir-3/nested-dir-gone
RUN rm -r top-dir-3 && mkdir -p top-dir-3/nested-dir

# resulting file tree should be:
# /top-dir-1/nested-file
# /top-dir-1/nested-dir/file-here
# /top-dir-1/nested-dir/file-recreated
# /top-dir-1/nested-dir/file-then-dir
# /top-dir-3/nested-dir

0 comments on commit 753290b

Please sign in to comment.