diff --git a/go.mod b/go.mod index 54c7f03fc5..e9c83db2fc 100644 --- a/go.mod +++ b/go.mod @@ -64,7 +64,7 @@ require ( github.com/mitchellh/copystructure v1.2.0 github.com/moby/buildkit v0.32.0 github.com/moby/docker-image-spec v1.3.1 - github.com/moby/go-archive v0.2.2-0.20260729101603-a11565d1683c + github.com/moby/go-archive v0.3.0 github.com/moby/ipvs v1.1.0 github.com/moby/locker v1.0.1 github.com/moby/moby/api v1.55.0 diff --git a/go.sum b/go.sum index 9ac7fdbe9e..2488067928 100644 --- a/go.sum +++ b/go.sum @@ -547,8 +547,8 @@ github.com/moby/buildkit v0.32.0 h1:slXarYQoMo4cp2d9x30M9t0L4R+c0CVMov+5P1hhiHY= github.com/moby/buildkit v0.32.0/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.2-0.20260729101603-a11565d1683c h1:FHEkke3COBYyr7/aIk1782G/4oVN/6L/ISEMD5dPIDk= -github.com/moby/go-archive v0.2.2-0.20260729101603-a11565d1683c/go.mod h1:Npdv43fFqlhZW7Xo8fbm3ZMYFvAGNviUPqX21VERbcE= +github.com/moby/go-archive v0.3.0 h1:nos4BtzzUIqB406BgQnWGMI4qib9BZ8XUHU+ucv/n1c= +github.com/moby/go-archive v0.3.0/go.mod h1:Npdv43fFqlhZW7Xo8fbm3ZMYFvAGNviUPqX21VERbcE= 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= diff --git a/vendor/github.com/moby/go-archive/archive.go b/vendor/github.com/moby/go-archive/archive.go index 4a247c5f50..4d9e87d204 100644 --- a/vendor/github.com/moby/go-archive/archive.go +++ b/vendor/github.com/moby/go-archive/archive.go @@ -610,13 +610,13 @@ func createTarFile(root *os.Root, dstPath string, hdr *tar.Header, reader io.Rea // Follow the hardlink only when its target is not itself a symlink. fi, err := root.Lstat(filepath.FromSlash(path.Clean(hdr.Linkname))) if err == nil && fi.Mode()&os.ModeSymlink == 0 { - if err := root.Chtimes(dstPath, aTime, mTime); err != nil { + if err := chtimes(root, dstPath, aTime, mTime); err != nil { return err } } default: // All other file types follow symlinks. - if err := root.Chtimes(dstPath, aTime, mTime); err != nil { + if err := chtimes(root, dstPath, aTime, mTime); err != nil { return err } } @@ -996,7 +996,7 @@ loop: for _, d := range dirs { aTime := boundTime(latestTime(d.hdr.AccessTime, d.hdr.ModTime)) - if err := root.Chtimes(d.name, aTime, boundTime(d.hdr.ModTime)); err != nil { + if err := chtimes(root, d.name, aTime, boundTime(d.hdr.ModTime)); err != nil { return err } } diff --git a/vendor/github.com/moby/go-archive/copy.go b/vendor/github.com/moby/go-archive/copy.go index b4ee74ef47..7447e8bd10 100644 --- a/vendor/github.com/moby/go-archive/copy.go +++ b/vendor/github.com/moby/go-archive/copy.go @@ -316,19 +316,40 @@ func PrepareArchiveCopy(srcContent io.Reader, srcInfo, dstInfo CopyInfo) (dstDir } } +// newNameRebaser returns a function that replaces oldBase with newBase at the +// beginning of POSIX-style archive entry names. It converts oldBase and newBase +// to forward-slash form and trims trailing slashes. +// +// When rebasing from the archive root, the returned function removes all +// leading slashes from names. It otherwise preserves the remainder verbatim +// and does not clean or canonicalize paths. +func newNameRebaser(oldBase, newBase string) func(string) string { + oldBase = strings.TrimRight(filepath.ToSlash(oldBase), "/") + newBase = strings.TrimRight(filepath.ToSlash(newBase), "/") + + if oldBase == "" { + return func(name string) string { + name = strings.TrimLeft(name, "/") + if newBase == "" { + return name + } + return newBase + "/" + name + } + } + + return func(name string) string { + suffix, ok := strings.CutPrefix(name, oldBase) + if !ok || suffix != "" && !strings.HasPrefix(suffix, "/") { + return name + } + return newBase + suffix + } +} + // 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 { - 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. - oldBase = "" - } - + rebase := newNameRebaser(oldBase, newBase) rebased, w := io.Pipe() go func() { @@ -356,9 +377,9 @@ func RebaseArchiveEntries(srcContent io.Reader, oldBase, newBase string) io.Read // // To fix, set the format to PAX here. See docker/for-linux issue #484. hdr.Format = tar.FormatPAX - hdr.Name = strings.Replace(hdr.Name, oldBase, newBase, 1) + hdr.Name = rebase(hdr.Name) if hdr.Typeflag == tar.TypeLink { - hdr.Linkname = strings.Replace(hdr.Linkname, oldBase, newBase, 1) + hdr.Linkname = rebase(hdr.Linkname) } if err = rebasedTar.WriteHeader(hdr); err != nil { diff --git a/vendor/github.com/moby/go-archive/diff.go b/vendor/github.com/moby/go-archive/diff.go index 75b84f97ca..055f3c11ee 100644 --- a/vendor/github.com/moby/go-archive/diff.go +++ b/vendor/github.com/moby/go-archive/diff.go @@ -213,7 +213,7 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64, } for _, d := range dirs { - if err := root.Chtimes(d.name, boundTime(latestTime(d.hdr.AccessTime, d.hdr.ModTime)), boundTime(d.hdr.ModTime)); err != nil { + if err := chtimes(root, d.name, boundTime(latestTime(d.hdr.AccessTime, d.hdr.ModTime)), boundTime(d.hdr.ModTime)); err != nil { return 0, err } } diff --git a/vendor/github.com/moby/go-archive/time_nonwindows.go b/vendor/github.com/moby/go-archive/time_nonwindows.go index 5fa042ec27..418bc887a9 100644 --- a/vendor/github.com/moby/go-archive/time_nonwindows.go +++ b/vendor/github.com/moby/go-archive/time_nonwindows.go @@ -15,12 +15,12 @@ import ( ) // chtimes changes the access and modification time of a file at the given -// path. +// path relative to root. // // Callers must use boundTime to ensure timestamps are within the range // supported by os.Chtimes. -func chtimes(name string, atime time.Time, mtime time.Time) error { - return os.Chtimes(name, atime, mtime) +func chtimes(root *os.Root, name string, atime, mtime time.Time) error { + return root.Chtimes(name, atime, mtime) } func lchtimes(root *os.Root, name string, atime, mtime time.Time) error { diff --git a/vendor/github.com/moby/go-archive/time_windows.go b/vendor/github.com/moby/go-archive/time_windows.go index aaa3399f73..66173c58bb 100644 --- a/vendor/github.com/moby/go-archive/time_windows.go +++ b/vendor/github.com/moby/go-archive/time_windows.go @@ -1,37 +1,111 @@ package archive import ( + "errors" "os" + "path/filepath" "time" + "unsafe" "golang.org/x/sys/windows" ) // chtimes changes the access and modification time of a file at the given -// path. +// path relative to root. +// +// Symlink entries are handled separately through lchtimes. The final path +// component is expected not to be a reparse point; if one is encountered, +// chtimes returns an error. // // Callers must use boundTime to ensure timestamps are within the range // supported by os.Chtimes. -func chtimes(name string, atime time.Time, mtime time.Time) error { - if err := os.Chtimes(name, atime, mtime); err != nil { +func chtimes(root *os.Root, name string, atime, mtime time.Time) error { + parent, err := root.OpenFile(filepath.Dir(name), os.O_RDONLY, 0) + if err != nil { return err } + defer parent.Close() - pathp, err := windows.UTF16PtrFromString(name) - if err != nil { - return err - } - h, err := windows.CreateFile(pathp, - windows.FILE_WRITE_ATTRIBUTES, windows.FILE_SHARE_WRITE, nil, - windows.OPEN_EXISTING, windows.FILE_FLAG_BACKUP_SEMANTICS, 0) - if err != nil { - return err - } - defer windows.Close(h) - c := windows.NsecToFiletime(mtime.UnixNano()) - return windows.SetFileTime(h, &c, nil, nil) + // Symlink entries are handled by lchtimes. The destination for all + // chtimes callers is therefore expected not to be a reparse point. + // + // Do not follow the final component: if it was concurrently replaced + // with a reparse point, fail instead of updating its target. + return chtimesAt(parent, filepath.Base(name), atime, mtime, true) } func lchtimes(root *os.Root, name string, atime time.Time, mtime time.Time) error { return nil } + +func chtimesAt(parent *os.File, name string, atime, mtime time.Time, noFollow bool) error { + h, err := openForWriteAttributesAt(windows.Handle(parent.Fd()), name, noFollow) + if err != nil { + if noFollow && errors.Is(err, windows.STATUS_REPARSE_POINT_ENCOUNTERED) { + // Encountering a reparse point when noFollow is requested is unexpected. + // Treat it as a potential breakout to fail extraction safely. + return breakoutError(err) + } + return err + } + defer func() { _ = windows.Close(h) }() + + var ( + creationTime = windows.NsecToFiletime(mtime.UnixNano()) + accessTime = windows.NsecToFiletime(atime.UnixNano()) + modificationTime = windows.NsecToFiletime(mtime.UnixNano()) + ) + return windows.SetFileTime(h, &creationTime, &accessTime, &modificationTime) +} + +// openForWriteAttributesAt opens name relative to parent with permission to +// modify its file attributes. If noFollow is true, it does not follow reparse +// points. +// +// This implementation is based on Go's internal Windows Openat support: +// +// https://github.com/golang/go/blob/go1.26.0/src/internal/syscall/windows/at_windows.go +// +// It is used by os.Root's Windows implementation for root-relative filesystem +// operations: +// +// https://github.com/golang/go/blob/go1.26.0/src/os/root_windows.go +// +// Keep this implementation aligned with the upstream code until an equivalent +// operation is available from golang.org/x/sys/windows. +func openForWriteAttributesAt(parent windows.Handle, name string, noFollow bool) (windows.Handle, error) { + name16, err := windows.UTF16FromString(name) + if err != nil { + return windows.InvalidHandle, err + } + + attrs := uint32(windows.OBJ_CASE_INSENSITIVE) + if noFollow { + attrs |= windows.OBJ_DONT_REPARSE + } + + var handle windows.Handle + err = windows.NtCreateFile( + &handle, + windows.SYNCHRONIZE|windows.FILE_WRITE_ATTRIBUTES, + &windows.OBJECT_ATTRIBUTES{ + Length: uint32(unsafe.Sizeof(windows.OBJECT_ATTRIBUTES{})), + RootDirectory: parent, + ObjectName: &windows.NTUnicodeString{ + Length: uint16((len(name16) - 1) * 2), // #nosec G115 -- Length is USHORT by definition. A Windows path component cannot exceed uint16 bytes. + MaximumLength: uint16(len(name16) * 2), // #nosec G115 -- MaximumLength is USHORT by definition. A Windows path component cannot exceed uint16 bytes. + Buffer: &name16[0], + }, + Attributes: attrs, + }, + &windows.IO_STATUS_BLOCK{}, + nil, + windows.FILE_ATTRIBUTE_NORMAL, + windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE, + windows.FILE_OPEN, + windows.FILE_OPEN_FOR_BACKUP_INTENT|windows.FILE_SYNCHRONOUS_IO_NONALERT, + 0, // EA buffer + 0, // EA length + ) + return handle, err +} diff --git a/vendor/modules.txt b/vendor/modules.txt index c898fcd155..d6421dde2c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1266,7 +1266,7 @@ 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.2.2-0.20260729101603-a11565d1683c +# github.com/moby/go-archive v0.3.0 ## explicit; go 1.25 github.com/moby/go-archive github.com/moby/go-archive/chrootarchive