vendor: update buildkit to v0.32.0-rc2
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
This commit is contained in:
@@ -30,8 +30,8 @@ require (
|
||||
github.com/hashicorp/hcl/v2 v2.24.0
|
||||
github.com/in-toto/in-toto-golang v0.11.0
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||
github.com/moby/buildkit v0.32.0-rc1
|
||||
github.com/moby/go-archive v0.2.0
|
||||
github.com/moby/buildkit v0.32.0-rc2
|
||||
github.com/moby/go-archive v0.2.1
|
||||
github.com/moby/moby/api v1.55.0
|
||||
github.com/moby/moby/client v0.5.0
|
||||
github.com/moby/policy-helpers v0.0.0-20260722051018-856be88baec4
|
||||
|
||||
@@ -395,12 +395,12 @@ github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/moby/buildkit v0.32.0-rc1 h1:gYtsqXOSA43i9zTf2w+0KzTxpZYao0ZYr/AaGx0RJhs=
|
||||
github.com/moby/buildkit v0.32.0-rc1/go.mod h1:0GB/EJ1d+4VIVqIAgy3asaoGkVXy7IrDfVy7mPhOvg8=
|
||||
github.com/moby/buildkit v0.32.0-rc2 h1:2XiJmnPSZyJ0akoKG2ShWAFbWRT5DKvyAI2+Johq9Ac=
|
||||
github.com/moby/buildkit v0.32.0-rc2/go.mod h1:Y10FBWvqxl/Wmhdzjee1Y2wQfjifTiwxENIUdaVNdME=
|
||||
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.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8=
|
||||
github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU=
|
||||
github.com/moby/go-archive v0.2.1 h1:fAa0wUS/ikZKyx7o/1fhUYmhZ7RgpthdeoDhJvunTLc=
|
||||
github.com/moby/go-archive v0.2.1/go.mod h1:Npdv43fFqlhZW7Xo8fbm3ZMYFvAGNviUPqX21VERbcE=
|
||||
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
|
||||
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
||||
github.com/moby/moby/api v1.55.0 h1:2/sexvQyqIWS8pRSCFddBfpW2qE7vR7FCL+vN8pxwMc=
|
||||
|
||||
+12
@@ -9,6 +9,7 @@ issues:
|
||||
linters:
|
||||
enable:
|
||||
- errorlint
|
||||
- gosec
|
||||
- unconvert
|
||||
- unparam
|
||||
exclusions:
|
||||
@@ -16,7 +17,18 @@ linters:
|
||||
presets:
|
||||
- comments
|
||||
- std-error-handling
|
||||
rules:
|
||||
# Ignore "G204: Subprocess launched with a potential tainted input or cmd arguments"
|
||||
- path: '(.+)_test\.go'
|
||||
linters:
|
||||
- gosec
|
||||
text: 'G204: Subprocess launched'
|
||||
settings:
|
||||
gosec:
|
||||
excludes:
|
||||
- G301 # Expect directory permissions to be 0750 or less
|
||||
- G304 # Potential file inclusion via variable
|
||||
- G306 # Expect WriteFile permissions to be 0600 or less
|
||||
staticcheck:
|
||||
# Enable all options, with some exceptions.
|
||||
# For defaults, see https://golangci-lint.run/usage/linters/#staticcheck
|
||||
|
||||
+116
-98
@@ -46,9 +46,18 @@ type (
|
||||
|
||||
// TarOptions wraps the tar options.
|
||||
TarOptions struct {
|
||||
IncludeFiles []string
|
||||
ExcludePatterns []string
|
||||
Compression compression.Compression
|
||||
// IncludeFiles lists archive-relative paths to include.
|
||||
// Paths use POSIX ('/') separators.
|
||||
IncludeFiles []string
|
||||
|
||||
// ExcludePatterns lists archive-relative exclude patterns.
|
||||
// Patterns use POSIX ('/') separators, matching patternmatcher semantics.
|
||||
ExcludePatterns []string
|
||||
Compression compression.Compression
|
||||
// NoLchown disables applying ownership from the archive to extracted files
|
||||
// and directories. Despite its historical name, it applies to all ownership
|
||||
// changes, leaving extracted filesystem objects owned by the user performing
|
||||
// the extraction.
|
||||
NoLchown bool
|
||||
IDMap user.IdentityMapping
|
||||
ChownOpts *ChownOpts
|
||||
@@ -86,10 +95,14 @@ func NewDefaultArchiver() *Archiver {
|
||||
return &Archiver{Untar: Untar}
|
||||
}
|
||||
|
||||
// breakoutError is used to differentiate errors related to breaking out
|
||||
// When testing archive breakout in the unit tests, this error is expected
|
||||
// in order for the test to pass.
|
||||
type breakoutError error
|
||||
// breakoutErr marks errors caused by archive breakout attempts.
|
||||
// Unit tests use it to distinguish expected breakout failures from other
|
||||
// errors.
|
||||
type breakoutErr struct{ error }
|
||||
|
||||
func breakoutError(err error) error {
|
||||
return &breakoutErr{error: err}
|
||||
}
|
||||
|
||||
const (
|
||||
AUFSWhiteoutFormat WhiteoutFormat = 0 // AUFSWhiteoutFormat is the default format for whiteouts
|
||||
@@ -98,17 +111,17 @@ const (
|
||||
|
||||
// IsArchivePath checks if the (possibly compressed) file at the given path
|
||||
// starts with a tar file header.
|
||||
func IsArchivePath(path string) bool {
|
||||
file, err := os.Open(path)
|
||||
func IsArchivePath(filePath string) bool {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer file.Close()
|
||||
defer func() { _ = file.Close() }()
|
||||
rdr, err := compression.DecompressStream(file)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer rdr.Close()
|
||||
defer func() { _ = rdr.Close() }()
|
||||
r := tar.NewReader(rdr)
|
||||
_, err = r.Next()
|
||||
return err == nil
|
||||
@@ -129,8 +142,10 @@ func ReplaceFileTarWrapper(inputTarStream io.ReadCloser, mods map[string]TarModi
|
||||
go func() {
|
||||
tarReader := tar.NewReader(inputTarStream)
|
||||
tarWriter := tar.NewWriter(pipeWriter)
|
||||
defer inputTarStream.Close()
|
||||
defer tarWriter.Close()
|
||||
defer func() {
|
||||
_ = tarWriter.Close()
|
||||
_ = inputTarStream.Close()
|
||||
}()
|
||||
|
||||
modify := func(name string, original *tar.Header, modifier TarModifierFunc, tarReader io.Reader) error {
|
||||
header, data, err := modifier(name, original, tarReader)
|
||||
@@ -164,7 +179,7 @@ func ReplaceFileTarWrapper(inputTarStream io.ReadCloser, mods map[string]TarModi
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
pipeWriter.CloseWithError(err)
|
||||
_ = pipeWriter.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -172,11 +187,11 @@ func ReplaceFileTarWrapper(inputTarStream io.ReadCloser, mods map[string]TarModi
|
||||
if !ok {
|
||||
// No modifiers for this file, copy the header and data
|
||||
if err := tarWriter.WriteHeader(originalHeader); err != nil {
|
||||
pipeWriter.CloseWithError(err)
|
||||
_ = pipeWriter.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
if err := copyWithBuffer(tarWriter, tarReader); err != nil {
|
||||
pipeWriter.CloseWithError(err)
|
||||
_ = pipeWriter.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
continue
|
||||
@@ -184,7 +199,7 @@ func ReplaceFileTarWrapper(inputTarStream io.ReadCloser, mods map[string]TarModi
|
||||
delete(mods, originalHeader.Name)
|
||||
|
||||
if err := modify(originalHeader.Name, originalHeader, modifier, tarReader); err != nil {
|
||||
pipeWriter.CloseWithError(err)
|
||||
_ = pipeWriter.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -192,12 +207,12 @@ func ReplaceFileTarWrapper(inputTarStream io.ReadCloser, mods map[string]TarModi
|
||||
// Apply the modifiers that haven't matched any files in the archive
|
||||
for name, modifier := range mods {
|
||||
if err := modify(name, nil, modifier, nil); err != nil {
|
||||
pipeWriter.CloseWithError(err)
|
||||
_ = pipeWriter.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
pipeWriter.Close()
|
||||
_ = pipeWriter.Close()
|
||||
}()
|
||||
return pipeReader
|
||||
}
|
||||
@@ -218,7 +233,7 @@ func FileInfoHeader(name string, fi os.FileInfo, link string) (*tar.Header, erro
|
||||
hdr.ModTime = hdr.ModTime.Truncate(time.Second)
|
||||
hdr.AccessTime = time.Time{}
|
||||
hdr.ChangeTime = time.Time{}
|
||||
hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
|
||||
hdr.Mode = chmodTarEntry(hdr.Mode)
|
||||
hdr.Name = canonicalTarName(name, fi.IsDir())
|
||||
return hdr, nil
|
||||
}
|
||||
@@ -227,7 +242,7 @@ const paxSchilyXattr = "SCHILY.xattr."
|
||||
|
||||
// ReadSecurityXattrToTarHeader reads security.capability xattr from filesystem
|
||||
// to a tar header
|
||||
func ReadSecurityXattrToTarHeader(path string, hdr *tar.Header) error {
|
||||
func ReadSecurityXattrToTarHeader(filePath string, hdr *tar.Header) error {
|
||||
const (
|
||||
// Values based on linux/include/uapi/linux/capability.h
|
||||
xattrCapsSz2 = 20
|
||||
@@ -235,7 +250,7 @@ func ReadSecurityXattrToTarHeader(path string, hdr *tar.Header) error {
|
||||
vfsCapRevision2 = 2
|
||||
vfsCapRevision3 = 3
|
||||
)
|
||||
capability, _ := lgetxattr(path, "security.capability")
|
||||
capability, _ := lgetxattr(filePath, "security.capability")
|
||||
if capability != nil {
|
||||
if capability[versionOffset] == vfsCapRevision3 {
|
||||
// Convert VFS_CAP_REVISION_3 to VFS_CAP_REVISION_2 as root UID makes no
|
||||
@@ -292,9 +307,10 @@ func canonicalTarName(name string, isDir bool) string {
|
||||
return name
|
||||
}
|
||||
|
||||
// addTarFile adds to the tar archive a file from `path` as `name`
|
||||
func (ta *tarAppender) addTarFile(path, name string) error {
|
||||
fi, err := os.Lstat(path)
|
||||
// addTarFile adds to the tar archive a file from `srcPath` as `name`
|
||||
func (ta *tarAppender) addTarFile(srcPath, archivePath string) error {
|
||||
archivePath = filepath.ToSlash(archivePath)
|
||||
fi, err := os.Lstat(srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -302,17 +318,17 @@ func (ta *tarAppender) addTarFile(path, name string) error {
|
||||
var link string
|
||||
if fi.Mode()&os.ModeSymlink != 0 {
|
||||
var err error
|
||||
link, err = os.Readlink(path)
|
||||
link, err = os.Readlink(srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
hdr, err := FileInfoHeader(name, fi, link)
|
||||
hdr, err := FileInfoHeader(archivePath, fi, link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ReadSecurityXattrToTarHeader(path, hdr); err != nil {
|
||||
if err := ReadSecurityXattrToTarHeader(srcPath, hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -321,7 +337,7 @@ func (ta *tarAppender) addTarFile(path, name string) error {
|
||||
if !fi.IsDir() && hasHardlinks(fi) {
|
||||
inode, err := getInodeFromStat(fi.Sys())
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("unexpected file info for %q: %w", srcPath, err)
|
||||
}
|
||||
// a link should have a name that it links too
|
||||
// and that linked name should be first in the tar archive
|
||||
@@ -330,7 +346,7 @@ func (ta *tarAppender) addTarFile(path, name string) error {
|
||||
hdr.Linkname = oldpath
|
||||
hdr.Size = 0 // This Must be here for the writer math to add up!
|
||||
} else {
|
||||
ta.SeenFiles[inode] = name
|
||||
ta.SeenFiles[inode] = hdr.Name
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,7 +375,7 @@ func (ta *tarAppender) addTarFile(path, name string) error {
|
||||
}
|
||||
|
||||
if ta.WhiteoutConverter != nil {
|
||||
wo, err := ta.WhiteoutConverter.ConvertWrite(hdr, path, fi)
|
||||
wo, err := ta.WhiteoutConverter.ConvertWrite(hdr, srcPath, fi)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -370,12 +386,12 @@ func (ta *tarAppender) addTarFile(path, name string) error {
|
||||
// hdr may have been updated to be a whiteout with returning
|
||||
// a whiteout header
|
||||
if wo != nil {
|
||||
if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 {
|
||||
return fmt.Errorf("tar: cannot use whiteout for non-empty file %q", hdr.Name)
|
||||
}
|
||||
if err := ta.TarWriter.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 {
|
||||
return fmt.Errorf("tar: cannot use whiteout for non-empty file")
|
||||
}
|
||||
hdr = wo
|
||||
}
|
||||
}
|
||||
@@ -387,13 +403,13 @@ func (ta *tarAppender) addTarFile(path, name string) error {
|
||||
if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 {
|
||||
// We use sequential file access to avoid depleting the standby list on
|
||||
// Windows. On Linux, this equates to a regular os.Open.
|
||||
file, err := sequential.Open(path)
|
||||
file, err := sequential.Open(srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = copyWithBuffer(ta.TarWriter, file)
|
||||
file.Close()
|
||||
_ = file.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -402,7 +418,7 @@ func (ta *tarAppender) addTarFile(path, name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, opts *TarOptions) error {
|
||||
func createTarFile(dstPath, extractDir string, hdr *tar.Header, reader io.Reader, opts *TarOptions) error {
|
||||
var (
|
||||
Lchown = true
|
||||
inUserns, bestEffortXattrs bool
|
||||
@@ -426,8 +442,8 @@ 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 err := os.Mkdir(path, hdrInfo.Mode()); err != nil {
|
||||
if fi, err := os.Lstat(dstPath); err != nil || !fi.IsDir() {
|
||||
if err := os.Mkdir(dstPath, hdrInfo.Mode()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -435,7 +451,7 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, o
|
||||
case tar.TypeReg:
|
||||
// Source is regular file. We use sequential file access to avoid depleting
|
||||
// the standby list on Windows. On Linux, this equates to a regular os.OpenFile.
|
||||
file, err := sequential.OpenFile(path, os.O_CREATE|os.O_WRONLY, hdrInfo.Mode())
|
||||
file, err := sequential.OpenFile(dstPath, os.O_CREATE|os.O_WRONLY, hdrInfo.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -447,20 +463,20 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, o
|
||||
|
||||
case tar.TypeBlock, tar.TypeChar:
|
||||
if inUserns { // cannot create devices in a userns
|
||||
log.G(context.TODO()).WithFields(log.Fields{"path": path, "type": hdr.Typeflag}).Debug("skipping device nodes in a userns")
|
||||
log.G(context.TODO()).WithFields(log.Fields{"path": dstPath, "type": hdr.Typeflag}).Debug("skipping device nodes in a userns")
|
||||
return nil
|
||||
}
|
||||
// Handle this is an OS-specific way
|
||||
if err := handleTarTypeBlockCharFifo(hdr, path); err != nil {
|
||||
if err := handleTarTypeBlockCharFifo(hdr, dstPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case tar.TypeFifo:
|
||||
// Handle this is an OS-specific way
|
||||
if err := handleTarTypeBlockCharFifo(hdr, path); err != nil {
|
||||
if err := handleTarTypeBlockCharFifo(hdr, dstPath); err != nil {
|
||||
if inUserns && errors.Is(err, syscall.EPERM) {
|
||||
// In most cases, cannot create a fifo if running in user namespace
|
||||
log.G(context.TODO()).WithFields(log.Fields{"error": err, "path": path, "type": hdr.Typeflag}).Debug("creating fifo node in a userns")
|
||||
log.G(context.TODO()).WithFields(log.Fields{"error": err, "path": dstPath, "type": hdr.Typeflag}).Debug("creating fifo node in a userns")
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
@@ -468,26 +484,26 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, o
|
||||
|
||||
case tar.TypeLink:
|
||||
// #nosec G305 -- The target path is checked for path traversal.
|
||||
targetPath := filepath.Join(extractDir, hdr.Linkname)
|
||||
linkTarget := filepath.Join(extractDir, hdr.Linkname)
|
||||
// check for hardlink breakout
|
||||
if !strings.HasPrefix(targetPath, extractDir) {
|
||||
return breakoutError(fmt.Errorf("invalid hardlink %q -> %q", targetPath, hdr.Linkname))
|
||||
if !strings.HasPrefix(linkTarget, extractDir) {
|
||||
return breakoutError(fmt.Errorf("invalid hardlink %q -> %q", linkTarget, hdr.Linkname))
|
||||
}
|
||||
if err := os.Link(targetPath, path); err != nil {
|
||||
if err := os.Link(linkTarget, dstPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case tar.TypeSymlink:
|
||||
// path -> hdr.Linkname = targetPath
|
||||
// e.g. /extractDir/path/to/symlink -> ../2/file = /extractDir/path/2/file
|
||||
targetPath := filepath.Join(filepath.Dir(path), hdr.Linkname) // #nosec G305 -- The target path is checked for path traversal.
|
||||
targetPath := filepath.Join(filepath.Dir(dstPath), hdr.Linkname) // #nosec G305 -- The target path is checked for path traversal.
|
||||
|
||||
// the reason we don't need to check symlinks in the path (with FollowSymlinkInScope) is because
|
||||
// that symlink would first have to be created, which would be caught earlier, at this very check:
|
||||
if !strings.HasPrefix(targetPath, extractDir) {
|
||||
return breakoutError(fmt.Errorf("invalid symlink %q -> %q", path, hdr.Linkname))
|
||||
return breakoutError(fmt.Errorf("invalid symlink %q -> %q", dstPath, hdr.Linkname))
|
||||
}
|
||||
if err := os.Symlink(hdr.Linkname, path); err != nil {
|
||||
if err := os.Symlink(hdr.Linkname, dstPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -504,12 +520,12 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, o
|
||||
if chownOpts == nil {
|
||||
chownOpts = &ChownOpts{UID: hdr.Uid, GID: hdr.Gid}
|
||||
}
|
||||
if err := os.Lchown(path, chownOpts.UID, chownOpts.GID); err != nil {
|
||||
if err := os.Lchown(dstPath, chownOpts.UID, chownOpts.GID); err != nil {
|
||||
var msg string
|
||||
if inUserns && errors.Is(err, syscall.EINVAL) {
|
||||
msg = " (try increasing the number of subordinate IDs in /etc/subuid and /etc/subgid)"
|
||||
}
|
||||
return fmt.Errorf("failed to Lchown %q for UID %d, GID %d%s: %w", path, hdr.Uid, hdr.Gid, msg, err)
|
||||
return fmt.Errorf("failed to Lchown %q for UID %d, GID %d%s: %w", dstPath, hdr.Uid, hdr.Gid, msg, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,7 +535,7 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, o
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if err := lsetxattr(path, xattr, []byte(value), 0); err != nil {
|
||||
if err := lsetxattr(dstPath, xattr, []byte(value), 0); err != nil {
|
||||
if bestEffortXattrs && errors.Is(err, syscall.ENOTSUP) || errors.Is(err, syscall.EPERM) {
|
||||
// EPERM occurs if modifying xattrs is not allowed. This can
|
||||
// happen when running in userns with restrictions (ChromeOS).
|
||||
@@ -538,39 +554,43 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, o
|
||||
|
||||
// There is no LChmod, so ignore mode for symlink. Also, this
|
||||
// must happen after chown, as that can modify the file mode
|
||||
if err := handleLChmod(hdr, path, hdrInfo); err != nil {
|
||||
if err := handleLChmod(hdr, dstPath, hdrInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
aTime := boundTime(latestTime(hdr.AccessTime, hdr.ModTime))
|
||||
mTime := boundTime(hdr.ModTime)
|
||||
|
||||
// chtimes doesn't support a NOFOLLOW flag atm
|
||||
if hdr.Typeflag == tar.TypeLink {
|
||||
if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
|
||||
if err := chtimes(path, aTime, mTime); err != nil {
|
||||
switch hdr.Typeflag {
|
||||
case tar.TypeSymlink:
|
||||
// Apply timestamps to the symlink itself (AT_SYMLINK_NOFOLLOW).
|
||||
if err := lchtimes(dstPath, aTime, mTime); err != nil {
|
||||
return err
|
||||
}
|
||||
case tar.TypeLink:
|
||||
// Follow the hardlink only when its target is not itself a symlink.
|
||||
fi, err := os.Lstat(hdr.Linkname)
|
||||
if err == nil && fi.Mode()&os.ModeSymlink == 0 {
|
||||
if err := chtimes(dstPath, aTime, mTime); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if hdr.Typeflag != tar.TypeSymlink {
|
||||
if err := chtimes(path, aTime, mTime); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := lchtimes(path, aTime, mTime); err != nil {
|
||||
default:
|
||||
// All other file types follow symlinks.
|
||||
if err := chtimes(dstPath, aTime, mTime); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Tar creates an archive from the directory at `path`, and returns it as a
|
||||
// Tar creates an archive from the directory at `srcPath`, and returns it as a
|
||||
// stream of bytes.
|
||||
func Tar(path string, comp compression.Compression) (io.ReadCloser, error) {
|
||||
return TarWithOptions(path, &TarOptions{Compression: comp})
|
||||
func Tar(srcPath string, comp compression.Compression) (io.ReadCloser, error) {
|
||||
return TarWithOptions(srcPath, &TarOptions{Compression: comp})
|
||||
}
|
||||
|
||||
// TarWithOptions creates an archive from the directory at `path`, only including files whose relative
|
||||
// TarWithOptions creates an archive from the directory at `srcPath`, only including files whose relative
|
||||
// paths are included in `options.IncludeFiles` (if non-nil) or not in `options.ExcludePatterns`.
|
||||
func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) {
|
||||
tb, err := NewTarballer(srcPath, options)
|
||||
@@ -805,6 +825,9 @@ func (t *Tarballer) Do() {
|
||||
|
||||
// Unpack unpacks the decompressedArchive to dest with options.
|
||||
func Unpack(decompressedArchive io.Reader, dest string, options *TarOptions) error {
|
||||
if options == nil {
|
||||
options = &TarOptions{}
|
||||
}
|
||||
tr := tar.NewReader(decompressedArchive)
|
||||
|
||||
var dirs []*tar.Header
|
||||
@@ -846,8 +869,8 @@ loop:
|
||||
}
|
||||
|
||||
// #nosec G305 -- The joined path is checked for path traversal.
|
||||
path := filepath.Join(dest, hdr.Name)
|
||||
rel, err := filepath.Rel(dest, path)
|
||||
dstPath := filepath.Join(dest, hdr.Name)
|
||||
rel, err := filepath.Rel(dest, dstPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -855,21 +878,21 @@ loop:
|
||||
return breakoutError(fmt.Errorf("%q is outside of %q", hdr.Name, dest))
|
||||
}
|
||||
|
||||
// If path exits we almost always just want to remove and replace it
|
||||
// If dstPath exists we almost always just want to remove and replace it.
|
||||
// The only exception is when it is a directory *and* the file from
|
||||
// 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, err := os.Lstat(dstPath); err == nil {
|
||||
if options.NoOverwriteDirNonDir && fi.IsDir() && hdr.Typeflag != tar.TypeDir {
|
||||
// If NoOverwriteDirNonDir is true then we cannot replace
|
||||
// an existing directory with a non-directory from the archive.
|
||||
return fmt.Errorf("cannot overwrite directory %q with non-directory %q", path, dest)
|
||||
return fmt.Errorf("cannot overwrite directory %q with non-directory %q", dstPath, dest)
|
||||
}
|
||||
|
||||
if options.NoOverwriteDirNonDir && !fi.IsDir() && hdr.Typeflag == tar.TypeDir {
|
||||
// If NoOverwriteDirNonDir is true then we cannot replace
|
||||
// an existing non-directory with a directory from the archive.
|
||||
return fmt.Errorf("cannot overwrite non-directory %q with directory %q", path, dest)
|
||||
return fmt.Errorf("cannot overwrite non-directory %q with directory %q", dstPath, dest)
|
||||
}
|
||||
|
||||
if fi.IsDir() && hdr.Name == "." {
|
||||
@@ -877,7 +900,7 @@ loop:
|
||||
}
|
||||
|
||||
if !fi.IsDir() || hdr.Typeflag != tar.TypeDir {
|
||||
if err := os.RemoveAll(path); err != nil {
|
||||
if err := os.RemoveAll(dstPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -888,7 +911,7 @@ loop:
|
||||
}
|
||||
|
||||
if whiteoutConverter != nil {
|
||||
writeFile, err := whiteoutConverter.ConvertRead(hdr, path)
|
||||
writeFile, err := whiteoutConverter.ConvertRead(hdr, dstPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -897,7 +920,7 @@ loop:
|
||||
}
|
||||
}
|
||||
|
||||
if err := createTarFile(path, dest, hdr, tr, options); err != nil {
|
||||
if err := createTarFile(dstPath, dest, hdr, tr, options); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -910,9 +933,8 @@ loop:
|
||||
|
||||
for _, hdr := range dirs {
|
||||
// #nosec G305 -- The header was checked for path traversal before it was appended to the dirs slice.
|
||||
path := filepath.Join(dest, hdr.Name)
|
||||
|
||||
if err := chtimes(path, boundTime(latestTime(hdr.AccessTime, hdr.ModTime)), boundTime(hdr.ModTime)); err != nil {
|
||||
dstPath := filepath.Join(dest, hdr.Name)
|
||||
if err := chtimes(dstPath, boundTime(latestTime(hdr.AccessTime, hdr.ModTime)), boundTime(hdr.ModTime)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -923,16 +945,15 @@ loop:
|
||||
// not already exist. This is possible as the tar format supports 'implicit' directories, where their existence is
|
||||
// defined by the paths of files in the tar, but there are no header entries for the directories themselves, and thus
|
||||
// we most both create them and choose metadata like permissions.
|
||||
//
|
||||
// The caller should have performed filepath.Clean(hdr.Name), so hdr.Name will now be in the filepath format for the OS
|
||||
// on which the daemon is running. This precondition is required because this function assumes a OS-specific path
|
||||
// separator when checking that a path is not the root.
|
||||
func createImpliedDirectories(dest string, hdr *tar.Header, options *TarOptions) error {
|
||||
// Not the root directory, ensure that the parent directory exists
|
||||
if !strings.HasSuffix(hdr.Name, string(os.PathSeparator)) {
|
||||
// For non-directory entries, ensure that the parent directory exists.
|
||||
if hdr.Typeflag != tar.TypeDir {
|
||||
parent := filepath.Dir(hdr.Name)
|
||||
parentPath := filepath.Join(dest, parent)
|
||||
if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
|
||||
if options.NoLchown {
|
||||
return os.MkdirAll(parentPath, ImpliedDirectoryMode)
|
||||
}
|
||||
// RootPair() is confined inside this loop as most cases will not require a call, so we can spend some
|
||||
// unneeded function calls in the uncommon case to encapsulate logic -- implied directories are a niche
|
||||
// usage that reduces the portability of an image.
|
||||
@@ -974,9 +995,6 @@ func untarHandler(tarArchive io.Reader, dest string, options *TarOptions, decomp
|
||||
if options == nil {
|
||||
options = &TarOptions{}
|
||||
}
|
||||
if options.ExcludePatterns == nil {
|
||||
options.ExcludePatterns = []string{}
|
||||
}
|
||||
|
||||
r := tarArchive
|
||||
if decompress {
|
||||
@@ -984,7 +1002,7 @@ func untarHandler(tarArchive io.Reader, dest string, options *TarOptions, decomp
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer decompressedArchive.Close()
|
||||
defer func() { _ = decompressedArchive.Close() }()
|
||||
r = decompressedArchive
|
||||
}
|
||||
|
||||
@@ -998,7 +1016,7 @@ func (archiver *Archiver) TarUntar(src, dst string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer archive.Close()
|
||||
defer func() { _ = archive.Close() }()
|
||||
return archiver.Untar(archive, dst, &TarOptions{
|
||||
IDMap: archiver.IDMapping,
|
||||
})
|
||||
@@ -1010,7 +1028,7 @@ func (archiver *Archiver) UntarPath(src, dst string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer archive.Close()
|
||||
defer func() { _ = archive.Close() }()
|
||||
return archiver.Untar(archive, dst, &TarOptions{
|
||||
IDMap: archiver.IDMapping,
|
||||
})
|
||||
@@ -1070,13 +1088,13 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
|
||||
defer close(errC)
|
||||
|
||||
errC <- func() error {
|
||||
defer w.Close()
|
||||
defer func() { _ = w.Close() }()
|
||||
|
||||
srcF, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer srcF.Close()
|
||||
defer func() { _ = srcF.Close() }()
|
||||
|
||||
hdr, err := tarheader.FileInfoHeaderNoLookups(srcSt, "")
|
||||
if err != nil {
|
||||
@@ -1087,14 +1105,14 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
|
||||
hdr.AccessTime = time.Time{}
|
||||
hdr.ChangeTime = time.Time{}
|
||||
hdr.Name = filepath.Base(dst)
|
||||
hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
|
||||
hdr.Mode = chmodTarEntry(hdr.Mode)
|
||||
|
||||
if err := remapIDs(archiver.IDMapping, hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tw := tar.NewWriter(w)
|
||||
defer tw.Close()
|
||||
defer func() { _ = tw.Close() }()
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1112,7 +1130,7 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
|
||||
|
||||
err = archiver.Untar(r, filepath.Dir(dst), nil)
|
||||
if err != nil {
|
||||
r.CloseWithError(err)
|
||||
_ = r.CloseWithError(err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
+60
-38
@@ -4,6 +4,7 @@ import (
|
||||
"archive/tar"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -13,36 +14,43 @@ import (
|
||||
|
||||
func getWhiteoutConverter(format WhiteoutFormat) tarWhiteoutConverter {
|
||||
if format == OverlayWhiteoutFormat {
|
||||
return overlayWhiteoutConverter{}
|
||||
return newOverlayWhiteoutConverter()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type overlayWhiteoutConverter struct{}
|
||||
type overlayWhiteoutConverter struct {
|
||||
opaqueXattr string
|
||||
}
|
||||
|
||||
func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os.FileInfo) (wo *tar.Header, _ error) {
|
||||
func newOverlayWhiteoutConverter() overlayWhiteoutConverter {
|
||||
opaqueXattr := "trusted.overlay.opaque"
|
||||
if userns.RunningInUserNS() {
|
||||
opaqueXattr = "user.overlay.opaque"
|
||||
}
|
||||
return overlayWhiteoutConverter{
|
||||
opaqueXattr: opaqueXattr,
|
||||
}
|
||||
}
|
||||
|
||||
func (c overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, filePath string, fi os.FileInfo) (wo *tar.Header, _ error) {
|
||||
// convert whiteouts to AUFS format
|
||||
if fi.Mode()&os.ModeCharDevice != 0 && hdr.Devmajor == 0 && hdr.Devminor == 0 {
|
||||
// we just rename the file and make it normal
|
||||
dir, filename := filepath.Split(hdr.Name)
|
||||
hdr.Name = filepath.Join(dir, WhiteoutPrefix+filename)
|
||||
dir, filename := path.Split(hdr.Name)
|
||||
hdr.Name = path.Join(dir, WhiteoutPrefix+filename)
|
||||
hdr.Mode = 0o600
|
||||
hdr.Typeflag = tar.TypeReg
|
||||
hdr.Size = 0
|
||||
}
|
||||
|
||||
if fi.Mode()&os.ModeDir == 0 {
|
||||
if !fi.IsDir() {
|
||||
// FIXME(thaJeztah): return a sentinel error instead of nil, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
opaqueXattrName := "trusted.overlay.opaque"
|
||||
if userns.RunningInUserNS() {
|
||||
opaqueXattrName = "user.overlay.opaque"
|
||||
}
|
||||
|
||||
// convert opaque dirs to AUFS format by writing an empty file with the prefix
|
||||
opaque, err := lgetxattr(path, opaqueXattrName)
|
||||
opaque, err := lgetxattr(filePath, c.opaqueXattr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -50,14 +58,14 @@ func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os
|
||||
// FIXME(thaJeztah): return a sentinel error instead of nil, nil
|
||||
return nil, nil
|
||||
}
|
||||
delete(hdr.PAXRecords, paxSchilyXattr+opaqueXattrName)
|
||||
delete(hdr.PAXRecords, paxSchilyXattr+c.opaqueXattr)
|
||||
|
||||
// create a header for the whiteout file
|
||||
// it should inherit some properties from the parent, but be a regular file
|
||||
return &tar.Header{
|
||||
Typeflag: tar.TypeReg,
|
||||
Mode: hdr.Mode & int64(os.ModePerm),
|
||||
Name: filepath.Join(hdr.Name, WhiteoutOpaqueDir), // #nosec G305 -- An archive is being created, not extracted.
|
||||
Name: path.Join(hdr.Name, WhiteoutOpaqueDir), // #nosec G305 -- An archive is being created, not extracted.
|
||||
Size: 0,
|
||||
Uid: hdr.Uid,
|
||||
Uname: hdr.Uname,
|
||||
@@ -68,40 +76,54 @@ func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (bool, error) {
|
||||
base := filepath.Base(path)
|
||||
dir := filepath.Dir(path)
|
||||
func (c overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, filePath string) (bool, error) {
|
||||
base := filepath.Base(filePath)
|
||||
dir := filepath.Dir(filePath)
|
||||
|
||||
// if a directory is marked as opaque by the AUFS special file, we need to translate that to overlay
|
||||
if base == WhiteoutOpaqueDir {
|
||||
opaqueXattrName := "trusted.overlay.opaque"
|
||||
if userns.RunningInUserNS() {
|
||||
opaqueXattrName = "user.overlay.opaque"
|
||||
switch base {
|
||||
case WhiteoutPrefix, WhiteoutPrefix + ".", WhiteoutPrefix + "..":
|
||||
return false, fmt.Errorf("invalid whiteout entry %q", hdr.Name)
|
||||
|
||||
case WhiteoutOpaqueDir:
|
||||
// If a directory is marked as opaque by the AUFS special file, we need to translate that to overlay.
|
||||
if err := unix.Setxattr(dir, c.opaqueXattr, []byte{'y'}, 0); err != nil {
|
||||
return false, fmt.Errorf("setxattr('%s', %s=y): %w", dir, c.opaqueXattr, err)
|
||||
}
|
||||
// Don't write the whiteout file itself.
|
||||
return false, nil
|
||||
|
||||
err := unix.Setxattr(dir, opaqueXattrName, []byte{'y'}, 0)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("setxattr('%s', %s=y): %w", dir, opaqueXattrName, err)
|
||||
default:
|
||||
originalBase, ok := strings.CutPrefix(base, WhiteoutPrefix)
|
||||
if !ok {
|
||||
// Regular file.
|
||||
return true, nil
|
||||
}
|
||||
// don't write the file itself
|
||||
return false, err
|
||||
}
|
||||
|
||||
// if a file was deleted and we are using overlay, we need to create a character device
|
||||
if strings.HasPrefix(base, WhiteoutPrefix) {
|
||||
originalBase := base[len(WhiteoutPrefix):]
|
||||
// If a file was deleted, and we are using overlay, we need to create a character device.
|
||||
originalPath := filepath.Join(dir, originalBase)
|
||||
|
||||
if err := unix.Mknod(originalPath, unix.S_IFCHR, 0); err != nil {
|
||||
return false, fmt.Errorf("failed to mknod('%s', S_IFCHR, 0): %w", originalPath, err)
|
||||
}
|
||||
if err := os.Chown(originalPath, hdr.Uid, hdr.Gid); err != nil {
|
||||
return false, err
|
||||
|
||||
// Header IDs have already been remapped. Optimize the common non-remapped
|
||||
// root-owned (0:0) case by assuming the created whiteout has the expected
|
||||
// ownership, rather than comparing against the effective UID/GID or stat'ing
|
||||
// the created node to verify it.
|
||||
if hdr.Uid != 0 || hdr.Gid != 0 {
|
||||
// TODO(thaJeztah): Revisit whether whiteout ownership needs to be preserved.
|
||||
//
|
||||
// This was added in the original overlay whiteout implementation:
|
||||
// https://github.com/moby/moby/pull/18560 / https://github.com/moby/moby/pull/22126
|
||||
//
|
||||
// OverlayFS documents whiteouts in terms of a character device with device
|
||||
// number 0:0, not ownership: https://docs.kernel.org/filesystems/overlayfs.html#whiteouts-and-opaque-directories
|
||||
//
|
||||
// If ownership is not required, this Lchown can be removed to avoid the remaining TOCTOU window.
|
||||
if err := os.Lchown(originalPath, hdr.Uid, hdr.Gid); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
// don't write the file itself
|
||||
// Don't write the whiteout file itself.
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
+24
-11
@@ -5,6 +5,8 @@ package archive
|
||||
import (
|
||||
"archive/tar"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -13,6 +15,8 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var errInvalidArchive = errors.New("invalid archive")
|
||||
|
||||
// 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 {
|
||||
@@ -29,20 +33,19 @@ func getWalkRoot(srcPath string, include string) string {
|
||||
|
||||
// chmodTarEntry is used to adjust the file permissions used in tar header based
|
||||
// on the platform the archival is done.
|
||||
func chmodTarEntry(perm os.FileMode) os.FileMode {
|
||||
return perm // noop for unix as golang APIs provide perm bits correctly
|
||||
func chmodTarEntry(mode int64) int64 {
|
||||
return mode // noop for unix as golang APIs provide perm bits correctly
|
||||
}
|
||||
|
||||
func getInodeFromStat(stat interface{}) (uint64, error) {
|
||||
func getInodeFromStat(stat any) (uint64, error) {
|
||||
s, ok := stat.(*syscall.Stat_t)
|
||||
if !ok {
|
||||
// FIXME(thaJeztah): this should likely return an error; see https://github.com/moby/moby/pull/49493#discussion_r1979152897
|
||||
return 0, nil
|
||||
return 0, fmt.Errorf("unexpected stat type %T", stat)
|
||||
}
|
||||
return s.Ino, nil
|
||||
}
|
||||
|
||||
func getFileUIDGID(stat interface{}) (int, int, error) {
|
||||
func getFileUIDGID(stat any) (int, int, error) {
|
||||
s, ok := stat.(*syscall.Stat_t)
|
||||
|
||||
if !ok {
|
||||
@@ -56,7 +59,7 @@ func getFileUIDGID(stat interface{}) (int, int, error) {
|
||||
//
|
||||
// Creating device nodes is not supported when running in a user namespace,
|
||||
// produces a [syscall.EPERM] in most cases.
|
||||
func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error {
|
||||
func handleTarTypeBlockCharFifo(hdr *tar.Header, dstPath string) error {
|
||||
mode := uint32(hdr.Mode & 0o7777)
|
||||
switch hdr.Typeflag {
|
||||
case tar.TypeBlock:
|
||||
@@ -67,18 +70,28 @@ func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error {
|
||||
mode |= unix.S_IFIFO
|
||||
}
|
||||
|
||||
return mknod(path, mode, unix.Mkdev(uint32(hdr.Devmajor), uint32(hdr.Devminor)))
|
||||
// Devmajor and Devminor come straight from the (untrusted) tar header as
|
||||
// int64, but Mkdev only takes uint32. Casting a value that does not fit
|
||||
// silently truncates it, so the node created on disk would carry a
|
||||
// different major/minor than the header declares. Reject those instead of
|
||||
// creating a mismatched device.
|
||||
if hdr.Devmajor < 0 || hdr.Devmajor > math.MaxUint32 ||
|
||||
hdr.Devminor < 0 || hdr.Devminor > math.MaxUint32 {
|
||||
return fmt.Errorf("device number %d:%d for %q out of range: %w", hdr.Devmajor, hdr.Devminor, hdr.Name, errInvalidArchive)
|
||||
}
|
||||
|
||||
return mknod(dstPath, mode, unix.Mkdev(uint32(hdr.Devmajor), uint32(hdr.Devminor)))
|
||||
}
|
||||
|
||||
func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error {
|
||||
func handleLChmod(hdr *tar.Header, dstPath string, hdrInfo os.FileInfo) error {
|
||||
if hdr.Typeflag == tar.TypeLink {
|
||||
if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
|
||||
if err := os.Chmod(path, hdrInfo.Mode()); err != nil {
|
||||
if err := os.Chmod(dstPath, hdrInfo.Mode()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if hdr.Typeflag != tar.TypeSymlink {
|
||||
if err := os.Chmod(path, hdrInfo.Mode()); err != nil {
|
||||
if err := os.Chmod(dstPath, hdrInfo.Mode()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -33,15 +33,15 @@ func getWalkRoot(srcPath string, include string) string {
|
||||
|
||||
// chmodTarEntry is used to adjust the file permissions used in tar header based
|
||||
// on the platform the archival is done.
|
||||
func chmodTarEntry(perm os.FileMode) os.FileMode {
|
||||
func chmodTarEntry(mode int64) int64 {
|
||||
// Remove group- and world-writable bits.
|
||||
perm &= 0o755
|
||||
mode &= 0o755
|
||||
|
||||
// Add the x bit: make everything +x on Windows
|
||||
return perm | 0o111
|
||||
return mode | 0o111
|
||||
}
|
||||
|
||||
func getInodeFromStat(stat interface{}) (uint64, error) {
|
||||
func getInodeFromStat(stat any) (uint64, error) {
|
||||
// do nothing. no notion of Inode in stat on Windows
|
||||
return 0, nil
|
||||
}
|
||||
@@ -56,7 +56,7 @@ func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getFileUIDGID(stat interface{}) (int, int, error) {
|
||||
func getFileUIDGID(stat any) (int, int, error) {
|
||||
// no notion of file ownership mapping yet on Windows
|
||||
return 0, 0, nil
|
||||
}
|
||||
|
||||
+9
-9
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"maps"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
@@ -217,8 +218,8 @@ func (info *FileInfo) LookUp(path string) *FileInfo {
|
||||
return info
|
||||
}
|
||||
|
||||
pathElements := strings.Split(path, string(os.PathSeparator))
|
||||
for _, elem := range pathElements {
|
||||
pathElements := strings.SplitSeq(path, string(os.PathSeparator))
|
||||
for elem := range pathElements {
|
||||
if elem != "" {
|
||||
child := parent.children[elem]
|
||||
if child == nil {
|
||||
@@ -256,9 +257,7 @@ func (info *FileInfo) addChanges(oldInfo *FileInfo, changes *[]Change) {
|
||||
// otherwise any previous delete/change is considered recursive
|
||||
oldChildren := make(map[string]*FileInfo)
|
||||
if oldInfo != nil && info.isDir() {
|
||||
for k, v := range oldInfo.children {
|
||||
oldChildren[k] = v
|
||||
}
|
||||
maps.Copy(oldChildren, oldInfo.children)
|
||||
}
|
||||
|
||||
for name, newChild := range info.children {
|
||||
@@ -401,7 +400,7 @@ func ExportChanges(dir string, changes []Change, idMap user.IdentityMapping) (io
|
||||
whiteOut := filepath.Join(whiteOutDir, WhiteoutPrefix+whiteOutBase)
|
||||
timestamp := time.Now()
|
||||
hdr := &tar.Header{
|
||||
Name: whiteOut[1:],
|
||||
Name: strings.TrimPrefix(filepath.ToSlash(whiteOut), "/"),
|
||||
Size: 0,
|
||||
ModTime: timestamp,
|
||||
AccessTime: timestamp,
|
||||
@@ -411,9 +410,10 @@ func ExportChanges(dir string, changes []Change, idMap user.IdentityMapping) (io
|
||||
log.G(context.TODO()).Debugf("Can't write whiteout header: %s", err)
|
||||
}
|
||||
} else {
|
||||
path := filepath.Join(dir, change.Path)
|
||||
if err := ta.addTarFile(path, change.Path[1:]); err != nil {
|
||||
log.G(context.TODO()).Debugf("Can't add file %s to tar: %s", path, err)
|
||||
srcPath := filepath.Join(dir, change.Path)
|
||||
archivePath := strings.TrimPrefix(filepath.ToSlash(change.Path), "/")
|
||||
if err := ta.addTarFile(srcPath, archivePath); err != nil {
|
||||
log.G(context.TODO()).Debugf("Can't add file %s to tar: %s", srcPath, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -265,7 +265,7 @@ func parseDirent(buf []byte, names []nameIno) (consumed int, newnames []nameIno)
|
||||
}
|
||||
|
||||
func clen(n []byte) int {
|
||||
for i := 0; i < len(n); i++ {
|
||||
for i := range n {
|
||||
if n[i] == 0 {
|
||||
return i
|
||||
}
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ func collectFileInfoForChanges(oldDir, newDir string) (*FileInfo, *FileInfo, err
|
||||
}()
|
||||
|
||||
// block until both routines have returned
|
||||
for i := 0; i < 2; i++ {
|
||||
for range 2 {
|
||||
if err := <-errs; err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
+2
-2
@@ -66,7 +66,7 @@ type nopWriteCloser struct {
|
||||
func (nopWriteCloser) Close() error { return nil }
|
||||
|
||||
var bufioReader32KPool = &sync.Pool{
|
||||
New: func() interface{} { return bufio.NewReaderSize(nil, 32*1024) },
|
||||
New: func() any { return bufio.NewReaderSize(nil, 32*1024) },
|
||||
}
|
||||
|
||||
type bufferedReader struct {
|
||||
@@ -217,7 +217,7 @@ func gzipDecompress(ctx context.Context, buf io.Reader) (io.ReadCloser, error) {
|
||||
|
||||
log.G(ctx).Debugf("Using %s to decompress", unpigzPath)
|
||||
|
||||
return cmdStream(exec.CommandContext(ctx, unpigzPath, "-d", "-c"), buf)
|
||||
return cmdStream(exec.CommandContext(ctx, unpigzPath, "-d", "-c"), buf) // #nosec G204 -- Subprocess launched with variable
|
||||
}
|
||||
|
||||
// cmdStream executes a command, and returns its stdout as a stream.
|
||||
|
||||
+14
-13
@@ -22,7 +22,7 @@ var (
|
||||
)
|
||||
|
||||
var copyPool = sync.Pool{
|
||||
New: func() interface{} { s := make([]byte, 32*1024); return &s },
|
||||
New: func() any { s := make([]byte, 32*1024); return &s },
|
||||
}
|
||||
|
||||
func copyWithBuffer(dst io.Writer, src io.Reader) error {
|
||||
@@ -319,7 +319,10 @@ func PrepareArchiveCopy(srcContent io.Reader, srcInfo, dstInfo CopyInfo) (dstDir
|
||||
// RebaseArchiveEntries rewrites the given srcContent archive replacing
|
||||
// an occurrence of oldBase with newBase at the beginning of entry names.
|
||||
func RebaseArchiveEntries(srcContent io.Reader, oldBase, newBase string) io.ReadCloser {
|
||||
if oldBase == string(os.PathSeparator) {
|
||||
oldBase = filepath.ToSlash(oldBase)
|
||||
newBase = filepath.ToSlash(newBase)
|
||||
|
||||
if oldBase == "/" {
|
||||
// If oldBase specifies the root directory, use an empty string as
|
||||
// oldBase instead so that newBase doesn't replace the path separator
|
||||
// that all paths will start with.
|
||||
@@ -336,12 +339,12 @@ func RebaseArchiveEntries(srcContent io.Reader, oldBase, newBase string) io.Read
|
||||
hdr, err := srcTar.Next()
|
||||
if errors.Is(err, io.EOF) {
|
||||
// Signals end of archive.
|
||||
rebasedTar.Close()
|
||||
w.Close()
|
||||
_ = rebasedTar.Close()
|
||||
_ = w.Close()
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
w.CloseWithError(err)
|
||||
_ = w.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -359,7 +362,7 @@ func RebaseArchiveEntries(srcContent io.Reader, oldBase, newBase string) io.Read
|
||||
}
|
||||
|
||||
if err = rebasedTar.WriteHeader(hdr); err != nil {
|
||||
w.CloseWithError(err)
|
||||
_ = w.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -374,7 +377,7 @@ func RebaseArchiveEntries(srcContent io.Reader, oldBase, newBase string) io.Read
|
||||
// not be vulnerable to this code consuming memory.
|
||||
//nolint:gosec // G110: Potential DoS vulnerability via decompression bomb (gosec)
|
||||
if _, err = io.Copy(rebasedTar, srcTar); err != nil {
|
||||
w.CloseWithError(err)
|
||||
_ = w.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -408,7 +411,7 @@ func CopyResource(srcPath, dstPath string, followLink bool) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer content.Close()
|
||||
defer func() { _ = content.Close() }()
|
||||
|
||||
return CopyTo(content, srcInfo, dstPath)
|
||||
}
|
||||
@@ -427,14 +430,12 @@ func CopyTo(content io.Reader, srcInfo CopyInfo, dstPath string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer copyArchive.Close()
|
||||
defer func() { _ = copyArchive.Close() }()
|
||||
|
||||
options := &TarOptions{
|
||||
return Untar(copyArchive, dstDir, &TarOptions{
|
||||
NoLchown: true,
|
||||
NoOverwriteDirNonDir: true,
|
||||
}
|
||||
|
||||
return Untar(copyArchive, dstDir, options)
|
||||
})
|
||||
}
|
||||
|
||||
// ResolveHostSourcePath decides real path need to be copied with parameters such as
|
||||
|
||||
+1
-1
@@ -5,5 +5,5 @@ package archive
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
func mknod(path string, mode uint32, dev uint64) error {
|
||||
return unix.Mknod(path, mode, int(dev))
|
||||
return unix.Mknod(path, mode, int(dev)) // #nosec G115 -- Required conversion for the platform-specific Mknod API.
|
||||
}
|
||||
|
||||
+12
-15
@@ -28,9 +28,6 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
|
||||
if options == nil {
|
||||
options = &TarOptions{}
|
||||
}
|
||||
if options.ExcludePatterns == nil {
|
||||
options.ExcludePatterns = []string{}
|
||||
}
|
||||
|
||||
aufsTempdir := ""
|
||||
aufsHardlinks := make(map[string]*tar.Header)
|
||||
@@ -102,8 +99,8 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
|
||||
}
|
||||
}
|
||||
// #nosec G305 -- The joined path is guarded against path traversal.
|
||||
path := filepath.Join(dest, hdr.Name)
|
||||
rel, err := filepath.Rel(dest, path)
|
||||
dstPath := filepath.Join(dest, hdr.Name)
|
||||
rel, err := filepath.Rel(dest, dstPath)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -112,10 +109,10 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
|
||||
if strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
|
||||
return 0, breakoutError(fmt.Errorf("%q is outside of %q", hdr.Name, dest))
|
||||
}
|
||||
base := filepath.Base(path)
|
||||
base := filepath.Base(dstPath)
|
||||
|
||||
if strings.HasPrefix(base, WhiteoutPrefix) {
|
||||
dir := filepath.Dir(path)
|
||||
dir := filepath.Dir(dstPath)
|
||||
if base == WhiteoutOpaqueDir {
|
||||
_, err := os.Lstat(dir)
|
||||
if err != nil {
|
||||
@@ -132,7 +129,7 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
|
||||
return nil
|
||||
}
|
||||
if _, exists := unpackedPaths[path]; !exists {
|
||||
return os.RemoveAll(path)
|
||||
return os.RemoveAll(path) // #nosec G122 -- FIXME: consider root-scoped APIs (e.g. os.Root) to prevent symlink TOCTOU traversal
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -147,13 +144,13 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If path exits we almost always just want to remove and replace it.
|
||||
// If dstPath exists we almost always just want to remove and replace it.
|
||||
// The only exception is when it is a directory *and* the file from
|
||||
// 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, err := os.Lstat(dstPath); err == nil {
|
||||
if !fi.IsDir() || hdr.Typeflag != tar.TypeDir {
|
||||
if err := os.RemoveAll(path); err != nil {
|
||||
if err := os.RemoveAll(dstPath); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
@@ -182,7 +179,7 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if err := createTarFile(path, dest, srcHdr, srcData, options); err != nil {
|
||||
if err := createTarFile(dstPath, dest, srcHdr, srcData, options); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -191,14 +188,14 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
|
||||
if hdr.Typeflag == tar.TypeDir {
|
||||
dirs = append(dirs, hdr)
|
||||
}
|
||||
unpackedPaths[path] = struct{}{}
|
||||
unpackedPaths[dstPath] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
for _, hdr := range dirs {
|
||||
// #nosec G305 -- The header was checked for path traversal before it was appended to the dirs slice.
|
||||
path := filepath.Join(dest, hdr.Name)
|
||||
if err := chtimes(path, hdr.AccessTime, hdr.ModTime); err != nil {
|
||||
dstPath := filepath.Join(dest, hdr.Name)
|
||||
if err := chtimes(dstPath, hdr.AccessTime, hdr.ModTime); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ func (fi nosysFileInfo) Gname() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (fi nosysFileInfo) Sys() interface{} {
|
||||
func (fi nosysFileInfo) Sys() any {
|
||||
// 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.
|
||||
|
||||
+5
-4
@@ -36,10 +36,11 @@ func sysStat(fi os.FileInfo, hdr *tar.Header) error {
|
||||
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
|
||||
if s.Mode&unix.S_IFBLK != 0 || s.Mode&unix.S_IFCHR != 0 {
|
||||
// #nosec G115 -- Rdev type varies by platform.
|
||||
rdev := uint64(s.Rdev) //nolint:unconvert // Rdev type varies by platform.
|
||||
hdr.Devmajor = int64(unix.Major(rdev))
|
||||
hdr.Devminor = int64(unix.Minor(rdev))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
+10
-10
@@ -13,26 +13,26 @@ import (
|
||||
// lgetxattr retrieves the value of the extended attribute identified by attr
|
||||
// and associated with the given path in the file system.
|
||||
// It returns a nil slice and nil error if the xattr is not set.
|
||||
func lgetxattr(path string, attr string) ([]byte, error) {
|
||||
func lgetxattr(filePath string, attr string) ([]byte, error) {
|
||||
// Start with a 128 length byte array
|
||||
dest := make([]byte, 128)
|
||||
sz, err := unix.Lgetxattr(path, attr, dest)
|
||||
sz, err := unix.Lgetxattr(filePath, attr, dest)
|
||||
|
||||
for errors.Is(err, unix.ERANGE) {
|
||||
// Buffer too small, use zero-sized buffer to get the actual size
|
||||
sz, err = unix.Lgetxattr(path, attr, []byte{})
|
||||
sz, err = unix.Lgetxattr(filePath, attr, []byte{})
|
||||
if err != nil {
|
||||
return nil, wrapPathError("lgetxattr", path, attr, err)
|
||||
return nil, wrapPathError("lgetxattr", filePath, attr, err)
|
||||
}
|
||||
dest = make([]byte, sz)
|
||||
sz, err = unix.Lgetxattr(path, attr, dest)
|
||||
sz, err = unix.Lgetxattr(filePath, attr, dest)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, noattr) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, wrapPathError("lgetxattr", path, attr, err)
|
||||
return nil, wrapPathError("lgetxattr", filePath, attr, err)
|
||||
}
|
||||
|
||||
return dest[:sz], nil
|
||||
@@ -40,13 +40,13 @@ func lgetxattr(path string, attr string) ([]byte, error) {
|
||||
|
||||
// lsetxattr sets the value of the extended attribute identified by attr
|
||||
// and associated with the given path in the file system.
|
||||
func lsetxattr(path string, attr string, data []byte, flags int) error {
|
||||
return wrapPathError("lsetxattr", path, attr, unix.Lsetxattr(path, attr, data, flags))
|
||||
func lsetxattr(filePath string, attr string, data []byte, flags int) error {
|
||||
return wrapPathError("lsetxattr", filePath, attr, unix.Lsetxattr(filePath, attr, data, flags))
|
||||
}
|
||||
|
||||
func wrapPathError(op, path, attr string, err error) error {
|
||||
func wrapPathError(op, filePath, attr string, err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &fs.PathError{Op: op, Path: path, Err: fmt.Errorf("xattr %q: %w", attr, err)}
|
||||
return &fs.PathError{Op: op, Path: filePath, Err: fmt.Errorf("xattr %q: %w", attr, err)}
|
||||
}
|
||||
|
||||
Vendored
+3
-3
@@ -644,7 +644,7 @@ github.com/mitchellh/go-wordwrap
|
||||
# github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||
## explicit; go 1.14
|
||||
github.com/mitchellh/hashstructure/v2
|
||||
# github.com/moby/buildkit v0.32.0-rc1
|
||||
# github.com/moby/buildkit v0.32.0-rc2
|
||||
## explicit; go 1.26.3
|
||||
github.com/moby/buildkit/api/services/control
|
||||
github.com/moby/buildkit/api/types
|
||||
@@ -747,8 +747,8 @@ github.com/moby/buildkit/version
|
||||
# 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.2.0
|
||||
## explicit; go 1.23.0
|
||||
# github.com/moby/go-archive v0.2.1
|
||||
## explicit; go 1.25
|
||||
github.com/moby/go-archive
|
||||
github.com/moby/go-archive/compression
|
||||
github.com/moby/go-archive/tarheader
|
||||
|
||||
Reference in New Issue
Block a user