Skip to content

Error opening an image with 0-byte layer.tar files #367

@briandealwis

Description

@briandealwis

Using tarball.ImageFromPath() fails against an image with 0-byte layer.tar files. Although empty layers seem to be typically represented by 1024-byte files with all NULs, 0-byte layers are produced in the wild and Moby considers 0-byte layer.tar files are considered valid.

A test case is at: https://github.com/briandealwis/go-cr-bug. This test case is using a windows image produced as a test by bazelbuild/rules_docker

$ tar tvf bazel-out/darwin-fastbuild/bin/tests/docker/basic_windows_image.tar 
-rw-r--r--  0 0      0         907 31 Dec  1969 378fb1f1c5d59102b2b942e7c5cd76ce913cb5a4b74e97f87041dc61ab14b21e.json
-rw-r--r--  0 0      0           3 31 Dec  1969 0239ecd27132f0d70c7946a5365ef1f0a3edc7ecf6bdeeb61b4215598beb9828/VERSION
-rw-r--r--  0 0      0       10240 31 Dec  1969 0239ecd27132f0d70c7946a5365ef1f0a3edc7ecf6bdeeb61b4215598beb9828/layer.tar
-rw-r--r--  0 0      0         461 31 Dec  1969 0239ecd27132f0d70c7946a5365ef1f0a3edc7ecf6bdeeb61b4215598beb9828/json
-rw-r--r--  0 0      0           3 31 Dec  1969 54625cd3a0909e177f99d40c983676e65cd2ba5e3231670bd3b3a060463e384c/VERSION
-rw-r--r--  0 0      0           0 31 Dec  1969 54625cd3a0909e177f99d40c983676e65cd2ba5e3231670bd3b3a060463e384c/layer.tar
-rw-r--r--  0 0      0         250 31 Dec  1969 54625cd3a0909e177f99d40c983676e65cd2ba5e3231670bd3b3a060463e384c/json
-rw-r--r--  0 0      0           3 31 Dec  1969 fa2da5bcc17fb2e5c2025c1b76104bdd0f5b9ce64d6d4b29762e58aa11d97ee8/VERSION
-rw-r--r--  0 0      0           0 31 Dec  1969 fa2da5bcc17fb2e5c2025c1b76104bdd0f5b9ce64d6d4b29762e58aa11d97ee8/layer.tar
-rw-r--r--  0 0      0         178 31 Dec  1969 fa2da5bcc17fb2e5c2025c1b76104bdd0f5b9ce64d6d4b29762e58aa11d97ee8/json
-rw-r--r--  0 0      0         115 31 Dec  1969 repositories
-rw-r--r--  0 0      0        1037 31 Dec  1969 manifest.json

I stumbled across this bug trying to update bazelbuild/rules_docker to use container-structure-test from v1.4.0 to v1.7.0, which led to test failures. container-structure-test switched out to use use go-containerregistry with v1.6.0.

The issue is that tarball.ImageFromPath calls into go-containerregistry to open the Image, which checks if the image layers are compressed. This is determined by checking if the top-most layer IsGzipped.

// IsGzipped detects whether the input stream is compressed.
func IsGzipped(r io.Reader) (bool, error) {
magicHeader := make([]byte, 2)
if _, err := r.Read(magicHeader); err != nil {
return false, err
}
return bytes.Equal(magicHeader, gzipMagicHeader), nil
}

The io.Reader's Read call returns (0, io.EOF) and this error is cascaded back to the callers, even though this is a normal return.

The following fix just treats a 0-byte file as not being compressed. But given that empty layers are usually represented as 1024-byte NUL files, I suppose it's possible that there could be a 1-byte or 2-byte layer.tar file too?

diff --git a/pkg/v1/v1util/zip.go b/pkg/v1/v1util/zip.go
index 57514a4..2b0f24f 100644
--- a/pkg/v1/v1util/zip.go
+++ b/pkg/v1/v1util/zip.go
@@ -73,7 +73,11 @@ func GunzipReadCloser(r io.ReadCloser) (io.ReadCloser, error) {
 // IsGzipped detects whether the input stream is compressed.
 func IsGzipped(r io.Reader) (bool, error) {
 	magicHeader := make([]byte, 2)
-	if _, err := r.Read(magicHeader); err != nil {
+	n, err := r.Read(magicHeader)
+	if n == 0 && err == io.EOF {
+		return false, nil
+	}
+	if err != nil {
 		return false, err
 	}
 	return bytes.Equal(magicHeader, gzipMagicHeader), nil

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions