mirror of
https://github.com/moby/moby.git
synced 2025-04-18 20:44:11 +03:00
vendor: github.com/moby/go-archive v0.1.0
full diff: https://github.com/moby/go-archive/compare/21f3f3385ab7...v0.1.0 Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
c58a765707
commit
fd550344b1
@ -9,8 +9,8 @@ import (
|
||||
"github.com/docker/docker/pkg/longpath"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/pkg/tarsum"
|
||||
"github.com/moby/go-archive"
|
||||
"github.com/moby/go-archive/chrootarchive"
|
||||
"github.com/moby/go-archive/compression"
|
||||
"github.com/moby/sys/symlink"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@ -66,7 +66,7 @@ func FromArchive(tarStream io.Reader) (builder.Source, error) {
|
||||
}
|
||||
}()
|
||||
|
||||
decompressedStream, err := archive.DecompressStream(tarStream)
|
||||
decompressedStream, err := compression.DecompressStream(tarStream)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import (
|
||||
"github.com/docker/docker/daemon/images"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
dockerarchive "github.com/moby/go-archive"
|
||||
"github.com/moby/go-archive/compression"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@ -230,7 +230,7 @@ func (i *ImageService) leaseContent(ctx context.Context, store content.Store, de
|
||||
// complement of ExportImage. The input stream is an uncompressed tar
|
||||
// ball containing images and metadata.
|
||||
func (i *ImageService) LoadImage(ctx context.Context, inTar io.ReadCloser, platform *ocispec.Platform, outStream io.Writer, quiet bool) error {
|
||||
decompressed, err := dockerarchive.DecompressStream(inTar)
|
||||
decompressed, err := compression.DecompressStream(inTar)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to decompress input tar archive")
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ import (
|
||||
"github.com/docker/docker/pkg/pools"
|
||||
"github.com/google/uuid"
|
||||
imagespec "github.com/moby/docker-image-spec/specs-go/v1"
|
||||
"github.com/moby/go-archive"
|
||||
"github.com/moby/go-archive/compression"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/opencontainers/image-spec/specs-go"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
@ -168,17 +168,17 @@ func saveArchive(ctx context.Context, cs content.Store, layerReader io.Reader) (
|
||||
bufRd := p.Get(layerReader)
|
||||
defer p.Put(bufRd)
|
||||
|
||||
compression, err := detectCompression(bufRd)
|
||||
comp, err := detectCompression(bufRd)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
var uncompressedReader io.Reader = bufRd
|
||||
switch compression {
|
||||
case archive.Gzip, archive.Zstd:
|
||||
switch comp {
|
||||
case compression.Gzip, compression.Zstd:
|
||||
// If the input is already a compressed layer, just save it as is.
|
||||
mediaType := ocispec.MediaTypeImageLayerGzip
|
||||
if compression == archive.Zstd {
|
||||
if comp == compression.Zstd {
|
||||
mediaType = ocispec.MediaTypeImageLayerZstd
|
||||
}
|
||||
|
||||
@ -188,19 +188,17 @@ func saveArchive(ctx context.Context, cs content.Store, layerReader io.Reader) (
|
||||
}
|
||||
|
||||
return compressedDigest, uncompressedDigest, mediaType, nil
|
||||
case archive.Bzip2, archive.Xz:
|
||||
r, err := archive.DecompressStream(bufRd)
|
||||
case compression.Bzip2, compression.Xz:
|
||||
r, err := compression.DecompressStream(bufRd)
|
||||
if err != nil {
|
||||
return "", "", "", errdefs.InvalidParameter(err)
|
||||
}
|
||||
defer r.Close()
|
||||
uncompressedReader = r
|
||||
fallthrough
|
||||
case archive.Uncompressed:
|
||||
case compression.None:
|
||||
mediaType := ocispec.MediaTypeImageLayerGzip
|
||||
compression := archive.Gzip
|
||||
|
||||
compressedDigest, uncompressedDigest, err := compressAndWriteBlob(ctx, cs, compression, mediaType, uncompressedReader)
|
||||
compressedDigest, uncompressedDigest, err := compressAndWriteBlob(ctx, cs, compression.Gzip, mediaType, uncompressedReader)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
@ -228,7 +226,7 @@ func writeCompressedBlob(ctx context.Context, cs content.Store, mediaType string
|
||||
digester := digest.Canonical.Digester()
|
||||
|
||||
// Decompress the piped blob.
|
||||
decompressedStream, err := archive.DecompressStream(pr)
|
||||
decompressedStream, err := compression.DecompressStream(pr)
|
||||
if err == nil {
|
||||
// Feed the digester with decompressed data.
|
||||
_, err = io.Copy(digester.Hash(), decompressedStream)
|
||||
@ -249,12 +247,12 @@ func writeCompressedBlob(ctx context.Context, cs content.Store, mediaType string
|
||||
}
|
||||
|
||||
// compressAndWriteBlob compresses the uncompressedReader and stores it in the content store.
|
||||
func compressAndWriteBlob(ctx context.Context, cs content.Store, compression archive.Compression, mediaType string, uncompressedLayerReader io.Reader) (digest.Digest, digest.Digest, error) {
|
||||
func compressAndWriteBlob(ctx context.Context, cs content.Store, comp compression.Compression, mediaType string, uncompressedLayerReader io.Reader) (digest.Digest, digest.Digest, error) {
|
||||
pr, pw := io.Pipe()
|
||||
defer pr.Close()
|
||||
defer pw.Close()
|
||||
|
||||
compressor, err := archive.CompressStream(pw, compression)
|
||||
compressor, err := compression.CompressStream(pw, comp)
|
||||
if err != nil {
|
||||
return "", "", errdefs.InvalidParameter(err)
|
||||
}
|
||||
@ -312,7 +310,7 @@ func (i *ImageService) unpackImage(ctx context.Context, snapshotter string, img
|
||||
}
|
||||
|
||||
// detectCompression detects the reader compression type.
|
||||
func detectCompression(bufRd *bufio.Reader) (archive.Compression, error) {
|
||||
func detectCompression(bufRd *bufio.Reader) (compression.Compression, error) {
|
||||
bs, err := bufRd.Peek(10)
|
||||
if err != nil && err != io.EOF {
|
||||
// Note: we'll ignore any io.EOF error because there are some odd
|
||||
@ -321,10 +319,10 @@ func detectCompression(bufRd *bufio.Reader) (archive.Compression, error) {
|
||||
// cases we'll just treat it as a non-compressed stream and
|
||||
// that means just create an empty layer.
|
||||
// See Issue 18170
|
||||
return archive.Uncompressed, errdefs.Unknown(err)
|
||||
return compression.None, errdefs.Unknown(err)
|
||||
}
|
||||
|
||||
return archive.DetectCompression(bs), nil
|
||||
return compression.Detect(bs), nil
|
||||
}
|
||||
|
||||
// fillUncompressedLabel sets the uncompressed digest label on the compressed blob metadata
|
||||
|
@ -15,7 +15,7 @@ import (
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/moby/go-archive"
|
||||
"github.com/moby/go-archive/compression"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
@ -40,7 +40,7 @@ func (i *ImageService) ImportImage(ctx context.Context, newRef reference.Named,
|
||||
return "", errdefs.InvalidParameter(err)
|
||||
}
|
||||
|
||||
inflatedLayerData, err := archive.DecompressStream(layerReader)
|
||||
inflatedLayerData, err := compression.DecompressStream(layerReader)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/progress"
|
||||
"github.com/moby/go-archive"
|
||||
"github.com/moby/go-archive/compression"
|
||||
)
|
||||
|
||||
const maxDownloadAttempts = 5
|
||||
@ -338,7 +338,7 @@ func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor,
|
||||
reader := progress.NewProgressReader(ioutils.NewCancelReadCloser(d.transfer.context(), downloadReader), progressOutput, size, descriptor.ID(), "Extracting")
|
||||
defer reader.Close()
|
||||
|
||||
inflatedLayerData, err := archive.DecompressStream(reader)
|
||||
inflatedLayerData, err := compression.DecompressStream(reader)
|
||||
if err != nil {
|
||||
d.err = fmt.Errorf("could not get decompression stream: %v", err)
|
||||
return
|
||||
|
@ -23,8 +23,8 @@ import (
|
||||
"github.com/docker/docker/pkg/progress"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/moby/go-archive"
|
||||
"github.com/moby/go-archive/chrootarchive"
|
||||
"github.com/moby/go-archive/compression"
|
||||
"github.com/moby/sys/sequential"
|
||||
"github.com/moby/sys/symlink"
|
||||
"github.com/opencontainers/go-digest"
|
||||
@ -231,7 +231,7 @@ func (l *tarexporter) loadLayer(ctx context.Context, filename string, rootFS ima
|
||||
r = rawTar
|
||||
}
|
||||
|
||||
inflatedLayerData, err := archive.DecompressStream(ioutils.NewCtxReader(ctx, r))
|
||||
inflatedLayerData, err := compression.DecompressStream(ioutils.NewCtxReader(ctx, r))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import (
|
||||
"github.com/docker/docker/testutil/fakestorage"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/command"
|
||||
"github.com/moby/go-archive"
|
||||
"github.com/moby/go-archive/compression"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
@ -2008,7 +2009,7 @@ func (s *DockerCLIBuildSuite) TestBuildAddLocalAndRemoteFilesWithAndWithoutCache
|
||||
}
|
||||
}
|
||||
|
||||
func testContextTar(c *testing.T, compression archive.Compression) {
|
||||
func testContextTar(c *testing.T, comp compression.Compression) {
|
||||
ctx := fakecontext.New(c, "",
|
||||
fakecontext.WithDockerfile(`FROM busybox
|
||||
ADD foo /foo
|
||||
@ -2018,13 +2019,13 @@ CMD ["cat", "/foo"]`),
|
||||
}),
|
||||
)
|
||||
defer ctx.Close()
|
||||
context, err := archive.Tar(ctx.Dir, compression)
|
||||
buildContext, err := archive.Tar(ctx.Dir, comp)
|
||||
if err != nil {
|
||||
c.Fatalf("failed to build context tar: %v", err)
|
||||
}
|
||||
const name = "contexttar"
|
||||
|
||||
cli.BuildCmd(c, name, build.WithStdinContext(context))
|
||||
cli.BuildCmd(c, name, build.WithStdinContext(buildContext))
|
||||
}
|
||||
|
||||
func (s *DockerCLIBuildSuite) TestBuildContextTarGzip(c *testing.T) {
|
||||
|
@ -23,7 +23,7 @@ import (
|
||||
"github.com/docker/docker/internal/testutils"
|
||||
"github.com/docker/docker/internal/testutils/specialimage"
|
||||
"github.com/docker/docker/testutil/fakecontext"
|
||||
"github.com/moby/go-archive"
|
||||
"github.com/moby/go-archive/compression"
|
||||
"github.com/opencontainers/go-digest"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"gotest.tools/v3/assert"
|
||||
@ -360,7 +360,7 @@ RUN touch /opt/a/b/c && chown user:user /opt/a/b/c`
|
||||
|
||||
func listTar(f io.Reader) ([]string, error) {
|
||||
// If using the containerd snapshotter, the tar file may be compressed
|
||||
dec, err := archive.DecompressStream(f)
|
||||
dec, err := compression.DecompressStream(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -3,13 +3,13 @@ package testutils
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/moby/go-archive"
|
||||
"github.com/moby/go-archive/compression"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// UncompressedTarDigest returns the canonical digest of the uncompressed tar stream.
|
||||
func UncompressedTarDigest(compressedTar io.Reader) (digest.Digest, error) {
|
||||
rd, err := archive.DecompressStream(compressedTar)
|
||||
rd, err := compression.DecompressStream(compressedTar)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ import (
|
||||
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/moby/go-archive"
|
||||
"github.com/moby/go-archive/compression"
|
||||
"github.com/moby/go-archive/tarheader"
|
||||
)
|
||||
|
||||
// ImpliedDirectoryMode represents the mode (Unix permissions) applied to directories that are implied by files in a
|
||||
@ -19,8 +21,8 @@ const ImpliedDirectoryMode = archive.ImpliedDirectoryMode
|
||||
type (
|
||||
// Compression is the state represents if compressed or not.
|
||||
//
|
||||
// Deprecated: use [archive.Compression] instead.
|
||||
Compression = archive.Compression
|
||||
// Deprecated: use [compression.Compression] instead.
|
||||
Compression = compression.Compression
|
||||
// WhiteoutFormat is the format of whiteouts unpacked
|
||||
//
|
||||
// Deprecated: use [archive.WhiteoutFormat] instead.
|
||||
@ -32,7 +34,7 @@ type (
|
||||
TarOptions struct {
|
||||
IncludeFiles []string
|
||||
ExcludePatterns []string
|
||||
Compression archive.Compression
|
||||
Compression compression.Compression
|
||||
NoLchown bool
|
||||
IDMap idtools.IdentityMapping
|
||||
ChownOpts *idtools.Identity
|
||||
@ -75,11 +77,11 @@ func NewDefaultArchiver() *Archiver {
|
||||
}
|
||||
|
||||
const (
|
||||
Uncompressed = archive.Uncompressed // Deprecated: use [archive.Uncompressed] instead.
|
||||
Bzip2 = archive.Bzip2 // Deprecated: use [archive.Bzip2] instead.
|
||||
Gzip = archive.Gzip // Deprecated: use [archive.Gzip] instead.
|
||||
Xz = archive.Xz // Deprecated: use [archive.Xz] instead.
|
||||
Zstd = archive.Zstd // Deprecated: use [archive.Zstd] instead.
|
||||
Uncompressed = compression.None // Deprecated: use [compression.None] instead.
|
||||
Bzip2 = compression.Bzip2 // Deprecated: use [compression.Bzip2] instead.
|
||||
Gzip = compression.Gzip // Deprecated: use [compression.Gzip] instead.
|
||||
Xz = compression.Xz // Deprecated: use [compression.Xz] instead.
|
||||
Zstd = compression.Zstd // Deprecated: use [compression.Zstd] instead.
|
||||
)
|
||||
|
||||
const (
|
||||
@ -97,23 +99,23 @@ func IsArchivePath(path string) bool {
|
||||
|
||||
// DetectCompression detects the compression algorithm of the source.
|
||||
//
|
||||
// Deprecated: use [archive.DetectCompression] instead.
|
||||
// Deprecated: use [compression.Detect] instead.
|
||||
func DetectCompression(source []byte) archive.Compression {
|
||||
return archive.DetectCompression(source)
|
||||
return compression.Detect(source)
|
||||
}
|
||||
|
||||
// DecompressStream decompresses the archive and returns a ReaderCloser with the decompressed archive.
|
||||
//
|
||||
// Deprecated: use [archive.DecompressStream] instead.
|
||||
// Deprecated: use [compression.DecompressStream] instead.
|
||||
func DecompressStream(arch io.Reader) (io.ReadCloser, error) {
|
||||
return archive.DecompressStream(arch)
|
||||
return compression.DecompressStream(arch)
|
||||
}
|
||||
|
||||
// CompressStream compresses the dest with specified compression algorithm.
|
||||
//
|
||||
// Deprecated: use [archive.CompressStream] instead.
|
||||
func CompressStream(dest io.Writer, compression archive.Compression) (io.WriteCloser, error) {
|
||||
return archive.CompressStream(dest, compression)
|
||||
// Deprecated: use [compression.CompressStream] instead.
|
||||
func CompressStream(dest io.Writer, comp compression.Compression) (io.WriteCloser, error) {
|
||||
return compression.CompressStream(dest, comp)
|
||||
}
|
||||
|
||||
// TarModifierFunc is a function that can be passed to ReplaceFileTarWrapper.
|
||||
@ -130,9 +132,9 @@ func ReplaceFileTarWrapper(inputTarStream io.ReadCloser, mods map[string]archive
|
||||
|
||||
// FileInfoHeaderNoLookups creates a partially-populated tar.Header from fi.
|
||||
//
|
||||
// Deprecated: use [archive.FileInfoHeaderNoLookups] instead.
|
||||
// Deprecated: use [tarheader.FileInfoHeaderNoLookups] instead.
|
||||
func FileInfoHeaderNoLookups(fi os.FileInfo, link string) (*tar.Header, error) {
|
||||
return archive.FileInfoHeaderNoLookups(fi, link)
|
||||
return tarheader.FileInfoHeaderNoLookups(fi, link)
|
||||
}
|
||||
|
||||
// FileInfoHeader creates a populated Header from fi.
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"io"
|
||||
|
||||
"github.com/moby/go-archive"
|
||||
"github.com/moby/go-archive/compression"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -51,7 +52,7 @@ func TarResourceRebase(sourcePath, rebaseName string) (content io.ReadCloser, _
|
||||
func TarResourceRebaseOpts(sourceBase string, rebaseName string) *TarOptions {
|
||||
filter := []string{sourceBase}
|
||||
return &TarOptions{
|
||||
Compression: archive.Uncompressed,
|
||||
Compression: compression.None,
|
||||
IncludeFiles: filter,
|
||||
IncludeSourceDir: true,
|
||||
RebaseNames: map[string]string{
|
||||
|
@ -64,7 +64,7 @@ require (
|
||||
github.com/mitchellh/copystructure v1.2.0
|
||||
github.com/moby/buildkit v0.21.0
|
||||
github.com/moby/docker-image-spec v1.3.1
|
||||
github.com/moby/go-archive v0.0.0-20250404171912-21f3f3385ab7
|
||||
github.com/moby/go-archive v0.1.0
|
||||
github.com/moby/ipvs v1.1.0
|
||||
github.com/moby/locker v1.0.1
|
||||
github.com/moby/patternmatcher v0.6.0
|
||||
|
@ -387,8 +387,8 @@ github.com/moby/buildkit v0.21.0 h1:+z4vVqgt0spLrOSxi4DLedRbIh2gbNVlZ5q4rsnNp60=
|
||||
github.com/moby/buildkit v0.21.0/go.mod h1:mBq0D44uCyz2PdX8T/qym5LBbkBO3GGv0wqgX9ABYYw=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/go-archive v0.0.0-20250404171912-21f3f3385ab7 h1:CWAY9uG9JhmLmnM7T64+bV0C9IraDrvxEwXq1HJ7hhk=
|
||||
github.com/moby/go-archive v0.0.0-20250404171912-21f3f3385ab7/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo=
|
||||
github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=
|
||||
github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo=
|
||||
github.com/moby/ipvs v1.1.0 h1:ONN4pGaZQgAx+1Scz5RvWV4Q7Gb+mvfRh3NsPS+1XQQ=
|
||||
github.com/moby/ipvs v1.1.0/go.mod h1:4VJMWuf098bsUMmZEiD4Tjk/O7mOn3l1PTD3s4OoYAs=
|
||||
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
|
||||
|
2
vendor/github.com/moby/go-archive/.gitattributes
generated
vendored
Normal file
2
vendor/github.com/moby/go-archive/.gitattributes
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.go -text diff=golang
|
||||
*.go text eol=lf
|
1
vendor/github.com/moby/go-archive/.gitignore
generated
vendored
Normal file
1
vendor/github.com/moby/go-archive/.gitignore
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
/coverage.txt
|
33
vendor/github.com/moby/go-archive/.golangci.yml
generated
vendored
Normal file
33
vendor/github.com/moby/go-archive/.golangci.yml
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
version: "2"
|
||||
|
||||
issues:
|
||||
# Disable maximum issues count per one linter.
|
||||
max-issues-per-linter: 0
|
||||
# Disable maximum count of issues with the same text.
|
||||
max-same-issues: 0
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- errorlint
|
||||
- unconvert
|
||||
- unparam
|
||||
exclusions:
|
||||
generated: disable
|
||||
presets:
|
||||
- comments
|
||||
- std-error-handling
|
||||
settings:
|
||||
staticcheck:
|
||||
# Enable all options, with some exceptions.
|
||||
# For defaults, see https://golangci-lint.run/usage/linters/#staticcheck
|
||||
checks:
|
||||
- all
|
||||
- -QF1008 # Omit embedded fields from selector expression; https://staticcheck.dev/docs/checks/#QF1008
|
||||
- -ST1003 # Poorly chosen identifier; https://staticcheck.dev/docs/checks/#ST1003
|
||||
|
||||
formatters:
|
||||
enable:
|
||||
- gofumpt
|
||||
- goimports
|
||||
exclusions:
|
||||
generated: disable
|
435
vendor/github.com/moby/go-archive/archive.go
generated
vendored
435
vendor/github.com/moby/go-archive/archive.go
generated
vendored
@ -3,32 +3,24 @@ package archive
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/bzip2"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/log"
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"github.com/moby/patternmatcher"
|
||||
"github.com/moby/sys/sequential"
|
||||
"github.com/moby/sys/user"
|
||||
|
||||
"github.com/moby/go-archive/compression"
|
||||
"github.com/moby/go-archive/tarheader"
|
||||
)
|
||||
|
||||
// ImpliedDirectoryMode represents the mode (Unix permissions) applied to directories that are implied by files in a
|
||||
@ -45,7 +37,9 @@ const ImpliedDirectoryMode = 0o755
|
||||
|
||||
type (
|
||||
// Compression is the state represents if compressed or not.
|
||||
Compression int
|
||||
//
|
||||
// Deprecated: use [compression.Compression].
|
||||
Compression = compression.Compression
|
||||
// WhiteoutFormat is the format of whiteouts unpacked
|
||||
WhiteoutFormat int
|
||||
|
||||
@ -58,7 +52,7 @@ type (
|
||||
TarOptions struct {
|
||||
IncludeFiles []string
|
||||
ExcludePatterns []string
|
||||
Compression Compression
|
||||
Compression compression.Compression
|
||||
NoLchown bool
|
||||
IDMap user.IdentityMapping
|
||||
ChownOpts *ChownOpts
|
||||
@ -102,11 +96,11 @@ func NewDefaultArchiver() *Archiver {
|
||||
type breakoutError error
|
||||
|
||||
const (
|
||||
Uncompressed Compression = 0 // Uncompressed represents the uncompressed.
|
||||
Bzip2 Compression = 1 // Bzip2 is bzip2 compression algorithm.
|
||||
Gzip Compression = 2 // Gzip is gzip compression algorithm.
|
||||
Xz Compression = 3 // Xz is xz compression algorithm.
|
||||
Zstd Compression = 4 // Zstd is zstd compression algorithm.
|
||||
Uncompressed = compression.None // Deprecated: use [compression.None].
|
||||
Bzip2 = compression.Bzip2 // Deprecated: use [compression.Bzip2].
|
||||
Gzip = compression.Gzip // Deprecated: use [compression.Gzip].
|
||||
Xz = compression.Xz // Deprecated: use [compression.Xz].
|
||||
Zstd = compression.Zstd // Deprecated: use [compression.Zstd].
|
||||
)
|
||||
|
||||
const (
|
||||
@ -122,7 +116,7 @@ func IsArchivePath(path string) bool {
|
||||
return false
|
||||
}
|
||||
defer file.Close()
|
||||
rdr, err := DecompressStream(file)
|
||||
rdr, err := compression.DecompressStream(file)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@ -132,242 +126,25 @@ func IsArchivePath(path string) bool {
|
||||
return err == nil
|
||||
}
|
||||
|
||||
const (
|
||||
zstdMagicSkippableStart = 0x184D2A50
|
||||
zstdMagicSkippableMask = 0xFFFFFFF0
|
||||
)
|
||||
|
||||
var (
|
||||
bzip2Magic = []byte{0x42, 0x5A, 0x68}
|
||||
gzipMagic = []byte{0x1F, 0x8B, 0x08}
|
||||
xzMagic = []byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}
|
||||
zstdMagic = []byte{0x28, 0xb5, 0x2f, 0xfd}
|
||||
)
|
||||
|
||||
type matcher = func([]byte) bool
|
||||
|
||||
func magicNumberMatcher(m []byte) matcher {
|
||||
return func(source []byte) bool {
|
||||
return bytes.HasPrefix(source, m)
|
||||
}
|
||||
}
|
||||
|
||||
// zstdMatcher detects zstd compression algorithm.
|
||||
// Zstandard compressed data is made of one or more frames.
|
||||
// There are two frame formats defined by Zstandard: Zstandard frames and Skippable frames.
|
||||
// See https://datatracker.ietf.org/doc/html/rfc8878#section-3 for more details.
|
||||
func zstdMatcher() matcher {
|
||||
return func(source []byte) bool {
|
||||
if bytes.HasPrefix(source, zstdMagic) {
|
||||
// Zstandard frame
|
||||
return true
|
||||
}
|
||||
// skippable frame
|
||||
if len(source) < 8 {
|
||||
return false
|
||||
}
|
||||
// magic number from 0x184D2A50 to 0x184D2A5F.
|
||||
if binary.LittleEndian.Uint32(source[:4])&zstdMagicSkippableMask == zstdMagicSkippableStart {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// DetectCompression detects the compression algorithm of the source.
|
||||
func DetectCompression(source []byte) Compression {
|
||||
compressionMap := map[Compression]matcher{
|
||||
Bzip2: magicNumberMatcher(bzip2Magic),
|
||||
Gzip: magicNumberMatcher(gzipMagic),
|
||||
Xz: magicNumberMatcher(xzMagic),
|
||||
Zstd: zstdMatcher(),
|
||||
}
|
||||
for _, compression := range []Compression{Bzip2, Gzip, Xz, Zstd} {
|
||||
fn := compressionMap[compression]
|
||||
if fn(source) {
|
||||
return compression
|
||||
}
|
||||
}
|
||||
return Uncompressed
|
||||
}
|
||||
|
||||
func xzDecompress(ctx context.Context, archive io.Reader) (io.ReadCloser, error) {
|
||||
args := []string{"xz", "-d", "-c", "-q"}
|
||||
|
||||
return cmdStream(exec.CommandContext(ctx, args[0], args[1:]...), archive)
|
||||
}
|
||||
|
||||
func gzDecompress(ctx context.Context, buf io.Reader) (io.ReadCloser, error) {
|
||||
if noPigzEnv := os.Getenv("MOBY_DISABLE_PIGZ"); noPigzEnv != "" {
|
||||
noPigz, err := strconv.ParseBool(noPigzEnv)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Warn("invalid value in MOBY_DISABLE_PIGZ env var")
|
||||
}
|
||||
if noPigz {
|
||||
log.G(ctx).Debugf("Use of pigz is disabled due to MOBY_DISABLE_PIGZ=%s", noPigzEnv)
|
||||
return gzip.NewReader(buf)
|
||||
}
|
||||
}
|
||||
|
||||
unpigzPath, err := exec.LookPath("unpigz")
|
||||
if err != nil {
|
||||
log.G(ctx).Debugf("unpigz binary not found, falling back to go gzip library")
|
||||
return gzip.NewReader(buf)
|
||||
}
|
||||
|
||||
log.G(ctx).Debugf("Using %s to decompress", unpigzPath)
|
||||
|
||||
return cmdStream(exec.CommandContext(ctx, unpigzPath, "-d", "-c"), buf)
|
||||
}
|
||||
|
||||
type readCloserWrapper struct {
|
||||
io.Reader
|
||||
closer func() error
|
||||
closed atomic.Bool
|
||||
}
|
||||
|
||||
func (r *readCloserWrapper) Close() error {
|
||||
if !r.closed.CompareAndSwap(false, true) {
|
||||
log.G(context.TODO()).Error("subsequent attempt to close readCloserWrapper")
|
||||
if log.GetLevel() >= log.DebugLevel {
|
||||
log.G(context.TODO()).Errorf("stack trace: %s", string(debug.Stack()))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
if r.closer != nil {
|
||||
return r.closer()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var bufioReader32KPool = &sync.Pool{
|
||||
New: func() interface{} { return bufio.NewReaderSize(nil, 32*1024) },
|
||||
}
|
||||
|
||||
type bufferedReader struct {
|
||||
buf *bufio.Reader
|
||||
}
|
||||
|
||||
func newBufferedReader(r io.Reader) *bufferedReader {
|
||||
buf := bufioReader32KPool.Get().(*bufio.Reader)
|
||||
buf.Reset(r)
|
||||
return &bufferedReader{buf}
|
||||
}
|
||||
|
||||
func (r *bufferedReader) Read(p []byte) (int, error) {
|
||||
if r.buf == nil {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n, err := r.buf.Read(p)
|
||||
if err == io.EOF {
|
||||
r.buf.Reset(nil)
|
||||
bufioReader32KPool.Put(r.buf)
|
||||
r.buf = nil
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (r *bufferedReader) Peek(n int) ([]byte, error) {
|
||||
if r.buf == nil {
|
||||
return nil, io.EOF
|
||||
}
|
||||
return r.buf.Peek(n)
|
||||
//
|
||||
// Deprecated: use [compression.Detect].
|
||||
func DetectCompression(source []byte) compression.Compression {
|
||||
return compression.Detect(source)
|
||||
}
|
||||
|
||||
// DecompressStream decompresses the archive and returns a ReaderCloser with the decompressed archive.
|
||||
//
|
||||
// Deprecated: use [compression.DecompressStream].
|
||||
func DecompressStream(archive io.Reader) (io.ReadCloser, error) {
|
||||
buf := newBufferedReader(archive)
|
||||
bs, err := buf.Peek(10)
|
||||
if err != nil && err != io.EOF {
|
||||
// Note: we'll ignore any io.EOF error because there are some odd
|
||||
// cases where the layer.tar file will be empty (zero bytes) and
|
||||
// that results in an io.EOF from the Peek() call. So, in those
|
||||
// cases we'll just treat it as a non-compressed stream and
|
||||
// that means just create an empty layer.
|
||||
// See Issue 18170
|
||||
return nil, err
|
||||
}
|
||||
|
||||
compression := DetectCompression(bs)
|
||||
switch compression {
|
||||
case Uncompressed:
|
||||
return &readCloserWrapper{
|
||||
Reader: buf,
|
||||
}, nil
|
||||
case Gzip:
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
gzReader, err := gzDecompress(ctx, buf)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return nil, err
|
||||
}
|
||||
return &readCloserWrapper{
|
||||
Reader: gzReader,
|
||||
closer: func() error {
|
||||
cancel()
|
||||
return gzReader.Close()
|
||||
},
|
||||
}, nil
|
||||
case Bzip2:
|
||||
bz2Reader := bzip2.NewReader(buf)
|
||||
return &readCloserWrapper{
|
||||
Reader: bz2Reader,
|
||||
}, nil
|
||||
case Xz:
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
xzReader, err := xzDecompress(ctx, buf)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &readCloserWrapper{
|
||||
Reader: xzReader,
|
||||
closer: func() error {
|
||||
cancel()
|
||||
return xzReader.Close()
|
||||
},
|
||||
}, nil
|
||||
case Zstd:
|
||||
zstdReader, err := zstd.NewReader(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &readCloserWrapper{
|
||||
Reader: zstdReader,
|
||||
closer: func() error {
|
||||
zstdReader.Close()
|
||||
return nil
|
||||
},
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension())
|
||||
}
|
||||
return compression.DecompressStream(archive)
|
||||
}
|
||||
|
||||
type nopWriteCloser struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (nopWriteCloser) Close() error { return nil }
|
||||
|
||||
// CompressStream compresses the dest with specified compression algorithm.
|
||||
func CompressStream(dest io.Writer, compression Compression) (io.WriteCloser, error) {
|
||||
switch compression {
|
||||
case Uncompressed:
|
||||
return nopWriteCloser{dest}, nil
|
||||
case Gzip:
|
||||
return gzip.NewWriter(dest), nil
|
||||
case Bzip2, Xz:
|
||||
// archive/bzip2 does not support writing, and there is no xz support at all
|
||||
// However, this is not a problem as docker only currently generates gzipped tars
|
||||
return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension())
|
||||
default:
|
||||
return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension())
|
||||
}
|
||||
//
|
||||
// Deprecated: use [compression.CompressStream].
|
||||
func CompressStream(dest io.Writer, comp compression.Compression) (io.WriteCloser, error) {
|
||||
return compression.CompressStream(dest, comp)
|
||||
}
|
||||
|
||||
// TarModifierFunc is a function that can be passed to ReplaceFileTarWrapper to
|
||||
@ -416,7 +193,7 @@ func ReplaceFileTarWrapper(inputTarStream io.ReadCloser, mods map[string]TarModi
|
||||
var originalHeader *tar.Header
|
||||
for {
|
||||
originalHeader, err = tarReader.Next()
|
||||
if err == io.EOF {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
@ -458,90 +235,11 @@ func ReplaceFileTarWrapper(inputTarStream io.ReadCloser, mods map[string]TarModi
|
||||
return pipeReader
|
||||
}
|
||||
|
||||
// Extension returns the extension of a file that uses the specified compression algorithm.
|
||||
func (compression *Compression) Extension() string {
|
||||
switch *compression {
|
||||
case Uncompressed:
|
||||
return "tar"
|
||||
case Bzip2:
|
||||
return "tar.bz2"
|
||||
case Gzip:
|
||||
return "tar.gz"
|
||||
case Xz:
|
||||
return "tar.xz"
|
||||
case Zstd:
|
||||
return "tar.zst"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// assert that we implement [tar.FileInfoNames].
|
||||
//
|
||||
// TODO(thaJeztah): disabled to allow compiling on < go1.23. un-comment once we drop support for older versions of go.
|
||||
// var _ tar.FileInfoNames = (*nosysFileInfo)(nil)
|
||||
|
||||
// nosysFileInfo hides the system-dependent info of the wrapped FileInfo to
|
||||
// prevent tar.FileInfoHeader from introspecting it and potentially calling into
|
||||
// glibc.
|
||||
//
|
||||
// It implements [tar.FileInfoNames] to further prevent [tar.FileInfoHeader]
|
||||
// from performing any lookups on go1.23 and up. see https://go.dev/issue/50102
|
||||
type nosysFileInfo struct {
|
||||
os.FileInfo
|
||||
}
|
||||
|
||||
// Uname stubs out looking up username. It implements [tar.FileInfoNames]
|
||||
// to prevent [tar.FileInfoHeader] from loading libraries to perform
|
||||
// username lookups.
|
||||
func (fi nosysFileInfo) Uname() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Gname stubs out looking up group-name. It implements [tar.FileInfoNames]
|
||||
// to prevent [tar.FileInfoHeader] from loading libraries to perform
|
||||
// username lookups.
|
||||
func (fi nosysFileInfo) Gname() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (fi nosysFileInfo) Sys() interface{} {
|
||||
// A Sys value of type *tar.Header is safe as it is system-independent.
|
||||
// The tar.FileInfoHeader function copies the fields into the returned
|
||||
// header without performing any OS lookups.
|
||||
if sys, ok := fi.FileInfo.Sys().(*tar.Header); ok {
|
||||
return sys
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// sysStat, if non-nil, populates hdr from system-dependent fields of fi.
|
||||
var sysStat func(fi os.FileInfo, hdr *tar.Header) error
|
||||
|
||||
// FileInfoHeaderNoLookups creates a partially-populated tar.Header from fi.
|
||||
//
|
||||
// Compared to the archive/tar.FileInfoHeader function, this function is safe to
|
||||
// call from a chrooted process as it does not populate fields which would
|
||||
// require operating system lookups. It behaves identically to
|
||||
// tar.FileInfoHeader when fi is a FileInfo value returned from
|
||||
// tar.Header.FileInfo().
|
||||
//
|
||||
// When fi is a FileInfo for a native file, such as returned from os.Stat() and
|
||||
// os.Lstat(), the returned Header value differs from one returned from
|
||||
// tar.FileInfoHeader in the following ways. The Uname and Gname fields are not
|
||||
// set as OS lookups would be required to populate them. The AccessTime and
|
||||
// ChangeTime fields are not currently set (not yet implemented) although that
|
||||
// is subject to change. Callers which require the AccessTime or ChangeTime
|
||||
// fields to be zeroed should explicitly zero them out in the returned Header
|
||||
// value to avoid any compatibility issues in the future.
|
||||
// Deprecated: use [tarheader.FileInfoHeaderNoLookups].
|
||||
func FileInfoHeaderNoLookups(fi os.FileInfo, link string) (*tar.Header, error) {
|
||||
hdr, err := tar.FileInfoHeader(nosysFileInfo{fi}, link)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if sysStat != nil {
|
||||
return hdr, sysStat(fi, hdr)
|
||||
}
|
||||
return hdr, nil
|
||||
return tarheader.FileInfoHeaderNoLookups(fi, link)
|
||||
}
|
||||
|
||||
// FileInfoHeader creates a populated Header from fi.
|
||||
@ -552,7 +250,7 @@ func FileInfoHeaderNoLookups(fi os.FileInfo, link string) (*tar.Header, error) {
|
||||
// precision, and the Uname and Gname fields are only set when fi is a FileInfo
|
||||
// value returned from tar.Header.FileInfo().
|
||||
func FileInfoHeader(name string, fi os.FileInfo, link string) (*tar.Header, error) {
|
||||
hdr, err := FileInfoHeaderNoLookups(fi, link)
|
||||
hdr, err := tarheader.FileInfoHeaderNoLookups(fi, link)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -768,7 +466,7 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, o
|
||||
case tar.TypeDir:
|
||||
// Create directory unless it exists as a directory already.
|
||||
// In that case we just want to merge the two
|
||||
if fi, err := os.Lstat(path); !(err == nil && fi.IsDir()) {
|
||||
if fi, err := os.Lstat(path); err != nil || !fi.IsDir() {
|
||||
if err := os.Mkdir(path, hdrInfo.Mode()); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -908,8 +606,8 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, o
|
||||
|
||||
// Tar creates an archive from the directory at `path`, and returns it as a
|
||||
// stream of bytes.
|
||||
func Tar(path string, compression Compression) (io.ReadCloser, error) {
|
||||
return TarWithOptions(path, &TarOptions{Compression: compression})
|
||||
func Tar(path string, comp compression.Compression) (io.ReadCloser, error) {
|
||||
return TarWithOptions(path, &TarOptions{Compression: comp})
|
||||
}
|
||||
|
||||
// TarWithOptions creates an archive from the directory at `path`, only including files whose relative
|
||||
@ -945,7 +643,7 @@ func NewTarballer(srcPath string, options *TarOptions) (*Tarballer, error) {
|
||||
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
|
||||
compressWriter, err := CompressStream(pipeWriter, options.Compression)
|
||||
compressWriter, err := compression.CompressStream(pipeWriter, options.Compression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1031,7 +729,8 @@ func (t *Tarballer) Do() {
|
||||
)
|
||||
|
||||
walkRoot := getWalkRoot(t.srcPath, include)
|
||||
filepath.WalkDir(walkRoot, func(filePath string, f os.DirEntry, err error) error {
|
||||
// TODO(thaJeztah): should this error be handled?
|
||||
_ = filepath.WalkDir(walkRoot, func(filePath string, f os.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
log.G(context.TODO()).Errorf("Tar: Can't stat file %s to tar: %s", t.srcPath, err)
|
||||
return nil
|
||||
@ -1135,7 +834,7 @@ func (t *Tarballer) Do() {
|
||||
if err := ta.addTarFile(filePath, relFilePath); err != nil {
|
||||
log.G(context.TODO()).Errorf("Can't add file %s to tar: %s", filePath, err)
|
||||
// if pipe is broken, stop writing tar stream to it
|
||||
if err == io.ErrClosedPipe {
|
||||
if errors.Is(err, io.ErrClosedPipe) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -1155,7 +854,7 @@ func Unpack(decompressedArchive io.Reader, dest string, options *TarOptions) err
|
||||
loop:
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
if errors.Is(err, io.EOF) {
|
||||
// end of tar archive
|
||||
break
|
||||
}
|
||||
@ -1217,7 +916,7 @@ loop:
|
||||
continue
|
||||
}
|
||||
|
||||
if !(fi.IsDir() && hdr.Typeflag == tar.TypeDir) {
|
||||
if !fi.IsDir() || hdr.Typeflag != tar.TypeDir {
|
||||
if err := os.RemoveAll(path); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1309,7 +1008,7 @@ func UntarUncompressed(tarArchive io.Reader, dest string, options *TarOptions) e
|
||||
// Handler for teasing out the automatic decompression
|
||||
func untarHandler(tarArchive io.Reader, dest string, options *TarOptions, decompress bool) error {
|
||||
if tarArchive == nil {
|
||||
return fmt.Errorf("Empty archive")
|
||||
return errors.New("empty archive")
|
||||
}
|
||||
dest = filepath.Clean(dest)
|
||||
if options == nil {
|
||||
@ -1321,7 +1020,7 @@ func untarHandler(tarArchive io.Reader, dest string, options *TarOptions, decomp
|
||||
|
||||
r := tarArchive
|
||||
if decompress {
|
||||
decompressedArchive, err := DecompressStream(tarArchive)
|
||||
decompressedArchive, err := compression.DecompressStream(tarArchive)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1335,15 +1034,14 @@ func untarHandler(tarArchive io.Reader, dest string, options *TarOptions, decomp
|
||||
// TarUntar is a convenience function which calls Tar and Untar, with the output of one piped into the other.
|
||||
// If either Tar or Untar fails, TarUntar aborts and returns the error.
|
||||
func (archiver *Archiver) TarUntar(src, dst string) error {
|
||||
archive, err := TarWithOptions(src, &TarOptions{Compression: Uncompressed})
|
||||
archive, err := Tar(src, compression.None)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer archive.Close()
|
||||
options := &TarOptions{
|
||||
return archiver.Untar(archive, dst, &TarOptions{
|
||||
IDMap: archiver.IDMapping,
|
||||
}
|
||||
return archiver.Untar(archive, dst, options)
|
||||
})
|
||||
}
|
||||
|
||||
// UntarPath untar a file from path to a destination, src is the source tar file path.
|
||||
@ -1353,10 +1051,9 @@ func (archiver *Archiver) UntarPath(src, dst string) error {
|
||||
return err
|
||||
}
|
||||
defer archive.Close()
|
||||
options := &TarOptions{
|
||||
return archiver.Untar(archive, dst, &TarOptions{
|
||||
IDMap: archiver.IDMapping,
|
||||
}
|
||||
return archiver.Untar(archive, dst, options)
|
||||
})
|
||||
}
|
||||
|
||||
// CopyWithTar creates a tar archive of filesystem path `src`, and
|
||||
@ -1393,7 +1090,7 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
|
||||
}
|
||||
|
||||
if srcSt.IsDir() {
|
||||
return fmt.Errorf("Can't copy a directory")
|
||||
return errors.New("can't copy a directory")
|
||||
}
|
||||
|
||||
// Clean up the trailing slash. This must be done in an operating
|
||||
@ -1421,7 +1118,7 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
|
||||
}
|
||||
defer srcF.Close()
|
||||
|
||||
hdr, err := FileInfoHeaderNoLookups(srcSt, "")
|
||||
hdr, err := tarheader.FileInfoHeaderNoLookups(srcSt, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1470,43 +1167,3 @@ func remapIDs(idMapping user.IdentityMapping, hdr *tar.Header) error {
|
||||
hdr.Uid, hdr.Gid = uid, gid
|
||||
return err
|
||||
}
|
||||
|
||||
// cmdStream executes a command, and returns its stdout as a stream.
|
||||
// If the command fails to run or doesn't complete successfully, an error
|
||||
// will be returned, including anything written on stderr.
|
||||
func cmdStream(cmd *exec.Cmd, input io.Reader) (io.ReadCloser, error) {
|
||||
cmd.Stdin = input
|
||||
pipeR, pipeW := io.Pipe()
|
||||
cmd.Stdout = pipeW
|
||||
var errBuf bytes.Buffer
|
||||
cmd.Stderr = &errBuf
|
||||
|
||||
// Run the command and return the pipe
|
||||
if err := cmd.Start(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Ensure the command has exited before we clean anything up
|
||||
done := make(chan struct{})
|
||||
|
||||
// Copy stdout to the returned pipe
|
||||
go func() {
|
||||
if err := cmd.Wait(); err != nil {
|
||||
pipeW.CloseWithError(fmt.Errorf("%s: %s", err, errBuf.String()))
|
||||
} else {
|
||||
pipeW.Close()
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
|
||||
return &readCloserWrapper{
|
||||
Reader: pipeR,
|
||||
closer: func() error {
|
||||
// Close pipeR, and then wait for the command to complete before returning. We have to close pipeR first, as
|
||||
// cmd.Wait waits for any non-file stdout/stderr/stdin to close.
|
||||
err := pipeR.Close()
|
||||
<-done
|
||||
return err
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
39
vendor/github.com/moby/go-archive/archive_unix.go
generated
vendored
39
vendor/github.com/moby/go-archive/archive_unix.go
generated
vendored
@ -7,17 +7,12 @@ import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func init() {
|
||||
sysStat = statUnix
|
||||
}
|
||||
|
||||
// addLongPathPrefix adds the Windows long path prefix to the path provided if
|
||||
// it does not already have it. It is a no-op on platforms other than Windows.
|
||||
func addLongPathPrefix(srcPath string) string {
|
||||
@ -38,40 +33,6 @@ func chmodTarEntry(perm os.FileMode) os.FileMode {
|
||||
return perm // noop for unix as golang APIs provide perm bits correctly
|
||||
}
|
||||
|
||||
// statUnix populates hdr from system-dependent fields of fi without performing
|
||||
// any OS lookups.
|
||||
func statUnix(fi os.FileInfo, hdr *tar.Header) error {
|
||||
// Devmajor and Devminor are only needed for special devices.
|
||||
|
||||
// In FreeBSD, RDev for regular files is -1 (unless overridden by FS):
|
||||
// https://cgit.freebsd.org/src/tree/sys/kern/vfs_default.c?h=stable/13#n1531
|
||||
// (NODEV is -1: https://cgit.freebsd.org/src/tree/sys/sys/param.h?h=stable/13#n241).
|
||||
|
||||
// ZFS in particular does not override the default:
|
||||
// https://cgit.freebsd.org/src/tree/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c?h=stable/13#n2027
|
||||
|
||||
// Since `Stat_t.Rdev` is uint64, the cast turns -1 into (2^64 - 1).
|
||||
// Such large values cannot be encoded in a tar header.
|
||||
if runtime.GOOS == "freebsd" && hdr.Typeflag != tar.TypeBlock && hdr.Typeflag != tar.TypeChar {
|
||||
return nil
|
||||
}
|
||||
s, ok := fi.Sys().(*syscall.Stat_t)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
hdr.Uid = int(s.Uid)
|
||||
hdr.Gid = int(s.Gid)
|
||||
|
||||
if s.Mode&unix.S_IFBLK != 0 ||
|
||||
s.Mode&unix.S_IFCHR != 0 {
|
||||
hdr.Devmajor = int64(unix.Major(uint64(s.Rdev))) //nolint: unconvert
|
||||
hdr.Devminor = int64(unix.Minor(uint64(s.Rdev))) //nolint: unconvert
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getInodeFromStat(stat interface{}) (uint64, error) {
|
||||
s, ok := stat.(*syscall.Stat_t)
|
||||
if !ok {
|
||||
|
5
vendor/github.com/moby/go-archive/archive_windows.go
generated
vendored
5
vendor/github.com/moby/go-archive/archive_windows.go
generated
vendored
@ -41,11 +41,6 @@ func chmodTarEntry(perm os.FileMode) os.FileMode {
|
||||
return perm | 0o111
|
||||
}
|
||||
|
||||
func setHeaderForSpecialDevice(hdr *tar.Header, name string, stat interface{}) (err error) {
|
||||
// do nothing. no notion of Rdev, Nlink in stat on Windows
|
||||
return
|
||||
}
|
||||
|
||||
func getInodeFromStat(stat interface{}) (uint64, error) {
|
||||
// do nothing. no notion of Inode in stat on Windows
|
||||
return 0, nil
|
||||
|
4
vendor/github.com/moby/go-archive/changes.go
generated
vendored
4
vendor/github.com/moby/go-archive/changes.go
generated
vendored
@ -75,7 +75,7 @@ func sameFsTime(a, b time.Time) bool {
|
||||
// Changes walks the path rw and determines changes for the files in the path,
|
||||
// with respect to the parent layers
|
||||
func Changes(layers []string, rw string) ([]Change, error) {
|
||||
return changes(layers, rw, aufsDeletedFile, aufsMetadataSkip)
|
||||
return collectChanges(layers, rw, aufsDeletedFile, aufsMetadataSkip)
|
||||
}
|
||||
|
||||
func aufsMetadataSkip(path string) (skip bool, err error) {
|
||||
@ -103,7 +103,7 @@ type (
|
||||
deleteChange func(string, string, os.FileInfo) (string, error)
|
||||
)
|
||||
|
||||
func changes(layers []string, rw string, dc deleteChange, sc skipChange) ([]Change, error) {
|
||||
func collectChanges(layers []string, rw string, dc deleteChange, sc skipChange) ([]Change, error) {
|
||||
var (
|
||||
changes []Change
|
||||
changedDirs = make(map[string]struct{})
|
||||
|
9
vendor/github.com/moby/go-archive/changes_linux.go
generated
vendored
9
vendor/github.com/moby/go-archive/changes_linux.go
generated
vendored
@ -132,14 +132,7 @@ func (w *walker) walk(path string, i1, i2 os.FileInfo) (err error) {
|
||||
ix1 := 0
|
||||
ix2 := 0
|
||||
|
||||
for {
|
||||
if ix1 >= len(names1) {
|
||||
break
|
||||
}
|
||||
if ix2 >= len(names2) {
|
||||
break
|
||||
}
|
||||
|
||||
for ix1 < len(names1) && ix2 < len(names2) {
|
||||
ni1 := names1[ix1]
|
||||
ni2 := names2[ix2]
|
||||
|
||||
|
7
vendor/github.com/moby/go-archive/chrootarchive/archive.go
generated
vendored
7
vendor/github.com/moby/go-archive/chrootarchive/archive.go
generated
vendored
@ -1,7 +1,7 @@
|
||||
package chrootarchive
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -9,6 +9,7 @@ import (
|
||||
"github.com/moby/sys/user"
|
||||
|
||||
"github.com/moby/go-archive"
|
||||
"github.com/moby/go-archive/compression"
|
||||
)
|
||||
|
||||
// NewArchiver returns a new Archiver which uses chrootarchive.Untar
|
||||
@ -53,7 +54,7 @@ func UntarUncompressed(tarArchive io.Reader, dest string, options *archive.TarOp
|
||||
// Handler for teasing out the automatic decompression
|
||||
func untarHandler(tarArchive io.Reader, dest string, options *archive.TarOptions, decompress bool, root string) error {
|
||||
if tarArchive == nil {
|
||||
return fmt.Errorf("Empty archive")
|
||||
return errors.New("empty archive")
|
||||
}
|
||||
if options == nil {
|
||||
options = &archive.TarOptions{}
|
||||
@ -77,7 +78,7 @@ func untarHandler(tarArchive io.Reader, dest string, options *archive.TarOptions
|
||||
|
||||
r := io.NopCloser(tarArchive)
|
||||
if decompress {
|
||||
decompressedArchive, err := archive.DecompressStream(tarArchive)
|
||||
decompressedArchive, err := compression.DecompressStream(tarArchive)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
3
vendor/github.com/moby/go-archive/chrootarchive/diff_unix.go
generated
vendored
3
vendor/github.com/moby/go-archive/chrootarchive/diff_unix.go
generated
vendored
@ -9,6 +9,7 @@ import (
|
||||
"github.com/moby/sys/userns"
|
||||
|
||||
"github.com/moby/go-archive"
|
||||
"github.com/moby/go-archive/compression"
|
||||
)
|
||||
|
||||
// applyLayerHandler parses a diff in the standard layer format from `layer`, and
|
||||
@ -16,7 +17,7 @@ import (
|
||||
// contents of the layer.
|
||||
func applyLayerHandler(dest string, layer io.Reader, options *archive.TarOptions, decompress bool) (size int64, _ error) {
|
||||
if decompress {
|
||||
decompressed, err := archive.DecompressStream(layer)
|
||||
decompressed, err := compression.DecompressStream(layer)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
5
vendor/github.com/moby/go-archive/chrootarchive/diff_windows.go
generated
vendored
5
vendor/github.com/moby/go-archive/chrootarchive/diff_windows.go
generated
vendored
@ -6,6 +6,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/moby/go-archive"
|
||||
"github.com/moby/go-archive/compression"
|
||||
)
|
||||
|
||||
// applyLayerHandler parses a diff in the standard layer format from `layer`, and
|
||||
@ -13,7 +14,7 @@ import (
|
||||
// contents of the layer.
|
||||
func applyLayerHandler(dest string, layer io.Reader, options *archive.TarOptions, decompress bool) (size int64, err error) {
|
||||
if decompress {
|
||||
decompressed, err := archive.DecompressStream(layer)
|
||||
decompressed, err := compression.DecompressStream(layer)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -26,7 +27,7 @@ func applyLayerHandler(dest string, layer io.Reader, options *archive.TarOptions
|
||||
dest = addLongPathPrefix(filepath.Clean(dest))
|
||||
s, err := archive.UnpackLayer(dest, layer, nil)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("ApplyLayer %s failed UnpackLayer to %s: %s", layer, dest, err)
|
||||
return 0, fmt.Errorf("ApplyLayer %s failed UnpackLayer to %s: %w", layer, dest, err)
|
||||
}
|
||||
|
||||
return s, nil
|
||||
|
263
vendor/github.com/moby/go-archive/compression/compression.go
generated
vendored
Normal file
263
vendor/github.com/moby/go-archive/compression/compression.go
generated
vendored
Normal file
@ -0,0 +1,263 @@
|
||||
package compression
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/bzip2"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/containerd/log"
|
||||
"github.com/klauspost/compress/zstd"
|
||||
)
|
||||
|
||||
// Compression is the state represents if compressed or not.
|
||||
type Compression int
|
||||
|
||||
const (
|
||||
None Compression = 0 // None represents the uncompressed.
|
||||
Bzip2 Compression = 1 // Bzip2 is bzip2 compression algorithm.
|
||||
Gzip Compression = 2 // Gzip is gzip compression algorithm.
|
||||
Xz Compression = 3 // Xz is xz compression algorithm.
|
||||
Zstd Compression = 4 // Zstd is zstd compression algorithm.
|
||||
)
|
||||
|
||||
// Extension returns the extension of a file that uses the specified compression algorithm.
|
||||
func (c *Compression) Extension() string {
|
||||
switch *c {
|
||||
case None:
|
||||
return "tar"
|
||||
case Bzip2:
|
||||
return "tar.bz2"
|
||||
case Gzip:
|
||||
return "tar.gz"
|
||||
case Xz:
|
||||
return "tar.xz"
|
||||
case Zstd:
|
||||
return "tar.zst"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
type readCloserWrapper struct {
|
||||
io.Reader
|
||||
closer func() error
|
||||
}
|
||||
|
||||
func (r *readCloserWrapper) Close() error {
|
||||
if r.closer != nil {
|
||||
return r.closer()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type nopWriteCloser struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (nopWriteCloser) Close() error { return nil }
|
||||
|
||||
var bufioReader32KPool = &sync.Pool{
|
||||
New: func() interface{} { return bufio.NewReaderSize(nil, 32*1024) },
|
||||
}
|
||||
|
||||
type bufferedReader struct {
|
||||
buf *bufio.Reader
|
||||
}
|
||||
|
||||
func newBufferedReader(r io.Reader) *bufferedReader {
|
||||
buf := bufioReader32KPool.Get().(*bufio.Reader)
|
||||
buf.Reset(r)
|
||||
return &bufferedReader{buf}
|
||||
}
|
||||
|
||||
func (r *bufferedReader) Read(p []byte) (int, error) {
|
||||
if r.buf == nil {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n, err := r.buf.Read(p)
|
||||
if errors.Is(err, io.EOF) {
|
||||
r.buf.Reset(nil)
|
||||
bufioReader32KPool.Put(r.buf)
|
||||
r.buf = nil
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (r *bufferedReader) Peek(n int) ([]byte, error) {
|
||||
if r.buf == nil {
|
||||
return nil, io.EOF
|
||||
}
|
||||
return r.buf.Peek(n)
|
||||
}
|
||||
|
||||
// DecompressStream decompresses the archive and returns a ReaderCloser with the decompressed archive.
|
||||
func DecompressStream(archive io.Reader) (io.ReadCloser, error) {
|
||||
buf := newBufferedReader(archive)
|
||||
bs, err := buf.Peek(10)
|
||||
if err != nil && !errors.Is(err, io.EOF) {
|
||||
// Note: we'll ignore any io.EOF error because there are some odd
|
||||
// cases where the layer.tar file will be empty (zero bytes) and
|
||||
// that results in an io.EOF from the Peek() call. So, in those
|
||||
// cases we'll just treat it as a non-compressed stream and
|
||||
// that means just create an empty layer.
|
||||
// See Issue 18170
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch compression := Detect(bs); compression {
|
||||
case None:
|
||||
return &readCloserWrapper{
|
||||
Reader: buf,
|
||||
}, nil
|
||||
case Gzip:
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
gzReader, err := gzipDecompress(ctx, buf)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &readCloserWrapper{
|
||||
Reader: gzReader,
|
||||
closer: func() error {
|
||||
cancel()
|
||||
return gzReader.Close()
|
||||
},
|
||||
}, nil
|
||||
case Bzip2:
|
||||
bz2Reader := bzip2.NewReader(buf)
|
||||
return &readCloserWrapper{
|
||||
Reader: bz2Reader,
|
||||
}, nil
|
||||
case Xz:
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
xzReader, err := xzDecompress(ctx, buf)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &readCloserWrapper{
|
||||
Reader: xzReader,
|
||||
closer: func() error {
|
||||
cancel()
|
||||
return xzReader.Close()
|
||||
},
|
||||
}, nil
|
||||
case Zstd:
|
||||
zstdReader, err := zstd.NewReader(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &readCloserWrapper{
|
||||
Reader: zstdReader,
|
||||
closer: func() error {
|
||||
zstdReader.Close()
|
||||
return nil
|
||||
},
|
||||
}, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported compression format (%d)", compression)
|
||||
}
|
||||
}
|
||||
|
||||
// CompressStream compresses the dest with specified compression algorithm.
|
||||
func CompressStream(dest io.Writer, compression Compression) (io.WriteCloser, error) {
|
||||
switch compression {
|
||||
case None:
|
||||
return nopWriteCloser{dest}, nil
|
||||
case Gzip:
|
||||
return gzip.NewWriter(dest), nil
|
||||
case Bzip2:
|
||||
// archive/bzip2 does not support writing.
|
||||
return nil, errors.New("unsupported compression format: tar.bz2")
|
||||
case Xz:
|
||||
// there is no xz support at all
|
||||
// However, this is not a problem as docker only currently generates gzipped tars
|
||||
return nil, errors.New("unsupported compression format: tar.xz")
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported compression format (%d)", compression)
|
||||
}
|
||||
}
|
||||
|
||||
func xzDecompress(ctx context.Context, archive io.Reader) (io.ReadCloser, error) {
|
||||
args := []string{"xz", "-d", "-c", "-q"}
|
||||
|
||||
return cmdStream(exec.CommandContext(ctx, args[0], args[1:]...), archive)
|
||||
}
|
||||
|
||||
func gzipDecompress(ctx context.Context, buf io.Reader) (io.ReadCloser, error) {
|
||||
if noPigzEnv := os.Getenv("MOBY_DISABLE_PIGZ"); noPigzEnv != "" {
|
||||
noPigz, err := strconv.ParseBool(noPigzEnv)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Warn("invalid value in MOBY_DISABLE_PIGZ env var")
|
||||
}
|
||||
if noPigz {
|
||||
log.G(ctx).Debugf("Use of pigz is disabled due to MOBY_DISABLE_PIGZ=%s", noPigzEnv)
|
||||
return gzip.NewReader(buf)
|
||||
}
|
||||
}
|
||||
|
||||
unpigzPath, err := exec.LookPath("unpigz")
|
||||
if err != nil {
|
||||
log.G(ctx).Debugf("unpigz binary not found, falling back to go gzip library")
|
||||
return gzip.NewReader(buf)
|
||||
}
|
||||
|
||||
log.G(ctx).Debugf("Using %s to decompress", unpigzPath)
|
||||
|
||||
return cmdStream(exec.CommandContext(ctx, unpigzPath, "-d", "-c"), buf)
|
||||
}
|
||||
|
||||
// cmdStream executes a command, and returns its stdout as a stream.
|
||||
// If the command fails to run or doesn't complete successfully, an error
|
||||
// will be returned, including anything written on stderr.
|
||||
func cmdStream(cmd *exec.Cmd, in io.Reader) (io.ReadCloser, error) {
|
||||
reader, writer := io.Pipe()
|
||||
|
||||
cmd.Stdin = in
|
||||
cmd.Stdout = writer
|
||||
|
||||
var errBuf bytes.Buffer
|
||||
cmd.Stderr = &errBuf
|
||||
|
||||
// Run the command and return the pipe
|
||||
if err := cmd.Start(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Ensure the command has exited before we clean anything up
|
||||
done := make(chan struct{})
|
||||
|
||||
// Copy stdout to the returned pipe
|
||||
go func() {
|
||||
if err := cmd.Wait(); err != nil {
|
||||
_ = writer.CloseWithError(fmt.Errorf("%w: %s", err, errBuf.String()))
|
||||
} else {
|
||||
_ = writer.Close()
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
|
||||
return &readCloserWrapper{
|
||||
Reader: reader,
|
||||
closer: func() error {
|
||||
// Close pipeR, and then wait for the command to complete before returning. We have to close pipeR first, as
|
||||
// cmd.Wait waits for any non-file stdout/stderr/stdin to close.
|
||||
err := reader.Close()
|
||||
<-done
|
||||
return err
|
||||
},
|
||||
}, nil
|
||||
}
|
65
vendor/github.com/moby/go-archive/compression/compression_detect.go
generated
vendored
Normal file
65
vendor/github.com/moby/go-archive/compression/compression_detect.go
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
package compression
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
const (
|
||||
zstdMagicSkippableStart = 0x184D2A50
|
||||
zstdMagicSkippableMask = 0xFFFFFFF0
|
||||
)
|
||||
|
||||
var (
|
||||
bzip2Magic = []byte{0x42, 0x5A, 0x68}
|
||||
gzipMagic = []byte{0x1F, 0x8B, 0x08}
|
||||
xzMagic = []byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}
|
||||
zstdMagic = []byte{0x28, 0xb5, 0x2f, 0xfd}
|
||||
)
|
||||
|
||||
type matcher = func([]byte) bool
|
||||
|
||||
// Detect detects the compression algorithm of the source.
|
||||
func Detect(source []byte) Compression {
|
||||
compressionMap := map[Compression]matcher{
|
||||
Bzip2: magicNumberMatcher(bzip2Magic),
|
||||
Gzip: magicNumberMatcher(gzipMagic),
|
||||
Xz: magicNumberMatcher(xzMagic),
|
||||
Zstd: zstdMatcher(),
|
||||
}
|
||||
for _, compression := range []Compression{Bzip2, Gzip, Xz, Zstd} {
|
||||
fn := compressionMap[compression]
|
||||
if fn(source) {
|
||||
return compression
|
||||
}
|
||||
}
|
||||
return None
|
||||
}
|
||||
|
||||
func magicNumberMatcher(m []byte) matcher {
|
||||
return func(source []byte) bool {
|
||||
return bytes.HasPrefix(source, m)
|
||||
}
|
||||
}
|
||||
|
||||
// zstdMatcher detects zstd compression algorithm.
|
||||
// Zstandard compressed data is made of one or more frames.
|
||||
// There are two frame formats defined by Zstandard: Zstandard frames and Skippable frames.
|
||||
// See https://datatracker.ietf.org/doc/html/rfc8878#section-3 for more details.
|
||||
func zstdMatcher() matcher {
|
||||
return func(source []byte) bool {
|
||||
if bytes.HasPrefix(source, zstdMagic) {
|
||||
// Zstandard frame
|
||||
return true
|
||||
}
|
||||
// skippable frame
|
||||
if len(source) < 8 {
|
||||
return false
|
||||
}
|
||||
// magic number from 0x184D2A50 to 0x184D2A5F.
|
||||
if binary.LittleEndian.Uint32(source[:4])&zstdMagicSkippableMask == zstdMagicSkippableStart {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
3
vendor/github.com/moby/go-archive/copy.go
generated
vendored
3
vendor/github.com/moby/go-archive/copy.go
generated
vendored
@ -128,7 +128,6 @@ func TarResourceRebase(sourcePath, rebaseName string) (content io.ReadCloser, _
|
||||
func TarResourceRebaseOpts(sourceBase string, rebaseName string) *TarOptions {
|
||||
filter := []string{sourceBase}
|
||||
return &TarOptions{
|
||||
Compression: Uncompressed,
|
||||
IncludeFiles: filter,
|
||||
IncludeSourceDir: true,
|
||||
RebaseNames: map[string]string{
|
||||
@ -335,7 +334,7 @@ func RebaseArchiveEntries(srcContent io.Reader, oldBase, newBase string) io.Read
|
||||
|
||||
for {
|
||||
hdr, err := srcTar.Next()
|
||||
if err == io.EOF {
|
||||
if errors.Is(err, io.EOF) {
|
||||
// Signals end of archive.
|
||||
rebasedTar.Close()
|
||||
w.Close()
|
||||
|
4
vendor/github.com/moby/go-archive/dev_freebsd.go
generated
vendored
4
vendor/github.com/moby/go-archive/dev_freebsd.go
generated
vendored
@ -4,4 +4,6 @@ package archive
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
var mknod = unix.Mknod
|
||||
func mknod(path string, mode uint32, dev uint64) error {
|
||||
return unix.Mknod(path, mode, dev)
|
||||
}
|
||||
|
19
vendor/github.com/moby/go-archive/diff.go
generated
vendored
19
vendor/github.com/moby/go-archive/diff.go
generated
vendored
@ -3,6 +3,7 @@ package archive
|
||||
import (
|
||||
"archive/tar"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
@ -11,6 +12,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/log"
|
||||
|
||||
"github.com/moby/go-archive/compression"
|
||||
)
|
||||
|
||||
// UnpackLayer unpack `layer` to a `dest`. The stream `layer` can be
|
||||
@ -35,7 +38,7 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
|
||||
// Iterate through the files in the archive.
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
if errors.Is(err, io.EOF) {
|
||||
// end of tar archive
|
||||
break
|
||||
}
|
||||
@ -149,7 +152,7 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
|
||||
// the layer is also a directory. Then we want to merge them (i.e.
|
||||
// just apply the metadata from the layer).
|
||||
if fi, err := os.Lstat(path); err == nil {
|
||||
if !(fi.IsDir() && hdr.Typeflag == tar.TypeDir) {
|
||||
if !fi.IsDir() || hdr.Typeflag != tar.TypeDir {
|
||||
if err := os.RemoveAll(path); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -165,7 +168,7 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
|
||||
linkBasename := filepath.Base(hdr.Linkname)
|
||||
srcHdr = aufsHardlinks[linkBasename]
|
||||
if srcHdr == nil {
|
||||
return 0, fmt.Errorf("Invalid aufs hardlink")
|
||||
return 0, errors.New("invalid aufs hardlink")
|
||||
}
|
||||
tmpFile, err := os.Open(filepath.Join(aufsTempdir, linkBasename))
|
||||
if err != nil {
|
||||
@ -221,18 +224,18 @@ func ApplyUncompressedLayer(dest string, layer io.Reader, options *TarOptions) (
|
||||
|
||||
// IsEmpty checks if the tar archive is empty (doesn't contain any entries).
|
||||
func IsEmpty(rd io.Reader) (bool, error) {
|
||||
decompRd, err := DecompressStream(rd)
|
||||
decompRd, err := compression.DecompressStream(rd)
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("failed to decompress archive: %v", err)
|
||||
return true, fmt.Errorf("failed to decompress archive: %w", err)
|
||||
}
|
||||
defer decompRd.Close()
|
||||
|
||||
tarReader := tar.NewReader(decompRd)
|
||||
if _, err := tarReader.Next(); err != nil {
|
||||
if err == io.EOF {
|
||||
if errors.Is(err, io.EOF) {
|
||||
return true, nil
|
||||
}
|
||||
return false, fmt.Errorf("failed to read next archive header: %v", err)
|
||||
return false, fmt.Errorf("failed to read next archive header: %w", err)
|
||||
}
|
||||
|
||||
return false, nil
|
||||
@ -247,7 +250,7 @@ func applyLayerHandler(dest string, layer io.Reader, options *TarOptions, decomp
|
||||
defer restore()
|
||||
|
||||
if decompress {
|
||||
decompLayer, err := DecompressStream(layer)
|
||||
decompLayer, err := compression.DecompressStream(layer)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
16
vendor/github.com/moby/go-archive/internal/mounttree/switchroot_linux.go
generated
vendored
16
vendor/github.com/moby/go-archive/internal/mounttree/switchroot_linux.go
generated
vendored
@ -26,7 +26,7 @@ func SwitchRoot(path string) error {
|
||||
// setup oldRoot for pivot_root
|
||||
pivotDir, err := os.MkdirTemp(path, ".pivot_root")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error setting up pivot dir: %v", err)
|
||||
return fmt.Errorf("error setting up pivot dir: %w", err)
|
||||
}
|
||||
|
||||
var mounted bool
|
||||
@ -45,7 +45,7 @@ func SwitchRoot(path string) error {
|
||||
// pivotDir doesn't exist if pivot_root failed and chroot+chdir was successful
|
||||
// because we already cleaned it up on failed pivot_root
|
||||
if errCleanup != nil && !os.IsNotExist(errCleanup) {
|
||||
errCleanup = fmt.Errorf("Error cleaning up after pivot: %v", errCleanup)
|
||||
errCleanup = fmt.Errorf("error cleaning up after pivot: %w", errCleanup)
|
||||
if err == nil {
|
||||
err = errCleanup
|
||||
}
|
||||
@ -55,7 +55,7 @@ func SwitchRoot(path string) error {
|
||||
if err := unix.PivotRoot(path, pivotDir); err != nil {
|
||||
// If pivot fails, fall back to the normal chroot after cleaning up temp dir
|
||||
if err := os.Remove(pivotDir); err != nil {
|
||||
return fmt.Errorf("Error cleaning up after failed pivot: %v", err)
|
||||
return fmt.Errorf("error cleaning up after failed pivot: %w", err)
|
||||
}
|
||||
return realChroot(path)
|
||||
}
|
||||
@ -66,17 +66,17 @@ func SwitchRoot(path string) error {
|
||||
pivotDir = filepath.Join("/", filepath.Base(pivotDir))
|
||||
|
||||
if err := unix.Chdir("/"); err != nil {
|
||||
return fmt.Errorf("Error changing to new root: %v", err)
|
||||
return fmt.Errorf("error changing to new root: %w", err)
|
||||
}
|
||||
|
||||
// Make the pivotDir (where the old root lives) private so it can be unmounted without propagating to the host
|
||||
if err := unix.Mount("", pivotDir, "", unix.MS_PRIVATE|unix.MS_REC, ""); err != nil {
|
||||
return fmt.Errorf("Error making old root private after pivot: %v", err)
|
||||
return fmt.Errorf("error making old root private after pivot: %w", err)
|
||||
}
|
||||
|
||||
// Now unmount the old root so it's no longer visible from the new root
|
||||
if err := unix.Unmount(pivotDir, unix.MNT_DETACH); err != nil {
|
||||
return fmt.Errorf("Error while unmounting old root after pivot: %v", err)
|
||||
return fmt.Errorf("error while unmounting old root after pivot: %w", err)
|
||||
}
|
||||
mounted = false
|
||||
|
||||
@ -85,10 +85,10 @@ func SwitchRoot(path string) error {
|
||||
|
||||
func realChroot(path string) error {
|
||||
if err := unix.Chroot(path); err != nil {
|
||||
return fmt.Errorf("Error after fallback to chroot: %v", err)
|
||||
return fmt.Errorf("error after fallback to chroot: %w", err)
|
||||
}
|
||||
if err := unix.Chdir("/"); err != nil {
|
||||
return fmt.Errorf("Error changing to new root after chroot: %v", err)
|
||||
return fmt.Errorf("error changing to new root after chroot: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
67
vendor/github.com/moby/go-archive/tarheader/tarheader.go
generated
vendored
Normal file
67
vendor/github.com/moby/go-archive/tarheader/tarheader.go
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
package tarheader
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"os"
|
||||
)
|
||||
|
||||
// assert that we implement [tar.FileInfoNames].
|
||||
var _ tar.FileInfoNames = (*nosysFileInfo)(nil)
|
||||
|
||||
// nosysFileInfo hides the system-dependent info of the wrapped FileInfo to
|
||||
// prevent tar.FileInfoHeader from introspecting it and potentially calling into
|
||||
// glibc.
|
||||
//
|
||||
// It implements [tar.FileInfoNames] to further prevent [tar.FileInfoHeader]
|
||||
// from performing any lookups on go1.23 and up. see https://go.dev/issue/50102
|
||||
type nosysFileInfo struct {
|
||||
os.FileInfo
|
||||
}
|
||||
|
||||
// Uname stubs out looking up username. It implements [tar.FileInfoNames]
|
||||
// to prevent [tar.FileInfoHeader] from loading libraries to perform
|
||||
// username lookups.
|
||||
func (fi nosysFileInfo) Uname() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Gname stubs out looking up group-name. It implements [tar.FileInfoNames]
|
||||
// to prevent [tar.FileInfoHeader] from loading libraries to perform
|
||||
// username lookups.
|
||||
func (fi nosysFileInfo) Gname() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (fi nosysFileInfo) Sys() interface{} {
|
||||
// A Sys value of type *tar.Header is safe as it is system-independent.
|
||||
// The tar.FileInfoHeader function copies the fields into the returned
|
||||
// header without performing any OS lookups.
|
||||
if sys, ok := fi.FileInfo.Sys().(*tar.Header); ok {
|
||||
return sys
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FileInfoHeaderNoLookups creates a partially-populated tar.Header from fi.
|
||||
//
|
||||
// Compared to the archive/tar.FileInfoHeader function, this function is safe to
|
||||
// call from a chrooted process as it does not populate fields which would
|
||||
// require operating system lookups. It behaves identically to
|
||||
// tar.FileInfoHeader when fi is a FileInfo value returned from
|
||||
// tar.Header.FileInfo().
|
||||
//
|
||||
// When fi is a FileInfo for a native file, such as returned from os.Stat() and
|
||||
// os.Lstat(), the returned Header value differs from one returned from
|
||||
// tar.FileInfoHeader in the following ways. The Uname and Gname fields are not
|
||||
// set as OS lookups would be required to populate them. The AccessTime and
|
||||
// ChangeTime fields are not currently set (not yet implemented) although that
|
||||
// is subject to change. Callers which require the AccessTime or ChangeTime
|
||||
// fields to be zeroed should explicitly zero them out in the returned Header
|
||||
// value to avoid any compatibility issues in the future.
|
||||
func FileInfoHeaderNoLookups(fi os.FileInfo, link string) (*tar.Header, error) {
|
||||
hdr, err := tar.FileInfoHeader(nosysFileInfo{fi}, link)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return hdr, sysStat(fi, hdr)
|
||||
}
|
46
vendor/github.com/moby/go-archive/tarheader/tarheader_unix.go
generated
vendored
Normal file
46
vendor/github.com/moby/go-archive/tarheader/tarheader_unix.go
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
//go:build !windows
|
||||
|
||||
package tarheader
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// sysStat populates hdr from system-dependent fields of fi without performing
|
||||
// any OS lookups.
|
||||
func sysStat(fi os.FileInfo, hdr *tar.Header) error {
|
||||
// Devmajor and Devminor are only needed for special devices.
|
||||
|
||||
// In FreeBSD, RDev for regular files is -1 (unless overridden by FS):
|
||||
// https://cgit.freebsd.org/src/tree/sys/kern/vfs_default.c?h=stable/13#n1531
|
||||
// (NODEV is -1: https://cgit.freebsd.org/src/tree/sys/sys/param.h?h=stable/13#n241).
|
||||
|
||||
// ZFS in particular does not override the default:
|
||||
// https://cgit.freebsd.org/src/tree/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c?h=stable/13#n2027
|
||||
|
||||
// Since `Stat_t.Rdev` is uint64, the cast turns -1 into (2^64 - 1).
|
||||
// Such large values cannot be encoded in a tar header.
|
||||
if runtime.GOOS == "freebsd" && hdr.Typeflag != tar.TypeBlock && hdr.Typeflag != tar.TypeChar {
|
||||
return nil
|
||||
}
|
||||
s, ok := fi.Sys().(*syscall.Stat_t)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
hdr.Uid = int(s.Uid)
|
||||
hdr.Gid = int(s.Gid)
|
||||
|
||||
if s.Mode&unix.S_IFBLK != 0 ||
|
||||
s.Mode&unix.S_IFCHR != 0 {
|
||||
hdr.Devmajor = int64(unix.Major(uint64(s.Rdev))) //nolint: unconvert
|
||||
hdr.Devminor = int64(unix.Minor(uint64(s.Rdev))) //nolint: unconvert
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
12
vendor/github.com/moby/go-archive/tarheader/tarheader_windows.go
generated
vendored
Normal file
12
vendor/github.com/moby/go-archive/tarheader/tarheader_windows.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
package tarheader
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"os"
|
||||
)
|
||||
|
||||
// sysStat populates hdr from system-dependent fields of fi without performing
|
||||
// any OS lookups. It is a no-op on Windows.
|
||||
func sysStat(os.FileInfo, *tar.Header) error {
|
||||
return nil
|
||||
}
|
4
vendor/modules.txt
vendored
4
vendor/modules.txt
vendored
@ -925,12 +925,14 @@ github.com/moby/buildkit/worker/label
|
||||
# github.com/moby/docker-image-spec v1.3.1
|
||||
## explicit; go 1.18
|
||||
github.com/moby/docker-image-spec/specs-go/v1
|
||||
# github.com/moby/go-archive v0.0.0-20250404171912-21f3f3385ab7
|
||||
# github.com/moby/go-archive v0.1.0
|
||||
## explicit; go 1.23.0
|
||||
github.com/moby/go-archive
|
||||
github.com/moby/go-archive/chrootarchive
|
||||
github.com/moby/go-archive/compression
|
||||
github.com/moby/go-archive/internal/mounttree
|
||||
github.com/moby/go-archive/internal/unshare
|
||||
github.com/moby/go-archive/tarheader
|
||||
# github.com/moby/ipvs v1.1.0
|
||||
## explicit; go 1.17
|
||||
github.com/moby/ipvs
|
||||
|
Loading…
x
Reference in New Issue
Block a user