diff --git a/pkg/v1/mutate/mutate.go b/pkg/v1/mutate/mutate.go index 7a1f59fed..c374f49a6 100644 --- a/pkg/v1/mutate/mutate.go +++ b/pkg/v1/mutate/mutate.go @@ -23,6 +23,7 @@ import ( "io" "io/ioutil" "path/filepath" + "runtime" "strings" "time" @@ -244,6 +245,8 @@ func extract(img v1.Image, w io.Writer) error { if err != nil { return fmt.Errorf("retrieving image layers: %w", err) } + + isWin := runtime.GOOS == "windows" // we iterate through the layers in reverse order because it makes handling // whiteout layers more efficient, since we can just keep track of the removed // files as we see .wh. layers and ignore those in previous layers. @@ -268,6 +271,14 @@ func extract(img v1.Image, w io.Writer) error { // name, we may have duplicate entries, which angers tar-split. header.Name = filepath.Clean(header.Name) + // tar.Next() sometimes mistakenly guesses format as USTAR, which creates a problem: + // if the header.Name is > 100 characters long, WriteHeader() returns an error like + // "archive/tar: cannot encode header: Format specifies USTAR; and USTAR cannot encode Name=...". + // To fix, change format from USTAR to PAX on Windows + if isWin && header.Format == tar.FormatUSTAR { + header.Format = tar.FormatPAX + } + basename := filepath.Base(header.Name) dirname := filepath.Dir(header.Name) tombstone := strings.HasPrefix(basename, whiteoutPrefix) @@ -297,7 +308,9 @@ func extract(img v1.Image, w io.Writer) error { // any entries with a matching (or child) name fileMap[name] = tombstone || !(header.Typeflag == tar.TypeDir) if !tombstone { - tarWriter.WriteHeader(header) + if err := tarWriter.WriteHeader(header); err != nil { + return err + } if header.Size > 0 { if _, err := io.CopyN(tarWriter, tarReader, header.Size); err != nil { return err