vendor: github.com/tonistiigi/fsutil v0.0.0-20220115021204-b19f7f9cb274
full diff: d72af97c0e...b19f7f9cb2
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
931b455f27
commit
d8e1746466
|
@ -72,7 +72,7 @@ require (
|
||||||
github.com/spf13/cobra v1.1.3
|
github.com/spf13/cobra v1.1.3
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/tchap/go-patricia v2.3.0+incompatible
|
github.com/tchap/go-patricia v2.3.0+incompatible
|
||||||
github.com/tonistiigi/fsutil v0.0.0-20210609172227-d72af97c0eaf
|
github.com/tonistiigi/fsutil v0.0.0-20220115021204-b19f7f9cb274
|
||||||
github.com/vbatts/tar-split v0.11.2
|
github.com/vbatts/tar-split v0.11.2
|
||||||
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852
|
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852
|
||||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae
|
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae
|
||||||
|
|
|
@ -269,6 +269,7 @@ github.com/docker/distribution v2.8.0+incompatible h1:l9EaZDICImO1ngI+uTifW+ZYvv
|
||||||
github.com/docker/distribution v2.8.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
github.com/docker/distribution v2.8.0+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 v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
github.com/docker/docker v20.10.3-0.20210609071616-4c2ec79bf2a8+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v20.10.3-0.20210609071616-4c2ec79bf2a8+incompatible/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.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v20.10.6+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 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.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
|
||||||
|
@ -729,8 +730,9 @@ github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU=
|
||||||
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||||
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-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-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/tonistiigi/fsutil v0.0.0-20210609172227-d72af97c0eaf h1:L0ixhsTk9j+dVnIvF6aiVCxPiaFvwTOyJxqimPq44p8=
|
|
||||||
github.com/tonistiigi/fsutil v0.0.0-20210609172227-d72af97c0eaf/go.mod h1:lJAxK//iyZ3yGbQswdrPTxugZIDM7sd4bEsD0x3XMHk=
|
github.com/tonistiigi/fsutil v0.0.0-20210609172227-d72af97c0eaf/go.mod h1:lJAxK//iyZ3yGbQswdrPTxugZIDM7sd4bEsD0x3XMHk=
|
||||||
|
github.com/tonistiigi/fsutil v0.0.0-20220115021204-b19f7f9cb274 h1:wbyZxD6IPFp0sl5uscMOJRsz5UKGFiNiD16e+MVfKZY=
|
||||||
|
github.com/tonistiigi/fsutil v0.0.0-20220115021204-b19f7f9cb274/go.mod h1:oPAfvw32vlUJSjyDcQ3Bu0nb2ON2B+G0dtVN/SZNJiA=
|
||||||
github.com/tonistiigi/go-immutable-radix v0.0.0-20170803185627-826af9ccf0fe h1:pd7hrFSqUPxYS9IB+UMG1AB/8EXGXo17ssx0bSQ5L6Y=
|
github.com/tonistiigi/go-immutable-radix v0.0.0-20170803185627-826af9ccf0fe h1:pd7hrFSqUPxYS9IB+UMG1AB/8EXGXo17ssx0bSQ5L6Y=
|
||||||
github.com/tonistiigi/go-immutable-radix v0.0.0-20170803185627-826af9ccf0fe/go.mod h1:/+MCh11CJf2oz0BXmlmqyopK/ad1rKkcOXPoYuPCJYU=
|
github.com/tonistiigi/go-immutable-radix v0.0.0-20170803185627-826af9ccf0fe/go.mod h1:/+MCh11CJf2oz0BXmlmqyopK/ad1rKkcOXPoYuPCJYU=
|
||||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0=
|
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0=
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -13,6 +14,7 @@ import (
|
||||||
"github.com/containerd/continuity/fs"
|
"github.com/containerd/continuity/fs"
|
||||||
"github.com/docker/docker/pkg/fileutils"
|
"github.com/docker/docker/pkg/fileutils"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/tonistiigi/fsutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var bufferPool = &sync.Pool{
|
var bufferPool = &sync.Pool{
|
||||||
|
@ -87,7 +89,7 @@ func Copy(ctx context.Context, srcRoot, src, dstRoot, dst string, opts ...Opt) e
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := newCopier(ci.Chown, ci.Utime, ci.Mode, ci.XAttrErrorHandler, ci.IncludePatterns, ci.ExcludePatterns)
|
c, err := newCopier(dstRoot, ci.Chown, ci.Utime, ci.Mode, ci.XAttrErrorHandler, ci.IncludePatterns, ci.ExcludePatterns, ci.ChangeFunc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -113,8 +115,7 @@ func Copy(ctx context.Context, srcRoot, src, dstRoot, dst string, opts ...Opt) e
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
skipIncludePatterns := c.includePatternMatcher == nil
|
if err := c.copy(ctx, srcFollowed, "", dst, false, fileutils.MatchInfo{}, fileutils.MatchInfo{}); err != nil {
|
||||||
if err := c.copy(ctx, srcFollowed, "", dst, false, skipIncludePatterns); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,6 +172,7 @@ type CopyInfo struct {
|
||||||
IncludePatterns []string
|
IncludePatterns []string
|
||||||
// Exclude files/dir matching any of these patterns (even if they match an include pattern)
|
// Exclude files/dir matching any of these patterns (even if they match an include pattern)
|
||||||
ExcludePatterns []string
|
ExcludePatterns []string
|
||||||
|
ChangeFunc fsutil.ChangeFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
type Opt func(*CopyInfo)
|
type Opt func(*CopyInfo)
|
||||||
|
@ -218,6 +220,12 @@ func WithExcludePattern(excludePattern string) Opt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithChangeNotifier(fn fsutil.ChangeFunc) Opt {
|
||||||
|
return func(ci *CopyInfo) {
|
||||||
|
ci.ChangeFunc = fn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type copier struct {
|
type copier struct {
|
||||||
chown Chowner
|
chown Chowner
|
||||||
utime *time.Time
|
utime *time.Time
|
||||||
|
@ -227,6 +235,8 @@ type copier struct {
|
||||||
includePatternMatcher *fileutils.PatternMatcher
|
includePatternMatcher *fileutils.PatternMatcher
|
||||||
excludePatternMatcher *fileutils.PatternMatcher
|
excludePatternMatcher *fileutils.PatternMatcher
|
||||||
parentDirs []parentDir
|
parentDirs []parentDir
|
||||||
|
changefn fsutil.ChangeFunc
|
||||||
|
root string
|
||||||
}
|
}
|
||||||
|
|
||||||
type parentDir struct {
|
type parentDir struct {
|
||||||
|
@ -235,7 +245,7 @@ type parentDir struct {
|
||||||
copied bool
|
copied bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCopier(chown Chowner, tm *time.Time, mode *int, xeh XAttrErrorHandler, includePatterns, excludePatterns []string) (*copier, error) {
|
func newCopier(root string, chown Chowner, tm *time.Time, mode *int, xeh XAttrErrorHandler, includePatterns, excludePatterns []string, changeFunc fsutil.ChangeFunc) (*copier, error) {
|
||||||
if xeh == nil {
|
if xeh == nil {
|
||||||
xeh = func(dst, src, key string, err error) error {
|
xeh = func(dst, src, key string, err error) error {
|
||||||
return err
|
return err
|
||||||
|
@ -261,6 +271,7 @@ func newCopier(chown Chowner, tm *time.Time, mode *int, xeh XAttrErrorHandler, i
|
||||||
}
|
}
|
||||||
|
|
||||||
return &copier{
|
return &copier{
|
||||||
|
root: root,
|
||||||
inodes: map[uint64]string{},
|
inodes: map[uint64]string{},
|
||||||
chown: chown,
|
chown: chown,
|
||||||
utime: tm,
|
utime: tm,
|
||||||
|
@ -268,11 +279,12 @@ func newCopier(chown Chowner, tm *time.Time, mode *int, xeh XAttrErrorHandler, i
|
||||||
mode: mode,
|
mode: mode,
|
||||||
includePatternMatcher: includePatternMatcher,
|
includePatternMatcher: includePatternMatcher,
|
||||||
excludePatternMatcher: excludePatternMatcher,
|
excludePatternMatcher: excludePatternMatcher,
|
||||||
|
changefn: changeFunc,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// dest is always clean
|
// dest is always clean
|
||||||
func (c *copier) copy(ctx context.Context, src, srcComponents, target string, overwriteTargetMetadata, skipIncludePatterns bool) error {
|
func (c *copier) copy(ctx context.Context, src, srcComponents, target string, overwriteTargetMetadata bool, parentIncludeMatchInfo, parentExcludeMatchInfo fileutils.MatchInfo) error {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
|
@ -285,18 +297,24 @@ func (c *copier) copy(ctx context.Context, src, srcComponents, target string, ov
|
||||||
}
|
}
|
||||||
|
|
||||||
include := true
|
include := true
|
||||||
|
var (
|
||||||
|
includeMatchInfo fileutils.MatchInfo
|
||||||
|
excludeMatchInfo fileutils.MatchInfo
|
||||||
|
)
|
||||||
if srcComponents != "" {
|
if srcComponents != "" {
|
||||||
if !skipIncludePatterns {
|
matchesIncludePattern := false
|
||||||
include, err = c.include(srcComponents, fi)
|
matchesExcludePattern := false
|
||||||
if err != nil {
|
matchesIncludePattern, includeMatchInfo, err = c.include(srcComponents, fi, parentIncludeMatchInfo)
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exclude, err := c.exclude(srcComponents, fi)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if exclude {
|
include = matchesIncludePattern
|
||||||
|
|
||||||
|
matchesExcludePattern, excludeMatchInfo, err = c.exclude(srcComponents, fi, parentExcludeMatchInfo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if matchesExcludePattern {
|
||||||
include = false
|
include = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -318,14 +336,19 @@ func (c *copier) copy(ctx context.Context, src, srcComponents, target string, ov
|
||||||
}
|
}
|
||||||
|
|
||||||
copyFileInfo := true
|
copyFileInfo := true
|
||||||
|
notify := true
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case fi.IsDir():
|
case fi.IsDir():
|
||||||
if created, err := c.copyDirectory(ctx, src, srcComponents, target, fi, overwriteTargetMetadata, skipIncludePatterns, include); err != nil {
|
if created, err := c.copyDirectory(
|
||||||
|
ctx, src, srcComponents, target, fi, overwriteTargetMetadata,
|
||||||
|
include, includeMatchInfo, excludeMatchInfo,
|
||||||
|
); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if !overwriteTargetMetadata || !skipIncludePatterns {
|
} else if !overwriteTargetMetadata || c.includePatternMatcher != nil {
|
||||||
copyFileInfo = created
|
copyFileInfo = created
|
||||||
}
|
}
|
||||||
|
notify = false
|
||||||
case (fi.Mode() & os.ModeType) == 0:
|
case (fi.Mode() & os.ModeType) == 0:
|
||||||
link, err := getLinkSource(target, fi, c.inodes)
|
link, err := getLinkSource(target, fi, c.inodes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -364,31 +387,45 @@ func (c *copier) copy(ctx context.Context, src, srcComponents, target string, ov
|
||||||
return errors.Wrap(err, "failed to copy xattrs")
|
return errors.Wrap(err, "failed to copy xattrs")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if notify {
|
||||||
|
if err := c.notifyChange(target, fi); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *copier) include(path string, fi os.FileInfo) (bool, error) {
|
func (c *copier) notifyChange(target string, fi os.FileInfo) error {
|
||||||
if c.includePatternMatcher == nil {
|
if c.changefn != nil {
|
||||||
return false, nil
|
if err := c.changefn(fsutil.ChangeKindAdd, path.Clean(strings.TrimPrefix(target, c.root)), fi, nil); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to notify file change")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
m, err := c.includePatternMatcher.Matches(path)
|
|
||||||
if err != nil {
|
|
||||||
return false, errors.Wrap(err, "failed to match includepatterns")
|
|
||||||
}
|
|
||||||
return m, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *copier) exclude(path string, fi os.FileInfo) (bool, error) {
|
func (c *copier) include(path string, fi os.FileInfo, parentIncludeMatchInfo fileutils.MatchInfo) (bool, fileutils.MatchInfo, error) {
|
||||||
if c.excludePatternMatcher == nil {
|
if c.includePatternMatcher == nil {
|
||||||
return false, nil
|
return true, fileutils.MatchInfo{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
m, err := c.excludePatternMatcher.Matches(path)
|
m, matchInfo, err := c.includePatternMatcher.MatchesUsingParentResults(path, parentIncludeMatchInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, errors.Wrap(err, "failed to match excludepatterns")
|
return false, matchInfo, errors.Wrap(err, "failed to match includepatterns")
|
||||||
}
|
}
|
||||||
return m, nil
|
return m, matchInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *copier) exclude(path string, fi os.FileInfo, parentExcludeMatchInfo fileutils.MatchInfo) (bool, fileutils.MatchInfo, error) {
|
||||||
|
if c.excludePatternMatcher == nil {
|
||||||
|
return false, fileutils.MatchInfo{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m, matchInfo, err := c.excludePatternMatcher.MatchesUsingParentResults(path, parentExcludeMatchInfo)
|
||||||
|
if err != nil {
|
||||||
|
return false, matchInfo, errors.Wrap(err, "failed to match excludepatterns")
|
||||||
|
}
|
||||||
|
return m, matchInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delayed creation of parent directories when a file or dir matches an include
|
// Delayed creation of parent directories when a file or dir matches an include
|
||||||
|
@ -426,30 +463,47 @@ func (c *copier) createParentDirs(src, srcComponents, target string, overwriteTa
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *copier) copyDirectory(ctx context.Context, src, srcComponents, dst string, stat os.FileInfo, overwriteTargetMetadata, skipIncludePatterns, matchedExactly bool) (bool, error) {
|
func (c *copier) copyDirectory(
|
||||||
|
ctx context.Context,
|
||||||
|
src string,
|
||||||
|
srcComponents string,
|
||||||
|
dst string,
|
||||||
|
stat os.FileInfo,
|
||||||
|
overwriteTargetMetadata bool,
|
||||||
|
include bool,
|
||||||
|
includeMatchInfo fileutils.MatchInfo,
|
||||||
|
excludeMatchInfo fileutils.MatchInfo,
|
||||||
|
) (bool, error) {
|
||||||
if !stat.IsDir() {
|
if !stat.IsDir() {
|
||||||
return false, errors.Errorf("source is not directory")
|
return false, errors.Errorf("source is not directory")
|
||||||
}
|
}
|
||||||
|
|
||||||
created := false
|
created := false
|
||||||
|
|
||||||
// If there are no include patterns or this directory matched an include
|
parentDir := parentDir{
|
||||||
// pattern exactly, go ahead and create the directory. Otherwise, delay to
|
srcPath: src,
|
||||||
// handle include patterns like a/*/c where we do not want to create a/b
|
dstPath: dst,
|
||||||
// until we encounter a/b/c.
|
}
|
||||||
if matchedExactly || skipIncludePatterns {
|
|
||||||
|
// If this directory passed include/exclude matching directly, go ahead
|
||||||
|
// and create the directory. Otherwise, delay to handle include
|
||||||
|
// patterns like a/*/c where we do not want to create a/b until we
|
||||||
|
// encounter a/b/c.
|
||||||
|
if include {
|
||||||
var err error
|
var err error
|
||||||
created, err = copyDirectoryOnly(src, dst, stat, overwriteTargetMetadata)
|
created, err = copyDirectoryOnly(src, dst, stat, overwriteTargetMetadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return created, err
|
return created, err
|
||||||
}
|
}
|
||||||
|
if created || overwriteTargetMetadata {
|
||||||
|
if err := c.notifyChange(dst, stat); err != nil {
|
||||||
|
return created, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parentDir.copied = true
|
||||||
}
|
}
|
||||||
|
|
||||||
c.parentDirs = append(c.parentDirs, parentDir{
|
c.parentDirs = append(c.parentDirs, parentDir)
|
||||||
srcPath: src,
|
|
||||||
dstPath: dst,
|
|
||||||
copied: skipIncludePatterns,
|
|
||||||
})
|
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
c.parentDirs = c.parentDirs[:len(c.parentDirs)-1]
|
c.parentDirs = c.parentDirs[:len(c.parentDirs)-1]
|
||||||
|
@ -461,7 +515,12 @@ func (c *copier) copyDirectory(ctx context.Context, src, srcComponents, dst stri
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, fi := range fis {
|
for _, fi := range fis {
|
||||||
if err := c.copy(ctx, filepath.Join(src, fi.Name()), filepath.Join(srcComponents, fi.Name()), filepath.Join(dst, fi.Name()), true, skipIncludePatterns); err != nil {
|
if err := c.copy(
|
||||||
|
ctx,
|
||||||
|
filepath.Join(src, fi.Name()), filepath.Join(srcComponents, fi.Name()),
|
||||||
|
filepath.Join(dst, fi.Name()),
|
||||||
|
true, includeMatchInfo, excludeMatchInfo,
|
||||||
|
); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
// +build freebsd
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func copyFile(source, target string) error {
|
||||||
|
src, err := os.Open(source)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to open source %s", source)
|
||||||
|
}
|
||||||
|
defer src.Close()
|
||||||
|
tgt, err := os.Create(target)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to open target %s", target)
|
||||||
|
}
|
||||||
|
defer tgt.Close()
|
||||||
|
|
||||||
|
return copyFileContent(tgt, src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyFileContent(dst, src *os.File) error {
|
||||||
|
buf := bufferPool.Get().(*[]byte)
|
||||||
|
_, err := io.CopyBuffer(dst, src, *buf)
|
||||||
|
bufferPool.Put(buf)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -97,7 +97,10 @@ func copyFileContent(dst, src *os.File) error {
|
||||||
buf := bufferPool.Get().(*[]byte)
|
buf := bufferPool.Get().(*[]byte)
|
||||||
_, err = io.CopyBuffer(dst, src, *buf)
|
_, err = io.CopyBuffer(dst, src, *buf)
|
||||||
bufferPool.Put(buf)
|
bufferPool.Put(buf)
|
||||||
return errors.Wrap(err, "userspace copy failed")
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "userspace copy failed")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
first = false
|
first = false
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build solaris || darwin || freebsd
|
||||||
// +build solaris darwin freebsd
|
// +build solaris darwin freebsd
|
||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
@ -51,11 +52,3 @@ func (c *copier) copyFileInfo(fi os.FileInfo, name string) error {
|
||||||
}
|
}
|
||||||
return nil
|
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))
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
//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))
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
//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)
|
||||||
|
}
|
|
@ -33,6 +33,19 @@ const (
|
||||||
ChangeKindDelete
|
ChangeKindDelete
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (k ChangeKind) String() string {
|
||||||
|
switch k {
|
||||||
|
case ChangeKindAdd:
|
||||||
|
return "add"
|
||||||
|
case ChangeKindModify:
|
||||||
|
return "modify"
|
||||||
|
case ChangeKindDelete:
|
||||||
|
return "delete"
|
||||||
|
default:
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ChangeFunc is the type of function called for each change
|
// ChangeFunc is the type of function called for each change
|
||||||
// computed during a directory changes calculation.
|
// computed during a directory changes calculation.
|
||||||
type ChangeFunc func(ChangeKind, string, os.FileInfo, error) error
|
type ChangeFunc func(ChangeKind, string, os.FileInfo, error) error
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
// +build freebsd
|
||||||
|
|
||||||
|
package fsutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/tonistiigi/fsutil/types"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
|
@ -45,7 +45,7 @@ func handleTarTypeBlockCharFifo(path string, stat *types.Stat) error {
|
||||||
mode |= syscall.S_IFBLK
|
mode |= syscall.S_IFBLK
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := syscall.Mknod(path, mode, int(mkdev(stat.Devmajor, stat.Devminor))); err != nil {
|
if err := createSpecialFile(path, mode, stat); err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
// +build !windows,!freebsd
|
||||||
|
|
||||||
|
package fsutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/tonistiigi/fsutil/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func createSpecialFile(path string, mode uint32, stat *types.Stat) error {
|
||||||
|
return syscall.Mknod(path, mode, int(mkdev(stat.Devmajor, stat.Devminor)))
|
||||||
|
}
|
|
@ -63,5 +63,5 @@ target "shfmt" {
|
||||||
|
|
||||||
target "cross" {
|
target "cross" {
|
||||||
inherits = ["build"]
|
inherits = ["build"]
|
||||||
platforms = ["linux/amd64", "linux/386", "linux/arm64", "linux/arm", "linux/ppc64le", "linux/s390x"]
|
platforms = ["linux/amd64", "linux/386", "linux/arm64", "linux/arm", "linux/ppc64le", "linux/s390x", "darwin/amd64", "darwin/arm64", "windows/amd64", "freebsd/amd64", "freebsd/arm64"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
package prefix
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Match matches a path against a pattern. It returns m = true if the path
|
|
||||||
// matches the pattern, and partial = true if the pattern has more separators
|
|
||||||
// than the path and the common components match (for example, name = foo and
|
|
||||||
// pattern = foo/bar/*). slashSeparator determines whether the path and pattern
|
|
||||||
// are '/' delimited (true) or use the native path separator (false).
|
|
||||||
func Match(pattern, name string, slashSeparator bool) (m bool, partial bool) {
|
|
||||||
separator := filepath.Separator
|
|
||||||
if slashSeparator {
|
|
||||||
separator = '/'
|
|
||||||
}
|
|
||||||
count := strings.Count(name, string(separator))
|
|
||||||
if strings.Count(pattern, string(separator)) > count {
|
|
||||||
pattern = trimUntilIndex(pattern, string(separator), count)
|
|
||||||
partial = true
|
|
||||||
}
|
|
||||||
if slashSeparator {
|
|
||||||
m, _ = path.Match(pattern, name)
|
|
||||||
} else {
|
|
||||||
m, _ = filepath.Match(pattern, name)
|
|
||||||
}
|
|
||||||
return m, partial
|
|
||||||
}
|
|
||||||
|
|
||||||
func trimUntilIndex(str, sep string, count int) string {
|
|
||||||
s := str
|
|
||||||
i := 0
|
|
||||||
c := 0
|
|
||||||
for {
|
|
||||||
idx := strings.Index(s, sep)
|
|
||||||
s = s[idx+len(sep):]
|
|
||||||
i += idx + len(sep)
|
|
||||||
c++
|
|
||||||
if c > count {
|
|
||||||
return str[:i-len(sep)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/fileutils"
|
"github.com/docker/docker/pkg/fileutils"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/tonistiigi/fsutil/prefix"
|
|
||||||
"github.com/tonistiigi/fsutil/types"
|
"github.com/tonistiigi/fsutil/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -36,20 +35,15 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
||||||
return errors.WithStack(&os.PathError{Op: "walk", Path: root, Err: syscall.ENOTDIR})
|
return errors.WithStack(&os.PathError{Op: "walk", Path: root, Err: syscall.ENOTDIR})
|
||||||
}
|
}
|
||||||
|
|
||||||
var pm *fileutils.PatternMatcher
|
var (
|
||||||
if opt != nil && opt.ExcludePatterns != nil {
|
includePatterns []string
|
||||||
pm, err = fileutils.NewPatternMatcher(opt.ExcludePatterns)
|
includeMatcher *fileutils.PatternMatcher
|
||||||
if err != nil {
|
excludeMatcher *fileutils.PatternMatcher
|
||||||
return errors.Wrapf(err, "invalid excludepatterns: %s", opt.ExcludePatterns)
|
)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var includePatterns []string
|
|
||||||
if opt != nil && opt.IncludePatterns != nil {
|
if opt != nil && opt.IncludePatterns != nil {
|
||||||
includePatterns = make([]string, len(opt.IncludePatterns))
|
includePatterns = make([]string, len(opt.IncludePatterns))
|
||||||
for k := range opt.IncludePatterns {
|
copy(includePatterns, opt.IncludePatterns)
|
||||||
includePatterns[k] = filepath.Clean(opt.IncludePatterns[k])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if opt != nil && opt.FollowPaths != nil {
|
if opt != nil && opt.FollowPaths != nil {
|
||||||
targets, err := FollowLinks(p, opt.FollowPaths)
|
targets, err := FollowLinks(p, opt.FollowPaths)
|
||||||
|
@ -62,18 +56,62 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastIncludedDir string
|
patternChars := "*[]?^"
|
||||||
|
if os.PathSeparator != '\\' {
|
||||||
|
patternChars += `\`
|
||||||
|
}
|
||||||
|
|
||||||
|
onlyPrefixIncludes := true
|
||||||
|
if len(includePatterns) != 0 {
|
||||||
|
includeMatcher, err = fileutils.NewPatternMatcher(includePatterns)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "invalid includepatterns: %s", opt.IncludePatterns)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range includeMatcher.Patterns() {
|
||||||
|
if !p.Exclusion() && strings.ContainsAny(patternWithoutTrailingGlob(p), patternChars) {
|
||||||
|
onlyPrefixIncludes = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onlyPrefixExcludeExceptions := true
|
||||||
|
if opt != nil && opt.ExcludePatterns != nil {
|
||||||
|
excludeMatcher, err = fileutils.NewPatternMatcher(opt.ExcludePatterns)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "invalid excludepatterns: %s", opt.ExcludePatterns)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range excludeMatcher.Patterns() {
|
||||||
|
if p.Exclusion() && strings.ContainsAny(patternWithoutTrailingGlob(p), patternChars) {
|
||||||
|
onlyPrefixExcludeExceptions = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type visitedDir struct {
|
||||||
|
fi os.FileInfo
|
||||||
|
path string
|
||||||
|
origpath string
|
||||||
|
pathWithSep string
|
||||||
|
includeMatchInfo fileutils.MatchInfo
|
||||||
|
excludeMatchInfo fileutils.MatchInfo
|
||||||
|
calledFn bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// used only for include/exclude handling
|
||||||
|
var parentDirs []visitedDir
|
||||||
|
|
||||||
seenFiles := make(map[uint64]string)
|
seenFiles := make(map[uint64]string)
|
||||||
return filepath.Walk(root, func(path string, fi os.FileInfo, err error) (retErr error) {
|
return filepath.Walk(root, func(path string, fi os.FileInfo, walkErr error) (retErr error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if retErr != nil && isNotExist(retErr) {
|
if retErr != nil && isNotExist(retErr) {
|
||||||
retErr = filepath.SkipDir
|
retErr = filepath.SkipDir
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
origpath := path
|
origpath := path
|
||||||
path, err = filepath.Rel(root, path)
|
path, err = filepath.Rel(root, path)
|
||||||
|
@ -85,67 +123,125 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if opt != nil {
|
var dir visitedDir
|
||||||
if includePatterns != nil {
|
|
||||||
skip := false
|
|
||||||
if lastIncludedDir != "" {
|
|
||||||
if strings.HasPrefix(path, lastIncludedDir+string(filepath.Separator)) {
|
|
||||||
skip = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !skip {
|
if includeMatcher != nil || excludeMatcher != nil {
|
||||||
matched := false
|
for len(parentDirs) != 0 {
|
||||||
partial := true
|
lastParentDir := parentDirs[len(parentDirs)-1].pathWithSep
|
||||||
for _, pattern := range includePatterns {
|
if strings.HasPrefix(path, lastParentDir) {
|
||||||
if ok, p := prefix.Match(pattern, path, false); ok {
|
break
|
||||||
matched = true
|
|
||||||
if !p {
|
|
||||||
partial = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !matched {
|
|
||||||
if fi.IsDir() {
|
|
||||||
return filepath.SkipDir
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if !partial && fi.IsDir() {
|
|
||||||
lastIncludedDir = path
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
parentDirs = parentDirs[:len(parentDirs)-1]
|
||||||
}
|
}
|
||||||
if pm != nil {
|
|
||||||
m, err := pm.Matches(path)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to match excludepatterns")
|
|
||||||
}
|
|
||||||
|
|
||||||
if m {
|
if fi.IsDir() {
|
||||||
if fi.IsDir() {
|
dir = visitedDir{
|
||||||
if !pm.Exclusions() {
|
fi: fi,
|
||||||
return filepath.SkipDir
|
path: path,
|
||||||
}
|
origpath: origpath,
|
||||||
dirSlash := path + string(filepath.Separator)
|
pathWithSep: path + string(filepath.Separator),
|
||||||
for _, pat := range pm.Patterns() {
|
|
||||||
if !pat.Exclusion() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
patStr := pat.String() + string(filepath.Separator)
|
|
||||||
if strings.HasPrefix(patStr, dirSlash) {
|
|
||||||
goto passedFilter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return filepath.SkipDir
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
passedFilter:
|
skip := false
|
||||||
|
|
||||||
|
if includeMatcher != nil {
|
||||||
|
var parentIncludeMatchInfo fileutils.MatchInfo
|
||||||
|
if len(parentDirs) != 0 {
|
||||||
|
parentIncludeMatchInfo = parentDirs[len(parentDirs)-1].includeMatchInfo
|
||||||
|
}
|
||||||
|
m, matchInfo, err := includeMatcher.MatchesUsingParentResults(path, parentIncludeMatchInfo)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to match includepatterns")
|
||||||
|
}
|
||||||
|
|
||||||
|
if fi.IsDir() {
|
||||||
|
dir.includeMatchInfo = matchInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
if !m {
|
||||||
|
if fi.IsDir() && onlyPrefixIncludes {
|
||||||
|
// Optimization: we can skip walking this dir if no include
|
||||||
|
// patterns could match anything inside it.
|
||||||
|
dirSlash := path + string(filepath.Separator)
|
||||||
|
for _, pat := range includeMatcher.Patterns() {
|
||||||
|
if pat.Exclusion() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
patStr := patternWithoutTrailingGlob(pat) + string(filepath.Separator)
|
||||||
|
if strings.HasPrefix(patStr, dirSlash) {
|
||||||
|
goto passedIncludeFilter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
passedIncludeFilter:
|
||||||
|
skip = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if excludeMatcher != nil {
|
||||||
|
var parentExcludeMatchInfo fileutils.MatchInfo
|
||||||
|
if len(parentDirs) != 0 {
|
||||||
|
parentExcludeMatchInfo = parentDirs[len(parentDirs)-1].excludeMatchInfo
|
||||||
|
}
|
||||||
|
m, matchInfo, err := excludeMatcher.MatchesUsingParentResults(path, parentExcludeMatchInfo)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to match excludepatterns")
|
||||||
|
}
|
||||||
|
|
||||||
|
if fi.IsDir() {
|
||||||
|
dir.excludeMatchInfo = matchInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
if m {
|
||||||
|
if fi.IsDir() && onlyPrefixExcludeExceptions {
|
||||||
|
// Optimization: we can skip walking this dir if no
|
||||||
|
// exceptions to exclude patterns could match anything
|
||||||
|
// inside it.
|
||||||
|
if !excludeMatcher.Exclusions() {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
|
||||||
|
dirSlash := path + string(filepath.Separator)
|
||||||
|
for _, pat := range excludeMatcher.Patterns() {
|
||||||
|
if !pat.Exclusion() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
patStr := patternWithoutTrailingGlob(pat) + string(filepath.Separator)
|
||||||
|
if strings.HasPrefix(patStr, dirSlash) {
|
||||||
|
goto passedExcludeFilter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
passedExcludeFilter:
|
||||||
|
skip = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if walkErr != nil {
|
||||||
|
if skip && errors.Is(walkErr, os.ErrPermission) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return walkErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if includeMatcher != nil || excludeMatcher != nil {
|
||||||
|
defer func() {
|
||||||
|
if fi.IsDir() {
|
||||||
|
parentDirs = append(parentDirs, dir)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
if skip {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dir.calledFn = true
|
||||||
|
|
||||||
stat, err := mkstat(origpath, path, fi, seenFiles)
|
stat, err := mkstat(origpath, path, fi, seenFiles)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -160,6 +256,31 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for i, parentDir := range parentDirs {
|
||||||
|
if parentDir.calledFn {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parentStat, err := mkstat(parentDir.origpath, parentDir.path, parentDir.fi, seenFiles)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
if opt != nil && opt.Map != nil {
|
||||||
|
if allowed := opt.Map(parentStat.Path, parentStat); !allowed {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fn(parentStat.Path, &StatInfo{parentStat}, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
parentDirs[i].calledFn = true
|
||||||
|
}
|
||||||
if err := fn(stat.Path, &StatInfo{stat}, nil); err != nil {
|
if err := fn(stat.Path, &StatInfo{stat}, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -168,6 +289,16 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func patternWithoutTrailingGlob(p *fileutils.Pattern) string {
|
||||||
|
patStr := p.String()
|
||||||
|
// We use filepath.Separator here because fileutils.Pattern patterns
|
||||||
|
// get transformed to use the native path separator:
|
||||||
|
// https://github.com/moby/moby/blob/79651b7a979b40e26af353ad283ca7ea5d67a855/pkg/fileutils/fileutils.go#L54
|
||||||
|
patStr = strings.TrimSuffix(patStr, string(filepath.Separator)+"**")
|
||||||
|
patStr = strings.TrimSuffix(patStr, string(filepath.Separator)+"*")
|
||||||
|
return patStr
|
||||||
|
}
|
||||||
|
|
||||||
type StatInfo struct {
|
type StatInfo struct {
|
||||||
*types.Stat
|
*types.Stat
|
||||||
}
|
}
|
||||||
|
|
|
@ -737,11 +737,10 @@ github.com/tchap/go-patricia/patricia
|
||||||
# github.com/tinylib/msgp v1.1.0
|
# github.com/tinylib/msgp v1.1.0
|
||||||
## explicit
|
## explicit
|
||||||
github.com/tinylib/msgp/msgp
|
github.com/tinylib/msgp/msgp
|
||||||
# github.com/tonistiigi/fsutil v0.0.0-20210609172227-d72af97c0eaf
|
# github.com/tonistiigi/fsutil v0.0.0-20220115021204-b19f7f9cb274
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/tonistiigi/fsutil
|
github.com/tonistiigi/fsutil
|
||||||
github.com/tonistiigi/fsutil/copy
|
github.com/tonistiigi/fsutil/copy
|
||||||
github.com/tonistiigi/fsutil/prefix
|
|
||||||
github.com/tonistiigi/fsutil/types
|
github.com/tonistiigi/fsutil/types
|
||||||
# github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea
|
# github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea
|
||||||
## explicit
|
## explicit
|
||||||
|
|
Loading…
Reference in New Issue