vendor: github.com/tonistiigi/fsutil v0.0.0-20221114235510-0127568185cf

full diff: 9ed612626d...0127568185

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Sebastiaan van Stijn 2022-11-24 23:51:06 +01:00 committed by Tonis Tiigi
parent f97763d7b3
commit 85169a04cf
No known key found for this signature in database
GPG key ID: AFA9DE5F8AB7AF39
28 changed files with 345 additions and 174 deletions

View file

@ -81,7 +81,7 @@ require (
github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.6.1
github.com/spf13/pflag v1.0.5
github.com/tonistiigi/fsutil v0.0.0-20220315205639-9ed612626da3
github.com/tonistiigi/fsutil v0.0.0-20221114235510-0127568185cf
github.com/tonistiigi/go-archvariant v1.0.0
github.com/vbatts/tar-split v0.11.2
github.com/vishvananda/netlink v1.2.1-beta.2

View file

@ -408,7 +408,6 @@ github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4Kfc
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.3-0.20211208011758-87521affb077+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c=
@ -1066,8 +1065,8 @@ github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOM
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tonistiigi/fsutil v0.0.0-20220315205639-9ed612626da3 h1:T1pEe+WB3SCPVAfVquvfPfagKZU2Z8c1OP3SuGB+id0=
github.com/tonistiigi/fsutil v0.0.0-20220315205639-9ed612626da3/go.mod h1:oPAfvw32vlUJSjyDcQ3Bu0nb2ON2B+G0dtVN/SZNJiA=
github.com/tonistiigi/fsutil v0.0.0-20221114235510-0127568185cf h1:2n2v98sRhXEG0Kh7+EvctaNIyOim36Ekp4pGDzbuvO8=
github.com/tonistiigi/fsutil v0.0.0-20221114235510-0127568185cf/go.mod h1:AvLEd1LEIl64G2Jpgwo7aVV5lGH0ePcKl0ygGIHNYl8=
github.com/tonistiigi/go-archvariant v1.0.0 h1:5LC1eDWiBNflnTF1prCiX09yfNHIxDC/aukdhCdTyb0=
github.com/tonistiigi/go-archvariant v1.0.0/go.mod h1:TxFmO5VS6vMq2kvs3ht04iPXtu2rUT/erOnGFYfk5Ho=
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0=
@ -1452,7 +1451,6 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210313202042-bd2e13477e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View file

@ -1,29 +1,30 @@
#syntax=docker/dockerfile:1.2
ARG GO_VERSION=1.16
#syntax=docker/dockerfile:1
ARG GO_VERSION=1.18
FROM --platform=amd64 tonistiigi/xx:golang AS goxx
FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.1.0 AS xx
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS base
RUN apk add --no-cache gcc musl-dev
COPY --from=goxx / /
RUN apk add --no-cache git
COPY --from=xx / /
WORKDIR /src
FROM base AS build
ARG TARGETPLATFORM
RUN --mount=target=. \
RUN --mount=target=. --mount=target=/go/pkg/mod,type=cache \
--mount=target=/root/.cache,type=cache \
go build ./...
xx-go build ./...
FROM base AS test
RUN --mount=target=. \
ARG TESTFLAGS
RUN --mount=target=. --mount=target=/go/pkg/mod,type=cache \
--mount=target=/root/.cache,type=cache \
go test -test.v ./...
CGO_ENABLED=0 xx-go test -test.v ${TESTFLAGS} ./...
FROM base AS test-noroot
RUN mkdir /go/pkg && chmod 0777 /go/pkg
USER 1000:1000
RUN --mount=target=. \
--mount=target=/tmp/.cache,type=cache \
GOCACHE=/tmp/gocache go test -test.v ./...
CGO_ENABLED=0 GOCACHE=/tmp/gocache xx-go test -test.v ./...
FROM build

View file

@ -1,3 +1,4 @@
//go:build linux
// +build linux
package fsutil

View file

@ -2,7 +2,6 @@ package fs
import (
"context"
"io/ioutil"
"os"
"path"
"path/filepath"
@ -12,7 +11,7 @@ import (
"time"
"github.com/containerd/continuity/fs"
"github.com/docker/docker/pkg/fileutils"
"github.com/moby/patternmatcher"
"github.com/pkg/errors"
"github.com/tonistiigi/fsutil"
)
@ -115,7 +114,7 @@ func Copy(ctx context.Context, srcRoot, src, dstRoot, dst string, opts ...Opt) e
if err != nil {
return err
}
if err := c.copy(ctx, srcFollowed, "", dst, false, fileutils.MatchInfo{}, fileutils.MatchInfo{}); err != nil {
if err := c.copy(ctx, srcFollowed, "", dst, false, patternmatcher.MatchInfo{}, patternmatcher.MatchInfo{}); err != nil {
return err
}
}
@ -154,6 +153,7 @@ func (c *copier) prepareTargetDir(srcFollowed, src, destPath string, copyDirCont
type User struct {
UID, GID int
SID string
}
type Chowner func(*User) (*User, error)
@ -232,8 +232,8 @@ type copier struct {
mode *int
inodes map[uint64]string
xattrErrorHandler XAttrErrorHandler
includePatternMatcher *fileutils.PatternMatcher
excludePatternMatcher *fileutils.PatternMatcher
includePatternMatcher *patternmatcher.PatternMatcher
excludePatternMatcher *patternmatcher.PatternMatcher
parentDirs []parentDir
changefn fsutil.ChangeFunc
root string
@ -252,19 +252,19 @@ func newCopier(root string, chown Chowner, tm *time.Time, mode *int, xeh XAttrEr
}
}
var includePatternMatcher *fileutils.PatternMatcher
var includePatternMatcher *patternmatcher.PatternMatcher
if len(includePatterns) != 0 {
var err error
includePatternMatcher, err = fileutils.NewPatternMatcher(includePatterns)
includePatternMatcher, err = patternmatcher.New(includePatterns)
if err != nil {
return nil, errors.Wrapf(err, "invalid includepatterns: %s", includePatterns)
}
}
var excludePatternMatcher *fileutils.PatternMatcher
var excludePatternMatcher *patternmatcher.PatternMatcher
if len(excludePatterns) != 0 {
var err error
excludePatternMatcher, err = fileutils.NewPatternMatcher(excludePatterns)
excludePatternMatcher, err = patternmatcher.New(excludePatterns)
if err != nil {
return nil, errors.Wrapf(err, "invalid excludepatterns: %s", excludePatterns)
}
@ -284,7 +284,7 @@ func newCopier(root string, chown Chowner, tm *time.Time, mode *int, xeh XAttrEr
}
// dest is always clean
func (c *copier) copy(ctx context.Context, src, srcComponents, target string, overwriteTargetMetadata bool, parentIncludeMatchInfo, parentExcludeMatchInfo fileutils.MatchInfo) error {
func (c *copier) copy(ctx context.Context, src, srcComponents, target string, overwriteTargetMetadata bool, parentIncludeMatchInfo, parentExcludeMatchInfo patternmatcher.MatchInfo) error {
select {
case <-ctx.Done():
return ctx.Err()
@ -295,11 +295,15 @@ func (c *copier) copy(ctx context.Context, src, srcComponents, target string, ov
if err != nil {
return errors.Wrapf(err, "failed to stat %s", src)
}
targetFi, err := os.Lstat(target)
if err != nil && !os.IsNotExist(err) {
return errors.Wrapf(err, "failed to stat %s", src)
}
include := true
var (
includeMatchInfo fileutils.MatchInfo
excludeMatchInfo fileutils.MatchInfo
includeMatchInfo patternmatcher.MatchInfo
excludeMatchInfo patternmatcher.MatchInfo
)
if srcComponents != "" {
matchesIncludePattern := false
@ -335,7 +339,8 @@ func (c *copier) copy(ctx context.Context, src, srcComponents, target string, ov
}
}
copyFileInfo := true
copyFileInfo := include
restoreFileTimestamp := false
notify := true
switch {
@ -345,8 +350,12 @@ func (c *copier) copy(ctx context.Context, src, srcComponents, target string, ov
include, includeMatchInfo, excludeMatchInfo,
); err != nil {
return err
} else if !overwriteTargetMetadata || c.includePatternMatcher != nil {
} else if !overwriteTargetMetadata {
// if we aren't supposed to overwrite existing target metadata,
// then we only need to copy the new file info if we newly created
// it, or restore the previous file timestamp if not
copyFileInfo = created
restoreFileTimestamp = !created
}
notify = false
case (fi.Mode() & os.ModeType) == 0:
@ -369,23 +378,26 @@ func (c *copier) copy(ctx context.Context, src, srcComponents, target string, ov
if err := os.Symlink(link, target); err != nil {
return errors.Wrapf(err, "failed to create symlink: %s", target)
}
case (fi.Mode() & os.ModeDevice) == os.ModeDevice:
case (fi.Mode() & os.ModeDevice) == os.ModeDevice,
(fi.Mode() & os.ModeNamedPipe) == os.ModeNamedPipe,
(fi.Mode() & os.ModeSocket) == os.ModeSocket:
if err := copyDevice(target, fi); err != nil {
return errors.Wrapf(err, "failed to create device")
}
default:
// TODO: Support pipes and sockets
return errors.Wrapf(err, "unsupported mode %s", fi.Mode())
}
if copyFileInfo {
if err := c.copyFileInfo(fi, target); err != nil {
if err := c.copyFileInfo(fi, src, target); err != nil {
return errors.Wrap(err, "failed to copy file info")
}
if err := copyXAttrs(target, src, c.xattrErrorHandler); err != nil {
return errors.Wrap(err, "failed to copy xattrs")
}
} else if restoreFileTimestamp && targetFi != nil {
if err := c.copyFileTimestamp(fi, target); err != nil {
return errors.Wrap(err, "failed to restore file timestamp")
}
}
if notify {
if err := c.notifyChange(target, fi); err != nil {
@ -404,9 +416,9 @@ func (c *copier) notifyChange(target string, fi os.FileInfo) error {
return nil
}
func (c *copier) include(path string, fi os.FileInfo, parentIncludeMatchInfo fileutils.MatchInfo) (bool, fileutils.MatchInfo, error) {
func (c *copier) include(path string, fi os.FileInfo, parentIncludeMatchInfo patternmatcher.MatchInfo) (bool, patternmatcher.MatchInfo, error) {
if c.includePatternMatcher == nil {
return true, fileutils.MatchInfo{}, nil
return true, patternmatcher.MatchInfo{}, nil
}
m, matchInfo, err := c.includePatternMatcher.MatchesUsingParentResults(path, parentIncludeMatchInfo)
@ -416,9 +428,9 @@ func (c *copier) include(path string, fi os.FileInfo, parentIncludeMatchInfo fil
return m, matchInfo, nil
}
func (c *copier) exclude(path string, fi os.FileInfo, parentExcludeMatchInfo fileutils.MatchInfo) (bool, fileutils.MatchInfo, error) {
func (c *copier) exclude(path string, fi os.FileInfo, parentExcludeMatchInfo patternmatcher.MatchInfo) (bool, patternmatcher.MatchInfo, error) {
if c.excludePatternMatcher == nil {
return false, fileutils.MatchInfo{}, nil
return false, patternmatcher.MatchInfo{}, nil
}
m, matchInfo, err := c.excludePatternMatcher.MatchesUsingParentResults(path, parentExcludeMatchInfo)
@ -449,7 +461,7 @@ func (c *copier) createParentDirs(src, srcComponents, target string, overwriteTa
return err
}
if created {
if err := c.copyFileInfo(fi, parentDir.dstPath); err != nil {
if err := c.copyFileInfo(fi, parentDir.srcPath, parentDir.dstPath); err != nil {
return errors.Wrap(err, "failed to copy file info")
}
@ -471,8 +483,8 @@ func (c *copier) copyDirectory(
stat os.FileInfo,
overwriteTargetMetadata bool,
include bool,
includeMatchInfo fileutils.MatchInfo,
excludeMatchInfo fileutils.MatchInfo,
includeMatchInfo patternmatcher.MatchInfo,
excludeMatchInfo patternmatcher.MatchInfo,
) (bool, error) {
if !stat.IsDir() {
return false, errors.Errorf("source is not directory")
@ -509,7 +521,7 @@ func (c *copier) copyDirectory(
c.parentDirs = c.parentDirs[:len(c.parentDirs)-1]
}()
fis, err := ioutil.ReadDir(src)
fis, err := os.ReadDir(src)
if err != nil {
return false, errors.Wrapf(err, "failed to read %s", src)
}

View file

@ -1,3 +1,4 @@
//go:build darwin
// +build darwin
package fs
@ -40,3 +41,7 @@ func copyFileContent(dst, src *os.File) error {
return err
}
func mknod(dst string, mode uint32, rDev int) error {
return unix.Mknod(dst, uint32(mode), rDev)
}

View file

@ -1,3 +1,4 @@
//go:build freebsd
// +build freebsd
package fs
@ -7,6 +8,7 @@ import (
"os"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
func copyFile(source, target string) error {
@ -30,3 +32,7 @@ func copyFileContent(dst, src *os.File) error {
bufferPool.Put(buf)
return err
}
func mknod(dst string, mode uint32, rDev int) error {
return unix.Mknod(dst, uint32(mode), uint64(rDev))
}

View file

@ -15,9 +15,7 @@ func getUIDGID(fi os.FileInfo) (uid, gid int) {
return int(st.Uid), int(st.Gid)
}
func (c *copier) copyFileInfo(fi os.FileInfo, name string) error {
st := fi.Sys().(*syscall.Stat_t)
func (c *copier) copyFileInfo(fi os.FileInfo, src, name string) error {
chown := c.chown
uid, gid := getUIDGID(fi)
old := &User{UID: uid, GID: gid}
@ -40,20 +38,26 @@ func (c *copier) copyFileInfo(fi os.FileInfo, name string) error {
}
}
if c.utime != nil {
if err := Utimes(name, c.utime); err != nil {
return err
}
} else {
timespec := []unix.Timespec{unix.Timespec(StatAtime(st)), unix.Timespec(StatMtime(st))}
if err := unix.UtimesNanoAt(unix.AT_FDCWD, name, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil {
return errors.Wrapf(err, "failed to utime %s", name)
}
if err := c.copyFileTimestamp(fi, name); err != nil {
return err
}
return nil
}
func (c *copier) copyFileTimestamp(fi os.FileInfo, name string) error {
if c.utime != nil {
return Utimes(name, c.utime)
}
st := fi.Sys().(*syscall.Stat_t)
timespec := []unix.Timespec{unix.Timespec(StatAtime(st)), unix.Timespec(StatMtime(st))}
if err := unix.UtimesNanoAt(unix.AT_FDCWD, name, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil {
return errors.Wrapf(err, "failed to utime %s", name)
}
return nil
}
func copyFile(source, target string) error {
src, err := os.Open(source)
if err != nil {
@ -109,10 +113,6 @@ func copyFileContent(dst, src *os.File) error {
return nil
}
func copyDevice(dst string, fi os.FileInfo) error {
st, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
return errors.New("unsupported stat type")
}
return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev))
func mknod(dst string, mode uint32, rDev int) error {
return unix.Mknod(dst, uint32(mode), rDev)
}

View file

@ -1,8 +1,12 @@
//go:build !windows
// +build !windows
package fs
import (
"os"
"syscall"
"github.com/pkg/errors"
"github.com/containerd/continuity/sysx"
@ -26,3 +30,17 @@ func copyXAttrs(dst, src string, xeh XAttrErrorHandler) error {
return nil
}
func copyDevice(dst string, fi os.FileInfo) error {
st, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
return errors.New("unsupported stat type")
}
var rDev int
if fi.Mode()&os.ModeDevice == os.ModeDevice || fi.Mode()&os.ModeCharDevice == os.ModeCharDevice {
rDev = int(st.Rdev)
}
mode := st.Mode
mode &^= syscall.S_IFSOCK // socket copied as stub
return mknod(dst, uint32(mode), rDev)
}

View file

@ -16,8 +16,7 @@ func getUIDGID(fi os.FileInfo) (uid, gid int) {
return int(st.Uid), int(st.Gid)
}
func (c *copier) copyFileInfo(fi os.FileInfo, name string) error {
st := fi.Sys().(*syscall.Stat_t)
func (c *copier) copyFileInfo(fi os.FileInfo, src, name string) error {
chown := c.chown
uid, gid := getUIDGID(fi)
old := &User{UID: uid, GID: gid}
@ -40,15 +39,21 @@ func (c *copier) copyFileInfo(fi os.FileInfo, name string) error {
}
}
if c.utime != nil {
if err := Utimes(name, c.utime); err != nil {
return err
}
} else {
timespec := []unix.Timespec{unix.Timespec(StatAtime(st)), unix.Timespec(StatMtime(st))}
if err := unix.UtimesNanoAt(unix.AT_FDCWD, name, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil {
return errors.Wrapf(err, "failed to utime %s", name)
}
if err := c.copyFileTimestamp(fi, name); err != nil {
return err
}
return nil
}
func (c *copier) copyFileTimestamp(fi os.FileInfo, name string) error {
if c.utime != nil {
return Utimes(name, c.utime)
}
st := fi.Sys().(*syscall.Stat_t)
timespec := []unix.Timespec{unix.Timespec(StatAtime(st)), unix.Timespec(StatMtime(st))}
if err := unix.UtimesNanoAt(unix.AT_FDCWD, name, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil {
return errors.Wrapf(err, "failed to utime %s", name)
}
return nil
}

View file

@ -4,14 +4,60 @@ import (
"io"
"os"
"github.com/Microsoft/go-winio"
"github.com/pkg/errors"
"golang.org/x/sys/windows"
)
func (c *copier) copyFileInfo(fi os.FileInfo, name string) error {
const (
seTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege"
)
func (c *copier) copyFileInfo(fi os.FileInfo, src, name string) error {
if err := os.Chmod(name, fi.Mode()); err != nil {
return errors.Wrapf(err, "failed to chmod %s", name)
}
// Copy file ownership and ACL
// We need SeRestorePrivilege and SeTakeOwnershipPrivilege in order
// to restore security info on a file, especially if we're trying to
// apply security info which includes SIDs not necessarily present on
// the host.
privileges := []string{winio.SeRestorePrivilege, seTakeOwnershipPrivilege}
if err := winio.EnableProcessPrivileges(privileges); err != nil {
return err
}
defer winio.DisableProcessPrivileges(privileges)
secInfo, err := windows.GetNamedSecurityInfo(
src, windows.SE_FILE_OBJECT,
windows.OWNER_SECURITY_INFORMATION|windows.DACL_SECURITY_INFORMATION)
if err != nil {
return err
}
dacl, _, err := secInfo.DACL()
if err != nil {
return err
}
sid, _, err := secInfo.Owner()
if err != nil {
return err
}
if err := windows.SetNamedSecurityInfo(
name, windows.SE_FILE_OBJECT,
windows.OWNER_SECURITY_INFORMATION|windows.DACL_SECURITY_INFORMATION,
sid, nil, dacl, nil); err != nil {
return err
}
return nil
}
func (c *copier) copyFileTimestamp(fi os.FileInfo, name string) error {
// TODO: copy windows specific metadata
return nil

View file

@ -1,20 +0,0 @@
//go:build darwin
// +build darwin
package fs
import (
"os"
"syscall"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
func copyDevice(dst string, fi os.FileInfo) error {
st, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
return errors.New("unsupported stat type")
}
return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev))
}

View file

@ -1,20 +0,0 @@
//go:build freebsd || solaris
// +build freebsd solaris
package fs
import (
"os"
"syscall"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
func copyDevice(dst string, fi os.FileInfo) error {
st, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
return errors.New("unsupported stat type")
}
return unix.Mknod(dst, uint32(fi.Mode()), st.Rdev)
}

View file

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
package fs

View file

@ -4,26 +4,8 @@ import (
"os"
"syscall"
"time"
"github.com/pkg/errors"
)
func Chown(p string, old *User, fn Chowner) error {
if fn == nil {
return nil
}
user, err := fn(old)
if err != nil {
return errors.WithStack(err)
}
if user != nil {
if err := os.Lchown(p, user.UID, user.GID); err != nil {
return err
}
}
return nil
}
// MkdirAll is forked os.MkdirAll
func MkdirAll(path string, perm os.FileMode, user Chowner, tm *time.Time) error {
// Fast path: if we can tell whether path is a directory or file, stop with success or error.

View file

@ -1,8 +1,10 @@
//go:build !windows
// +build !windows
package fs
import (
"os"
"time"
"github.com/pkg/errors"
@ -30,3 +32,19 @@ func Utimes(p string, tm *time.Time) error {
return nil
}
func Chown(p string, old *User, fn Chowner) error {
if fn == nil {
return nil
}
user, err := fn(old)
if err != nil {
return errors.WithStack(err)
}
if user != nil {
if err := os.Lchown(p, user.UID, user.GID); err != nil {
return err
}
}
return nil
}

View file

@ -1,10 +1,21 @@
//go:build windows
// +build windows
package fs
import (
"fmt"
"os"
"syscall"
"time"
"github.com/Microsoft/go-winio"
"github.com/pkg/errors"
"golang.org/x/sys/windows"
)
const (
containerAdministratorSidString = "S-1-5-93-2-1"
)
func fixRootDirectory(p string) string {
@ -19,3 +30,64 @@ func fixRootDirectory(p string) string {
func Utimes(p string, tm *time.Time) error {
return nil
}
func Chown(p string, old *User, fn Chowner) error {
if fn == nil {
return nil
}
user, err := fn(old)
if err != nil {
return errors.WithStack(err)
}
userSIDstring := user.SID
if userSIDstring == "" {
userSIDstring = containerAdministratorSidString
}
// Copy file ownership and ACL
// We need SeRestorePrivilege and SeTakeOwnershipPrivilege in order
// to restore security info on a file, especially if we're trying to
// apply security info which includes SIDs not necessarily present on
// the host.
privileges := []string{winio.SeRestorePrivilege, seTakeOwnershipPrivilege}
if err := winio.EnableProcessPrivileges(privileges); err != nil {
return err
}
defer winio.DisableProcessPrivileges(privileges)
sidPtr, err := syscall.UTF16PtrFromString(userSIDstring)
if err != nil {
return errors.Wrap(err, "converting to utf16 ptr")
}
var userSID *windows.SID
if err := windows.ConvertStringSidToSid(sidPtr, &userSID); err != nil {
return errors.Wrap(err, "converting to windows SID")
}
var dacl *windows.ACL
newEntries := []windows.EXPLICIT_ACCESS{
{
AccessPermissions: windows.GENERIC_ALL,
AccessMode: windows.GRANT_ACCESS,
Inheritance: windows.SUB_CONTAINERS_AND_OBJECTS_INHERIT,
Trustee: windows.TRUSTEE{
TrusteeForm: windows.TRUSTEE_IS_SID,
TrusteeValue: windows.TrusteeValueFromSID(userSID),
},
},
}
newAcl, err := windows.ACLFromEntries(newEntries, dacl)
if err != nil {
return fmt.Errorf("adding acls: %w", err)
}
if err := windows.SetNamedSecurityInfo(
p, windows.SE_FILE_OBJECT,
windows.OWNER_SECURITY_INFORMATION|windows.DACL_SECURITY_INFORMATION,
userSID, nil, newAcl, nil); err != nil {
return err
}
return nil
}

View file

@ -1,3 +1,4 @@
//go:build dragonfly || linux || solaris
// +build dragonfly linux solaris
package fs

View file

@ -4,6 +4,7 @@ import (
"context"
"hash"
"io"
gofs "io/fs"
"os"
"path/filepath"
"strconv"
@ -33,10 +34,11 @@ type DiskWriter struct {
opt DiskWriterOpt
dest string
ctx context.Context
cancel func()
eg *errgroup.Group
filter FilterFunc
ctx context.Context
cancel func()
eg *errgroup.Group
filter FilterFunc
dirModTimes map[string]int64
}
func NewDiskWriter(ctx context.Context, dest string, opt DiskWriterOpt) (*DiskWriter, error) {
@ -51,17 +53,32 @@ func NewDiskWriter(ctx context.Context, dest string, opt DiskWriterOpt) (*DiskWr
eg, ctx := errgroup.WithContext(ctx)
return &DiskWriter{
opt: opt,
dest: dest,
eg: eg,
ctx: ctx,
cancel: cancel,
filter: opt.Filter,
opt: opt,
dest: dest,
eg: eg,
ctx: ctx,
cancel: cancel,
filter: opt.Filter,
dirModTimes: map[string]int64{},
}, nil
}
func (dw *DiskWriter) Wait(ctx context.Context) error {
return dw.eg.Wait()
if err := dw.eg.Wait(); err != nil {
return err
}
return filepath.WalkDir(dw.dest, func(path string, d gofs.DirEntry, prevErr error) error {
if prevErr != nil {
return prevErr
}
if !d.IsDir() {
return nil
}
if mtime, ok := dw.dirModTimes[path]; ok {
return chtimes(path, mtime)
}
return nil
})
}
func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, err error) (retErr error) {
@ -147,6 +164,7 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er
if err := os.Mkdir(newPath, fi.Mode()); err != nil {
return errors.Wrapf(err, "failed to create dir %s", newPath)
}
dw.dirModTimes[destPath] = statCopy.ModTime
case fi.Mode()&os.ModeDevice != 0 || fi.Mode()&os.ModeNamedPipe != 0:
if err := handleTarTypeBlockCharFifo(newPath, &statCopy); err != nil {
return errors.Wrapf(err, "failed to create device %s", newPath)
@ -323,10 +341,6 @@ func (lfw *lazyFileWriter) Close() error {
return err
}
func mkdev(major int64, minor int64) uint32 {
return uint32(((minor & 0xfff00) << 12) | ((major & 0xfff) << 8) | (minor & 0xff))
}
// Random number state.
// We generate random temporary file names so that there's a good
// chance the file doesn't exist yet - keeps the number of tries in

View file

@ -1,3 +1,4 @@
//go:build freebsd
// +build freebsd
package fsutil
@ -8,7 +9,9 @@ import (
)
func createSpecialFile(path string, mode uint32, stat *types.Stat) error {
dev := unix.Mkdev(uint32(stat.Devmajor), uint32(stat.Devminor))
return unix.Mknod(path, mode, dev)
return unix.Mknod(path, mode, mkdev(stat.Devmajor, stat.Devminor))
}
func mkdev(major int64, minor int64) uint64 {
return unix.Mkdev(uint32(major), uint32(minor))
}

View file

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
package fsutil

View file

@ -1,13 +1,17 @@
//go:build !windows && !freebsd
// +build !windows,!freebsd
package fsutil
import (
"syscall"
"github.com/tonistiigi/fsutil/types"
"golang.org/x/sys/unix"
)
func createSpecialFile(path string, mode uint32, stat *types.Stat) error {
return syscall.Mknod(path, mode, int(mkdev(stat.Devmajor, stat.Devminor)))
return unix.Mknod(path, mode, mkdev(stat.Devmajor, stat.Devminor))
}
func mkdev(major int64, minor int64) int {
return int(unix.Mkdev(uint32(major), uint32(minor)))
}

View file

@ -1,5 +1,5 @@
variable "GO_VERSION" {
default = "1.16"
default = "1.18"
}
group "default" {
@ -63,5 +63,5 @@ target "shfmt" {
target "cross" {
inherits = ["build"]
platforms = ["linux/amd64", "linux/386", "linux/arm64", "linux/arm", "linux/ppc64le", "linux/s390x", "darwin/amd64", "darwin/arm64", "windows/amd64", "freebsd/amd64", "freebsd/arm64"]
platforms = ["linux/amd64", "linux/386", "linux/arm64", "linux/arm", "linux/ppc64le", "linux/s390x", "darwin/amd64", "darwin/arm64", "windows/amd64", "windows/arm64", "freebsd/amd64", "freebsd/arm64"]
}

View file

@ -1,7 +1,6 @@
package fsutil
import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
@ -75,7 +74,7 @@ func (r *symlinkResolver) readSymlink(p string, allowWildcard bool) ([]string, e
realPath := filepath.Join(r.root, p)
base := filepath.Base(p)
if allowWildcard && containsWildcards(base) {
fis, err := ioutil.ReadDir(filepath.Dir(realPath))
fis, err := os.ReadDir(filepath.Dir(realPath))
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return nil, nil

View file

@ -3,7 +3,6 @@ package fsutil
import (
"context"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
@ -103,7 +102,7 @@ func (fs *subDirFS) Walk(ctx context.Context, fn filepath.WalkFunc) error {
func (fs *subDirFS) Open(p string) (io.ReadCloser, error) {
parts := strings.SplitN(filepath.Clean(p), string(filepath.Separator), 2)
if len(parts) == 0 {
return ioutil.NopCloser(&emptyReader{}), nil
return io.NopCloser(&emptyReader{}), nil
}
d, ok := fs.m[parts[0]]
if !ok {

View file

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
package fsutil

View file

@ -8,7 +8,7 @@ import (
"syscall"
"time"
"github.com/docker/docker/pkg/fileutils"
"github.com/moby/patternmatcher"
"github.com/pkg/errors"
"github.com/tonistiigi/fsutil/types"
)
@ -19,9 +19,29 @@ type WalkOpt struct {
// FollowPaths contains symlinks that are resolved into include patterns
// before performing the fs walk
FollowPaths []string
Map FilterFunc
Map MapFunc
}
type MapFunc func(string, *types.Stat) MapResult
// The result of the walk function controls
// both how WalkDir continues and whether the path is kept.
type MapResult int
const (
// Keep the current path and continue.
MapResultKeep MapResult = iota
// Exclude the current path and continue.
MapResultExclude
// Exclude the current path, and skip the rest of the dir.
// If path is a dir, skip the current directory.
// If path is a file, skip the rest of the parent directory.
// (This matches the semantics of fs.SkipDir.)
MapResultSkipDir
)
func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) error {
root, err := filepath.EvalSymlinks(p)
if err != nil {
@ -37,8 +57,8 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
var (
includePatterns []string
includeMatcher *fileutils.PatternMatcher
excludeMatcher *fileutils.PatternMatcher
includeMatcher *patternmatcher.PatternMatcher
excludeMatcher *patternmatcher.PatternMatcher
)
if opt != nil && opt.IncludePatterns != nil {
@ -63,7 +83,7 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
onlyPrefixIncludes := true
if len(includePatterns) != 0 {
includeMatcher, err = fileutils.NewPatternMatcher(includePatterns)
includeMatcher, err = patternmatcher.New(includePatterns)
if err != nil {
return errors.Wrapf(err, "invalid includepatterns: %s", opt.IncludePatterns)
}
@ -79,7 +99,7 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
onlyPrefixExcludeExceptions := true
if opt != nil && opt.ExcludePatterns != nil {
excludeMatcher, err = fileutils.NewPatternMatcher(opt.ExcludePatterns)
excludeMatcher, err = patternmatcher.New(opt.ExcludePatterns)
if err != nil {
return errors.Wrapf(err, "invalid excludepatterns: %s", opt.ExcludePatterns)
}
@ -97,8 +117,8 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
path string
origpath string
pathWithSep string
includeMatchInfo fileutils.MatchInfo
excludeMatchInfo fileutils.MatchInfo
includeMatchInfo patternmatcher.MatchInfo
excludeMatchInfo patternmatcher.MatchInfo
calledFn bool
}
@ -153,7 +173,7 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
skip := false
if includeMatcher != nil {
var parentIncludeMatchInfo fileutils.MatchInfo
var parentIncludeMatchInfo patternmatcher.MatchInfo
if len(parentDirs) != 0 {
parentIncludeMatchInfo = parentDirs[len(parentDirs)-1].includeMatchInfo
}
@ -188,7 +208,7 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
}
if excludeMatcher != nil {
var parentExcludeMatchInfo fileutils.MatchInfo
var parentExcludeMatchInfo patternmatcher.MatchInfo
if len(parentDirs) != 0 {
parentExcludeMatchInfo = parentDirs[len(parentDirs)-1].excludeMatchInfo
}
@ -258,7 +278,10 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
return ctx.Err()
default:
if opt != nil && opt.Map != nil {
if allowed := opt.Map(stat.Path, stat); !allowed {
result := opt.Map(stat.Path, stat)
if result == MapResultSkipDir {
return filepath.SkipDir
} else if result == MapResultExclude {
return nil
}
}
@ -277,7 +300,8 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
default:
}
if opt != nil && opt.Map != nil {
if allowed := opt.Map(parentStat.Path, parentStat); !allowed {
result := opt.Map(parentStat.Path, parentStat)
if result == MapResultSkipDir || result == MapResultExclude {
continue
}
}
@ -295,11 +319,11 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
})
}
func patternWithoutTrailingGlob(p *fileutils.Pattern) string {
func patternWithoutTrailingGlob(p *patternmatcher.Pattern) string {
patStr := p.String()
// We use filepath.Separator here because fileutils.Pattern patterns
// We use filepath.Separator here because patternmatcher.Pattern patterns
// get transformed to use the native path separator:
// https://github.com/moby/moby/blob/79651b7a979b40e26af353ad283ca7ea5d67a855/pkg/fileutils/fileutils.go#L54
// https://github.com/moby/patternmatcher/blob/130b41bafc16209dc1b52a103fdac1decad04f1a/patternmatcher.go#L52
patStr = strings.TrimSuffix(patStr, string(filepath.Separator)+"**")
patStr = strings.TrimSuffix(patStr, string(filepath.Separator)+"*")
return patStr

4
vendor/modules.txt vendored
View file

@ -814,8 +814,8 @@ github.com/spf13/pflag
# github.com/tinylib/msgp v1.1.6
## explicit; go 1.14
github.com/tinylib/msgp/msgp
# github.com/tonistiigi/fsutil v0.0.0-20220315205639-9ed612626da3
## explicit; go 1.13
# github.com/tonistiigi/fsutil v0.0.0-20221114235510-0127568185cf
## explicit; go 1.18
github.com/tonistiigi/fsutil
github.com/tonistiigi/fsutil/copy
github.com/tonistiigi/fsutil/types