mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-07-16 16:37:10 +08:00
vendor: github.com/moby/buildkit 5ae9b23c40a9 (master / v0.13.0-dev)
full diff: -36ef4d8c0d...f098008783
-d5c1d785b0...5ae9b23c40
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
@ -2,25 +2,35 @@ package fsutil
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
gofs "io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/moby/patternmatcher"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tonistiigi/fsutil/types"
|
||||
)
|
||||
|
||||
type WalkOpt struct {
|
||||
type FilterOpt struct {
|
||||
// IncludePatterns requires that the path matches at least one of the
|
||||
// specified patterns.
|
||||
IncludePatterns []string
|
||||
|
||||
// ExcludePatterns requires that the path does not match any of the
|
||||
// specified patterns.
|
||||
ExcludePatterns []string
|
||||
// FollowPaths contains symlinks that are resolved into include patterns
|
||||
// before performing the fs walk
|
||||
|
||||
// FollowPaths contains symlinks that are resolved into IncludePatterns
|
||||
// at the time of the call to NewFilterFS.
|
||||
FollowPaths []string
|
||||
Map MapFunc
|
||||
|
||||
// Map is called for each path that is included in the result.
|
||||
// The function can modify the stat info for each element, while the result
|
||||
// of the function controls both how Walk continues.
|
||||
Map MapFunc
|
||||
}
|
||||
|
||||
type MapFunc func(string, *types.Stat) MapResult
|
||||
@ -43,33 +53,41 @@ const (
|
||||
MapResultSkipDir
|
||||
)
|
||||
|
||||
func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) error {
|
||||
root, err := filepath.EvalSymlinks(p)
|
||||
if err != nil {
|
||||
return errors.WithStack(&os.PathError{Op: "resolve", Path: root, Err: err})
|
||||
}
|
||||
rootFI, err := os.Stat(root)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
if !rootFI.IsDir() {
|
||||
return errors.WithStack(&os.PathError{Op: "walk", Path: root, Err: syscall.ENOTDIR})
|
||||
type filterFS struct {
|
||||
fs FS
|
||||
|
||||
includeMatcher *patternmatcher.PatternMatcher
|
||||
excludeMatcher *patternmatcher.PatternMatcher
|
||||
onlyPrefixIncludes bool
|
||||
onlyPrefixExcludeExceptions bool
|
||||
|
||||
mapFn MapFunc
|
||||
}
|
||||
|
||||
// NewFilterFS creates a new FS that filters the given FS using the given
|
||||
// FilterOpt.
|
||||
|
||||
// The returned FS will not contain any paths that do not match the provided
|
||||
// include and exclude patterns, or that are are exlcluded using the mapping
|
||||
// function.
|
||||
//
|
||||
// The FS is assumed to be a snapshot of the filesystem at the time of the
|
||||
// call to NewFilterFS. If the underlying filesystem changes, calls to the
|
||||
// underlying FS may be inconsistent.
|
||||
func NewFilterFS(fs FS, opt *FilterOpt) (FS, error) {
|
||||
if opt == nil {
|
||||
return fs, nil
|
||||
}
|
||||
|
||||
var (
|
||||
includePatterns []string
|
||||
includeMatcher *patternmatcher.PatternMatcher
|
||||
excludeMatcher *patternmatcher.PatternMatcher
|
||||
)
|
||||
|
||||
if opt != nil && opt.IncludePatterns != nil {
|
||||
var includePatterns []string
|
||||
if opt.IncludePatterns != nil {
|
||||
includePatterns = make([]string, len(opt.IncludePatterns))
|
||||
copy(includePatterns, opt.IncludePatterns)
|
||||
}
|
||||
if opt != nil && opt.FollowPaths != nil {
|
||||
targets, err := FollowLinks(p, opt.FollowPaths)
|
||||
if opt.FollowPaths != nil {
|
||||
targets, err := FollowLinks(fs, opt.FollowPaths)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
if targets != nil {
|
||||
includePatterns = append(includePatterns, targets...)
|
||||
@ -82,11 +100,18 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
||||
patternChars += `\`
|
||||
}
|
||||
|
||||
onlyPrefixIncludes := true
|
||||
if len(includePatterns) != 0 {
|
||||
var (
|
||||
includeMatcher *patternmatcher.PatternMatcher
|
||||
excludeMatcher *patternmatcher.PatternMatcher
|
||||
err error
|
||||
onlyPrefixIncludes = true
|
||||
onlyPrefixExcludeExceptions = true
|
||||
)
|
||||
|
||||
if len(includePatterns) > 0 {
|
||||
includeMatcher, err = patternmatcher.New(includePatterns)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "invalid includepatterns: %s", opt.IncludePatterns)
|
||||
return nil, errors.Wrapf(err, "invalid includepatterns: %s", opt.IncludePatterns)
|
||||
}
|
||||
|
||||
for _, p := range includeMatcher.Patterns() {
|
||||
@ -98,11 +123,10 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
||||
|
||||
}
|
||||
|
||||
onlyPrefixExcludeExceptions := true
|
||||
if opt != nil && opt.ExcludePatterns != nil {
|
||||
if len(opt.ExcludePatterns) > 0 {
|
||||
excludeMatcher, err = patternmatcher.New(opt.ExcludePatterns)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "invalid excludepatterns: %s", opt.ExcludePatterns)
|
||||
return nil, errors.Wrapf(err, "invalid excludepatterns: %s", opt.ExcludePatterns)
|
||||
}
|
||||
|
||||
for _, p := range excludeMatcher.Patterns() {
|
||||
@ -113,10 +137,41 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
||||
}
|
||||
}
|
||||
|
||||
return &filterFS{
|
||||
fs: fs,
|
||||
includeMatcher: includeMatcher,
|
||||
excludeMatcher: excludeMatcher,
|
||||
onlyPrefixIncludes: onlyPrefixIncludes,
|
||||
onlyPrefixExcludeExceptions: onlyPrefixExcludeExceptions,
|
||||
mapFn: opt.Map,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (fs *filterFS) Open(p string) (io.ReadCloser, error) {
|
||||
if fs.includeMatcher != nil {
|
||||
m, err := fs.includeMatcher.MatchesOrParentMatches(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !m {
|
||||
return nil, errors.Wrapf(os.ErrNotExist, "open %s", p)
|
||||
}
|
||||
}
|
||||
if fs.excludeMatcher != nil {
|
||||
m, err := fs.excludeMatcher.MatchesOrParentMatches(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if m {
|
||||
return nil, errors.Wrapf(os.ErrNotExist, "open %s", p)
|
||||
}
|
||||
}
|
||||
return fs.fs.Open(p)
|
||||
}
|
||||
|
||||
func (fs *filterFS) Walk(ctx context.Context, target string, fn gofs.WalkDirFunc) error {
|
||||
type visitedDir struct {
|
||||
fi os.FileInfo
|
||||
path string
|
||||
origpath string
|
||||
entry gofs.DirEntry
|
||||
pathWithSep string
|
||||
includeMatchInfo patternmatcher.MatchInfo
|
||||
excludeMatchInfo patternmatcher.MatchInfo
|
||||
@ -126,34 +181,22 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
||||
// used only for include/exclude handling
|
||||
var parentDirs []visitedDir
|
||||
|
||||
seenFiles := make(map[uint64]string)
|
||||
return filepath.WalkDir(root, func(path string, dirEntry gofs.DirEntry, walkErr error) (retErr error) {
|
||||
return fs.fs.Walk(ctx, target, func(path string, dirEntry gofs.DirEntry, walkErr error) (retErr error) {
|
||||
defer func() {
|
||||
if retErr != nil && isNotExist(retErr) {
|
||||
retErr = filepath.SkipDir
|
||||
}
|
||||
}()
|
||||
|
||||
origpath := path
|
||||
path, err = filepath.Rel(root, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Skip root
|
||||
if path == "." {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
dir visitedDir
|
||||
isDir bool
|
||||
fi gofs.FileInfo
|
||||
)
|
||||
if dirEntry != nil {
|
||||
isDir = dirEntry.IsDir()
|
||||
}
|
||||
|
||||
if includeMatcher != nil || excludeMatcher != nil {
|
||||
if fs.includeMatcher != nil || fs.excludeMatcher != nil {
|
||||
for len(parentDirs) != 0 {
|
||||
lastParentDir := parentDirs[len(parentDirs)-1].pathWithSep
|
||||
if strings.HasPrefix(path, lastParentDir) {
|
||||
@ -163,15 +206,8 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
||||
}
|
||||
|
||||
if isDir {
|
||||
fi, err = dirEntry.Info()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dir = visitedDir{
|
||||
fi: fi,
|
||||
path: path,
|
||||
origpath: origpath,
|
||||
entry: dirEntry,
|
||||
pathWithSep: path + string(filepath.Separator),
|
||||
}
|
||||
}
|
||||
@ -179,12 +215,12 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
||||
|
||||
skip := false
|
||||
|
||||
if includeMatcher != nil {
|
||||
if fs.includeMatcher != nil {
|
||||
var parentIncludeMatchInfo patternmatcher.MatchInfo
|
||||
if len(parentDirs) != 0 {
|
||||
parentIncludeMatchInfo = parentDirs[len(parentDirs)-1].includeMatchInfo
|
||||
}
|
||||
m, matchInfo, err := includeMatcher.MatchesUsingParentResults(path, parentIncludeMatchInfo)
|
||||
m, matchInfo, err := fs.includeMatcher.MatchesUsingParentResults(path, parentIncludeMatchInfo)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to match includepatterns")
|
||||
}
|
||||
@ -194,11 +230,11 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
||||
}
|
||||
|
||||
if !m {
|
||||
if isDir && onlyPrefixIncludes {
|
||||
if isDir && fs.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() {
|
||||
for _, pat := range fs.includeMatcher.Patterns() {
|
||||
if pat.Exclusion() {
|
||||
continue
|
||||
}
|
||||
@ -214,12 +250,12 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
||||
}
|
||||
}
|
||||
|
||||
if excludeMatcher != nil {
|
||||
if fs.excludeMatcher != nil {
|
||||
var parentExcludeMatchInfo patternmatcher.MatchInfo
|
||||
if len(parentDirs) != 0 {
|
||||
parentExcludeMatchInfo = parentDirs[len(parentDirs)-1].excludeMatchInfo
|
||||
}
|
||||
m, matchInfo, err := excludeMatcher.MatchesUsingParentResults(path, parentExcludeMatchInfo)
|
||||
m, matchInfo, err := fs.excludeMatcher.MatchesUsingParentResults(path, parentExcludeMatchInfo)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to match excludepatterns")
|
||||
}
|
||||
@ -229,16 +265,16 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
||||
}
|
||||
|
||||
if m {
|
||||
if isDir && onlyPrefixExcludeExceptions {
|
||||
if isDir && fs.onlyPrefixExcludeExceptions {
|
||||
// Optimization: we can skip walking this dir if no
|
||||
// exceptions to exclude patterns could match anything
|
||||
// inside it.
|
||||
if !excludeMatcher.Exclusions() {
|
||||
if !fs.excludeMatcher.Exclusions() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
dirSlash := path + string(filepath.Separator)
|
||||
for _, pat := range excludeMatcher.Patterns() {
|
||||
for _, pat := range fs.excludeMatcher.Patterns() {
|
||||
if !pat.Exclusion() {
|
||||
continue
|
||||
}
|
||||
@ -261,7 +297,7 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
||||
return walkErr
|
||||
}
|
||||
|
||||
if includeMatcher != nil || excludeMatcher != nil {
|
||||
if fs.includeMatcher != nil || fs.excludeMatcher != nil {
|
||||
defer func() {
|
||||
if isDir {
|
||||
parentDirs = append(parentDirs, dir)
|
||||
@ -275,25 +311,21 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
||||
|
||||
dir.calledFn = true
|
||||
|
||||
// The FileInfo might have already been read further up.
|
||||
if fi == nil {
|
||||
fi, err = dirEntry.Info()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
stat, err := mkstat(origpath, path, fi, seenFiles)
|
||||
fi, err := dirEntry.Info()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stat, ok := fi.Sys().(*types.Stat)
|
||||
if !ok {
|
||||
return errors.WithStack(&os.PathError{Path: path, Err: syscall.EBADMSG, Op: "fileinfo without stat info"})
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
if opt != nil && opt.Map != nil {
|
||||
result := opt.Map(stat.Path, stat)
|
||||
if fs.mapFn != nil {
|
||||
result := fs.mapFn(stat.Path, stat)
|
||||
if result == MapResultSkipDir {
|
||||
return filepath.SkipDir
|
||||
} else if result == MapResultExclude {
|
||||
@ -304,29 +336,33 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
||||
if parentDir.calledFn {
|
||||
continue
|
||||
}
|
||||
parentStat, err := mkstat(parentDir.origpath, parentDir.path, parentDir.fi, seenFiles)
|
||||
parentFi, err := parentDir.entry.Info()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parentStat, ok := parentFi.Sys().(*types.Stat)
|
||||
if !ok {
|
||||
return errors.WithStack(&os.PathError{Path: path, Err: syscall.EBADMSG, Op: "fileinfo without stat info"})
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
if opt != nil && opt.Map != nil {
|
||||
result := opt.Map(parentStat.Path, parentStat)
|
||||
if fs.mapFn != nil {
|
||||
result := fs.mapFn(parentStat.Path, parentStat)
|
||||
if result == MapResultSkipDir || result == MapResultExclude {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if err := fn(parentStat.Path, &StatInfo{parentStat}, nil); err != nil {
|
||||
if err := fn(parentStat.Path, &DirEntryInfo{Stat: 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, &DirEntryInfo{Stat: stat}, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -334,6 +370,40 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
|
||||
})
|
||||
}
|
||||
|
||||
func Walk(ctx context.Context, p string, opt *FilterOpt, fn filepath.WalkFunc) error {
|
||||
f, err := NewFS(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f, err = NewFilterFS(f, opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f.Walk(ctx, "/", func(path string, d gofs.DirEntry, err error) error {
|
||||
var info gofs.FileInfo
|
||||
if d != nil {
|
||||
var err2 error
|
||||
info, err2 = d.Info()
|
||||
if err == nil {
|
||||
err = err2
|
||||
}
|
||||
}
|
||||
return fn(path, info, err)
|
||||
})
|
||||
}
|
||||
|
||||
func WalkDir(ctx context.Context, p string, opt *FilterOpt, fn gofs.WalkDirFunc) error {
|
||||
f, err := NewFS(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f, err = NewFilterFS(f, opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f.Walk(ctx, "/", fn)
|
||||
}
|
||||
|
||||
func patternWithoutTrailingGlob(p *patternmatcher.Pattern) string {
|
||||
patStr := p.String()
|
||||
// We use filepath.Separator here because patternmatcher.Pattern patterns
|
||||
@ -344,29 +414,6 @@ func patternWithoutTrailingGlob(p *patternmatcher.Pattern) string {
|
||||
return patStr
|
||||
}
|
||||
|
||||
type StatInfo struct {
|
||||
*types.Stat
|
||||
}
|
||||
|
||||
func (s *StatInfo) Name() string {
|
||||
return filepath.Base(s.Stat.Path)
|
||||
}
|
||||
func (s *StatInfo) Size() int64 {
|
||||
return s.Stat.Size_
|
||||
}
|
||||
func (s *StatInfo) Mode() os.FileMode {
|
||||
return os.FileMode(s.Stat.Mode)
|
||||
}
|
||||
func (s *StatInfo) ModTime() time.Time {
|
||||
return time.Unix(s.Stat.ModTime/1e9, s.Stat.ModTime%1e9)
|
||||
}
|
||||
func (s *StatInfo) IsDir() bool {
|
||||
return s.Mode().IsDir()
|
||||
}
|
||||
func (s *StatInfo) Sys() interface{} {
|
||||
return s.Stat
|
||||
}
|
||||
|
||||
func isNotExist(err error) bool {
|
||||
return errors.Is(err, os.ErrNotExist) || errors.Is(err, syscall.ENOTDIR)
|
||||
}
|
100
vendor/github.com/tonistiigi/fsutil/followlinks.go
generated
vendored
100
vendor/github.com/tonistiigi/fsutil/followlinks.go
generated
vendored
@ -1,17 +1,21 @@
|
||||
package fsutil
|
||||
|
||||
import (
|
||||
"context"
|
||||
gofs "io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
strings "strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tonistiigi/fsutil/types"
|
||||
)
|
||||
|
||||
func FollowLinks(root string, paths []string) ([]string, error) {
|
||||
r := &symlinkResolver{root: root, resolved: map[string]struct{}{}}
|
||||
func FollowLinks(fs FS, paths []string) ([]string, error) {
|
||||
r := &symlinkResolver{fs: fs, resolved: map[string]struct{}{}}
|
||||
for _, p := range paths {
|
||||
if err := r.append(p); err != nil {
|
||||
return nil, err
|
||||
@ -26,7 +30,7 @@ func FollowLinks(root string, paths []string) ([]string, error) {
|
||||
}
|
||||
|
||||
type symlinkResolver struct {
|
||||
root string
|
||||
fs FS
|
||||
resolved map[string]struct{}
|
||||
}
|
||||
|
||||
@ -76,10 +80,9 @@ func (r *symlinkResolver) append(p string) error {
|
||||
}
|
||||
|
||||
func (r *symlinkResolver) readSymlink(p string, allowWildcard bool) ([]string, error) {
|
||||
realPath := filepath.Join(r.root, p)
|
||||
base := filepath.Base(p)
|
||||
if allowWildcard && containsWildcards(base) {
|
||||
fis, err := os.ReadDir(filepath.Dir(realPath))
|
||||
fis, err := readDir(r.fs, filepath.Dir(p))
|
||||
if err != nil {
|
||||
if isNotFound(err) {
|
||||
return nil, nil
|
||||
@ -99,21 +102,30 @@ func (r *symlinkResolver) readSymlink(p string, allowWildcard bool) ([]string, e
|
||||
return out, nil
|
||||
}
|
||||
|
||||
fi, err := os.Lstat(realPath)
|
||||
entry, err := statFile(r.fs, p)
|
||||
if err != nil {
|
||||
if isNotFound(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
if fi.Mode()&os.ModeSymlink == 0 {
|
||||
if entry == nil {
|
||||
return nil, nil
|
||||
}
|
||||
link, err := os.Readlink(realPath)
|
||||
if entry.Type()&os.ModeSymlink == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
fi, err := entry.Info()
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
link = filepath.Clean(link)
|
||||
stat, ok := fi.Sys().(*types.Stat)
|
||||
if !ok {
|
||||
return nil, errors.WithStack(&os.PathError{Path: p, Err: syscall.EBADMSG, Op: "fileinfo without stat info"})
|
||||
}
|
||||
|
||||
link := filepath.Clean(stat.Linkname)
|
||||
if filepath.IsAbs(link) {
|
||||
return []string{link}, nil
|
||||
}
|
||||
@ -122,6 +134,76 @@ func (r *symlinkResolver) readSymlink(p string, allowWildcard bool) ([]string, e
|
||||
}, nil
|
||||
}
|
||||
|
||||
func statFile(fs FS, root string) (os.DirEntry, error) {
|
||||
var out os.DirEntry
|
||||
|
||||
root = filepath.Clean(root)
|
||||
if root == "/" || root == "." {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
err := fs.Walk(context.TODO(), root, func(p string, entry os.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if p != root {
|
||||
return errors.Errorf("expected single entry %q but got %q", root, p)
|
||||
}
|
||||
out = entry
|
||||
if entry.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if out == nil {
|
||||
return nil, errors.Wrapf(os.ErrNotExist, "readFile %s", root)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func readDir(fs FS, root string) ([]os.DirEntry, error) {
|
||||
var out []os.DirEntry
|
||||
|
||||
root = filepath.Clean(root)
|
||||
if root == "/" || root == "." {
|
||||
root = "."
|
||||
out = make([]gofs.DirEntry, 0)
|
||||
}
|
||||
|
||||
err := fs.Walk(context.TODO(), root, func(p string, entry os.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if p == root {
|
||||
if !entry.IsDir() {
|
||||
return errors.WithStack(&os.PathError{Op: "walk", Path: root, Err: syscall.ENOTDIR})
|
||||
}
|
||||
out = make([]gofs.DirEntry, 0)
|
||||
return nil
|
||||
}
|
||||
if out == nil {
|
||||
return errors.Errorf("expected to read parent entry %q before child %q", root, p)
|
||||
}
|
||||
out = append(out, entry)
|
||||
if entry.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if out == nil && root != "." {
|
||||
return nil, errors.Wrapf(os.ErrNotExist, "readDir %s", root)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func containsWildcards(name string) bool {
|
||||
isWindows := runtime.GOOS == "windows"
|
||||
for i := 0; i < len(name); i++ {
|
||||
|
156
vendor/github.com/tonistiigi/fsutil/fs.go
generated
vendored
156
vendor/github.com/tonistiigi/fsutil/fs.go
generated
vendored
@ -3,36 +3,86 @@ package fsutil
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
gofs "io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tonistiigi/fsutil/types"
|
||||
)
|
||||
|
||||
type FS interface {
|
||||
Walk(context.Context, filepath.WalkFunc) error
|
||||
Walk(context.Context, string, gofs.WalkDirFunc) error
|
||||
Open(string) (io.ReadCloser, error)
|
||||
}
|
||||
|
||||
func NewFS(root string, opt *WalkOpt) FS {
|
||||
// NewFS creates a new FS from a root directory on the host filesystem.
|
||||
func NewFS(root string) (FS, error) {
|
||||
root, err := filepath.EvalSymlinks(root)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(&os.PathError{Op: "resolve", Path: root, Err: err})
|
||||
}
|
||||
fi, err := os.Stat(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !fi.IsDir() {
|
||||
return nil, errors.WithStack(&os.PathError{Op: "stat", Path: root, Err: syscall.ENOTDIR})
|
||||
}
|
||||
|
||||
return &fs{
|
||||
root: root,
|
||||
opt: opt,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
type fs struct {
|
||||
root string
|
||||
opt *WalkOpt
|
||||
}
|
||||
|
||||
func (fs *fs) Walk(ctx context.Context, fn filepath.WalkFunc) error {
|
||||
return Walk(ctx, fs.root, fs.opt, fn)
|
||||
func (fs *fs) Walk(ctx context.Context, target string, fn gofs.WalkDirFunc) error {
|
||||
seenFiles := make(map[uint64]string)
|
||||
return filepath.WalkDir(filepath.Join(fs.root, target), func(path string, dirEntry gofs.DirEntry, walkErr error) (retErr error) {
|
||||
defer func() {
|
||||
if retErr != nil && isNotExist(retErr) {
|
||||
retErr = filepath.SkipDir
|
||||
}
|
||||
}()
|
||||
|
||||
origpath := path
|
||||
path, err := filepath.Rel(fs.root, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Skip root
|
||||
if path == "." {
|
||||
return nil
|
||||
}
|
||||
|
||||
var entry gofs.DirEntry
|
||||
if dirEntry != nil {
|
||||
entry = &DirEntryInfo{
|
||||
path: path,
|
||||
origpath: origpath,
|
||||
entry: dirEntry,
|
||||
seenFiles: seenFiles,
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
if err := fn(path, entry, walkErr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (fs *fs) Open(p string) (io.ReadCloser, error) {
|
||||
@ -67,16 +117,31 @@ type subDirFS struct {
|
||||
dirs []Dir
|
||||
}
|
||||
|
||||
func (fs *subDirFS) Walk(ctx context.Context, fn filepath.WalkFunc) error {
|
||||
func (fs *subDirFS) Walk(ctx context.Context, target string, fn gofs.WalkDirFunc) error {
|
||||
first, rest, _ := strings.Cut(target, string(filepath.Separator))
|
||||
|
||||
for _, d := range fs.dirs {
|
||||
fi := &StatInfo{Stat: &d.Stat}
|
||||
if first != "" && first != d.Stat.Path {
|
||||
continue
|
||||
}
|
||||
|
||||
fi := &StatInfo{&d.Stat}
|
||||
if !fi.IsDir() {
|
||||
return errors.WithStack(&os.PathError{Path: d.Stat.Path, Err: syscall.ENOTDIR, Op: "walk subdir"})
|
||||
}
|
||||
if err := fn(d.Stat.Path, fi, nil); err != nil {
|
||||
dStat := d.Stat
|
||||
if err := fn(d.Stat.Path, &DirEntryInfo{Stat: &dStat}, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.FS.Walk(ctx, func(p string, fi os.FileInfo, err error) error {
|
||||
if err := d.FS.Walk(ctx, rest, func(p string, entry gofs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fi, err := entry.Info()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stat, ok := fi.Sys().(*types.Stat)
|
||||
if !ok {
|
||||
return errors.WithStack(&os.PathError{Path: d.Stat.Path, Err: syscall.EBADMSG, Op: "fileinfo without stat info"})
|
||||
@ -91,7 +156,7 @@ func (fs *subDirFS) Walk(ctx context.Context, fn filepath.WalkFunc) error {
|
||||
stat.Linkname = path.Join(d.Stat.Path, stat.Linkname)
|
||||
}
|
||||
}
|
||||
return fn(filepath.Join(d.Stat.Path, p), &StatInfo{stat}, nil)
|
||||
return fn(filepath.Join(d.Stat.Path, p), &DirEntryInfo{Stat: stat}, nil)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -117,3 +182,70 @@ type emptyReader struct {
|
||||
func (*emptyReader) Read([]byte) (int, error) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
type StatInfo struct {
|
||||
*types.Stat
|
||||
}
|
||||
|
||||
func (s *StatInfo) Name() string {
|
||||
return filepath.Base(s.Stat.Path)
|
||||
}
|
||||
func (s *StatInfo) Size() int64 {
|
||||
return s.Stat.Size_
|
||||
}
|
||||
func (s *StatInfo) Mode() os.FileMode {
|
||||
return os.FileMode(s.Stat.Mode)
|
||||
}
|
||||
func (s *StatInfo) ModTime() time.Time {
|
||||
return time.Unix(s.Stat.ModTime/1e9, s.Stat.ModTime%1e9)
|
||||
}
|
||||
func (s *StatInfo) IsDir() bool {
|
||||
return s.Mode().IsDir()
|
||||
}
|
||||
func (s *StatInfo) Sys() interface{} {
|
||||
return s.Stat
|
||||
}
|
||||
|
||||
type DirEntryInfo struct {
|
||||
*types.Stat
|
||||
|
||||
entry gofs.DirEntry
|
||||
path string
|
||||
origpath string
|
||||
seenFiles map[uint64]string
|
||||
}
|
||||
|
||||
func (s *DirEntryInfo) Name() string {
|
||||
if s.Stat != nil {
|
||||
return filepath.Base(s.Stat.Path)
|
||||
}
|
||||
return s.entry.Name()
|
||||
}
|
||||
func (s *DirEntryInfo) IsDir() bool {
|
||||
if s.Stat != nil {
|
||||
return s.Stat.IsDir()
|
||||
}
|
||||
return s.entry.IsDir()
|
||||
}
|
||||
func (s *DirEntryInfo) Type() gofs.FileMode {
|
||||
if s.Stat != nil {
|
||||
return gofs.FileMode(s.Stat.Mode)
|
||||
}
|
||||
return s.entry.Type()
|
||||
}
|
||||
func (s *DirEntryInfo) Info() (gofs.FileInfo, error) {
|
||||
if s.Stat == nil {
|
||||
fi, err := s.entry.Info()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stat, err := mkstat(s.origpath, s.path, fi, s.seenFiles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Stat = stat
|
||||
}
|
||||
|
||||
st := *s.Stat
|
||||
return &StatInfo{&st}, nil
|
||||
}
|
||||
|
6
vendor/github.com/tonistiigi/fsutil/send.go
generated
vendored
6
vendor/github.com/tonistiigi/fsutil/send.go
generated
vendored
@ -144,7 +144,11 @@ func (s *sender) sendFile(h *sendHandle) error {
|
||||
|
||||
func (s *sender) walk(ctx context.Context) error {
|
||||
var i uint32 = 0
|
||||
err := s.fs.Walk(ctx, func(path string, fi os.FileInfo, err error) error {
|
||||
err := s.fs.Walk(ctx, "/", func(path string, entry os.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fi, err := entry.Info()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
7
vendor/github.com/tonistiigi/fsutil/tarwriter.go
generated
vendored
7
vendor/github.com/tonistiigi/fsutil/tarwriter.go
generated
vendored
@ -15,10 +15,15 @@ import (
|
||||
|
||||
func WriteTar(ctx context.Context, fs FS, w io.Writer) error {
|
||||
tw := tar.NewWriter(w)
|
||||
err := fs.Walk(ctx, func(path string, fi os.FileInfo, err error) error {
|
||||
err := fs.Walk(ctx, "/", func(path string, entry os.DirEntry, err error) error {
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
fi, err := entry.Info()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stat, ok := fi.Sys().(*types.Stat)
|
||||
if !ok {
|
||||
return errors.WithStack(&os.PathError{Path: path, Err: syscall.EBADMSG, Op: "fileinfo without stat info"})
|
||||
|
Reference in New Issue
Block a user