From d88f61738d73fd9670af812c9906b2a52b910c73 Mon Sep 17 00:00:00 2001 From: Kotaro Inoue Date: Tue, 26 Sep 2023 03:38:44 +0900 Subject: [PATCH 01/39] Fix to respect special bit specified in the mode of copier Signed-off-by: Kotaro Inoue --- copy/copy_linux.go | 8 +++++++- copy/copy_unix.go | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/copy/copy_linux.go b/copy/copy_linux.go index 971cb5c5..d2553098 100644 --- a/copy/copy_linux.go +++ b/copy/copy_linux.go @@ -30,7 +30,13 @@ func (c *copier) copyFileInfo(fi os.FileInfo, src, name string) error { m := fi.Mode() if c.mode != nil { - m = (m & ^os.FileMode(0777)) | os.FileMode(*c.mode&0777) + m = os.FileMode(*c.mode & 0777) + sb := *c.mode & 0777 + if sb != 0 { + m |= os.FileMode(sb) + } else { + m |= m & ^os.FileMode(0777) + } } if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink { if err := os.Chmod(name, m); err != nil { diff --git a/copy/copy_unix.go b/copy/copy_unix.go index 945e96c5..4a79e63d 100644 --- a/copy/copy_unix.go +++ b/copy/copy_unix.go @@ -31,7 +31,13 @@ func (c *copier) copyFileInfo(fi os.FileInfo, src, name string) error { m := fi.Mode() if c.mode != nil { - m = (m & ^os.FileMode(0777)) | os.FileMode(*c.mode&0777) + m = os.FileMode(*c.mode & 0777) + sb := *c.mode & 0777 + if sb != 0 { + m |= os.FileMode(sb) + } else { + m |= m & ^os.FileMode(0777) + } } if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink { if err := os.Chmod(name, m); err != nil { From 0db3c1fbec08c77441b3f49a0dc2ac9d66b6000d Mon Sep 17 00:00:00 2001 From: Kotaro Inoue Date: Tue, 26 Sep 2023 03:39:03 +0900 Subject: [PATCH 02/39] Add tests Signed-off-by: Kotaro Inoue --- copy/copy_unix_test.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/copy/copy_unix_test.go b/copy/copy_unix_test.go index df710d4c..5156a875 100644 --- a/copy/copy_unix_test.go +++ b/copy/copy_unix_test.go @@ -56,3 +56,33 @@ func TestCopyDevicesAndFifo(t *testing.T) { assert.NotEqual(t, os.ModeSocket, fi.Mode()&os.ModeSocket) // socket copied as stub assert.Equal(t, os.FileMode(0555), fi.Mode()&0777) } + +func TestCopySetuid(t *testing.T) { + requiresRoot(t) + + t1 := t.TempDir() + + err := mknod(filepath.Join(t1, "char"), unix.S_IFCHR|0444, int(unix.Mkdev(1, 9))) + require.NoError(t, err) + + t2 := t.TempDir() + + err = Copy(context.TODO(), t1, ".", t2, ".") + require.NoError(t, err) + + fi, err := os.Lstat(filepath.Join(t2, "char")) + require.NoError(t, err) + assert.Equal(t, os.FileMode(0444), fi.Mode()&07777) + + t3 := t.TempDir() + + p := 04444 + err = Copy(context.TODO(), t1, ".", t3, ".", WithCopyInfo(CopyInfo{ + Mode: &p, + })) + require.NoError(t, err) + + fi, err = os.Lstat(filepath.Join(t3, "char")) + require.NoError(t, err) + assert.Equal(t, os.FileMode(04444), fi.Mode()&07777) +} From 03de04d2236a1d6e1f6be20acb8687cdfd35ec5b Mon Sep 17 00:00:00 2001 From: Erik Sipsma Date: Sun, 5 Nov 2023 19:56:31 -0800 Subject: [PATCH 03/39] Add support for always overwriting existing paths Before this, copy would always refuse to replace an existing path in the dest with something from the source of a different type. E.g. if there was a symlink at the dest, you'd get an error if copying src meant replacing the symlink w/ a directory. This is the right default behavior but there are situations in which callers may want to opt in to instead always replacing what's in dest with what's in source. This adds an option to enable that behavior. Signed-off-by: Erik Sipsma --- copy/copy.go | 69 ++++++++++++++++++++++++++++++++--------------- copy/copy_test.go | 54 +++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 22 deletions(-) diff --git a/copy/copy.go b/copy/copy.go index 558c553f..f8cc0a43 100644 --- a/copy/copy.go +++ b/copy/copy.go @@ -88,7 +88,7 @@ func Copy(ctx context.Context, srcRoot, src, dstRoot, dst string, opts ...Opt) e return err } - c, err := newCopier(dstRoot, ci.Chown, ci.Utime, ci.Mode, ci.XAttrErrorHandler, ci.IncludePatterns, ci.ExcludePatterns, ci.ChangeFunc) + c, err := newCopier(dstRoot, ci.Chown, ci.Utime, ci.Mode, ci.XAttrErrorHandler, ci.IncludePatterns, ci.ExcludePatterns, ci.AlwaysReplaceExistingDestPaths, ci.ChangeFunc) if err != nil { return err } @@ -172,7 +172,11 @@ type CopyInfo struct { IncludePatterns []string // Exclude files/dir matching any of these patterns (even if they match an include pattern) ExcludePatterns []string - ChangeFunc fsutil.ChangeFunc + // If true, any source path that overwrite existing destination paths will always replace + // the existing destination path, even if they are of different types (e.g. a directory will + // replace any existing symlink or file) + AlwaysReplaceExistingDestPaths bool + ChangeFunc fsutil.ChangeFunc } type Opt func(*CopyInfo) @@ -227,16 +231,17 @@ func WithChangeNotifier(fn fsutil.ChangeFunc) Opt { } type copier struct { - chown Chowner - utime *time.Time - mode *int - inodes map[uint64]string - xattrErrorHandler XAttrErrorHandler - includePatternMatcher *patternmatcher.PatternMatcher - excludePatternMatcher *patternmatcher.PatternMatcher - parentDirs []parentDir - changefn fsutil.ChangeFunc - root string + chown Chowner + utime *time.Time + mode *int + inodes map[uint64]string + xattrErrorHandler XAttrErrorHandler + includePatternMatcher *patternmatcher.PatternMatcher + excludePatternMatcher *patternmatcher.PatternMatcher + parentDirs []parentDir + changefn fsutil.ChangeFunc + root string + alwaysReplaceExistingDestPaths bool } type parentDir struct { @@ -245,7 +250,7 @@ type parentDir struct { copied bool } -func newCopier(root string, chown Chowner, tm *time.Time, mode *int, xeh XAttrErrorHandler, includePatterns, excludePatterns []string, changeFunc fsutil.ChangeFunc) (*copier, error) { +func newCopier(root string, chown Chowner, tm *time.Time, mode *int, xeh XAttrErrorHandler, includePatterns, excludePatterns []string, alwaysReplaceExistingDestPaths bool, changeFunc fsutil.ChangeFunc) (*copier, error) { if xeh == nil { xeh = func(dst, src, key string, err error) error { return err @@ -271,15 +276,16 @@ func newCopier(root string, chown Chowner, tm *time.Time, mode *int, xeh XAttrEr } return &copier{ - root: root, - inodes: map[uint64]string{}, - chown: chown, - utime: tm, - xattrErrorHandler: xeh, - mode: mode, - includePatternMatcher: includePatternMatcher, - excludePatternMatcher: excludePatternMatcher, - changefn: changeFunc, + root: root, + inodes: map[uint64]string{}, + chown: chown, + utime: tm, + xattrErrorHandler: xeh, + mode: mode, + includePatternMatcher: includePatternMatcher, + excludePatternMatcher: excludePatternMatcher, + changefn: changeFunc, + alwaysReplaceExistingDestPaths: alwaysReplaceExistingDestPaths, }, nil } @@ -324,6 +330,10 @@ func (c *copier) copy(ctx context.Context, src, srcComponents, target string, ov } if include { + if err := c.removeTargetIfNeeded(src, target, fi, targetFi); err != nil { + return err + } + if err := c.createParentDirs(src, srcComponents, target, overwriteTargetMetadata); err != nil { return err } @@ -440,6 +450,21 @@ func (c *copier) exclude(path string, fi os.FileInfo, parentExcludeMatchInfo pat return m, matchInfo, nil } +func (c *copier) removeTargetIfNeeded(src, target string, srcFi, targetFi os.FileInfo) error { + if !c.alwaysReplaceExistingDestPaths { + return nil + } + if targetFi == nil { + // already doesn't exist + return nil + } + if srcFi.IsDir() && targetFi.IsDir() { + // directories are merged, not replaced + return nil + } + return os.RemoveAll(target) +} + // Delayed creation of parent directories when a file or dir matches an include // pattern. func (c *copier) createParentDirs(src, srcComponents, target string, overwriteTargetMetadata bool) error { diff --git a/copy/copy_test.go b/copy/copy_test.go index e77d54e7..425ade68 100644 --- a/copy/copy_test.go +++ b/copy/copy_test.go @@ -359,6 +359,60 @@ func TestCopySymlinks(t *testing.T) { require.Equal(t, "foo.txt", link) } +func TestCopyWithAlwaysReplaceExistingDestPaths(t *testing.T) { + destDir := t.TempDir() + require.NoError(t, fstest.Apply( + fstest.CreateDir("root", 0755), + fstest.CreateDir("root/overwritedir", 0755), + fstest.CreateFile("root/overwritedir/subfile", nil, 0755), + fstest.CreateFile("root/overwritefile", nil, 0755), + fstest.Symlink("dir", "root/overwritesymlink"), + fstest.CreateDir("root/dir", 0755), + fstest.CreateFile("root/dir/dirfile1", nil, 0755), + fstest.CreateDir("root/dir/overwritesubdir", 0755), + fstest.CreateFile("root/dir/overwritesubfile", nil, 0755), + fstest.Symlink("dirfile1", "root/dir/overwritesymlink"), + ).Apply(destDir)) + + srcDir := t.TempDir() + require.NoError(t, fstest.Apply( + fstest.CreateDir("root", 0755), + fstest.CreateFile("root/overwritedir", nil, 0755), + fstest.CreateDir("root/overwritefile", 0755), + fstest.CreateFile("root/overwritefile/foo", nil, 0755), + fstest.CreateDir("root/overwritesymlink", 0755), + fstest.CreateDir("root/dir", 0755), + fstest.CreateFile("root/dir/dirfile2", nil, 0755), + fstest.CreateFile("root/dir/overwritesubdir", nil, 0755), + fstest.CreateDir("root/dir/overwritesubfile", 0755), + fstest.CreateDir("root/dir/overwritesymlink", 0755), + ).Apply(srcDir)) + + expectedDir := t.TempDir() + require.NoError(t, fstest.Apply( + fstest.CreateDir("root", 0755), + fstest.CreateFile("root/overwritedir", nil, 0755), + fstest.CreateDir("root/overwritefile", 0755), + fstest.CreateFile("root/overwritefile/foo", nil, 0755), + fstest.CreateDir("root/overwritesymlink", 0755), + fstest.CreateDir("root/dir", 0755), + fstest.CreateFile("root/dir/dirfile1", nil, 0755), + fstest.CreateFile("root/dir/dirfile2", nil, 0755), + fstest.CreateFile("root/dir/overwritesubdir", nil, 0755), + fstest.CreateDir("root/dir/overwritesubfile", 0755), + fstest.CreateDir("root/dir/overwritesymlink", 0755), + ).Apply(expectedDir)) + + err := Copy(context.TODO(), srcDir, "root", destDir, "root", WithCopyInfo(CopyInfo{ + AlwaysReplaceExistingDestPaths: true, + CopyDirContents: true, + })) + require.NoError(t, err) + + err = fstest.CheckDirectoryEqual(destDir, expectedDir) + require.NoError(t, err) +} + func testCopy(t *testing.T, apply fstest.Applier, exp string) error { t1 := t.TempDir() t2 := t.TempDir() From bea810c0c14d36b7b141c4cf236a2830e29a14a7 Mon Sep 17 00:00:00 2001 From: Kazuyoshi Kato Date: Thu, 16 Nov 2023 00:45:26 -0800 Subject: [PATCH 04/39] Migrate off of gogo/protobuf gogo/protobuf has been deprecated since 2022. This change uses Google's code generator instead of gogo/protobuf. Signed-off-by: Kazuyoshi Kato --- diff_containerd.go | 8 +- diskwriter.go | 12 +- fs.go | 11 +- fs_test.go | 4 +- go.mod | 4 +- go.sum | 29 -- receive.go | 3 +- receive_test.go | 33 +- send.go | 5 +- stat.go | 21 +- stat_test.go | 6 +- stat_unix.go | 2 +- types/generate.go | 10 +- types/stat.go | 2 +- types/stat.pb.go | 1014 +++++++------------------------------------- types/stat.proto | 2 +- types/wire.pb.go | 701 ++++++++---------------------- types/wire.proto | 2 +- 18 files changed, 428 insertions(+), 1441 deletions(-) diff --git a/diff_containerd.go b/diff_containerd.go index d8619abf..261431b8 100644 --- a/diff_containerd.go +++ b/diff_containerd.go @@ -106,11 +106,11 @@ func doubleWalkDiff(ctx context.Context, changeFn ChangeFunc, a, b walkerFn, fil var f *types.Stat var f2copy *currentPath if f2 != nil { - statCopy := *f2.stat + statCopy := cloneStat(f2.stat) if filter != nil { - filter(f2.path, &statCopy) + filter(f2.path, statCopy) } - f2copy = ¤tPath{path: f2.path, stat: &statCopy} + f2copy = ¤tPath{path: f2.path, stat: statCopy} } k, p := pathChange(f1, f2copy) switch k { @@ -188,7 +188,7 @@ func sameFile(f1, f2 *currentPath, differ DiffType) (same bool, retErr error) { } // If not a directory also check size, modtime, and content if !f1.stat.IsDir() { - if f1.stat.Size_ != f2.stat.Size_ { + if f1.stat.Size != f2.stat.Size { return false, nil } diff --git a/diskwriter.go b/diskwriter.go index 10b60851..815f4e07 100644 --- a/diskwriter.go +++ b/diskwriter.go @@ -124,10 +124,10 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er return errors.WithStack(&os.PathError{Path: p, Err: syscall.EBADMSG, Op: "change without stat info"}) } - statCopy := *stat + statCopy := cloneStat(stat) if dw.filter != nil { - if ok := dw.filter(p, &statCopy); !ok { + if ok := dw.filter(p, statCopy); !ok { return nil } } @@ -146,7 +146,7 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er } if oldFi != nil && fi.IsDir() && oldFi.IsDir() { - if err := rewriteMetadata(destPath, &statCopy); err != nil { + if err := rewriteMetadata(destPath, statCopy); err != nil { return errors.Wrapf(err, "error setting dir metadata for %s", destPath) } return nil @@ -170,7 +170,7 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er } dw.dirModTimes[destPath] = statCopy.ModTime case fi.Mode()&os.ModeDevice != 0 || fi.Mode()&os.ModeNamedPipe != 0: - if err := handleTarTypeBlockCharFifo(newPath, &statCopy); err != nil { + if err := handleTarTypeBlockCharFifo(newPath, statCopy); err != nil { return errors.Wrapf(err, "failed to create device %s", newPath) } case fi.Mode()&os.ModeSymlink != 0: @@ -198,7 +198,7 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er } } - if err := rewriteMetadata(newPath, &statCopy); err != nil { + if err := rewriteMetadata(newPath, statCopy); err != nil { return errors.Wrapf(err, "error setting metadata for %s", newPath) } @@ -216,7 +216,7 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er if isRegularFile { if dw.opt.AsyncDataCb != nil { - dw.requestAsyncFileData(p, destPath, fi, &statCopy) + dw.requestAsyncFileData(p, destPath, fi, statCopy) } } else { return dw.processChange(kind, p, fi, nil) diff --git a/fs.go b/fs.go index fe370194..8b7a702e 100644 --- a/fs.go +++ b/fs.go @@ -91,7 +91,7 @@ func (fs *fs) Open(p string) (io.ReadCloser, error) { } type Dir struct { - Stat types.Stat + Stat *types.Stat FS FS } @@ -125,12 +125,12 @@ func (fs *subDirFS) Walk(ctx context.Context, target string, fn gofs.WalkDirFunc continue } - fi := &StatInfo{&d.Stat} + fi := &StatInfo{d.Stat} if !fi.IsDir() { return errors.WithStack(&os.PathError{Path: d.Stat.Path, Err: syscall.ENOTDIR, Op: "walk subdir"}) } dStat := d.Stat - if err := fn(d.Stat.Path, &DirEntryInfo{Stat: &dStat}, nil); err != nil { + if err := fn(d.Stat.Path, &DirEntryInfo{Stat: dStat}, nil); err != nil { return err } if err := d.FS.Walk(ctx, rest, func(p string, entry gofs.DirEntry, err error) error { @@ -191,7 +191,7 @@ func (s *StatInfo) Name() string { return filepath.Base(s.Stat.Path) } func (s *StatInfo) Size() int64 { - return s.Stat.Size_ + return s.Stat.Size } func (s *StatInfo) Mode() os.FileMode { return os.FileMode(s.Stat.Mode) @@ -246,6 +246,5 @@ func (s *DirEntryInfo) Info() (gofs.FileInfo, error) { s.Stat = stat } - st := *s.Stat - return &StatInfo{&st}, nil + return &StatInfo{s.Stat}, nil } diff --git a/fs_test.go b/fs_test.go index 54e7653b..df6cef62 100644 --- a/fs_test.go +++ b/fs_test.go @@ -73,14 +73,14 @@ func TestWalkDir(t *testing.T) { f, err := SubDirFS([]Dir{ { - Stat: types.Stat{ + Stat: &types.Stat{ Mode: uint32(os.ModeDir | 0755), Path: "1", }, FS: tmpfs, }, { - Stat: types.Stat{ + Stat: &types.Stat{ Mode: uint32(os.ModeDir | 0755), Path: "2", }, diff --git a/go.mod b/go.mod index 3dceea97..a261438c 100644 --- a/go.mod +++ b/go.mod @@ -5,13 +5,13 @@ go 1.19 require ( github.com/Microsoft/go-winio v0.5.2 github.com/containerd/continuity v0.4.1 - github.com/gogo/protobuf v1.3.2 github.com/moby/patternmatcher v0.5.0 github.com/opencontainers/go-digest v1.0.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.8.4 golang.org/x/sync v0.1.0 golang.org/x/sys v0.1.0 + google.golang.org/protobuf v1.26.0 ) require ( @@ -19,7 +19,7 @@ require ( github.com/kr/pretty v0.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sirupsen/logrus v1.8.1 // indirect - google.golang.org/protobuf v1.26.0 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 46c6359f..234ef4d8 100644 --- a/go.sum +++ b/go.sum @@ -4,13 +4,9 @@ github.com/containerd/continuity v0.4.1 h1:wQnVrjIyQ8vhU2sgOiL5T07jo+ouqc2bnKsv5 github.com/containerd/continuity v0.4.1/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -30,37 +26,12 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/receive.go b/receive.go index 209d1d2f..82da82ae 100644 --- a/receive.go +++ b/receive.go @@ -9,6 +9,7 @@ import ( "github.com/pkg/errors" "github.com/tonistiigi/fsutil/types" "golang.org/x/sync/errgroup" + "google.golang.org/protobuf/proto" ) type DiffType int @@ -170,7 +171,7 @@ func (r *receiver) run(ctx context.Context) error { return err } if r.progressCb != nil { - size += p.Size() + size += proto.Size(&p) r.progressCb(size, false) } diff --git a/receive_test.go b/receive_test.go index a36d5013..ebb92475 100644 --- a/receive_test.go +++ b/receive_test.go @@ -19,6 +19,7 @@ import ( "github.com/stretchr/testify/require" "github.com/tonistiigi/fsutil/types" "golang.org/x/sync/errgroup" + "google.golang.org/protobuf/proto" ) func TestSendError(t *testing.T) { @@ -79,7 +80,7 @@ func TestCopyWithSubDir(t *testing.T) { eg.Go(func() error { defer s1.(*fakeConnProto).closeSend() - subdir, err := SubDirFS([]Dir{{FS: fs, Stat: types.Stat{Path: "sub", Mode: uint32(os.ModeDir | 0755)}}}) + subdir, err := SubDirFS([]Dir{{FS: fs, Stat: &types.Stat{Path: "sub", Mode: uint32(os.ModeDir | 0755)}}}) if err != nil { return err } @@ -385,6 +386,19 @@ func sockPairProto(ctx context.Context) (Stream, Stream) { return &fakeConnProto{ctx, c1, c2}, &fakeConnProto{ctx, c2, c1} } +//nolint:unused +func clonePacket(from *types.Packet) *types.Packet { + var data []byte + copy(data, from.Data) + + return &types.Packet{ + Type: from.Type, + Stat: cloneStat(from.Stat), + ID: from.ID, + Data: data, + } +} + //nolint:unused type fakeConn struct { ctx context.Context @@ -399,15 +413,14 @@ func (fc *fakeConn) Context() context.Context { //nolint:unused func (fc *fakeConn) RecvMsg(m interface{}) error { - p, ok := m.(*types.Packet) + _, ok := m.(*types.Packet) if !ok { return errors.Errorf("invalid msg: %#v", m) } select { case <-fc.ctx.Done(): return fc.ctx.Err() - case p2 := <-fc.recvChan: - *p = *p2 + case _ = <-fc.recvChan: return nil } } @@ -418,12 +431,12 @@ func (fc *fakeConn) SendMsg(m interface{}) error { if !ok { return errors.Errorf("invalid msg: %#v", m) } - p2 := *p + p2 := clonePacket(p) p2.Data = append([]byte{}, p2.Data...) select { case <-fc.ctx.Done(): return fc.ctx.Err() - case fc.sendChan <- &p2: + case fc.sendChan <- p2: return nil } } @@ -450,7 +463,7 @@ func (fc *fakeConnProto) RecvMsg(m interface{}) error { if !ok { return io.EOF } - return p.Unmarshal(dt) + return proto.Unmarshal(dt, p) } } @@ -459,7 +472,7 @@ func (fc *fakeConnProto) SendMsg(m interface{}) error { if !ok { return errors.Errorf("invalid msg: %#v", m) } - dt, err := p.Marshal() + dt, err := proto.Marshal(p) if err != nil { return err } @@ -493,7 +506,7 @@ func (c *changes) HandleChange(kind ChangeKind, p string, fi os.FileInfo, err er func simpleSHA256Hasher(s *types.Stat) (hash.Hash, error) { h := sha256.New() - ss := *s + ss := cloneStat(s) ss.ModTime = 0 // Unlike Linux, on FreeBSD's stat() call returns -1 in st_rdev for regular files ss.Devminor = 0 @@ -503,7 +516,7 @@ func simpleSHA256Hasher(s *types.Stat) (hash.Hash, error) { ss.Mode = ss.Mode | 0777 } - dt, err := ss.Marshal() + dt, err := proto.Marshal(ss) if err != nil { return nil, err } diff --git a/send.go b/send.go index a044d04d..fac2de68 100644 --- a/send.go +++ b/send.go @@ -10,6 +10,7 @@ import ( "github.com/pkg/errors" "github.com/tonistiigi/fsutil/types" "golang.org/x/sync/errgroup" + "google.golang.org/protobuf/proto" ) var bufPool = sync.Pool{ @@ -167,7 +168,7 @@ func (s *sender) walk(ctx context.Context) error { s.mu.Unlock() } i++ - s.updateProgress(p.Size(), false) + s.updateProgress(proto.Size(p), false) return errors.Wrapf(s.conn.SendMsg(p), "failed to send stat %s", path) }) if err != nil { @@ -195,7 +196,7 @@ func (fs *fileSender) Write(dt []byte) (int, error) { if err := fs.sender.conn.SendMsg(p); err != nil { return 0, err } - fs.sender.updateProgress(p.Size(), false) + fs.sender.updateProgress(proto.Size(p), false) return len(dt), nil } diff --git a/stat.go b/stat.go index 2ab8da11..2217f833 100644 --- a/stat.go +++ b/stat.go @@ -27,7 +27,7 @@ func mkstat(path, relpath string, fi os.FileInfo, inodemap map[uint64]string) (* setUnixOpt(fi, stat, relpath, inodemap) if !fi.IsDir() { - stat.Size_ = fi.Size() + stat.Size = fi.Size() if fi.Mode()&os.ModeSymlink != 0 { link, err := os.Readlink(path) if err != nil { @@ -62,3 +62,22 @@ func Stat(path string) (*types.Stat, error) { } return mkstat(path, filepath.Base(path), fi, nil) } + +func cloneStat(from *types.Stat) *types.Stat { + clone := &types.Stat{ + Path: from.Path, + Mode: from.Mode, + Uid: from.Uid, + Gid: from.Gid, + Size: from.Size, + ModTime: from.ModTime, + Linkname: from.Linkname, + Devmajor: from.Devmajor, + Devminor: from.Devminor, + Xattrs: make(map[string][]byte, len(from.Xattrs)), + } + for k, v := range from.Xattrs { + clone.Xattrs[k] = v + } + return clone +} diff --git a/stat_test.go b/stat_test.go index 0678a850..05eb0af6 100644 --- a/stat_test.go +++ b/stat_test.go @@ -28,7 +28,7 @@ func TestStat(t *testing.T) { assert.NoError(t, err) assert.NotZero(t, st.ModTime) st.ModTime = 0 - assert.Equal(t, &types.Stat{Path: "foo", Mode: 0644, Size_: 5}, st) + assert.Equal(t, &types.Stat{Path: "foo", Mode: 0644, Size: 5}, st) st, err = Stat(filepath.Join(d, "zzz")) assert.NoError(t, err) @@ -40,13 +40,13 @@ func TestStat(t *testing.T) { assert.NoError(t, err) assert.NotZero(t, st.ModTime) st.ModTime = 0 - assert.Equal(t, &types.Stat{Path: "aa", Mode: 0644, Size_: 5}, st) + assert.Equal(t, &types.Stat{Path: "aa", Mode: 0644, Size: 5}, st) st, err = Stat(filepath.Join(d, "zzz/bb/cc/dd")) assert.NoError(t, err) assert.NotZero(t, st.ModTime) st.ModTime = 0 - assert.Equal(t, &types.Stat{Path: "dd", Mode: uint32(os.ModeSymlink | 0777), Size_: 6, Linkname: "../../"}, st) + assert.Equal(t, &types.Stat{Path: "dd", Mode: uint32(os.ModeSymlink | 0777), Size: 6, Linkname: "../../"}, st) st, err = Stat(filepath.Join(d, "sock")) assert.NoError(t, err) diff --git a/stat_unix.go b/stat_unix.go index 5923aefe..def901e3 100644 --- a/stat_unix.go +++ b/stat_unix.go @@ -52,7 +52,7 @@ func setUnixOpt(fi os.FileInfo, stat *types.Stat, path string, seenFiles map[uin if s.Nlink > 1 { if oldpath, ok := seenFiles[ino]; ok { stat.Linkname = oldpath - stat.Size_ = 0 + stat.Size = 0 linked = true } } diff --git a/types/generate.go b/types/generate.go index 5c03178f..e8a1183e 100644 --- a/types/generate.go +++ b/types/generate.go @@ -1,3 +1,11 @@ package types -//go:generate protoc --gogoslick_out=. stat.proto wire.proto +//go:generate protoc --go_out=paths=source_relative:. stat.proto wire.proto + +const ( + PACKET_STAT = Packet_PACKET_STAT + PACKET_REQ = Packet_PACKET_REQ + PACKET_DATA = Packet_PACKET_DATA + PACKET_FIN = Packet_PACKET_FIN + PACKET_ERR = Packet_PACKET_ERR +) diff --git a/types/stat.go b/types/stat.go index b79fd2bd..fd04e15c 100644 --- a/types/stat.go +++ b/types/stat.go @@ -2,6 +2,6 @@ package types import "os" -func (s Stat) IsDir() bool { +func (s *Stat) IsDir() bool { return os.FileMode(s.Mode).IsDir() } diff --git a/types/stat.pb.go b/types/stat.pb.go index 91200fb7..c9678ba3 100644 --- a/types/stat.pb.go +++ b/types/stat.pb.go @@ -1,37 +1,35 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.12.4 // source: stat.proto package types import ( - bytes "bytes" - fmt "fmt" - proto "github.com/gogo/protobuf/proto" - github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" - io "io" - math "math" - math_bits "math/bits" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" - strings "strings" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) type Stat struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` Mode uint32 `protobuf:"varint,2,opt,name=mode,proto3" json:"mode,omitempty"` Uid uint32 `protobuf:"varint,3,opt,name=uid,proto3" json:"uid,omitempty"` Gid uint32 `protobuf:"varint,4,opt,name=gid,proto3" json:"gid,omitempty"` - Size_ int64 `protobuf:"varint,5,opt,name=size,proto3" json:"size,omitempty"` + Size int64 `protobuf:"varint,5,opt,name=size,proto3" json:"size,omitempty"` ModTime int64 `protobuf:"varint,6,opt,name=modTime,proto3" json:"modTime,omitempty"` // int32 typeflag = 7; Linkname string `protobuf:"bytes,7,opt,name=linkname,proto3" json:"linkname,omitempty"` @@ -40,890 +38,200 @@ type Stat struct { Xattrs map[string][]byte `protobuf:"bytes,10,rep,name=xattrs,proto3" json:"xattrs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } -func (m *Stat) Reset() { *m = Stat{} } -func (*Stat) ProtoMessage() {} -func (*Stat) Descriptor() ([]byte, []int) { - return fileDescriptor_01fabdc1b78bd68b, []int{0} +func (x *Stat) Reset() { + *x = Stat{} + if protoimpl.UnsafeEnabled { + mi := &file_stat_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Stat) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) + +func (x *Stat) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Stat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_Stat.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err + +func (*Stat) ProtoMessage() {} + +func (x *Stat) ProtoReflect() protoreflect.Message { + mi := &file_stat_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } -} -func (m *Stat) XXX_Merge(src proto.Message) { - xxx_messageInfo_Stat.Merge(m, src) -} -func (m *Stat) XXX_Size() int { - return m.Size() -} -func (m *Stat) XXX_DiscardUnknown() { - xxx_messageInfo_Stat.DiscardUnknown(m) + return mi.MessageOf(x) } -var xxx_messageInfo_Stat proto.InternalMessageInfo +// Deprecated: Use Stat.ProtoReflect.Descriptor instead. +func (*Stat) Descriptor() ([]byte, []int) { + return file_stat_proto_rawDescGZIP(), []int{0} +} -func (m *Stat) GetPath() string { - if m != nil { - return m.Path +func (x *Stat) GetPath() string { + if x != nil { + return x.Path } return "" } -func (m *Stat) GetMode() uint32 { - if m != nil { - return m.Mode +func (x *Stat) GetMode() uint32 { + if x != nil { + return x.Mode } return 0 } -func (m *Stat) GetUid() uint32 { - if m != nil { - return m.Uid +func (x *Stat) GetUid() uint32 { + if x != nil { + return x.Uid } return 0 } -func (m *Stat) GetGid() uint32 { - if m != nil { - return m.Gid +func (x *Stat) GetGid() uint32 { + if x != nil { + return x.Gid } return 0 } -func (m *Stat) GetSize_() int64 { - if m != nil { - return m.Size_ +func (x *Stat) GetSize() int64 { + if x != nil { + return x.Size } return 0 } -func (m *Stat) GetModTime() int64 { - if m != nil { - return m.ModTime +func (x *Stat) GetModTime() int64 { + if x != nil { + return x.ModTime } return 0 } -func (m *Stat) GetLinkname() string { - if m != nil { - return m.Linkname +func (x *Stat) GetLinkname() string { + if x != nil { + return x.Linkname } return "" } -func (m *Stat) GetDevmajor() int64 { - if m != nil { - return m.Devmajor +func (x *Stat) GetDevmajor() int64 { + if x != nil { + return x.Devmajor } return 0 } -func (m *Stat) GetDevminor() int64 { - if m != nil { - return m.Devminor +func (x *Stat) GetDevminor() int64 { + if x != nil { + return x.Devminor } return 0 } -func (m *Stat) GetXattrs() map[string][]byte { - if m != nil { - return m.Xattrs +func (x *Stat) GetXattrs() map[string][]byte { + if x != nil { + return x.Xattrs } return nil } -func init() { - proto.RegisterType((*Stat)(nil), "fsutil.types.Stat") - proto.RegisterMapType((map[string][]byte)(nil), "fsutil.types.Stat.XattrsEntry") -} - -func init() { proto.RegisterFile("stat.proto", fileDescriptor_01fabdc1b78bd68b) } - -var fileDescriptor_01fabdc1b78bd68b = []byte{ - // 317 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x91, 0xbd, 0x4e, 0xc3, 0x30, - 0x14, 0x85, 0x73, 0x9b, 0xfe, 0xba, 0x45, 0x42, 0x16, 0x83, 0xd5, 0xe1, 0x2a, 0x62, 0xca, 0x94, - 0x01, 0x24, 0xc4, 0xcf, 0x86, 0xc4, 0x0b, 0x04, 0x06, 0xc4, 0x66, 0x64, 0x53, 0x4c, 0x9b, 0xb8, - 0x4a, 0xdc, 0x8a, 0x32, 0xf1, 0x08, 0x3c, 0x06, 0x6f, 0x02, 0x63, 0xc7, 0x8e, 0xd4, 0x5d, 0x18, - 0xfb, 0x08, 0xc8, 0x4e, 0x5b, 0xba, 0x9d, 0xf3, 0x9d, 0x7b, 0x95, 0x9c, 0x6b, 0x42, 0x4a, 0xc3, - 0x4d, 0x32, 0x2e, 0xb4, 0xd1, 0xb4, 0xf7, 0x54, 0x4e, 0x8c, 0x1a, 0x25, 0x66, 0x36, 0x96, 0xe5, - 0xf1, 0x57, 0x8d, 0xd4, 0x6f, 0x0d, 0x37, 0x94, 0x92, 0xfa, 0x98, 0x9b, 0x67, 0x06, 0x11, 0xc4, - 0x9d, 0xd4, 0x6b, 0xc7, 0x32, 0x2d, 0x24, 0xab, 0x45, 0x10, 0x1f, 0xa4, 0x5e, 0xd3, 0x43, 0x12, - 0x4e, 0x94, 0x60, 0xa1, 0x47, 0x4e, 0x3a, 0x32, 0x50, 0x82, 0xd5, 0x2b, 0x32, 0x50, 0xc2, 0xed, - 0x95, 0xea, 0x4d, 0xb2, 0x46, 0x04, 0x71, 0x98, 0x7a, 0x4d, 0x19, 0x69, 0x65, 0x5a, 0xdc, 0xa9, - 0x4c, 0xb2, 0xa6, 0xc7, 0x5b, 0x4b, 0xfb, 0xa4, 0x3d, 0x52, 0xf9, 0x30, 0xe7, 0x99, 0x64, 0x2d, - 0xff, 0xf5, 0x9d, 0x77, 0x99, 0x90, 0xd3, 0x8c, 0xbf, 0xe8, 0x82, 0xb5, 0xfd, 0xda, 0xce, 0x6f, - 0x33, 0x95, 0xeb, 0x82, 0x75, 0xfe, 0x33, 0xe7, 0xe9, 0x19, 0x69, 0xbe, 0x72, 0x63, 0x8a, 0x92, - 0x91, 0x28, 0x8c, 0xbb, 0x27, 0x98, 0xec, 0xb7, 0x4e, 0x5c, 0xe3, 0xe4, 0xde, 0x0f, 0xdc, 0xe4, - 0xa6, 0x98, 0xa5, 0x9b, 0xe9, 0xfe, 0x05, 0xe9, 0xee, 0x61, 0x57, 0x6d, 0x28, 0x67, 0x9b, 0x9b, - 0x38, 0x49, 0x8f, 0x48, 0x63, 0xca, 0x47, 0x93, 0xea, 0x26, 0xbd, 0xb4, 0x32, 0x97, 0xb5, 0x73, - 0xb8, 0xbe, 0x9a, 0x2f, 0x31, 0x58, 0x2c, 0x31, 0x58, 0x2f, 0x11, 0xde, 0x2d, 0xc2, 0xa7, 0x45, - 0xf8, 0xb6, 0x08, 0x73, 0x8b, 0xf0, 0x63, 0x11, 0x7e, 0x2d, 0x06, 0x6b, 0x8b, 0xf0, 0xb1, 0xc2, - 0x60, 0xbe, 0xc2, 0x60, 0xb1, 0xc2, 0xe0, 0xa1, 0xe1, 0x7f, 0xe8, 0xb1, 0xe9, 0xdf, 0xe6, 0xf4, - 0x2f, 0x00, 0x00, 0xff, 0xff, 0x06, 0x97, 0xf3, 0xd7, 0xa9, 0x01, 0x00, 0x00, -} - -func (this *Stat) Equal(that interface{}) bool { - if that == nil { - return this == nil - } - - that1, ok := that.(*Stat) - if !ok { - that2, ok := that.(Stat) - if ok { - that1 = &that2 - } else { - return false - } - } - if that1 == nil { - return this == nil - } else if this == nil { - return false - } - if this.Path != that1.Path { - return false - } - if this.Mode != that1.Mode { - return false - } - if this.Uid != that1.Uid { - return false - } - if this.Gid != that1.Gid { - return false - } - if this.Size_ != that1.Size_ { - return false - } - if this.ModTime != that1.ModTime { - return false - } - if this.Linkname != that1.Linkname { - return false - } - if this.Devmajor != that1.Devmajor { - return false - } - if this.Devminor != that1.Devminor { - return false - } - if len(this.Xattrs) != len(that1.Xattrs) { - return false - } - for i := range this.Xattrs { - if !bytes.Equal(this.Xattrs[i], that1.Xattrs[i]) { - return false - } - } - return true -} -func (this *Stat) GoString() string { - if this == nil { - return "nil" - } - s := make([]string, 0, 14) - s = append(s, "&types.Stat{") - s = append(s, "Path: "+fmt.Sprintf("%#v", this.Path)+",\n") - s = append(s, "Mode: "+fmt.Sprintf("%#v", this.Mode)+",\n") - s = append(s, "Uid: "+fmt.Sprintf("%#v", this.Uid)+",\n") - s = append(s, "Gid: "+fmt.Sprintf("%#v", this.Gid)+",\n") - s = append(s, "Size_: "+fmt.Sprintf("%#v", this.Size_)+",\n") - s = append(s, "ModTime: "+fmt.Sprintf("%#v", this.ModTime)+",\n") - s = append(s, "Linkname: "+fmt.Sprintf("%#v", this.Linkname)+",\n") - s = append(s, "Devmajor: "+fmt.Sprintf("%#v", this.Devmajor)+",\n") - s = append(s, "Devminor: "+fmt.Sprintf("%#v", this.Devminor)+",\n") - keysForXattrs := make([]string, 0, len(this.Xattrs)) - for k, _ := range this.Xattrs { - keysForXattrs = append(keysForXattrs, k) - } - github_com_gogo_protobuf_sortkeys.Strings(keysForXattrs) - mapStringForXattrs := "map[string][]byte{" - for _, k := range keysForXattrs { - mapStringForXattrs += fmt.Sprintf("%#v: %#v,", k, this.Xattrs[k]) - } - mapStringForXattrs += "}" - if this.Xattrs != nil { - s = append(s, "Xattrs: "+mapStringForXattrs+",\n") - } - s = append(s, "}") - return strings.Join(s, "") -} -func valueToGoStringStat(v interface{}, typ string) string { - rv := reflect.ValueOf(v) - if rv.IsNil() { - return "nil" - } - pv := reflect.Indirect(rv).Interface() - return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) -} -func (m *Stat) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *Stat) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) +var File_stat_proto protoreflect.FileDescriptor + +var file_stat_proto_rawDesc = []byte{ + 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x66, 0x73, + 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x22, 0xc7, 0x02, 0x0a, 0x04, 0x53, + 0x74, 0x61, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, + 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x10, 0x0a, + 0x03, 0x67, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x67, 0x69, 0x64, 0x12, + 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, + 0x69, 0x7a, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1a, 0x0a, + 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x76, + 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x64, 0x65, 0x76, + 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x76, 0x6d, 0x69, 0x6e, 0x6f, + 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x64, 0x65, 0x76, 0x6d, 0x69, 0x6e, 0x6f, + 0x72, 0x12, 0x36, 0x0a, 0x06, 0x78, 0x61, 0x74, 0x74, 0x72, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1e, 0x2e, 0x66, 0x73, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x2e, 0x58, 0x61, 0x74, 0x74, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x06, 0x78, 0x61, 0x74, 0x74, 0x72, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x58, 0x61, 0x74, + 0x74, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x42, 0x24, 0x5a, 0x22, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x74, 0x6f, 0x6e, 0x69, 0x73, 0x74, 0x69, 0x69, 0x67, 0x69, 0x2f, 0x66, 0x73, + 0x75, 0x74, 0x69, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } -func (m *Stat) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Xattrs) > 0 { - for k := range m.Xattrs { - v := m.Xattrs[k] - baseI := i - if len(v) > 0 { - i -= len(v) - copy(dAtA[i:], v) - i = encodeVarintStat(dAtA, i, uint64(len(v))) - i-- - dAtA[i] = 0x12 - } - i -= len(k) - copy(dAtA[i:], k) - i = encodeVarintStat(dAtA, i, uint64(len(k))) - i-- - dAtA[i] = 0xa - i = encodeVarintStat(dAtA, i, uint64(baseI-i)) - i-- - dAtA[i] = 0x52 - } - } - if m.Devminor != 0 { - i = encodeVarintStat(dAtA, i, uint64(m.Devminor)) - i-- - dAtA[i] = 0x48 - } - if m.Devmajor != 0 { - i = encodeVarintStat(dAtA, i, uint64(m.Devmajor)) - i-- - dAtA[i] = 0x40 - } - if len(m.Linkname) > 0 { - i -= len(m.Linkname) - copy(dAtA[i:], m.Linkname) - i = encodeVarintStat(dAtA, i, uint64(len(m.Linkname))) - i-- - dAtA[i] = 0x3a - } - if m.ModTime != 0 { - i = encodeVarintStat(dAtA, i, uint64(m.ModTime)) - i-- - dAtA[i] = 0x30 - } - if m.Size_ != 0 { - i = encodeVarintStat(dAtA, i, uint64(m.Size_)) - i-- - dAtA[i] = 0x28 - } - if m.Gid != 0 { - i = encodeVarintStat(dAtA, i, uint64(m.Gid)) - i-- - dAtA[i] = 0x20 - } - if m.Uid != 0 { - i = encodeVarintStat(dAtA, i, uint64(m.Uid)) - i-- - dAtA[i] = 0x18 - } - if m.Mode != 0 { - i = encodeVarintStat(dAtA, i, uint64(m.Mode)) - i-- - dAtA[i] = 0x10 - } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintStat(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func encodeVarintStat(dAtA []byte, offset int, v uint64) int { - offset -= sovStat(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *Stat) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovStat(uint64(l)) - } - if m.Mode != 0 { - n += 1 + sovStat(uint64(m.Mode)) - } - if m.Uid != 0 { - n += 1 + sovStat(uint64(m.Uid)) - } - if m.Gid != 0 { - n += 1 + sovStat(uint64(m.Gid)) - } - if m.Size_ != 0 { - n += 1 + sovStat(uint64(m.Size_)) - } - if m.ModTime != 0 { - n += 1 + sovStat(uint64(m.ModTime)) - } - l = len(m.Linkname) - if l > 0 { - n += 1 + l + sovStat(uint64(l)) - } - if m.Devmajor != 0 { - n += 1 + sovStat(uint64(m.Devmajor)) - } - if m.Devminor != 0 { - n += 1 + sovStat(uint64(m.Devminor)) - } - if len(m.Xattrs) > 0 { - for k, v := range m.Xattrs { - _ = k - _ = v - l = 0 - if len(v) > 0 { - l = 1 + len(v) + sovStat(uint64(len(v))) - } - mapEntrySize := 1 + len(k) + sovStat(uint64(len(k))) + l - n += mapEntrySize + 1 + sovStat(uint64(mapEntrySize)) - } - } - return n -} - -func sovStat(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozStat(x uint64) (n int) { - return sovStat(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (this *Stat) String() string { - if this == nil { - return "nil" - } - keysForXattrs := make([]string, 0, len(this.Xattrs)) - for k, _ := range this.Xattrs { - keysForXattrs = append(keysForXattrs, k) - } - github_com_gogo_protobuf_sortkeys.Strings(keysForXattrs) - mapStringForXattrs := "map[string][]byte{" - for _, k := range keysForXattrs { - mapStringForXattrs += fmt.Sprintf("%v: %v,", k, this.Xattrs[k]) - } - mapStringForXattrs += "}" - s := strings.Join([]string{`&Stat{`, - `Path:` + fmt.Sprintf("%v", this.Path) + `,`, - `Mode:` + fmt.Sprintf("%v", this.Mode) + `,`, - `Uid:` + fmt.Sprintf("%v", this.Uid) + `,`, - `Gid:` + fmt.Sprintf("%v", this.Gid) + `,`, - `Size_:` + fmt.Sprintf("%v", this.Size_) + `,`, - `ModTime:` + fmt.Sprintf("%v", this.ModTime) + `,`, - `Linkname:` + fmt.Sprintf("%v", this.Linkname) + `,`, - `Devmajor:` + fmt.Sprintf("%v", this.Devmajor) + `,`, - `Devminor:` + fmt.Sprintf("%v", this.Devminor) + `,`, - `Xattrs:` + mapStringForXattrs + `,`, - `}`, - }, "") - return s -} -func valueToStringStat(v interface{}) string { - rv := reflect.ValueOf(v) - if rv.IsNil() { - return "nil" - } - pv := reflect.Indirect(rv).Interface() - return fmt.Sprintf("*%v", pv) -} -func (m *Stat) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowStat - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Stat: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Stat: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowStat - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthStat - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthStat - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Path = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) - } - m.Mode = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowStat - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Mode |= uint32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Uid", wireType) - } - m.Uid = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowStat - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Uid |= uint32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Gid", wireType) - } - m.Gid = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowStat - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Gid |= uint32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 5: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Size_", wireType) - } - m.Size_ = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowStat - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Size_ |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 6: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ModTime", wireType) - } - m.ModTime = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowStat - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.ModTime |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 7: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Linkname", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowStat - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthStat - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthStat - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Linkname = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 8: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Devmajor", wireType) - } - m.Devmajor = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowStat - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Devmajor |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 9: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Devminor", wireType) - } - m.Devminor = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowStat - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Devminor |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 10: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Xattrs", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowStat - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthStat - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthStat - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Xattrs == nil { - m.Xattrs = make(map[string][]byte) - } - var mapkey string - mapvalue := []byte{} - for iNdEx < postIndex { - entryPreIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowStat - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - if fieldNum == 1 { - var stringLenmapkey uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowStat - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapkey |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapkey := int(stringLenmapkey) - if intStringLenmapkey < 0 { - return ErrInvalidLengthStat - } - postStringIndexmapkey := iNdEx + intStringLenmapkey - if postStringIndexmapkey < 0 { - return ErrInvalidLengthStat - } - if postStringIndexmapkey > l { - return io.ErrUnexpectedEOF - } - mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) - iNdEx = postStringIndexmapkey - } else if fieldNum == 2 { - var mapbyteLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowStat - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - mapbyteLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intMapbyteLen := int(mapbyteLen) - if intMapbyteLen < 0 { - return ErrInvalidLengthStat - } - postbytesIndex := iNdEx + intMapbyteLen - if postbytesIndex < 0 { - return ErrInvalidLengthStat - } - if postbytesIndex > l { - return io.ErrUnexpectedEOF - } - mapvalue = make([]byte, mapbyteLen) - copy(mapvalue, dAtA[iNdEx:postbytesIndex]) - iNdEx = postbytesIndex - } else { - iNdEx = entryPreIndex - skippy, err := skipStat(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthStat - } - if (iNdEx + skippy) > postIndex { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - m.Xattrs[mapkey] = mapvalue - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipStat(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthStat - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthStat - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } +var ( + file_stat_proto_rawDescOnce sync.Once + file_stat_proto_rawDescData = file_stat_proto_rawDesc +) - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func skipStat(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - depth := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowStat - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowStat - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - case 1: - iNdEx += 8 - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowStat - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if length < 0 { - return 0, ErrInvalidLengthStat +func file_stat_proto_rawDescGZIP() []byte { + file_stat_proto_rawDescOnce.Do(func() { + file_stat_proto_rawDescData = protoimpl.X.CompressGZIP(file_stat_proto_rawDescData) + }) + return file_stat_proto_rawDescData +} + +var file_stat_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_stat_proto_goTypes = []interface{}{ + (*Stat)(nil), // 0: fsutil.types.Stat + nil, // 1: fsutil.types.Stat.XattrsEntry +} +var file_stat_proto_depIdxs = []int32{ + 1, // 0: fsutil.types.Stat.xattrs:type_name -> fsutil.types.Stat.XattrsEntry + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_stat_proto_init() } +func file_stat_proto_init() { + if File_stat_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_stat_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Stat); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil } - iNdEx += length - case 3: - depth++ - case 4: - if depth == 0 { - return 0, ErrUnexpectedEndOfGroupStat - } - depth-- - case 5: - iNdEx += 4 - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - if iNdEx < 0 { - return 0, ErrInvalidLengthStat - } - if depth == 0 { - return iNdEx, nil } } - return 0, io.ErrUnexpectedEOF + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_stat_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_stat_proto_goTypes, + DependencyIndexes: file_stat_proto_depIdxs, + MessageInfos: file_stat_proto_msgTypes, + }.Build() + File_stat_proto = out.File + file_stat_proto_rawDesc = nil + file_stat_proto_goTypes = nil + file_stat_proto_depIdxs = nil } - -var ( - ErrInvalidLengthStat = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowStat = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroupStat = fmt.Errorf("proto: unexpected end of group") -) diff --git a/types/stat.proto b/types/stat.proto index 4138be69..9b7728c4 100644 --- a/types/stat.proto +++ b/types/stat.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package fsutil.types; -option go_package = "types"; +option go_package = "github.com/tonistiigi/fsutil/types"; message Stat { string path = 1; diff --git a/types/wire.pb.go b/types/wire.pb.go index 9e22269e..fdf28ef1 100644 --- a/types/wire.pb.go +++ b/types/wire.pb.go @@ -1,575 +1,242 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.12.4 // source: wire.proto package types import ( - bytes "bytes" - fmt "fmt" - proto "github.com/gogo/protobuf/proto" - io "io" - math "math" - math_bits "math/bits" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" - strconv "strconv" - strings "strings" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) type Packet_PacketType int32 const ( - PACKET_STAT Packet_PacketType = 0 - PACKET_REQ Packet_PacketType = 1 - PACKET_DATA Packet_PacketType = 2 - PACKET_FIN Packet_PacketType = 3 - PACKET_ERR Packet_PacketType = 4 + Packet_PACKET_STAT Packet_PacketType = 0 + Packet_PACKET_REQ Packet_PacketType = 1 + Packet_PACKET_DATA Packet_PacketType = 2 + Packet_PACKET_FIN Packet_PacketType = 3 + Packet_PACKET_ERR Packet_PacketType = 4 +) + +// Enum value maps for Packet_PacketType. +var ( + Packet_PacketType_name = map[int32]string{ + 0: "PACKET_STAT", + 1: "PACKET_REQ", + 2: "PACKET_DATA", + 3: "PACKET_FIN", + 4: "PACKET_ERR", + } + Packet_PacketType_value = map[string]int32{ + "PACKET_STAT": 0, + "PACKET_REQ": 1, + "PACKET_DATA": 2, + "PACKET_FIN": 3, + "PACKET_ERR": 4, + } ) -var Packet_PacketType_name = map[int32]string{ - 0: "PACKET_STAT", - 1: "PACKET_REQ", - 2: "PACKET_DATA", - 3: "PACKET_FIN", - 4: "PACKET_ERR", +func (x Packet_PacketType) Enum() *Packet_PacketType { + p := new(Packet_PacketType) + *p = x + return p } -var Packet_PacketType_value = map[string]int32{ - "PACKET_STAT": 0, - "PACKET_REQ": 1, - "PACKET_DATA": 2, - "PACKET_FIN": 3, - "PACKET_ERR": 4, +func (x Packet_PacketType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } +func (Packet_PacketType) Descriptor() protoreflect.EnumDescriptor { + return file_wire_proto_enumTypes[0].Descriptor() +} + +func (Packet_PacketType) Type() protoreflect.EnumType { + return &file_wire_proto_enumTypes[0] +} + +func (x Packet_PacketType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Packet_PacketType.Descriptor instead. func (Packet_PacketType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_f2dcdddcdf68d8e0, []int{0, 0} + return file_wire_proto_rawDescGZIP(), []int{0, 0} } type Packet struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + Type Packet_PacketType `protobuf:"varint,1,opt,name=type,proto3,enum=fsutil.types.Packet_PacketType" json:"type,omitempty"` Stat *Stat `protobuf:"bytes,2,opt,name=stat,proto3" json:"stat,omitempty"` ID uint32 `protobuf:"varint,3,opt,name=ID,proto3" json:"ID,omitempty"` Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` } -func (m *Packet) Reset() { *m = Packet{} } -func (*Packet) ProtoMessage() {} -func (*Packet) Descriptor() ([]byte, []int) { - return fileDescriptor_f2dcdddcdf68d8e0, []int{0} -} -func (m *Packet) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *Packet) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_Packet.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *Packet) Reset() { + *x = Packet{} + if protoimpl.UnsafeEnabled { + mi := &file_wire_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *Packet) XXX_Merge(src proto.Message) { - xxx_messageInfo_Packet.Merge(m, src) -} -func (m *Packet) XXX_Size() int { - return m.Size() -} -func (m *Packet) XXX_DiscardUnknown() { - xxx_messageInfo_Packet.DiscardUnknown(m) + +func (x *Packet) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_Packet proto.InternalMessageInfo +func (*Packet) ProtoMessage() {} -func (m *Packet) GetType() Packet_PacketType { - if m != nil { - return m.Type +func (x *Packet) ProtoReflect() protoreflect.Message { + mi := &file_wire_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return PACKET_STAT + return mi.MessageOf(x) } -func (m *Packet) GetStat() *Stat { - if m != nil { - return m.Stat - } - return nil +// Deprecated: Use Packet.ProtoReflect.Descriptor instead. +func (*Packet) Descriptor() ([]byte, []int) { + return file_wire_proto_rawDescGZIP(), []int{0} } -func (m *Packet) GetID() uint32 { - if m != nil { - return m.ID +func (x *Packet) GetType() Packet_PacketType { + if x != nil { + return x.Type } - return 0 + return Packet_PACKET_STAT } -func (m *Packet) GetData() []byte { - if m != nil { - return m.Data +func (x *Packet) GetStat() *Stat { + if x != nil { + return x.Stat } return nil } -func init() { - proto.RegisterEnum("fsutil.types.Packet_PacketType", Packet_PacketType_name, Packet_PacketType_value) - proto.RegisterType((*Packet)(nil), "fsutil.types.Packet") -} - -func init() { proto.RegisterFile("wire.proto", fileDescriptor_f2dcdddcdf68d8e0) } - -var fileDescriptor_f2dcdddcdf68d8e0 = []byte{ - // 276 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2a, 0xcf, 0x2c, 0x4a, - 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x49, 0x2b, 0x2e, 0x2d, 0xc9, 0xcc, 0xd1, 0x2b, - 0xa9, 0x2c, 0x48, 0x2d, 0x96, 0xe2, 0x2a, 0x2e, 0x49, 0x2c, 0x81, 0xc8, 0x28, 0xbd, 0x64, 0xe4, - 0x62, 0x0b, 0x48, 0x4c, 0xce, 0x4e, 0x2d, 0x11, 0x32, 0xe6, 0x62, 0x01, 0xc9, 0x4b, 0x30, 0x2a, - 0x30, 0x6a, 0xf0, 0x19, 0xc9, 0xeb, 0x21, 0xeb, 0xd1, 0x83, 0xa8, 0x81, 0x52, 0x21, 0x95, 0x05, - 0xa9, 0x41, 0x60, 0xc5, 0x42, 0x6a, 0x5c, 0x2c, 0x20, 0xd3, 0x24, 0x98, 0x14, 0x18, 0x35, 0xb8, - 0x8d, 0x84, 0x50, 0x35, 0x05, 0x97, 0x24, 0x96, 0x04, 0x81, 0xe5, 0x85, 0xf8, 0xb8, 0x98, 0x3c, - 0x5d, 0x24, 0x98, 0x15, 0x18, 0x35, 0x78, 0x83, 0x98, 0x3c, 0x5d, 0x84, 0x84, 0xb8, 0x58, 0x52, - 0x12, 0x4b, 0x12, 0x25, 0x58, 0x14, 0x18, 0x35, 0x78, 0x82, 0xc0, 0x6c, 0xa5, 0x38, 0x2e, 0x2e, - 0x84, 0xf9, 0x42, 0xfc, 0x5c, 0xdc, 0x01, 0x8e, 0xce, 0xde, 0xae, 0x21, 0xf1, 0xc1, 0x21, 0x8e, - 0x21, 0x02, 0x0c, 0x42, 0x7c, 0x5c, 0x5c, 0x50, 0x81, 0x20, 0xd7, 0x40, 0x01, 0x46, 0x24, 0x05, - 0x2e, 0x8e, 0x21, 0x8e, 0x02, 0x4c, 0x48, 0x0a, 0xdc, 0x3c, 0xfd, 0x04, 0x98, 0x91, 0xf8, 0xae, - 0x41, 0x41, 0x02, 0x2c, 0x4e, 0xd6, 0x17, 0x1e, 0xca, 0x31, 0xdc, 0x78, 0x28, 0xc7, 0xf0, 0xe1, - 0xa1, 0x1c, 0x63, 0xc3, 0x23, 0x39, 0xc6, 0x15, 0x8f, 0xe4, 0x18, 0x4f, 0x3c, 0x92, 0x63, 0xbc, - 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x17, 0x8f, 0xe4, 0x18, 0x3e, 0x3c, 0x92, 0x63, - 0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0x58, 0xc1, - 0x7e, 0x49, 0x62, 0x03, 0x87, 0x97, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x3f, 0x9d, 0xe3, 0x51, - 0x57, 0x01, 0x00, 0x00, -} - -func (x Packet_PacketType) String() string { - s, ok := Packet_PacketType_name[int32(x)] - if ok { - return s - } - return strconv.Itoa(int(x)) -} -func (this *Packet) Equal(that interface{}) bool { - if that == nil { - return this == nil - } - - that1, ok := that.(*Packet) - if !ok { - that2, ok := that.(Packet) - if ok { - that1 = &that2 - } else { - return false - } - } - if that1 == nil { - return this == nil - } else if this == nil { - return false - } - if this.Type != that1.Type { - return false - } - if !this.Stat.Equal(that1.Stat) { - return false - } - if this.ID != that1.ID { - return false - } - if !bytes.Equal(this.Data, that1.Data) { - return false +func (x *Packet) GetID() uint32 { + if x != nil { + return x.ID } - return true -} -func (this *Packet) GoString() string { - if this == nil { - return "nil" - } - s := make([]string, 0, 8) - s = append(s, "&types.Packet{") - s = append(s, "Type: "+fmt.Sprintf("%#v", this.Type)+",\n") - if this.Stat != nil { - s = append(s, "Stat: "+fmt.Sprintf("%#v", this.Stat)+",\n") - } - s = append(s, "ID: "+fmt.Sprintf("%#v", this.ID)+",\n") - s = append(s, "Data: "+fmt.Sprintf("%#v", this.Data)+",\n") - s = append(s, "}") - return strings.Join(s, "") -} -func valueToGoStringWire(v interface{}, typ string) string { - rv := reflect.ValueOf(v) - if rv.IsNil() { - return "nil" - } - pv := reflect.Indirect(rv).Interface() - return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) -} -func (m *Packet) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *Packet) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) + return 0 } -func (m *Packet) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Data) > 0 { - i -= len(m.Data) - copy(dAtA[i:], m.Data) - i = encodeVarintWire(dAtA, i, uint64(len(m.Data))) - i-- - dAtA[i] = 0x22 - } - if m.ID != 0 { - i = encodeVarintWire(dAtA, i, uint64(m.ID)) - i-- - dAtA[i] = 0x18 - } - if m.Stat != nil { - { - size, err := m.Stat.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintWire(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - if m.Type != 0 { - i = encodeVarintWire(dAtA, i, uint64(m.Type)) - i-- - dAtA[i] = 0x8 +func (x *Packet) GetData() []byte { + if x != nil { + return x.Data } - return len(dAtA) - i, nil + return nil } -func encodeVarintWire(dAtA []byte, offset int, v uint64) int { - offset -= sovWire(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *Packet) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Type != 0 { - n += 1 + sovWire(uint64(m.Type)) - } - if m.Stat != nil { - l = m.Stat.Size() - n += 1 + l + sovWire(uint64(l)) - } - if m.ID != 0 { - n += 1 + sovWire(uint64(m.ID)) - } - l = len(m.Data) - if l > 0 { - n += 1 + l + sovWire(uint64(l)) - } - return n +var File_wire_proto protoreflect.FileDescriptor + +var file_wire_proto_rawDesc = []byte{ + 0x0a, 0x0a, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x66, 0x73, + 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x1a, 0x0a, 0x73, 0x74, 0x61, 0x74, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe9, 0x01, 0x0a, 0x06, 0x50, 0x61, 0x63, 0x6b, 0x65, + 0x74, 0x12, 0x33, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x1f, 0x2e, 0x66, 0x73, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x50, + 0x61, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x73, 0x74, 0x61, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x66, 0x73, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x74, 0x79, + 0x70, 0x65, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x52, 0x04, 0x73, 0x74, 0x61, 0x74, 0x12, 0x0e, + 0x0a, 0x02, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, + 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, + 0x74, 0x61, 0x22, 0x5e, 0x0a, 0x0a, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x41, 0x43, 0x4b, 0x45, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x10, + 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x41, 0x43, 0x4b, 0x45, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x10, + 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x41, 0x43, 0x4b, 0x45, 0x54, 0x5f, 0x44, 0x41, 0x54, 0x41, + 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x41, 0x43, 0x4b, 0x45, 0x54, 0x5f, 0x46, 0x49, 0x4e, + 0x10, 0x03, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x41, 0x43, 0x4b, 0x45, 0x54, 0x5f, 0x45, 0x52, 0x52, + 0x10, 0x04, 0x42, 0x24, 0x5a, 0x22, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x74, 0x6f, 0x6e, 0x69, 0x73, 0x74, 0x69, 0x69, 0x67, 0x69, 0x2f, 0x66, 0x73, 0x75, 0x74, + 0x69, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } -func sovWire(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozWire(x uint64) (n int) { - return sovWire(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (this *Packet) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&Packet{`, - `Type:` + fmt.Sprintf("%v", this.Type) + `,`, - `Stat:` + strings.Replace(fmt.Sprintf("%v", this.Stat), "Stat", "Stat", 1) + `,`, - `ID:` + fmt.Sprintf("%v", this.ID) + `,`, - `Data:` + fmt.Sprintf("%v", this.Data) + `,`, - `}`, - }, "") - return s -} -func valueToStringWire(v interface{}) string { - rv := reflect.ValueOf(v) - if rv.IsNil() { - return "nil" - } - pv := reflect.Indirect(rv).Interface() - return fmt.Sprintf("*%v", pv) -} -func (m *Packet) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowWire - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Packet: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Packet: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) - } - m.Type = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowWire - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Type |= Packet_PacketType(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Stat", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowWire - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthWire - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthWire - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Stat == nil { - m.Stat = &Stat{} - } - if err := m.Stat.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) - } - m.ID = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowWire - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.ID |= uint32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowWire - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthWire - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthWire - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) - if m.Data == nil { - m.Data = []byte{} - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipWire(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthWire - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthWire - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } +var ( + file_wire_proto_rawDescOnce sync.Once + file_wire_proto_rawDescData = file_wire_proto_rawDesc +) - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func skipWire(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - depth := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowWire - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break +func file_wire_proto_rawDescGZIP() []byte { + file_wire_proto_rawDescOnce.Do(func() { + file_wire_proto_rawDescData = protoimpl.X.CompressGZIP(file_wire_proto_rawDescData) + }) + return file_wire_proto_rawDescData +} + +var file_wire_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_wire_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_wire_proto_goTypes = []interface{}{ + (Packet_PacketType)(0), // 0: fsutil.types.Packet.PacketType + (*Packet)(nil), // 1: fsutil.types.Packet + (*Stat)(nil), // 2: fsutil.types.Stat +} +var file_wire_proto_depIdxs = []int32{ + 0, // 0: fsutil.types.Packet.type:type_name -> fsutil.types.Packet.PacketType + 2, // 1: fsutil.types.Packet.stat:type_name -> fsutil.types.Stat + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_wire_proto_init() } +func file_wire_proto_init() { + if File_wire_proto != nil { + return + } + file_stat_proto_init() + if !protoimpl.UnsafeEnabled { + file_wire_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Packet); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil } } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowWire - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - case 1: - iNdEx += 8 - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowWire - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if length < 0 { - return 0, ErrInvalidLengthWire - } - iNdEx += length - case 3: - depth++ - case 4: - if depth == 0 { - return 0, ErrUnexpectedEndOfGroupWire - } - depth-- - case 5: - iNdEx += 4 - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - if iNdEx < 0 { - return 0, ErrInvalidLengthWire - } - if depth == 0 { - return iNdEx, nil - } } - return 0, io.ErrUnexpectedEOF + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_wire_proto_rawDesc, + NumEnums: 1, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_wire_proto_goTypes, + DependencyIndexes: file_wire_proto_depIdxs, + EnumInfos: file_wire_proto_enumTypes, + MessageInfos: file_wire_proto_msgTypes, + }.Build() + File_wire_proto = out.File + file_wire_proto_rawDesc = nil + file_wire_proto_goTypes = nil + file_wire_proto_depIdxs = nil } - -var ( - ErrInvalidLengthWire = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowWire = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroupWire = fmt.Errorf("proto: unexpected end of group") -) diff --git a/types/wire.proto b/types/wire.proto index 3e85000c..a00300b9 100644 --- a/types/wire.proto +++ b/types/wire.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package fsutil.types; -option go_package = "types"; +option go_package = "github.com/tonistiigi/fsutil/types"; import "stat.proto"; From 8bcf9db06cc560425eb26d3e5175c68de7694eba Mon Sep 17 00:00:00 2001 From: Kotaro Inoue Date: Sat, 6 Jan 2024 22:16:41 +0900 Subject: [PATCH 05/39] Fix to use internal representation for setting special bits Signed-off-by: Kotaro Inoue --- copy/copy_linux.go | 15 +++++++++------ copy/copy_unix.go | 15 +++++++++------ copy/copy_unix_test.go | 13 ++++++++++--- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/copy/copy_linux.go b/copy/copy_linux.go index d2553098..6d9b490c 100644 --- a/copy/copy_linux.go +++ b/copy/copy_linux.go @@ -30,12 +30,15 @@ func (c *copier) copyFileInfo(fi os.FileInfo, src, name string) error { m := fi.Mode() if c.mode != nil { - m = os.FileMode(*c.mode & 0777) - sb := *c.mode & 0777 - if sb != 0 { - m |= os.FileMode(sb) - } else { - m |= m & ^os.FileMode(0777) + m = os.FileMode(*c.mode).Perm() + if *c.mode&syscall.S_ISGID != 0 { + m |= os.ModeSetgid + } + if *c.mode&syscall.S_ISUID != 0 { + m |= os.ModeSetuid + } + if *c.mode&syscall.S_ISVTX != 0 { + m |= os.ModeSticky } } if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink { diff --git a/copy/copy_unix.go b/copy/copy_unix.go index 4a79e63d..4a7d0c86 100644 --- a/copy/copy_unix.go +++ b/copy/copy_unix.go @@ -31,12 +31,15 @@ func (c *copier) copyFileInfo(fi os.FileInfo, src, name string) error { m := fi.Mode() if c.mode != nil { - m = os.FileMode(*c.mode & 0777) - sb := *c.mode & 0777 - if sb != 0 { - m |= os.FileMode(sb) - } else { - m |= m & ^os.FileMode(0777) + m = os.FileMode(*c.mode).Perm() + if *c.mode&syscall.S_ISGID != 0 { + m |= os.ModeSetgid + } + if *c.mode&syscall.S_ISUID != 0 { + m |= os.ModeSetuid + } + if *c.mode&syscall.S_ISVTX != 0 { + m |= os.ModeSticky } } if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink { diff --git a/copy/copy_unix_test.go b/copy/copy_unix_test.go index 5156a875..405f757c 100644 --- a/copy/copy_unix_test.go +++ b/copy/copy_unix_test.go @@ -7,6 +7,7 @@ import ( "context" "os" "path/filepath" + "syscall" "testing" "github.com/stretchr/testify/assert" @@ -72,11 +73,14 @@ func TestCopySetuid(t *testing.T) { fi, err := os.Lstat(filepath.Join(t2, "char")) require.NoError(t, err) - assert.Equal(t, os.FileMode(0444), fi.Mode()&07777) + assert.Equal(t, os.FileMode(0444), fi.Mode().Perm()) + assert.Equal(t, os.FileMode(0), fi.Mode()&os.ModeSetuid) + assert.Equal(t, os.FileMode(0), fi.Mode()&os.ModeSetgid) + assert.Equal(t, os.FileMode(0), fi.Mode()&os.ModeSticky) t3 := t.TempDir() - p := 04444 + p := 0444 | syscall.S_ISUID err = Copy(context.TODO(), t1, ".", t3, ".", WithCopyInfo(CopyInfo{ Mode: &p, })) @@ -84,5 +88,8 @@ func TestCopySetuid(t *testing.T) { fi, err = os.Lstat(filepath.Join(t3, "char")) require.NoError(t, err) - assert.Equal(t, os.FileMode(04444), fi.Mode()&07777) + assert.Equal(t, os.FileMode(0444), fi.Mode().Perm()) + assert.Equal(t, os.ModeSetuid, fi.Mode()&os.ModeSetuid) + assert.Equal(t, os.FileMode(0), fi.Mode()&os.ModeSetgid) + assert.Equal(t, os.FileMode(0), fi.Mode()&os.ModeSticky) } From 9ff87189278a250dfc7e49764a9642e93fcbfb65 Mon Sep 17 00:00:00 2001 From: Marat Radchenko Date: Sun, 12 Nov 2023 21:33:02 +0300 Subject: [PATCH 06/39] Fix cross-device copy failing on macOS Downstream issue: https://github.com/macOScontainers/rund/issues/28 Signed-off-by: Marat Radchenko --- copy/copy_darwin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copy/copy_darwin.go b/copy/copy_darwin.go index bc93b21c..0cdc00a8 100644 --- a/copy/copy_darwin.go +++ b/copy/copy_darwin.go @@ -13,7 +13,7 @@ import ( func copyFile(source, target string) error { if err := unix.Clonefileat(unix.AT_FDCWD, source, unix.AT_FDCWD, target, unix.CLONE_NOFOLLOW); err != nil { - if err != unix.EINVAL { + if err != unix.EINVAL && err != unix.EXDEV { return err } } else { From b3997ab7d31242a855e8dd5b27c82208efeaa3f4 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Wed, 24 Jan 2024 17:11:15 +0100 Subject: [PATCH 07/39] followlinks: sanitize root path Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- followlinks.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/followlinks.go b/followlinks.go index 3a0bd2bb..7a843cb1 100644 --- a/followlinks.go +++ b/followlinks.go @@ -137,7 +137,7 @@ func (r *symlinkResolver) readSymlink(p string, allowWildcard bool) ([]string, e func statFile(fs FS, root string) (os.DirEntry, error) { var out os.DirEntry - root = filepath.Clean(root) + root = filepath.ToSlash(filepath.Clean(root)) if root == "/" || root == "." { return nil, nil } @@ -168,7 +168,7 @@ func statFile(fs FS, root string) (os.DirEntry, error) { func readDir(fs FS, root string) ([]os.DirEntry, error) { var out []os.DirEntry - root = filepath.Clean(root) + root = filepath.ToSlash(filepath.Clean(root)) if root == "/" || root == "." { root = "." out = make([]gofs.DirEntry, 0) From b6cfc0f0a7b0671222dcb3a9e73b066ebb0d5bb9 Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Wed, 24 Jan 2024 16:54:17 +0000 Subject: [PATCH 08/39] chore: fix typos in NewFilterFS docstring Signed-off-by: Justin Chadwell --- filter.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/filter.go b/filter.go index 05f4a466..6585db4b 100644 --- a/filter.go +++ b/filter.go @@ -66,9 +66,9 @@ type filterFS struct { // 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 +// include and exclude patterns, or that are are excluded using the mapping // function. // // The FS is assumed to be a snapshot of the filesystem at the time of the From 6ec7d912485e80580d8de8bf13f7e9b74b6d8eb2 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Mon, 29 Jan 2024 08:46:48 +0100 Subject: [PATCH 09/39] Revert "Migrate off of gogo/protobuf" This reverts commit bea810c0c14d36b7b141c4cf236a2830e29a14a7. Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- diff_containerd.go | 8 +- diskwriter.go | 12 +- fs.go | 11 +- fs_test.go | 4 +- go.mod | 4 +- go.sum | 29 ++ receive.go | 3 +- receive_test.go | 33 +- send.go | 5 +- stat.go | 21 +- stat_test.go | 6 +- stat_unix.go | 2 +- types/generate.go | 10 +- types/stat.go | 2 +- types/stat.pb.go | 1014 +++++++++++++++++++++++++++++++++++++------- types/stat.proto | 2 +- types/wire.pb.go | 701 ++++++++++++++++++++++-------- types/wire.proto | 2 +- 18 files changed, 1441 insertions(+), 428 deletions(-) diff --git a/diff_containerd.go b/diff_containerd.go index 261431b8..d8619abf 100644 --- a/diff_containerd.go +++ b/diff_containerd.go @@ -106,11 +106,11 @@ func doubleWalkDiff(ctx context.Context, changeFn ChangeFunc, a, b walkerFn, fil var f *types.Stat var f2copy *currentPath if f2 != nil { - statCopy := cloneStat(f2.stat) + statCopy := *f2.stat if filter != nil { - filter(f2.path, statCopy) + filter(f2.path, &statCopy) } - f2copy = ¤tPath{path: f2.path, stat: statCopy} + f2copy = ¤tPath{path: f2.path, stat: &statCopy} } k, p := pathChange(f1, f2copy) switch k { @@ -188,7 +188,7 @@ func sameFile(f1, f2 *currentPath, differ DiffType) (same bool, retErr error) { } // If not a directory also check size, modtime, and content if !f1.stat.IsDir() { - if f1.stat.Size != f2.stat.Size { + if f1.stat.Size_ != f2.stat.Size_ { return false, nil } diff --git a/diskwriter.go b/diskwriter.go index 815f4e07..10b60851 100644 --- a/diskwriter.go +++ b/diskwriter.go @@ -124,10 +124,10 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er return errors.WithStack(&os.PathError{Path: p, Err: syscall.EBADMSG, Op: "change without stat info"}) } - statCopy := cloneStat(stat) + statCopy := *stat if dw.filter != nil { - if ok := dw.filter(p, statCopy); !ok { + if ok := dw.filter(p, &statCopy); !ok { return nil } } @@ -146,7 +146,7 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er } if oldFi != nil && fi.IsDir() && oldFi.IsDir() { - if err := rewriteMetadata(destPath, statCopy); err != nil { + if err := rewriteMetadata(destPath, &statCopy); err != nil { return errors.Wrapf(err, "error setting dir metadata for %s", destPath) } return nil @@ -170,7 +170,7 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er } dw.dirModTimes[destPath] = statCopy.ModTime case fi.Mode()&os.ModeDevice != 0 || fi.Mode()&os.ModeNamedPipe != 0: - if err := handleTarTypeBlockCharFifo(newPath, statCopy); err != nil { + if err := handleTarTypeBlockCharFifo(newPath, &statCopy); err != nil { return errors.Wrapf(err, "failed to create device %s", newPath) } case fi.Mode()&os.ModeSymlink != 0: @@ -198,7 +198,7 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er } } - if err := rewriteMetadata(newPath, statCopy); err != nil { + if err := rewriteMetadata(newPath, &statCopy); err != nil { return errors.Wrapf(err, "error setting metadata for %s", newPath) } @@ -216,7 +216,7 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er if isRegularFile { if dw.opt.AsyncDataCb != nil { - dw.requestAsyncFileData(p, destPath, fi, statCopy) + dw.requestAsyncFileData(p, destPath, fi, &statCopy) } } else { return dw.processChange(kind, p, fi, nil) diff --git a/fs.go b/fs.go index 8b7a702e..fe370194 100644 --- a/fs.go +++ b/fs.go @@ -91,7 +91,7 @@ func (fs *fs) Open(p string) (io.ReadCloser, error) { } type Dir struct { - Stat *types.Stat + Stat types.Stat FS FS } @@ -125,12 +125,12 @@ func (fs *subDirFS) Walk(ctx context.Context, target string, fn gofs.WalkDirFunc continue } - fi := &StatInfo{d.Stat} + fi := &StatInfo{&d.Stat} if !fi.IsDir() { return errors.WithStack(&os.PathError{Path: d.Stat.Path, Err: syscall.ENOTDIR, Op: "walk subdir"}) } dStat := d.Stat - if err := fn(d.Stat.Path, &DirEntryInfo{Stat: dStat}, nil); err != nil { + if err := fn(d.Stat.Path, &DirEntryInfo{Stat: &dStat}, nil); err != nil { return err } if err := d.FS.Walk(ctx, rest, func(p string, entry gofs.DirEntry, err error) error { @@ -191,7 +191,7 @@ func (s *StatInfo) Name() string { return filepath.Base(s.Stat.Path) } func (s *StatInfo) Size() int64 { - return s.Stat.Size + return s.Stat.Size_ } func (s *StatInfo) Mode() os.FileMode { return os.FileMode(s.Stat.Mode) @@ -246,5 +246,6 @@ func (s *DirEntryInfo) Info() (gofs.FileInfo, error) { s.Stat = stat } - return &StatInfo{s.Stat}, nil + st := *s.Stat + return &StatInfo{&st}, nil } diff --git a/fs_test.go b/fs_test.go index df6cef62..54e7653b 100644 --- a/fs_test.go +++ b/fs_test.go @@ -73,14 +73,14 @@ func TestWalkDir(t *testing.T) { f, err := SubDirFS([]Dir{ { - Stat: &types.Stat{ + Stat: types.Stat{ Mode: uint32(os.ModeDir | 0755), Path: "1", }, FS: tmpfs, }, { - Stat: &types.Stat{ + Stat: types.Stat{ Mode: uint32(os.ModeDir | 0755), Path: "2", }, diff --git a/go.mod b/go.mod index a261438c..3dceea97 100644 --- a/go.mod +++ b/go.mod @@ -5,13 +5,13 @@ go 1.19 require ( github.com/Microsoft/go-winio v0.5.2 github.com/containerd/continuity v0.4.1 + github.com/gogo/protobuf v1.3.2 github.com/moby/patternmatcher v0.5.0 github.com/opencontainers/go-digest v1.0.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.8.4 golang.org/x/sync v0.1.0 golang.org/x/sys v0.1.0 - google.golang.org/protobuf v1.26.0 ) require ( @@ -19,7 +19,7 @@ require ( github.com/kr/pretty v0.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sirupsen/logrus v1.8.1 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/protobuf v1.26.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 234ef4d8..46c6359f 100644 --- a/go.sum +++ b/go.sum @@ -4,9 +4,13 @@ github.com/containerd/continuity v0.4.1 h1:wQnVrjIyQ8vhU2sgOiL5T07jo+ouqc2bnKsv5 github.com/containerd/continuity v0.4.1/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -26,12 +30,37 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/receive.go b/receive.go index 82da82ae..209d1d2f 100644 --- a/receive.go +++ b/receive.go @@ -9,7 +9,6 @@ import ( "github.com/pkg/errors" "github.com/tonistiigi/fsutil/types" "golang.org/x/sync/errgroup" - "google.golang.org/protobuf/proto" ) type DiffType int @@ -171,7 +170,7 @@ func (r *receiver) run(ctx context.Context) error { return err } if r.progressCb != nil { - size += proto.Size(&p) + size += p.Size() r.progressCb(size, false) } diff --git a/receive_test.go b/receive_test.go index ebb92475..a36d5013 100644 --- a/receive_test.go +++ b/receive_test.go @@ -19,7 +19,6 @@ import ( "github.com/stretchr/testify/require" "github.com/tonistiigi/fsutil/types" "golang.org/x/sync/errgroup" - "google.golang.org/protobuf/proto" ) func TestSendError(t *testing.T) { @@ -80,7 +79,7 @@ func TestCopyWithSubDir(t *testing.T) { eg.Go(func() error { defer s1.(*fakeConnProto).closeSend() - subdir, err := SubDirFS([]Dir{{FS: fs, Stat: &types.Stat{Path: "sub", Mode: uint32(os.ModeDir | 0755)}}}) + subdir, err := SubDirFS([]Dir{{FS: fs, Stat: types.Stat{Path: "sub", Mode: uint32(os.ModeDir | 0755)}}}) if err != nil { return err } @@ -386,19 +385,6 @@ func sockPairProto(ctx context.Context) (Stream, Stream) { return &fakeConnProto{ctx, c1, c2}, &fakeConnProto{ctx, c2, c1} } -//nolint:unused -func clonePacket(from *types.Packet) *types.Packet { - var data []byte - copy(data, from.Data) - - return &types.Packet{ - Type: from.Type, - Stat: cloneStat(from.Stat), - ID: from.ID, - Data: data, - } -} - //nolint:unused type fakeConn struct { ctx context.Context @@ -413,14 +399,15 @@ func (fc *fakeConn) Context() context.Context { //nolint:unused func (fc *fakeConn) RecvMsg(m interface{}) error { - _, ok := m.(*types.Packet) + p, ok := m.(*types.Packet) if !ok { return errors.Errorf("invalid msg: %#v", m) } select { case <-fc.ctx.Done(): return fc.ctx.Err() - case _ = <-fc.recvChan: + case p2 := <-fc.recvChan: + *p = *p2 return nil } } @@ -431,12 +418,12 @@ func (fc *fakeConn) SendMsg(m interface{}) error { if !ok { return errors.Errorf("invalid msg: %#v", m) } - p2 := clonePacket(p) + p2 := *p p2.Data = append([]byte{}, p2.Data...) select { case <-fc.ctx.Done(): return fc.ctx.Err() - case fc.sendChan <- p2: + case fc.sendChan <- &p2: return nil } } @@ -463,7 +450,7 @@ func (fc *fakeConnProto) RecvMsg(m interface{}) error { if !ok { return io.EOF } - return proto.Unmarshal(dt, p) + return p.Unmarshal(dt) } } @@ -472,7 +459,7 @@ func (fc *fakeConnProto) SendMsg(m interface{}) error { if !ok { return errors.Errorf("invalid msg: %#v", m) } - dt, err := proto.Marshal(p) + dt, err := p.Marshal() if err != nil { return err } @@ -506,7 +493,7 @@ func (c *changes) HandleChange(kind ChangeKind, p string, fi os.FileInfo, err er func simpleSHA256Hasher(s *types.Stat) (hash.Hash, error) { h := sha256.New() - ss := cloneStat(s) + ss := *s ss.ModTime = 0 // Unlike Linux, on FreeBSD's stat() call returns -1 in st_rdev for regular files ss.Devminor = 0 @@ -516,7 +503,7 @@ func simpleSHA256Hasher(s *types.Stat) (hash.Hash, error) { ss.Mode = ss.Mode | 0777 } - dt, err := proto.Marshal(ss) + dt, err := ss.Marshal() if err != nil { return nil, err } diff --git a/send.go b/send.go index fac2de68..a044d04d 100644 --- a/send.go +++ b/send.go @@ -10,7 +10,6 @@ import ( "github.com/pkg/errors" "github.com/tonistiigi/fsutil/types" "golang.org/x/sync/errgroup" - "google.golang.org/protobuf/proto" ) var bufPool = sync.Pool{ @@ -168,7 +167,7 @@ func (s *sender) walk(ctx context.Context) error { s.mu.Unlock() } i++ - s.updateProgress(proto.Size(p), false) + s.updateProgress(p.Size(), false) return errors.Wrapf(s.conn.SendMsg(p), "failed to send stat %s", path) }) if err != nil { @@ -196,7 +195,7 @@ func (fs *fileSender) Write(dt []byte) (int, error) { if err := fs.sender.conn.SendMsg(p); err != nil { return 0, err } - fs.sender.updateProgress(proto.Size(p), false) + fs.sender.updateProgress(p.Size(), false) return len(dt), nil } diff --git a/stat.go b/stat.go index 2217f833..2ab8da11 100644 --- a/stat.go +++ b/stat.go @@ -27,7 +27,7 @@ func mkstat(path, relpath string, fi os.FileInfo, inodemap map[uint64]string) (* setUnixOpt(fi, stat, relpath, inodemap) if !fi.IsDir() { - stat.Size = fi.Size() + stat.Size_ = fi.Size() if fi.Mode()&os.ModeSymlink != 0 { link, err := os.Readlink(path) if err != nil { @@ -62,22 +62,3 @@ func Stat(path string) (*types.Stat, error) { } return mkstat(path, filepath.Base(path), fi, nil) } - -func cloneStat(from *types.Stat) *types.Stat { - clone := &types.Stat{ - Path: from.Path, - Mode: from.Mode, - Uid: from.Uid, - Gid: from.Gid, - Size: from.Size, - ModTime: from.ModTime, - Linkname: from.Linkname, - Devmajor: from.Devmajor, - Devminor: from.Devminor, - Xattrs: make(map[string][]byte, len(from.Xattrs)), - } - for k, v := range from.Xattrs { - clone.Xattrs[k] = v - } - return clone -} diff --git a/stat_test.go b/stat_test.go index 05eb0af6..0678a850 100644 --- a/stat_test.go +++ b/stat_test.go @@ -28,7 +28,7 @@ func TestStat(t *testing.T) { assert.NoError(t, err) assert.NotZero(t, st.ModTime) st.ModTime = 0 - assert.Equal(t, &types.Stat{Path: "foo", Mode: 0644, Size: 5}, st) + assert.Equal(t, &types.Stat{Path: "foo", Mode: 0644, Size_: 5}, st) st, err = Stat(filepath.Join(d, "zzz")) assert.NoError(t, err) @@ -40,13 +40,13 @@ func TestStat(t *testing.T) { assert.NoError(t, err) assert.NotZero(t, st.ModTime) st.ModTime = 0 - assert.Equal(t, &types.Stat{Path: "aa", Mode: 0644, Size: 5}, st) + assert.Equal(t, &types.Stat{Path: "aa", Mode: 0644, Size_: 5}, st) st, err = Stat(filepath.Join(d, "zzz/bb/cc/dd")) assert.NoError(t, err) assert.NotZero(t, st.ModTime) st.ModTime = 0 - assert.Equal(t, &types.Stat{Path: "dd", Mode: uint32(os.ModeSymlink | 0777), Size: 6, Linkname: "../../"}, st) + assert.Equal(t, &types.Stat{Path: "dd", Mode: uint32(os.ModeSymlink | 0777), Size_: 6, Linkname: "../../"}, st) st, err = Stat(filepath.Join(d, "sock")) assert.NoError(t, err) diff --git a/stat_unix.go b/stat_unix.go index def901e3..5923aefe 100644 --- a/stat_unix.go +++ b/stat_unix.go @@ -52,7 +52,7 @@ func setUnixOpt(fi os.FileInfo, stat *types.Stat, path string, seenFiles map[uin if s.Nlink > 1 { if oldpath, ok := seenFiles[ino]; ok { stat.Linkname = oldpath - stat.Size = 0 + stat.Size_ = 0 linked = true } } diff --git a/types/generate.go b/types/generate.go index e8a1183e..5c03178f 100644 --- a/types/generate.go +++ b/types/generate.go @@ -1,11 +1,3 @@ package types -//go:generate protoc --go_out=paths=source_relative:. stat.proto wire.proto - -const ( - PACKET_STAT = Packet_PACKET_STAT - PACKET_REQ = Packet_PACKET_REQ - PACKET_DATA = Packet_PACKET_DATA - PACKET_FIN = Packet_PACKET_FIN - PACKET_ERR = Packet_PACKET_ERR -) +//go:generate protoc --gogoslick_out=. stat.proto wire.proto diff --git a/types/stat.go b/types/stat.go index fd04e15c..b79fd2bd 100644 --- a/types/stat.go +++ b/types/stat.go @@ -2,6 +2,6 @@ package types import "os" -func (s *Stat) IsDir() bool { +func (s Stat) IsDir() bool { return os.FileMode(s.Mode).IsDir() } diff --git a/types/stat.pb.go b/types/stat.pb.go index c9678ba3..91200fb7 100644 --- a/types/stat.pb.go +++ b/types/stat.pb.go @@ -1,35 +1,37 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.1 -// protoc v3.12.4 +// Code generated by protoc-gen-gogo. DO NOT EDIT. // source: stat.proto package types import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" + bytes "bytes" + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + io "io" + math "math" + math_bits "math/bits" reflect "reflect" - sync "sync" + strings "strings" ) -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf -type Stat struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +type Stat struct { Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` Mode uint32 `protobuf:"varint,2,opt,name=mode,proto3" json:"mode,omitempty"` Uid uint32 `protobuf:"varint,3,opt,name=uid,proto3" json:"uid,omitempty"` Gid uint32 `protobuf:"varint,4,opt,name=gid,proto3" json:"gid,omitempty"` - Size int64 `protobuf:"varint,5,opt,name=size,proto3" json:"size,omitempty"` + Size_ int64 `protobuf:"varint,5,opt,name=size,proto3" json:"size,omitempty"` ModTime int64 `protobuf:"varint,6,opt,name=modTime,proto3" json:"modTime,omitempty"` // int32 typeflag = 7; Linkname string `protobuf:"bytes,7,opt,name=linkname,proto3" json:"linkname,omitempty"` @@ -38,200 +40,890 @@ type Stat struct { Xattrs map[string][]byte `protobuf:"bytes,10,rep,name=xattrs,proto3" json:"xattrs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } -func (x *Stat) Reset() { - *x = Stat{} - if protoimpl.UnsafeEnabled { - mi := &file_stat_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *Stat) Reset() { *m = Stat{} } +func (*Stat) ProtoMessage() {} +func (*Stat) Descriptor() ([]byte, []int) { + return fileDescriptor_01fabdc1b78bd68b, []int{0} } - -func (x *Stat) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *Stat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) } - -func (*Stat) ProtoMessage() {} - -func (x *Stat) ProtoReflect() protoreflect.Message { - mi := &file_stat_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) +func (m *Stat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Stat.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err } - return ms + return b[:n], nil } - return mi.MessageOf(x) } - -// Deprecated: Use Stat.ProtoReflect.Descriptor instead. -func (*Stat) Descriptor() ([]byte, []int) { - return file_stat_proto_rawDescGZIP(), []int{0} +func (m *Stat) XXX_Merge(src proto.Message) { + xxx_messageInfo_Stat.Merge(m, src) +} +func (m *Stat) XXX_Size() int { + return m.Size() } +func (m *Stat) XXX_DiscardUnknown() { + xxx_messageInfo_Stat.DiscardUnknown(m) +} + +var xxx_messageInfo_Stat proto.InternalMessageInfo -func (x *Stat) GetPath() string { - if x != nil { - return x.Path +func (m *Stat) GetPath() string { + if m != nil { + return m.Path } return "" } -func (x *Stat) GetMode() uint32 { - if x != nil { - return x.Mode +func (m *Stat) GetMode() uint32 { + if m != nil { + return m.Mode } return 0 } -func (x *Stat) GetUid() uint32 { - if x != nil { - return x.Uid +func (m *Stat) GetUid() uint32 { + if m != nil { + return m.Uid } return 0 } -func (x *Stat) GetGid() uint32 { - if x != nil { - return x.Gid +func (m *Stat) GetGid() uint32 { + if m != nil { + return m.Gid } return 0 } -func (x *Stat) GetSize() int64 { - if x != nil { - return x.Size +func (m *Stat) GetSize_() int64 { + if m != nil { + return m.Size_ } return 0 } -func (x *Stat) GetModTime() int64 { - if x != nil { - return x.ModTime +func (m *Stat) GetModTime() int64 { + if m != nil { + return m.ModTime } return 0 } -func (x *Stat) GetLinkname() string { - if x != nil { - return x.Linkname +func (m *Stat) GetLinkname() string { + if m != nil { + return m.Linkname } return "" } -func (x *Stat) GetDevmajor() int64 { - if x != nil { - return x.Devmajor +func (m *Stat) GetDevmajor() int64 { + if m != nil { + return m.Devmajor } return 0 } -func (x *Stat) GetDevminor() int64 { - if x != nil { - return x.Devminor +func (m *Stat) GetDevminor() int64 { + if m != nil { + return m.Devminor } return 0 } -func (x *Stat) GetXattrs() map[string][]byte { - if x != nil { - return x.Xattrs +func (m *Stat) GetXattrs() map[string][]byte { + if m != nil { + return m.Xattrs } return nil } -var File_stat_proto protoreflect.FileDescriptor - -var file_stat_proto_rawDesc = []byte{ - 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x66, 0x73, - 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x22, 0xc7, 0x02, 0x0a, 0x04, 0x53, - 0x74, 0x61, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, - 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x10, 0x0a, - 0x03, 0x67, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x67, 0x69, 0x64, 0x12, - 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, - 0x69, 0x7a, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1a, 0x0a, - 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x76, - 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x64, 0x65, 0x76, - 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x76, 0x6d, 0x69, 0x6e, 0x6f, - 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x64, 0x65, 0x76, 0x6d, 0x69, 0x6e, 0x6f, - 0x72, 0x12, 0x36, 0x0a, 0x06, 0x78, 0x61, 0x74, 0x74, 0x72, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1e, 0x2e, 0x66, 0x73, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, - 0x2e, 0x53, 0x74, 0x61, 0x74, 0x2e, 0x58, 0x61, 0x74, 0x74, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x06, 0x78, 0x61, 0x74, 0x74, 0x72, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x58, 0x61, 0x74, - 0x74, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x42, 0x24, 0x5a, 0x22, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x74, 0x6f, 0x6e, 0x69, 0x73, 0x74, 0x69, 0x69, 0x67, 0x69, 0x2f, 0x66, 0x73, - 0x75, 0x74, 0x69, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, +func init() { + proto.RegisterType((*Stat)(nil), "fsutil.types.Stat") + proto.RegisterMapType((map[string][]byte)(nil), "fsutil.types.Stat.XattrsEntry") } -var ( - file_stat_proto_rawDescOnce sync.Once - file_stat_proto_rawDescData = file_stat_proto_rawDesc -) +func init() { proto.RegisterFile("stat.proto", fileDescriptor_01fabdc1b78bd68b) } + +var fileDescriptor_01fabdc1b78bd68b = []byte{ + // 317 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x91, 0xbd, 0x4e, 0xc3, 0x30, + 0x14, 0x85, 0x73, 0x9b, 0xfe, 0xba, 0x45, 0x42, 0x16, 0x83, 0xd5, 0xe1, 0x2a, 0x62, 0xca, 0x94, + 0x01, 0x24, 0xc4, 0xcf, 0x86, 0xc4, 0x0b, 0x04, 0x06, 0xc4, 0x66, 0x64, 0x53, 0x4c, 0x9b, 0xb8, + 0x4a, 0xdc, 0x8a, 0x32, 0xf1, 0x08, 0x3c, 0x06, 0x6f, 0x02, 0x63, 0xc7, 0x8e, 0xd4, 0x5d, 0x18, + 0xfb, 0x08, 0xc8, 0x4e, 0x5b, 0xba, 0x9d, 0xf3, 0x9d, 0x7b, 0x95, 0x9c, 0x6b, 0x42, 0x4a, 0xc3, + 0x4d, 0x32, 0x2e, 0xb4, 0xd1, 0xb4, 0xf7, 0x54, 0x4e, 0x8c, 0x1a, 0x25, 0x66, 0x36, 0x96, 0xe5, + 0xf1, 0x57, 0x8d, 0xd4, 0x6f, 0x0d, 0x37, 0x94, 0x92, 0xfa, 0x98, 0x9b, 0x67, 0x06, 0x11, 0xc4, + 0x9d, 0xd4, 0x6b, 0xc7, 0x32, 0x2d, 0x24, 0xab, 0x45, 0x10, 0x1f, 0xa4, 0x5e, 0xd3, 0x43, 0x12, + 0x4e, 0x94, 0x60, 0xa1, 0x47, 0x4e, 0x3a, 0x32, 0x50, 0x82, 0xd5, 0x2b, 0x32, 0x50, 0xc2, 0xed, + 0x95, 0xea, 0x4d, 0xb2, 0x46, 0x04, 0x71, 0x98, 0x7a, 0x4d, 0x19, 0x69, 0x65, 0x5a, 0xdc, 0xa9, + 0x4c, 0xb2, 0xa6, 0xc7, 0x5b, 0x4b, 0xfb, 0xa4, 0x3d, 0x52, 0xf9, 0x30, 0xe7, 0x99, 0x64, 0x2d, + 0xff, 0xf5, 0x9d, 0x77, 0x99, 0x90, 0xd3, 0x8c, 0xbf, 0xe8, 0x82, 0xb5, 0xfd, 0xda, 0xce, 0x6f, + 0x33, 0x95, 0xeb, 0x82, 0x75, 0xfe, 0x33, 0xe7, 0xe9, 0x19, 0x69, 0xbe, 0x72, 0x63, 0x8a, 0x92, + 0x91, 0x28, 0x8c, 0xbb, 0x27, 0x98, 0xec, 0xb7, 0x4e, 0x5c, 0xe3, 0xe4, 0xde, 0x0f, 0xdc, 0xe4, + 0xa6, 0x98, 0xa5, 0x9b, 0xe9, 0xfe, 0x05, 0xe9, 0xee, 0x61, 0x57, 0x6d, 0x28, 0x67, 0x9b, 0x9b, + 0x38, 0x49, 0x8f, 0x48, 0x63, 0xca, 0x47, 0x93, 0xea, 0x26, 0xbd, 0xb4, 0x32, 0x97, 0xb5, 0x73, + 0xb8, 0xbe, 0x9a, 0x2f, 0x31, 0x58, 0x2c, 0x31, 0x58, 0x2f, 0x11, 0xde, 0x2d, 0xc2, 0xa7, 0x45, + 0xf8, 0xb6, 0x08, 0x73, 0x8b, 0xf0, 0x63, 0x11, 0x7e, 0x2d, 0x06, 0x6b, 0x8b, 0xf0, 0xb1, 0xc2, + 0x60, 0xbe, 0xc2, 0x60, 0xb1, 0xc2, 0xe0, 0xa1, 0xe1, 0x7f, 0xe8, 0xb1, 0xe9, 0xdf, 0xe6, 0xf4, + 0x2f, 0x00, 0x00, 0xff, 0xff, 0x06, 0x97, 0xf3, 0xd7, 0xa9, 0x01, 0x00, 0x00, +} + +func (this *Stat) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Stat) + if !ok { + that2, ok := that.(Stat) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Path != that1.Path { + return false + } + if this.Mode != that1.Mode { + return false + } + if this.Uid != that1.Uid { + return false + } + if this.Gid != that1.Gid { + return false + } + if this.Size_ != that1.Size_ { + return false + } + if this.ModTime != that1.ModTime { + return false + } + if this.Linkname != that1.Linkname { + return false + } + if this.Devmajor != that1.Devmajor { + return false + } + if this.Devminor != that1.Devminor { + return false + } + if len(this.Xattrs) != len(that1.Xattrs) { + return false + } + for i := range this.Xattrs { + if !bytes.Equal(this.Xattrs[i], that1.Xattrs[i]) { + return false + } + } + return true +} +func (this *Stat) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 14) + s = append(s, "&types.Stat{") + s = append(s, "Path: "+fmt.Sprintf("%#v", this.Path)+",\n") + s = append(s, "Mode: "+fmt.Sprintf("%#v", this.Mode)+",\n") + s = append(s, "Uid: "+fmt.Sprintf("%#v", this.Uid)+",\n") + s = append(s, "Gid: "+fmt.Sprintf("%#v", this.Gid)+",\n") + s = append(s, "Size_: "+fmt.Sprintf("%#v", this.Size_)+",\n") + s = append(s, "ModTime: "+fmt.Sprintf("%#v", this.ModTime)+",\n") + s = append(s, "Linkname: "+fmt.Sprintf("%#v", this.Linkname)+",\n") + s = append(s, "Devmajor: "+fmt.Sprintf("%#v", this.Devmajor)+",\n") + s = append(s, "Devminor: "+fmt.Sprintf("%#v", this.Devminor)+",\n") + keysForXattrs := make([]string, 0, len(this.Xattrs)) + for k, _ := range this.Xattrs { + keysForXattrs = append(keysForXattrs, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForXattrs) + mapStringForXattrs := "map[string][]byte{" + for _, k := range keysForXattrs { + mapStringForXattrs += fmt.Sprintf("%#v: %#v,", k, this.Xattrs[k]) + } + mapStringForXattrs += "}" + if this.Xattrs != nil { + s = append(s, "Xattrs: "+mapStringForXattrs+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func valueToGoStringStat(v interface{}, typ string) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) +} +func (m *Stat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} -func file_stat_proto_rawDescGZIP() []byte { - file_stat_proto_rawDescOnce.Do(func() { - file_stat_proto_rawDescData = protoimpl.X.CompressGZIP(file_stat_proto_rawDescData) - }) - return file_stat_proto_rawDescData -} - -var file_stat_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_stat_proto_goTypes = []interface{}{ - (*Stat)(nil), // 0: fsutil.types.Stat - nil, // 1: fsutil.types.Stat.XattrsEntry -} -var file_stat_proto_depIdxs = []int32{ - 1, // 0: fsutil.types.Stat.xattrs:type_name -> fsutil.types.Stat.XattrsEntry - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name -} - -func init() { file_stat_proto_init() } -func file_stat_proto_init() { - if File_stat_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_stat_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Stat); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil +func (m *Stat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Stat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Xattrs) > 0 { + for k := range m.Xattrs { + v := m.Xattrs[k] + baseI := i + if len(v) > 0 { + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintStat(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 } + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintStat(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintStat(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x52 } } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_stat_proto_rawDesc, - NumEnums: 0, - NumMessages: 2, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_stat_proto_goTypes, - DependencyIndexes: file_stat_proto_depIdxs, - MessageInfos: file_stat_proto_msgTypes, - }.Build() - File_stat_proto = out.File - file_stat_proto_rawDesc = nil - file_stat_proto_goTypes = nil - file_stat_proto_depIdxs = nil + if m.Devminor != 0 { + i = encodeVarintStat(dAtA, i, uint64(m.Devminor)) + i-- + dAtA[i] = 0x48 + } + if m.Devmajor != 0 { + i = encodeVarintStat(dAtA, i, uint64(m.Devmajor)) + i-- + dAtA[i] = 0x40 + } + if len(m.Linkname) > 0 { + i -= len(m.Linkname) + copy(dAtA[i:], m.Linkname) + i = encodeVarintStat(dAtA, i, uint64(len(m.Linkname))) + i-- + dAtA[i] = 0x3a + } + if m.ModTime != 0 { + i = encodeVarintStat(dAtA, i, uint64(m.ModTime)) + i-- + dAtA[i] = 0x30 + } + if m.Size_ != 0 { + i = encodeVarintStat(dAtA, i, uint64(m.Size_)) + i-- + dAtA[i] = 0x28 + } + if m.Gid != 0 { + i = encodeVarintStat(dAtA, i, uint64(m.Gid)) + i-- + dAtA[i] = 0x20 + } + if m.Uid != 0 { + i = encodeVarintStat(dAtA, i, uint64(m.Uid)) + i-- + dAtA[i] = 0x18 + } + if m.Mode != 0 { + i = encodeVarintStat(dAtA, i, uint64(m.Mode)) + i-- + dAtA[i] = 0x10 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintStat(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintStat(dAtA []byte, offset int, v uint64) int { + offset -= sovStat(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base } +func (m *Stat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + sovStat(uint64(l)) + } + if m.Mode != 0 { + n += 1 + sovStat(uint64(m.Mode)) + } + if m.Uid != 0 { + n += 1 + sovStat(uint64(m.Uid)) + } + if m.Gid != 0 { + n += 1 + sovStat(uint64(m.Gid)) + } + if m.Size_ != 0 { + n += 1 + sovStat(uint64(m.Size_)) + } + if m.ModTime != 0 { + n += 1 + sovStat(uint64(m.ModTime)) + } + l = len(m.Linkname) + if l > 0 { + n += 1 + l + sovStat(uint64(l)) + } + if m.Devmajor != 0 { + n += 1 + sovStat(uint64(m.Devmajor)) + } + if m.Devminor != 0 { + n += 1 + sovStat(uint64(m.Devminor)) + } + if len(m.Xattrs) > 0 { + for k, v := range m.Xattrs { + _ = k + _ = v + l = 0 + if len(v) > 0 { + l = 1 + len(v) + sovStat(uint64(len(v))) + } + mapEntrySize := 1 + len(k) + sovStat(uint64(len(k))) + l + n += mapEntrySize + 1 + sovStat(uint64(mapEntrySize)) + } + } + return n +} + +func sovStat(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozStat(x uint64) (n int) { + return sovStat(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *Stat) String() string { + if this == nil { + return "nil" + } + keysForXattrs := make([]string, 0, len(this.Xattrs)) + for k, _ := range this.Xattrs { + keysForXattrs = append(keysForXattrs, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForXattrs) + mapStringForXattrs := "map[string][]byte{" + for _, k := range keysForXattrs { + mapStringForXattrs += fmt.Sprintf("%v: %v,", k, this.Xattrs[k]) + } + mapStringForXattrs += "}" + s := strings.Join([]string{`&Stat{`, + `Path:` + fmt.Sprintf("%v", this.Path) + `,`, + `Mode:` + fmt.Sprintf("%v", this.Mode) + `,`, + `Uid:` + fmt.Sprintf("%v", this.Uid) + `,`, + `Gid:` + fmt.Sprintf("%v", this.Gid) + `,`, + `Size_:` + fmt.Sprintf("%v", this.Size_) + `,`, + `ModTime:` + fmt.Sprintf("%v", this.ModTime) + `,`, + `Linkname:` + fmt.Sprintf("%v", this.Linkname) + `,`, + `Devmajor:` + fmt.Sprintf("%v", this.Devmajor) + `,`, + `Devminor:` + fmt.Sprintf("%v", this.Devminor) + `,`, + `Xattrs:` + mapStringForXattrs + `,`, + `}`, + }, "") + return s +} +func valueToStringStat(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *Stat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Stat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Stat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthStat + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStat + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) + } + m.Mode = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Mode |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Uid", wireType) + } + m.Uid = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Uid |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Gid", wireType) + } + m.Gid = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Gid |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Size_", wireType) + } + m.Size_ = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Size_ |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ModTime", wireType) + } + m.ModTime = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ModTime |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Linkname", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthStat + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStat + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Linkname = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Devmajor", wireType) + } + m.Devmajor = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Devmajor |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Devminor", wireType) + } + m.Devminor = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Devminor |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Xattrs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStat + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStat + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Xattrs == nil { + m.Xattrs = make(map[string][]byte) + } + var mapkey string + mapvalue := []byte{} + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthStat + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthStat + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapbyteLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapbyteLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intMapbyteLen := int(mapbyteLen) + if intMapbyteLen < 0 { + return ErrInvalidLengthStat + } + postbytesIndex := iNdEx + intMapbyteLen + if postbytesIndex < 0 { + return ErrInvalidLengthStat + } + if postbytesIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = make([]byte, mapbyteLen) + copy(mapvalue, dAtA[iNdEx:postbytesIndex]) + iNdEx = postbytesIndex + } else { + iNdEx = entryPreIndex + skippy, err := skipStat(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthStat + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Xattrs[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStat(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthStat + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthStat + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipStat(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowStat + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowStat + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowStat + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthStat + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupStat + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthStat + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthStat = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowStat = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupStat = fmt.Errorf("proto: unexpected end of group") +) diff --git a/types/stat.proto b/types/stat.proto index 9b7728c4..4138be69 100644 --- a/types/stat.proto +++ b/types/stat.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package fsutil.types; -option go_package = "github.com/tonistiigi/fsutil/types"; +option go_package = "types"; message Stat { string path = 1; diff --git a/types/wire.pb.go b/types/wire.pb.go index fdf28ef1..9e22269e 100644 --- a/types/wire.pb.go +++ b/types/wire.pb.go @@ -1,242 +1,575 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.1 -// protoc v3.12.4 +// Code generated by protoc-gen-gogo. DO NOT EDIT. // source: wire.proto package types import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" + bytes "bytes" + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" reflect "reflect" - sync "sync" + strconv "strconv" + strings "strings" ) -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type Packet_PacketType int32 const ( - Packet_PACKET_STAT Packet_PacketType = 0 - Packet_PACKET_REQ Packet_PacketType = 1 - Packet_PACKET_DATA Packet_PacketType = 2 - Packet_PACKET_FIN Packet_PacketType = 3 - Packet_PACKET_ERR Packet_PacketType = 4 -) - -// Enum value maps for Packet_PacketType. -var ( - Packet_PacketType_name = map[int32]string{ - 0: "PACKET_STAT", - 1: "PACKET_REQ", - 2: "PACKET_DATA", - 3: "PACKET_FIN", - 4: "PACKET_ERR", - } - Packet_PacketType_value = map[string]int32{ - "PACKET_STAT": 0, - "PACKET_REQ": 1, - "PACKET_DATA": 2, - "PACKET_FIN": 3, - "PACKET_ERR": 4, - } + PACKET_STAT Packet_PacketType = 0 + PACKET_REQ Packet_PacketType = 1 + PACKET_DATA Packet_PacketType = 2 + PACKET_FIN Packet_PacketType = 3 + PACKET_ERR Packet_PacketType = 4 ) -func (x Packet_PacketType) Enum() *Packet_PacketType { - p := new(Packet_PacketType) - *p = x - return p +var Packet_PacketType_name = map[int32]string{ + 0: "PACKET_STAT", + 1: "PACKET_REQ", + 2: "PACKET_DATA", + 3: "PACKET_FIN", + 4: "PACKET_ERR", } -func (x Packet_PacketType) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +var Packet_PacketType_value = map[string]int32{ + "PACKET_STAT": 0, + "PACKET_REQ": 1, + "PACKET_DATA": 2, + "PACKET_FIN": 3, + "PACKET_ERR": 4, } -func (Packet_PacketType) Descriptor() protoreflect.EnumDescriptor { - return file_wire_proto_enumTypes[0].Descriptor() -} - -func (Packet_PacketType) Type() protoreflect.EnumType { - return &file_wire_proto_enumTypes[0] -} - -func (x Packet_PacketType) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use Packet_PacketType.Descriptor instead. func (Packet_PacketType) EnumDescriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{0, 0} + return fileDescriptor_f2dcdddcdf68d8e0, []int{0, 0} } type Packet struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - Type Packet_PacketType `protobuf:"varint,1,opt,name=type,proto3,enum=fsutil.types.Packet_PacketType" json:"type,omitempty"` Stat *Stat `protobuf:"bytes,2,opt,name=stat,proto3" json:"stat,omitempty"` ID uint32 `protobuf:"varint,3,opt,name=ID,proto3" json:"ID,omitempty"` Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` } -func (x *Packet) Reset() { - *x = Packet{} - if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (m *Packet) Reset() { *m = Packet{} } +func (*Packet) ProtoMessage() {} +func (*Packet) Descriptor() ([]byte, []int) { + return fileDescriptor_f2dcdddcdf68d8e0, []int{0} +} +func (m *Packet) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Packet) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Packet.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } } - -func (x *Packet) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *Packet) XXX_Merge(src proto.Message) { + xxx_messageInfo_Packet.Merge(m, src) +} +func (m *Packet) XXX_Size() int { + return m.Size() +} +func (m *Packet) XXX_DiscardUnknown() { + xxx_messageInfo_Packet.DiscardUnknown(m) } -func (*Packet) ProtoMessage() {} +var xxx_messageInfo_Packet proto.InternalMessageInfo -func (x *Packet) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms +func (m *Packet) GetType() Packet_PacketType { + if m != nil { + return m.Type } - return mi.MessageOf(x) + return PACKET_STAT } -// Deprecated: Use Packet.ProtoReflect.Descriptor instead. -func (*Packet) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{0} +func (m *Packet) GetStat() *Stat { + if m != nil { + return m.Stat + } + return nil } -func (x *Packet) GetType() Packet_PacketType { - if x != nil { - return x.Type +func (m *Packet) GetID() uint32 { + if m != nil { + return m.ID } - return Packet_PACKET_STAT + return 0 } -func (x *Packet) GetStat() *Stat { - if x != nil { - return x.Stat +func (m *Packet) GetData() []byte { + if m != nil { + return m.Data } return nil } -func (x *Packet) GetID() uint32 { - if x != nil { - return x.ID +func init() { + proto.RegisterEnum("fsutil.types.Packet_PacketType", Packet_PacketType_name, Packet_PacketType_value) + proto.RegisterType((*Packet)(nil), "fsutil.types.Packet") +} + +func init() { proto.RegisterFile("wire.proto", fileDescriptor_f2dcdddcdf68d8e0) } + +var fileDescriptor_f2dcdddcdf68d8e0 = []byte{ + // 276 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2a, 0xcf, 0x2c, 0x4a, + 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x49, 0x2b, 0x2e, 0x2d, 0xc9, 0xcc, 0xd1, 0x2b, + 0xa9, 0x2c, 0x48, 0x2d, 0x96, 0xe2, 0x2a, 0x2e, 0x49, 0x2c, 0x81, 0xc8, 0x28, 0xbd, 0x64, 0xe4, + 0x62, 0x0b, 0x48, 0x4c, 0xce, 0x4e, 0x2d, 0x11, 0x32, 0xe6, 0x62, 0x01, 0xc9, 0x4b, 0x30, 0x2a, + 0x30, 0x6a, 0xf0, 0x19, 0xc9, 0xeb, 0x21, 0xeb, 0xd1, 0x83, 0xa8, 0x81, 0x52, 0x21, 0x95, 0x05, + 0xa9, 0x41, 0x60, 0xc5, 0x42, 0x6a, 0x5c, 0x2c, 0x20, 0xd3, 0x24, 0x98, 0x14, 0x18, 0x35, 0xb8, + 0x8d, 0x84, 0x50, 0x35, 0x05, 0x97, 0x24, 0x96, 0x04, 0x81, 0xe5, 0x85, 0xf8, 0xb8, 0x98, 0x3c, + 0x5d, 0x24, 0x98, 0x15, 0x18, 0x35, 0x78, 0x83, 0x98, 0x3c, 0x5d, 0x84, 0x84, 0xb8, 0x58, 0x52, + 0x12, 0x4b, 0x12, 0x25, 0x58, 0x14, 0x18, 0x35, 0x78, 0x82, 0xc0, 0x6c, 0xa5, 0x38, 0x2e, 0x2e, + 0x84, 0xf9, 0x42, 0xfc, 0x5c, 0xdc, 0x01, 0x8e, 0xce, 0xde, 0xae, 0x21, 0xf1, 0xc1, 0x21, 0x8e, + 0x21, 0x02, 0x0c, 0x42, 0x7c, 0x5c, 0x5c, 0x50, 0x81, 0x20, 0xd7, 0x40, 0x01, 0x46, 0x24, 0x05, + 0x2e, 0x8e, 0x21, 0x8e, 0x02, 0x4c, 0x48, 0x0a, 0xdc, 0x3c, 0xfd, 0x04, 0x98, 0x91, 0xf8, 0xae, + 0x41, 0x41, 0x02, 0x2c, 0x4e, 0xd6, 0x17, 0x1e, 0xca, 0x31, 0xdc, 0x78, 0x28, 0xc7, 0xf0, 0xe1, + 0xa1, 0x1c, 0x63, 0xc3, 0x23, 0x39, 0xc6, 0x15, 0x8f, 0xe4, 0x18, 0x4f, 0x3c, 0x92, 0x63, 0xbc, + 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x17, 0x8f, 0xe4, 0x18, 0x3e, 0x3c, 0x92, 0x63, + 0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0x58, 0xc1, + 0x7e, 0x49, 0x62, 0x03, 0x87, 0x97, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x3f, 0x9d, 0xe3, 0x51, + 0x57, 0x01, 0x00, 0x00, +} + +func (x Packet_PacketType) String() string { + s, ok := Packet_PacketType_name[int32(x)] + if ok { + return s } - return 0 + return strconv.Itoa(int(x)) } +func (this *Packet) Equal(that interface{}) bool { + if that == nil { + return this == nil + } -func (x *Packet) GetData() []byte { - if x != nil { - return x.Data + that1, ok := that.(*Packet) + if !ok { + that2, ok := that.(Packet) + if ok { + that1 = &that2 + } else { + return false + } } - return nil + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Type != that1.Type { + return false + } + if !this.Stat.Equal(that1.Stat) { + return false + } + if this.ID != that1.ID { + return false + } + if !bytes.Equal(this.Data, that1.Data) { + return false + } + return true +} +func (this *Packet) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 8) + s = append(s, "&types.Packet{") + s = append(s, "Type: "+fmt.Sprintf("%#v", this.Type)+",\n") + if this.Stat != nil { + s = append(s, "Stat: "+fmt.Sprintf("%#v", this.Stat)+",\n") + } + s = append(s, "ID: "+fmt.Sprintf("%#v", this.ID)+",\n") + s = append(s, "Data: "+fmt.Sprintf("%#v", this.Data)+",\n") + s = append(s, "}") + return strings.Join(s, "") +} +func valueToGoStringWire(v interface{}, typ string) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) +} +func (m *Packet) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil } -var File_wire_proto protoreflect.FileDescriptor - -var file_wire_proto_rawDesc = []byte{ - 0x0a, 0x0a, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x66, 0x73, - 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x1a, 0x0a, 0x73, 0x74, 0x61, 0x74, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe9, 0x01, 0x0a, 0x06, 0x50, 0x61, 0x63, 0x6b, 0x65, - 0x74, 0x12, 0x33, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x1f, 0x2e, 0x66, 0x73, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x50, - 0x61, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x73, 0x74, 0x61, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x66, 0x73, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x74, 0x79, - 0x70, 0x65, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x52, 0x04, 0x73, 0x74, 0x61, 0x74, 0x12, 0x0e, - 0x0a, 0x02, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, - 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, - 0x74, 0x61, 0x22, 0x5e, 0x0a, 0x0a, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x41, 0x43, 0x4b, 0x45, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x10, - 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x41, 0x43, 0x4b, 0x45, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x10, - 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x41, 0x43, 0x4b, 0x45, 0x54, 0x5f, 0x44, 0x41, 0x54, 0x41, - 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x41, 0x43, 0x4b, 0x45, 0x54, 0x5f, 0x46, 0x49, 0x4e, - 0x10, 0x03, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x41, 0x43, 0x4b, 0x45, 0x54, 0x5f, 0x45, 0x52, 0x52, - 0x10, 0x04, 0x42, 0x24, 0x5a, 0x22, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x74, 0x6f, 0x6e, 0x69, 0x73, 0x74, 0x69, 0x69, 0x67, 0x69, 0x2f, 0x66, 0x73, 0x75, 0x74, - 0x69, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +func (m *Packet) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) } -var ( - file_wire_proto_rawDescOnce sync.Once - file_wire_proto_rawDescData = file_wire_proto_rawDesc -) +func (m *Packet) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintWire(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x22 + } + if m.ID != 0 { + i = encodeVarintWire(dAtA, i, uint64(m.ID)) + i-- + dAtA[i] = 0x18 + } + if m.Stat != nil { + { + size, err := m.Stat.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintWire(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Type != 0 { + i = encodeVarintWire(dAtA, i, uint64(m.Type)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} -func file_wire_proto_rawDescGZIP() []byte { - file_wire_proto_rawDescOnce.Do(func() { - file_wire_proto_rawDescData = protoimpl.X.CompressGZIP(file_wire_proto_rawDescData) - }) - return file_wire_proto_rawDescData -} - -var file_wire_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_wire_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_wire_proto_goTypes = []interface{}{ - (Packet_PacketType)(0), // 0: fsutil.types.Packet.PacketType - (*Packet)(nil), // 1: fsutil.types.Packet - (*Stat)(nil), // 2: fsutil.types.Stat -} -var file_wire_proto_depIdxs = []int32{ - 0, // 0: fsutil.types.Packet.type:type_name -> fsutil.types.Packet.PacketType - 2, // 1: fsutil.types.Packet.stat:type_name -> fsutil.types.Stat - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name -} - -func init() { file_wire_proto_init() } -func file_wire_proto_init() { - if File_wire_proto != nil { - return - } - file_stat_proto_init() - if !protoimpl.UnsafeEnabled { - file_wire_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Packet); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil +func encodeVarintWire(dAtA []byte, offset int, v uint64) int { + offset -= sovWire(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Packet) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Type != 0 { + n += 1 + sovWire(uint64(m.Type)) + } + if m.Stat != nil { + l = m.Stat.Size() + n += 1 + l + sovWire(uint64(l)) + } + if m.ID != 0 { + n += 1 + sovWire(uint64(m.ID)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + sovWire(uint64(l)) + } + return n +} + +func sovWire(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozWire(x uint64) (n int) { + return sovWire(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *Packet) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&Packet{`, + `Type:` + fmt.Sprintf("%v", this.Type) + `,`, + `Stat:` + strings.Replace(fmt.Sprintf("%v", this.Stat), "Stat", "Stat", 1) + `,`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `Data:` + fmt.Sprintf("%v", this.Data) + `,`, + `}`, + }, "") + return s +} +func valueToStringWire(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *Packet) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWire + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Packet: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Packet: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + m.Type = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWire + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Type |= Packet_PacketType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stat", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWire + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthWire + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWire + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Stat == nil { + m.Stat = &Stat{} + } + if err := m.Stat.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + m.ID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWire + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ID |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWire + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthWire + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthWire + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipWire(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthWire + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthWire + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipWire(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowWire + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break } } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowWire + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowWire + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthWire + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupWire + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthWire + } + if depth == 0 { + return iNdEx, nil + } } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_wire_proto_rawDesc, - NumEnums: 1, - NumMessages: 1, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_wire_proto_goTypes, - DependencyIndexes: file_wire_proto_depIdxs, - EnumInfos: file_wire_proto_enumTypes, - MessageInfos: file_wire_proto_msgTypes, - }.Build() - File_wire_proto = out.File - file_wire_proto_rawDesc = nil - file_wire_proto_goTypes = nil - file_wire_proto_depIdxs = nil + return 0, io.ErrUnexpectedEOF } + +var ( + ErrInvalidLengthWire = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowWire = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupWire = fmt.Errorf("proto: unexpected end of group") +) diff --git a/types/wire.proto b/types/wire.proto index a00300b9..3e85000c 100644 --- a/types/wire.proto +++ b/types/wire.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package fsutil.types; -option go_package = "github.com/tonistiigi/fsutil/types"; +option go_package = "types"; import "stat.proto"; From 9193209c73cdbf9e113b00f5a6e0330bb67661d7 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Fri, 5 Jan 2024 14:15:43 +0100 Subject: [PATCH 10/39] chore: run once on vagrant init Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- .github/workflows/ci.yml | 6 ++++-- hack/Vagrantfile.freebsd13 | 33 ++++++++++----------------------- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bdfc06f6..159a34da 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -120,7 +120,9 @@ jobs: ${{ runner.os }}-vagrant- - name: Set up vagrant - run: vagrant up + run: | + vagrant up --no-tty - name: Test - run: vagrant ssh -- "cd /vagrant; go test -buildvcs=false ./..." + run: | + vagrant ssh -- "cd /vagrant; go test -buildvcs=false ./..." diff --git a/hack/Vagrantfile.freebsd13 b/hack/Vagrantfile.freebsd13 index 0014347e..4a4b027d 100644 --- a/hack/Vagrantfile.freebsd13 +++ b/hack/Vagrantfile.freebsd13 @@ -1,31 +1,18 @@ -# This code is taken from the Vagrantfile from libjail-rs -# https://github.com/fubarnetes/libjail-rs/blob/727353bd6565c5e7a9be2664258d0197a1c8bb35/Vagrantfile -# licensed under BSD-3 Clause License: -# BSD 3-Clause License - -# Copyright (c) 2018, Fabian Freyer All rights reserved. - -# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -# * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -# * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -# * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# -*- mode: ruby -*- +# vi: set ft=ruby : Vagrant.configure("2") do |config| - # Stable version - # config.vm.define "fbsd_13_2" do |fbsd_13_2| fbsd_13_2.vm.box = "freebsd/FreeBSD-13.2-STABLE" end - config.vm.synced_folder ".", "/vagrant", type: "rsync", rsync__auto: true + config.vm.synced_folder ".", "/vagrant", type: "rsync" + config.ssh.keep_alive = true - config.vm.provision "shell", inline: <<-SHELL - pkg bootstrap - pkg install -y go - SHELL + config.vm.provision "init", type: "shell", run: "once" do |sh| + sh.inline = <<~SHELL + pkg bootstrap + pkg install -y go + SHELL + end end From 152fee1d6339fae4597abfaf37cf053604324a0d Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Fri, 5 Jan 2024 14:16:21 +0100 Subject: [PATCH 11/39] ci: increase vm boot time to 15m Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- hack/Vagrantfile.freebsd13 | 1 + 1 file changed, 1 insertion(+) diff --git a/hack/Vagrantfile.freebsd13 b/hack/Vagrantfile.freebsd13 index 4a4b027d..7885e148 100644 --- a/hack/Vagrantfile.freebsd13 +++ b/hack/Vagrantfile.freebsd13 @@ -6,6 +6,7 @@ Vagrant.configure("2") do |config| fbsd_13_2.vm.box = "freebsd/FreeBSD-13.2-STABLE" end + config.vm.boot_timeout = 900 config.vm.synced_folder ".", "/vagrant", type: "rsync" config.ssh.keep_alive = true From c586cb2b8cdecf0deb24635cf149619ea51c022a Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Fri, 5 Jan 2024 14:18:40 +0100 Subject: [PATCH 12/39] ci: switch to macos-13 runner for freebsd job Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- .github/workflows/ci.yml | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 159a34da..ccc3c9e1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -102,8 +102,7 @@ jobs: go test ./... test-freebsd-amd64: - runs-on: macos-latest - timeout-minutes: 60 + runs-on: macos-13 env: VAGRANT_VAGRANTFILE: hack/Vagrantfile.freebsd13 steps: @@ -118,6 +117,22 @@ jobs: key: ${{ runner.os }}-vagrant-${{ hashFiles('hack/Vagrantfile.freebsd13') }} restore-keys: | ${{ runner.os }}-vagrant- + - + name: Install Vagrant and VirtualBox + run: | + set -x + brew tap hashicorp/tap + brew install hashicorp/tap/hashicorp-vagrant + brew install --cask virtualbox + env: + HOMEBREW_NO_AUTO_UPDATE: 1 + HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1 + - + name: Check versions + run: | + set -x + vagrant --version + VBoxManage -v - name: Set up vagrant run: | From 30180b0c3b453c06fd3e7d47f66767c0bc6cc70e Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Fri, 5 Jan 2024 14:19:27 +0100 Subject: [PATCH 13/39] ci: retry logic for freebsd test step Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- .github/workflows/ci.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ccc3c9e1..fa86cf6e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -139,5 +139,9 @@ jobs: vagrant up --no-tty - name: Test - run: | - vagrant ssh -- "cd /vagrant; go test -buildvcs=false ./..." + uses: nick-fields/retry@14672906e672a08bd6eeb15720e9ed3ce869cdd4 # v2.9.0 + with: + timeout_minutes: 20 + max_attempts: 5 + command: | + vagrant ssh -- "cd /vagrant; go test -buildvcs=false ./..." From 330b1ffbd967055f91c50d0f5817653cad3ebd33 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Wed, 7 Feb 2024 13:19:29 +0100 Subject: [PATCH 14/39] ci: add windows to test matrix Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- .github/workflows/ci.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fa86cf6e..a8489553 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,11 +79,14 @@ jobs: with: targets: test - test-macos: - runs-on: macos-latest + test-os: + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: + os: + - macos-latest + - windows-latest go: - "1.19" - "1.20" From b843da975eab05a701f9f0682b00f44acbb7b97b Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Wed, 24 Jan 2024 16:45:20 +0100 Subject: [PATCH 15/39] diskwriter: fix test scope Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- diskwriter_test.go | 104 ----------------------------------- diskwriter_unix_test.go | 117 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 104 deletions(-) create mode 100644 diskwriter_unix_test.go diff --git a/diskwriter_test.go b/diskwriter_test.go index 4b6edb3b..90f3d525 100644 --- a/diskwriter_test.go +++ b/diskwriter_test.go @@ -7,15 +7,12 @@ import ( "os" "path/filepath" "sync" - "syscall" "testing" "time" "github.com/opencontainers/go-digest" "github.com/pkg/errors" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "golang.org/x/sys/unix" ) // requiresRoot skips tests that require root @@ -163,107 +160,6 @@ file foo2 dt, err := os.ReadFile(filepath.Join(dest, "foo")) assert.NoError(t, err) assert.Equal(t, []byte("mydata"), dt) - -} - -func TestWalkerWriterAsync(t *testing.T) { - d, err := tmpDir(changeStream([]string{ - "ADD foo dir", - "ADD foo/foo1 file data1", - "ADD foo/foo2 file data2", - "ADD foo/foo3 file data3", - "ADD foo/foo4 file >foo/foo3", - "ADD foo5 file data5", - })) - assert.NoError(t, err) - defer os.RemoveAll(d) - - dest := t.TempDir() - - dw, err := NewDiskWriter(context.TODO(), dest, DiskWriterOpt{ - AsyncDataCb: newWriteToFunc(d, 300*time.Millisecond), - }) - assert.NoError(t, err) - - st := time.Now() - - err = Walk(context.Background(), d, nil, readAsAdd(dw.HandleChange)) - assert.NoError(t, err) - - err = dw.Wait(context.TODO()) - assert.NoError(t, err) - - dt, err := os.ReadFile(filepath.Join(dest, "foo/foo3")) - assert.NoError(t, err) - assert.Equal(t, "data3", string(dt)) - - dt, err = os.ReadFile(filepath.Join(dest, "foo/foo4")) - assert.NoError(t, err) - assert.Equal(t, "data3", string(dt)) - - fi1, err := os.Lstat(filepath.Join(dest, "foo/foo3")) - assert.NoError(t, err) - fi2, err := os.Lstat(filepath.Join(dest, "foo/foo4")) - assert.NoError(t, err) - stat1, ok1 := fi1.Sys().(*syscall.Stat_t) - stat2, ok2 := fi2.Sys().(*syscall.Stat_t) - if ok1 && ok2 { - assert.Equal(t, stat1.Ino, stat2.Ino) - } - - dt, err = os.ReadFile(filepath.Join(dest, "foo5")) - assert.NoError(t, err) - assert.Equal(t, "data5", string(dt)) - - duration := time.Since(st) - assert.True(t, duration < 500*time.Millisecond) -} - -func TestWalkerWriterDevices(t *testing.T) { - requiresRoot(t) - - d, err := tmpDir(changeStream([]string{ - "ADD foo dir", - "ADD foo/foo1 file data1", - })) - require.NoError(t, err) - - err = unix.Mknod(filepath.Join(d, "foo/block"), syscall.S_IFBLK|0600, mkdev(2, 3)) - require.NoError(t, err) - - err = unix.Mknod(filepath.Join(d, "foo/char"), syscall.S_IFCHR|0400, mkdev(1, 9)) - require.NoError(t, err) - - dest := t.TempDir() - - dw, err := NewDiskWriter(context.TODO(), dest, DiskWriterOpt{ - SyncDataCb: newWriteToFunc(d, 0), - }) - assert.NoError(t, err) - - err = Walk(context.Background(), d, nil, readAsAdd(dw.HandleChange)) - assert.NoError(t, err) - - err = dw.Wait(context.TODO()) - assert.NoError(t, err) - - fi, err := os.Lstat(filepath.Join(dest, "foo/char")) - require.NoError(t, err) - - stat, ok := fi.Sys().(*syscall.Stat_t) - require.True(t, ok) - - assert.Equal(t, uint64(1), stat.Rdev>>8) - assert.Equal(t, uint64(9), stat.Rdev&0xff) - - fi, err = os.Lstat(filepath.Join(dest, "foo/block")) - require.NoError(t, err) - - stat, ok = fi.Sys().(*syscall.Stat_t) - require.True(t, ok) - - assert.Equal(t, uint64(2), stat.Rdev>>8) - assert.Equal(t, uint64(3), stat.Rdev&0xff) } func readAsAdd(f HandleChangeFn) filepath.WalkFunc { diff --git a/diskwriter_unix_test.go b/diskwriter_unix_test.go new file mode 100644 index 00000000..7cd6eb55 --- /dev/null +++ b/diskwriter_unix_test.go @@ -0,0 +1,117 @@ +//go:build !windows +// +build !windows + +package fsutil + +import ( + "context" + "os" + "path/filepath" + "syscall" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/sys/unix" +) + +func TestWalkerWriterAsync(t *testing.T) { + d, err := tmpDir(changeStream([]string{ + "ADD foo dir", + "ADD foo/foo1 file data1", + "ADD foo/foo2 file data2", + "ADD foo/foo3 file data3", + "ADD foo/foo4 file >foo/foo3", + "ADD foo5 file data5", + })) + assert.NoError(t, err) + defer os.RemoveAll(d) + + dest := t.TempDir() + + dw, err := NewDiskWriter(context.TODO(), dest, DiskWriterOpt{ + AsyncDataCb: newWriteToFunc(d, 300*time.Millisecond), + }) + assert.NoError(t, err) + + st := time.Now() + + err = Walk(context.Background(), d, nil, readAsAdd(dw.HandleChange)) + assert.NoError(t, err) + + err = dw.Wait(context.TODO()) + assert.NoError(t, err) + + dt, err := os.ReadFile(filepath.Join(dest, "foo/foo3")) + assert.NoError(t, err) + assert.Equal(t, "data3", string(dt)) + + dt, err = os.ReadFile(filepath.Join(dest, "foo/foo4")) + assert.NoError(t, err) + assert.Equal(t, "data3", string(dt)) + + fi1, err := os.Lstat(filepath.Join(dest, "foo/foo3")) + assert.NoError(t, err) + fi2, err := os.Lstat(filepath.Join(dest, "foo/foo4")) + assert.NoError(t, err) + stat1, ok1 := fi1.Sys().(*syscall.Stat_t) + stat2, ok2 := fi2.Sys().(*syscall.Stat_t) + if ok1 && ok2 { + assert.Equal(t, stat1.Ino, stat2.Ino) + } + + dt, err = os.ReadFile(filepath.Join(dest, "foo5")) + assert.NoError(t, err) + assert.Equal(t, "data5", string(dt)) + + duration := time.Since(st) + assert.True(t, duration < 500*time.Millisecond) +} + +func TestWalkerWriterDevices(t *testing.T) { + requiresRoot(t) + + d, err := tmpDir(changeStream([]string{ + "ADD foo dir", + "ADD foo/foo1 file data1", + })) + require.NoError(t, err) + + err = unix.Mknod(filepath.Join(d, "foo/block"), syscall.S_IFBLK|0600, mkdev(2, 3)) + require.NoError(t, err) + + err = unix.Mknod(filepath.Join(d, "foo/char"), syscall.S_IFCHR|0400, mkdev(1, 9)) + require.NoError(t, err) + + dest := t.TempDir() + + dw, err := NewDiskWriter(context.TODO(), dest, DiskWriterOpt{ + SyncDataCb: newWriteToFunc(d, 0), + }) + assert.NoError(t, err) + + err = Walk(context.Background(), d, nil, readAsAdd(dw.HandleChange)) + assert.NoError(t, err) + + err = dw.Wait(context.TODO()) + assert.NoError(t, err) + + fi, err := os.Lstat(filepath.Join(dest, "foo/char")) + require.NoError(t, err) + + stat, ok := fi.Sys().(*syscall.Stat_t) + require.True(t, ok) + + assert.Equal(t, uint64(1), stat.Rdev>>8) + assert.Equal(t, uint64(9), stat.Rdev&0xff) + + fi, err = os.Lstat(filepath.Join(dest, "foo/block")) + require.NoError(t, err) + + stat, ok = fi.Sys().(*syscall.Stat_t) + require.True(t, ok) + + assert.Equal(t, uint64(2), stat.Rdev>>8) + assert.Equal(t, uint64(3), stat.Rdev&0xff) +} From 3d01fa58d7d052226c0021f5c3a03b58455bbf11 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Mon, 29 Jan 2024 08:39:55 +0100 Subject: [PATCH 16/39] fix file path separator Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- diff_containerd.go | 7 +- diskwriter_test.go | 16 ++-- filter.go | 2 +- filter_test.go | 179 ++++++++++++++++++++++++++------------------- followlinks.go | 8 +- fs_test.go | 7 +- receive_test.go | 139 ++++++++++++++++++++++++++--------- stat.go | 2 +- validator.go | 22 +++--- validator_test.go | 3 +- 10 files changed, 239 insertions(+), 146 deletions(-) diff --git a/diff_containerd.go b/diff_containerd.go index d8619abf..84fdc89d 100644 --- a/diff_containerd.go +++ b/diff_containerd.go @@ -5,6 +5,7 @@ import ( "context" "io" "os" + "path/filepath" "strings" "github.com/tonistiigi/fsutil/types" @@ -110,7 +111,7 @@ func doubleWalkDiff(ctx context.Context, changeFn ChangeFunc, a, b walkerFn, fil if filter != nil { filter(f2.path, &statCopy) } - f2copy = ¤tPath{path: f2.path, stat: &statCopy} + f2copy = ¤tPath{path: filepath.FromSlash(f2.path), stat: &statCopy} } k, p := pathChange(f1, f2copy) switch k { @@ -127,7 +128,7 @@ func doubleWalkDiff(ctx context.Context, changeFn ChangeFunc, a, b walkerFn, fil f1 = nil continue } else if rmdir == "" && f1.stat.IsDir() { - rmdir = f1.path + string(os.PathSeparator) + rmdir = f1.path + string(filepath.Separator) } else if rmdir != "" { rmdir = "" } @@ -138,7 +139,7 @@ func doubleWalkDiff(ctx context.Context, changeFn ChangeFunc, a, b walkerFn, fil return err } if f1.stat.IsDir() && !f2copy.stat.IsDir() { - rmdir = f1.path + string(os.PathSeparator) + rmdir = f1.path + string(filepath.Separator) } else if rmdir != "" { rmdir = "" } diff --git a/diskwriter_test.go b/diskwriter_test.go index 90f3d525..ed8e0e7d 100644 --- a/diskwriter_test.go +++ b/diskwriter_test.go @@ -51,12 +51,12 @@ func TestWriterSimple(t *testing.T) { err = Walk(context.Background(), dest, nil, bufWalk(b)) assert.NoError(t, err) - assert.Equal(t, string(b.Bytes()), `dir bar + assert.Equal(t, `dir bar file bar/foo symlink:../foo bar/foo2 file foo file foo2 >foo -`) +`, b.String()) } @@ -88,9 +88,9 @@ func TestWriterFileToDir(t *testing.T) { err = Walk(context.Background(), dest, nil, bufWalk(b)) assert.NoError(t, err) - assert.Equal(t, string(b.Bytes()), `dir foo + assert.Equal(t, `dir foo file foo/bar -`) +`, b.String()) } func TestWriterDirToFile(t *testing.T) { @@ -121,8 +121,8 @@ func TestWriterDirToFile(t *testing.T) { err = Walk(context.Background(), dest, nil, bufWalk(b)) assert.NoError(t, err) - assert.Equal(t, string(b.Bytes()), `file foo -`) + assert.Equal(t, `file foo +`, b.String()) } func TestWalkerWriterSimple(t *testing.T) { @@ -150,12 +150,12 @@ func TestWalkerWriterSimple(t *testing.T) { err = Walk(context.Background(), dest, nil, bufWalk(b)) assert.NoError(t, err) - assert.Equal(t, string(b.Bytes()), `dir bar + assert.Equal(t, filepath.FromSlash(`dir bar file bar/foo symlink:../foo bar/foo2 file foo file foo2 -`) +`), b.String()) dt, err := os.ReadFile(filepath.Join(dest, "foo")) assert.NoError(t, err) diff --git a/filter.go b/filter.go index 6585db4b..5257c285 100644 --- a/filter.go +++ b/filter.go @@ -96,7 +96,7 @@ func NewFilterFS(fs FS, opt *FilterOpt) (FS, error) { } patternChars := "*[]?^" - if os.PathSeparator != '\\' { + if filepath.Separator != '\\' { patternChars += `\` } diff --git a/filter_test.go b/filter_test.go index 5d25ffdd..2e0619be 100644 --- a/filter_test.go +++ b/filter_test.go @@ -30,10 +30,9 @@ func TestWalkerSimple(t *testing.T) { err = Walk(context.Background(), d, nil, bufWalk(b)) assert.NoError(t, err) - assert.Equal(t, string(b.Bytes()), `file foo + assert.Equal(t, `file foo file foo2 -`) - +`, b.String()) } func TestInvalidExcludePatterns(t *testing.T) { @@ -62,9 +61,9 @@ func TestWalkerInclude(t *testing.T) { }, bufWalk(b)) assert.NoError(t, err) - assert.Equal(t, `dir bar + assert.Equal(t, filepath.FromSlash(`dir bar file bar/foo -`, string(b.Bytes())) +`), b.String()) b.Reset() err = Walk(context.Background(), d, &FilterOpt{ @@ -72,9 +71,9 @@ file bar/foo }, bufWalk(b)) assert.NoError(t, err) - assert.Equal(t, `dir bar + assert.Equal(t, filepath.FromSlash(`dir bar file bar/foo -`, string(b.Bytes())) +`), b.String()) b.Reset() err = Walk(context.Background(), d, &FilterOpt{ @@ -82,9 +81,9 @@ file bar/foo }, bufWalk(b)) assert.NoError(t, err) - assert.Equal(t, `dir bar + assert.Equal(t, filepath.FromSlash(`dir bar file bar/foo -`, string(b.Bytes())) +`), b.String()) b.Reset() err = Walk(context.Background(), d, &FilterOpt{ @@ -92,9 +91,9 @@ file bar/foo }, bufWalk(b)) assert.NoError(t, err) - assert.Equal(t, `dir bar + assert.Equal(t, filepath.FromSlash(`dir bar file bar/foo -`, string(b.Bytes())) +`), b.String()) b.Reset() err = Walk(context.Background(), d, &FilterOpt{ @@ -111,7 +110,7 @@ file bar/foo assert.NoError(t, err) assert.Equal(t, `file foo2 -`, string(b.Bytes())) +`, b.String()) b.Reset() err = Walk(context.Background(), d, &FilterOpt{ @@ -119,9 +118,9 @@ file bar/foo }, bufWalk(b)) assert.NoError(t, err) - assert.Equal(t, `dir bar + assert.Equal(t, filepath.FromSlash(`dir bar file bar/foo -`, string(b.Bytes())) +`), b.String()) b.Reset() err = Walk(context.Background(), d, &FilterOpt{ @@ -129,9 +128,9 @@ file bar/foo }, bufWalk(b)) assert.NoError(t, err) - assert.Equal(t, `dir bar + assert.Equal(t, filepath.FromSlash(`dir bar file bar/foo -`, string(b.Bytes())) +`), b.String()) b.Reset() err = Walk(context.Background(), d, &FilterOpt{ @@ -139,9 +138,9 @@ file bar/foo }, bufWalk(b)) assert.NoError(t, err) - assert.Equal(t, `dir bar + assert.Equal(t, filepath.FromSlash(`dir bar file bar/foo -`, string(b.Bytes())) +`), b.String()) } func TestWalkerExclude(t *testing.T) { @@ -159,24 +158,40 @@ func TestWalkerExclude(t *testing.T) { }, bufWalk(b)) assert.NoError(t, err) - assert.Equal(t, `file bar + assert.Equal(t, filepath.FromSlash(`file bar dir foo file foo/bar2 -`, string(b.Bytes())) +`), b.String()) } func TestWalkerFollowLinks(t *testing.T) { - d, err := tmpDir(changeStream([]string{ - "ADD bar file", - "ADD foo dir", - "ADD foo/l1 symlink /baz/one", - "ADD foo/l2 symlink /baz/two", - "ADD baz dir", - "ADD baz/one file", - "ADD baz/two symlink ../bax", - "ADD bax file", - "ADD bay file", // not included - })) + var d string + var err error + if runtime.GOOS == "windows" { + d, err = tmpDir(changeStream([]string{ + "ADD bar file", + "ADD foo dir", + "ADD foo/l1 symlink C:/baz/one", + "ADD foo/l2 symlink C:/baz/two", + "ADD baz dir", + "ADD baz/one file", + "ADD baz/two symlink ../bax", + "ADD bax file", + "ADD bay file", // not included + })) + } else { + d, err = tmpDir(changeStream([]string{ + "ADD bar file", + "ADD foo dir", + "ADD foo/l1 symlink /baz/one", + "ADD foo/l2 symlink /baz/two", + "ADD baz dir", + "ADD baz/one file", + "ADD baz/two symlink ../bax", + "ADD bax file", + "ADD bay file", // not included + })) + } assert.NoError(t, err) defer os.RemoveAll(d) b := &bytes.Buffer{} @@ -185,7 +200,18 @@ func TestWalkerFollowLinks(t *testing.T) { }, bufWalk(b)) assert.NoError(t, err) - assert.Equal(t, `file bar + if runtime.GOOS == "windows" { + assert.Equal(t, filepath.FromSlash(`file bar +file bax +dir baz +file baz/one +symlink:../bax baz/two +dir foo +symlink:C:/baz/one foo/l1 +symlink:C:/baz/two foo/l2 +`), b.String()) + } else { + assert.Equal(t, filepath.FromSlash(`file bar file bax dir baz file baz/one @@ -193,7 +219,8 @@ symlink:../bax baz/two dir foo symlink:/baz/one foo/l1 symlink:/baz/two foo/l2 -`, string(b.Bytes())) +`), b.String()) + } } func TestWalkerFollowLinksToRoot(t *testing.T) { @@ -212,12 +239,12 @@ func TestWalkerFollowLinksToRoot(t *testing.T) { }, bufWalk(b)) assert.NoError(t, err) - assert.Equal(t, `file bar + assert.Equal(t, filepath.FromSlash(`file bar file bax dir bay file bay/baz symlink:. foo -`, string(b.Bytes())) +`), b.String()) } func TestWalkerMap(t *testing.T) { @@ -241,10 +268,10 @@ func TestWalkerMap(t *testing.T) { }, bufWalk(b)) assert.NoError(t, err) - assert.Equal(t, `dir _foo + assert.Equal(t, filepath.FromSlash(`dir _foo file _foo/bar2 file _foo2 -`, string(b.Bytes())) +`), b.String()) } func TestWalkerMapSkipDir(t *testing.T) { @@ -275,10 +302,10 @@ func TestWalkerMapSkipDir(t *testing.T) { }, bufWalk(b)) assert.NoError(t, err) - assert.Equal(t, `dir includeDir + assert.Equal(t, filepath.FromSlash(`dir includeDir file includeDir/a.txt -`, string(b.Bytes())) - assert.Equal(t, []string{"excludeDir", "includeDir", "includeDir/a.txt"}, walked) +`), b.String()) + assert.Equal(t, []string{"excludeDir", "includeDir", filepath.FromSlash("includeDir/a.txt")}, walked) } func TestWalkerPermissionDenied(t *testing.T) { @@ -313,7 +340,7 @@ func TestWalkerPermissionDenied(t *testing.T) { }, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, `dir foo -`, string(b.Bytes())) +`, b.String()) b.Reset() err = Walk(context.Background(), d, &FilterOpt{ @@ -321,7 +348,7 @@ func TestWalkerPermissionDenied(t *testing.T) { }, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, `dir foo -`, string(b.Bytes())) +`, b.String()) b.Reset() err = Walk(context.Background(), d, &FilterOpt{ @@ -337,7 +364,7 @@ func TestWalkerPermissionDenied(t *testing.T) { }, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, `dir foo -`, string(b.Bytes())) +`, b.String()) } func bufWalk(buf *bytes.Buffer) filepath.WalkFunc { @@ -582,7 +609,7 @@ func TestWalkerDoublestarInclude(t *testing.T) { }, bufWalk(b)) assert.NoError(t, err) - trimEqual(t, ` + trimEqual(t, filepath.FromSlash(` dir a dir a/b dir a/b/bar @@ -596,7 +623,7 @@ func TestWalkerDoublestarInclude(t *testing.T) { dir foo/bar file foo/bar/bee file foo2 - `, string(b.Bytes())) + `), b.String()) b.Reset() err = Walk(context.Background(), d, &FilterOpt{ @@ -604,7 +631,7 @@ func TestWalkerDoublestarInclude(t *testing.T) { }, bufWalk(b)) assert.NoError(t, err) - trimEqual(t, ` + trimEqual(t, filepath.FromSlash(` dir a dir a/b dir a/b/bar @@ -615,7 +642,7 @@ func TestWalkerDoublestarInclude(t *testing.T) { dir foo dir foo/bar file foo/bar/bee - `, string(b.Bytes())) + `), b.String()) b.Reset() err = Walk(context.Background(), d, &FilterOpt{ @@ -623,14 +650,14 @@ func TestWalkerDoublestarInclude(t *testing.T) { }, bufWalk(b)) assert.NoError(t, err) - trimEqual(t, ` + trimEqual(t, filepath.FromSlash(` dir a dir a/b dir a/b/bar file a/b/bar/foo dir bar file bar/foo - `, string(b.Bytes())) + `), b.String()) b.Reset() err = Walk(context.Background(), d, &FilterOpt{ @@ -638,7 +665,7 @@ func TestWalkerDoublestarInclude(t *testing.T) { }, bufWalk(b)) assert.NoError(t, err) - trimEqual(t, ` + trimEqual(t, filepath.FromSlash(` dir a dir a/b dir a/b/bar @@ -651,7 +678,7 @@ func TestWalkerDoublestarInclude(t *testing.T) { dir foo dir foo/bar file foo/bar/bee - `, string(b.Bytes())) + `), b.String()) b.Reset() err = Walk(context.Background(), d, &FilterOpt{ @@ -659,7 +686,7 @@ func TestWalkerDoublestarInclude(t *testing.T) { }, bufWalk(b)) assert.NoError(t, err) - trimEqual(t, ` + trimEqual(t, filepath.FromSlash(` dir a dir a/b dir a/b/bar @@ -667,7 +694,7 @@ func TestWalkerDoublestarInclude(t *testing.T) { file a/b/bar/fop dir bar file bar/foo - `, string(b.Bytes())) + `), b.String()) b.Reset() err = Walk(context.Background(), d, &FilterOpt{ @@ -675,7 +702,7 @@ func TestWalkerDoublestarInclude(t *testing.T) { }, bufWalk(b)) assert.NoError(t, err) - trimEqual(t, ``, string(b.Bytes())) + trimEqual(t, ``, b.String()) b.Reset() err = Walk(context.Background(), d, &FilterOpt{ @@ -683,7 +710,7 @@ func TestWalkerDoublestarInclude(t *testing.T) { }, bufWalk(b)) assert.NoError(t, err) - trimEqual(t, ` + trimEqual(t, filepath.FromSlash(` dir a dir a/b dir a/b/bar @@ -695,7 +722,7 @@ func TestWalkerDoublestarInclude(t *testing.T) { dir foo/bar file foo/bar/bee file foo2 - `, string(b.Bytes())) + `), b.String()) b.Reset() err = Walk(context.Background(), d, &FilterOpt{ @@ -703,7 +730,7 @@ func TestWalkerDoublestarInclude(t *testing.T) { }, bufWalk(b)) assert.NoError(t, err) - trimEqual(t, ` + trimEqual(t, filepath.FromSlash(` dir a dir a/b dir a/b/bar @@ -711,7 +738,7 @@ func TestWalkerDoublestarInclude(t *testing.T) { file a/b/bar/fop dir bar file bar/foo - `, string(b.Bytes())) + `), b.String()) b.Reset() err = Walk(context.Background(), d, &FilterOpt{ @@ -719,14 +746,14 @@ func TestWalkerDoublestarInclude(t *testing.T) { }, bufWalk(b)) assert.NoError(t, err) - trimEqual(t, ` + trimEqual(t, filepath.FromSlash(` dir a dir a/b dir a/b/bar file a/b/bar/foo dir bar file bar/foo - `, string(b.Bytes())) + `), b.String()) b.Reset() err = Walk(context.Background(), d, &FilterOpt{ @@ -734,11 +761,11 @@ func TestWalkerDoublestarInclude(t *testing.T) { }, bufWalk(b)) assert.NoError(t, err) - trimEqual(t, ` + trimEqual(t, filepath.FromSlash(` dir foo dir foo/bar file foo/bar/bee - `, string(b.Bytes())) + `), b.String()) b.Reset() err = Walk(context.Background(), d, &FilterOpt{ @@ -746,12 +773,12 @@ func TestWalkerDoublestarInclude(t *testing.T) { }, bufWalk(b)) assert.NoError(t, err) - trimEqual(t, ` + trimEqual(t, filepath.FromSlash(` dir a dir a/b dir a/b/baz dir baz - `, string(b.Bytes())) + `), b.String()) } func TestFSWalk(t *testing.T) { @@ -768,23 +795,23 @@ func TestFSWalk(t *testing.T) { b := &bytes.Buffer{} err = f.Walk(context.Background(), "", bufWalkDir(b)) assert.NoError(t, err) - assert.Equal(t, string(b.Bytes()), `dir bar + assert.Equal(t, filepath.FromSlash(`dir bar file bar/foo2 file foo -`) +`), b.String()) b = &bytes.Buffer{} err = f.Walk(context.Background(), "foo", bufWalkDir(b)) assert.NoError(t, err) - assert.Equal(t, string(b.Bytes()), `file foo -`) + assert.Equal(t, `file foo +`, b.String()) b = &bytes.Buffer{} err = f.Walk(context.Background(), "bar", bufWalkDir(b)) assert.NoError(t, err) - assert.Equal(t, string(b.Bytes()), `dir bar + assert.Equal(t, filepath.FromSlash(`dir bar file bar/foo2 -`) +`), b.String()) } func TestFSWalkNested(t *testing.T) { @@ -804,9 +831,9 @@ func TestFSWalkNested(t *testing.T) { b := &bytes.Buffer{} err = f2.Walk(context.Background(), "", bufWalkDir(b)) assert.NoError(t, err) - assert.Equal(t, string(b.Bytes()), `dir foo + assert.Equal(t, filepath.FromSlash(`dir foo file foo/bar -`) +`), b.String()) f2, err = NewFilterFS(f, &FilterOpt{ ExcludePatterns: []string{"!foo/bar"}, @@ -819,7 +846,7 @@ file foo/bar b = &bytes.Buffer{} err = f2.Walk(context.Background(), "", bufWalkDir(b)) assert.NoError(t, err) - assert.Equal(t, string(b.Bytes()), ``) + assert.Equal(t, ``, b.String()) f2, err = NewFilterFS(f, &FilterOpt{ ExcludePatterns: []string{"foo"}, @@ -832,7 +859,7 @@ file foo/bar b = &bytes.Buffer{} err = f2.Walk(context.Background(), "", bufWalkDir(b)) assert.NoError(t, err) - assert.Equal(t, string(b.Bytes()), ``) + assert.Equal(t, ``, b.String()) } func TestFilteredOpen(t *testing.T) { @@ -853,8 +880,8 @@ func TestFilteredOpen(t *testing.T) { b := &bytes.Buffer{} err = f.Walk(context.Background(), "", bufWalkDir(b)) assert.NoError(t, err) - assert.Equal(t, string(b.Bytes()), `file foo -`) + assert.Equal(t, `file foo +`, b.String()) r, err := f.Open("foo") assert.NoError(t, err) diff --git a/followlinks.go b/followlinks.go index 7a843cb1..559291d7 100644 --- a/followlinks.go +++ b/followlinks.go @@ -137,8 +137,8 @@ func (r *symlinkResolver) readSymlink(p string, allowWildcard bool) ([]string, e func statFile(fs FS, root string) (os.DirEntry, error) { var out os.DirEntry - root = filepath.ToSlash(filepath.Clean(root)) - if root == "/" || root == "." { + root = filepath.FromSlash(filepath.Clean(root)) + if root == string(filepath.Separator) || root == "." { return nil, nil } @@ -168,8 +168,8 @@ func statFile(fs FS, root string) (os.DirEntry, error) { func readDir(fs FS, root string) ([]os.DirEntry, error) { var out []os.DirEntry - root = filepath.ToSlash(filepath.Clean(root)) - if root == "/" || root == "." { + root = filepath.FromSlash(filepath.Clean(root)) + if root == string(filepath.Separator) || root == "." { root = "." out = make([]gofs.DirEntry, 0) } diff --git a/fs_test.go b/fs_test.go index 54e7653b..fc60c1f7 100644 --- a/fs_test.go +++ b/fs_test.go @@ -4,6 +4,7 @@ import ( "context" gofs "io/fs" "os" + "path/filepath" "testing" "github.com/containerd/continuity/fs/fstest" @@ -32,7 +33,7 @@ func TestWalk(t *testing.T) { }) require.NoError(t, err) - require.Equal(t, paths, []string{"dir", "dir/foo"}) + require.Equal(t, []string{"dir", filepath.FromSlash("dir/foo")}, paths) require.Len(t, files, 2) require.Equal(t, "dir", files[0].Name()) require.Equal(t, "foo", files[1].Name()) @@ -49,7 +50,7 @@ func TestWalk(t *testing.T) { require.Equal(t, len("contents"), int(fis[1].Size())) require.Equal(t, "dir", fis[0].(*StatInfo).Path) - require.Equal(t, "dir/foo", fis[1].(*StatInfo).Path) + require.Equal(t, filepath.FromSlash("dir/foo"), fis[1].(*StatInfo).Path) } func TestWalkDir(t *testing.T) { @@ -98,7 +99,7 @@ func TestWalkDir(t *testing.T) { }) require.NoError(t, err) - require.Equal(t, paths, []string{"1", "1/dir", "1/dir/foo", "2", "2/dir", "2/dir/bar"}) + require.Equal(t, []string{"1", filepath.FromSlash("1/dir"), filepath.FromSlash("1/dir/foo"), "2", filepath.FromSlash("2/dir"), filepath.FromSlash("2/dir/bar")}, paths) require.Equal(t, "1", files[0].Name()) require.Equal(t, "dir", files[1].Name()) require.Equal(t, "foo", files[2].Name()) diff --git a/receive_test.go b/receive_test.go index a36d5013..7fc90bbb 100644 --- a/receive_test.go +++ b/receive_test.go @@ -9,6 +9,7 @@ import ( gofs "io/fs" "os" "path/filepath" + "runtime" "sync" "testing" "time" @@ -144,10 +145,10 @@ func TestCopySwitchDirToFile(t *testing.T) { dest, err := tmpDir(changeStream([]string{ "ADD foo dir", - "ADD foo/bar dile data2", + "ADD foo/bar file data2", })) assert.NoError(t, err) - defer os.RemoveAll(d) + defer os.RemoveAll(dest) copy := func(src, dest string) (*changes, error) { ts := newNotificationBuffer() @@ -180,8 +181,13 @@ func TestCopySwitchDirToFile(t *testing.T) { NotifyHashed: chs.HandleChange, ContentHasher: simpleSHA256Hasher, Filter: func(_ string, s *types.Stat) bool { - s.Uid = uint32(os.Getuid()) - s.Gid = uint32(os.Getgid()) + if runtime.GOOS != "windows" { + // On Windows, Getuid() and Getgid() always return -1 + // See: https://pkg.go.dev/os#Getgid + // See: https://pkg.go.dev/os#Geteuid + s.Uid = uint32(os.Getuid()) + s.Gid = uint32(os.Getgid()) + } return true }, }) @@ -206,8 +212,8 @@ func TestCopySwitchDirToFile(t *testing.T) { err = Walk(context.Background(), dest, nil, bufWalk(b)) assert.NoError(t, err) - assert.Equal(t, string(b.Bytes()), `file foo -`) + assert.Equal(t, `file foo +`, b.String()) } func TestCopySimple(t *testing.T) { @@ -253,8 +259,13 @@ func TestCopySimple(t *testing.T) { NotifyHashed: chs.HandleChange, ContentHasher: simpleSHA256Hasher, Filter: func(p string, s *types.Stat) bool { - s.Uid = uint32(os.Getuid()) - s.Gid = uint32(os.Getgid()) + if runtime.GOOS != "windows" { + // On Windows, Getuid() and Getgid() always return -1 + // See: https://pkg.go.dev/os#Getgid + // See: https://pkg.go.dev/os#Geteuid + s.Uid = uint32(os.Getuid()) + s.Gid = uint32(os.Getgid()) + } s.ModTime = tm.UnixNano() return true }, @@ -267,7 +278,18 @@ func TestCopySimple(t *testing.T) { err = Walk(context.Background(), dest, nil, bufWalk(b)) assert.NoError(t, err) - assert.Equal(t, string(b.Bytes()), `file foo + if runtime.GOOS == "windows" { + assert.Equal(t, `file foo +file foo2 +dir zzz +file zzz\aa +dir zzz\bb +dir zzz\bb\cc +symlink:..\..\ zzz\bb\cc\dd +file zzz.aa +`, b.String()) + } else { + assert.Equal(t, `file foo file foo2 dir zzz file zzz/aa @@ -275,7 +297,8 @@ dir zzz/bb dir zzz/bb/cc symlink:../../ zzz/bb/cc/dd file zzz.aa -`) +`, b.String()) + } dt, err := os.ReadFile(filepath.Join(dest, "zzz/aa")) assert.NoError(t, err) @@ -286,24 +309,36 @@ file zzz.aa assert.Equal(t, "dat2", string(dt)) fi, err := os.Stat(filepath.Join(dest, "foo2")) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, tm, fi.ModTime()) - h, ok := ts.Hash("zzz/aa") + h, ok := ts.Hash(filepath.FromSlash("zzz/aa")) assert.True(t, ok) - assert.Equal(t, digest.Digest("sha256:99b6ef96ee0572b5b3a4eb28f00b715d820bfd73836e59cc1565e241f4d1bb2f"), h) + if runtime.GOOS == "windows" { + assert.Equal(t, digest.Digest("sha256:5da6b6a222dca8d9260384da15378d4389f7e16943e812e08c39759b8514b456"), h) + } else { + assert.Equal(t, digest.Digest("sha256:99b6ef96ee0572b5b3a4eb28f00b715d820bfd73836e59cc1565e241f4d1bb2f"), h) + } h, ok = ts.Hash("foo2") assert.True(t, ok) - assert.Equal(t, digest.Digest("sha256:dd2529f7749ba45ea55de3b2e10086d6494cc45a94e57650c2882a6a14b4ff32"), h) + if runtime.GOOS == "windows" { + assert.Equal(t, digest.Digest("sha256:cd83620e5308f6ddb9953a82b2c7450832eac78521dbf067d2882318cabc1311"), h) + } else { + assert.Equal(t, digest.Digest("sha256:dd2529f7749ba45ea55de3b2e10086d6494cc45a94e57650c2882a6a14b4ff32"), h) + } - h, ok = ts.Hash("zzz/bb/cc/dd") + h, ok = ts.Hash(filepath.FromSlash("zzz/bb/cc/dd")) assert.True(t, ok) - assert.Equal(t, digest.Digest("sha256:eca07e8f2d09bd574ea2496312e6de1685ef15b8e6a49a534ed9e722bcac8adc"), h) + if runtime.GOOS == "windows" { + assert.Equal(t, digest.Digest("sha256:47dc68d117ae85dc688103d6ba2cee54caabbbcf606e54ca62fda6a3d9deae19"), h) + } else { + assert.Equal(t, digest.Digest("sha256:eca07e8f2d09bd574ea2496312e6de1685ef15b8e6a49a534ed9e722bcac8adc"), h) + } - k, ok := chs.c["zzz/aa"] - assert.Equal(t, ok, true) - assert.Equal(t, k, ChangeKindAdd) + k, ok := chs.c[filepath.FromSlash("zzz/aa")] + assert.Equal(t, true, ok) + assert.Equal(t, ChangeKindAdd, k) err = os.WriteFile(filepath.Join(d, "zzz/bb/cc/foo"), []byte("data5"), 0600) assert.NoError(t, err) @@ -325,8 +360,13 @@ file zzz.aa NotifyHashed: chs.HandleChange, ContentHasher: simpleSHA256Hasher, Filter: func(_ string, s *types.Stat) bool { - s.Uid = uint32(os.Getuid()) - s.Gid = uint32(os.Getgid()) + if runtime.GOOS != "windows" { + // On Windows, Getuid() and Getgid() always return -1 + // See: https://pkg.go.dev/os#Getgid + // See: https://pkg.go.dev/os#Geteuid + s.Uid = uint32(os.Getuid()) + s.Gid = uint32(os.Getgid()) + } s.ModTime = tm.UnixNano() return true }, @@ -339,7 +379,18 @@ file zzz.aa err = Walk(context.Background(), dest, nil, bufWalk(b)) assert.NoError(t, err) - assert.Equal(t, string(b.Bytes()), `file foo + if runtime.GOOS == "windows" { + assert.Equal(t, `file foo +dir zzz +file zzz\aa +dir zzz\bb +dir zzz\bb\cc +symlink:..\..\ zzz\bb\cc\dd +file zzz\bb\cc\foo +file zzz.aa +`, b.String()) + } else { + assert.Equal(t, `file foo dir zzz file zzz/aa dir zzz/bb @@ -347,36 +398,45 @@ dir zzz/bb/cc symlink:../../ zzz/bb/cc/dd file zzz/bb/cc/foo file zzz.aa -`) +`, b.String()) + } dt, err = os.ReadFile(filepath.Join(dest, "zzz/bb/cc/foo")) assert.NoError(t, err) assert.Equal(t, "data5", string(dt)) - h, ok = ts.Hash("zzz/bb/cc/dd") + h, ok = ts.Hash(filepath.FromSlash("zzz/bb/cc/dd")) assert.True(t, ok) - assert.Equal(t, digest.Digest("sha256:eca07e8f2d09bd574ea2496312e6de1685ef15b8e6a49a534ed9e722bcac8adc"), h) + if runtime.GOOS == "windows" { + assert.Equal(t, digest.Digest("sha256:47dc68d117ae85dc688103d6ba2cee54caabbbcf606e54ca62fda6a3d9deae19"), h) + } else { + assert.Equal(t, digest.Digest("sha256:eca07e8f2d09bd574ea2496312e6de1685ef15b8e6a49a534ed9e722bcac8adc"), h) + } - h, ok = ts.Hash("zzz/bb/cc/foo") + h, ok = ts.Hash(filepath.FromSlash("zzz/bb/cc/foo")) assert.True(t, ok) - assert.Equal(t, digest.Digest("sha256:cd14a931fc2e123ded338093f2864b173eecdee578bba6ec24d0724272326c3a"), h) + if runtime.GOOS == "windows" { + assert.Equal(t, digest.Digest("sha256:9184a7db8d056ee43838613279db9a7ab02272e50d5e20d253393521bb34aa46"), h) + } else { + assert.Equal(t, digest.Digest("sha256:cd14a931fc2e123ded338093f2864b173eecdee578bba6ec24d0724272326c3a"), h) + } _, ok = ts.Hash("foo2") assert.False(t, ok) k, ok = chs.c["foo2"] - assert.Equal(t, ok, true) - assert.Equal(t, k, ChangeKindDelete) + assert.Equal(t, true, ok) + assert.Equal(t, ChangeKindDelete, k) - k, ok = chs.c["zzz/bb/cc/foo"] - assert.Equal(t, ok, true) - assert.Equal(t, k, ChangeKindAdd) + k, ok = chs.c[filepath.FromSlash("zzz/bb/cc/foo")] + assert.Equal(t, true, ok) + assert.Equal(t, ChangeKindAdd, k) - _, ok = chs.c["zzz/aa"] - assert.Equal(t, ok, false) + _, ok = chs.c[filepath.FromSlash("zzz/aa")] + assert.Equal(t, false, ok) _, ok = chs.c["zzz.aa"] - assert.Equal(t, ok, false) + assert.Equal(t, false, ok) } func sockPairProto(ctx context.Context) (Stream, Stream) { @@ -498,8 +558,15 @@ func simpleSHA256Hasher(s *types.Stat) (hash.Hash, error) { // Unlike Linux, on FreeBSD's stat() call returns -1 in st_rdev for regular files ss.Devminor = 0 ss.Devmajor = 0 + if runtime.GOOS == "windows" { + // On Windows, Getuid() and Getgid() always return -1 + // See: https://pkg.go.dev/os#Getgid + // See: https://pkg.go.dev/os#Geteuid + ss.Uid = 0 + ss.Gid = 0 + } - if os.FileMode(ss.Mode)&os.ModeSymlink != 0 { + if os.FileMode(ss.Mode)&os.ModeSymlink != 0 && runtime.GOOS != "windows" { ss.Mode = ss.Mode | 0777 } diff --git a/stat.go b/stat.go index 2ab8da11..44441cb6 100644 --- a/stat.go +++ b/stat.go @@ -19,7 +19,7 @@ func mkstat(path, relpath string, fi os.FileInfo, inodemap map[uint64]string) (* relpath = filepath.ToSlash(relpath) stat := &types.Stat{ - Path: relpath, + Path: filepath.FromSlash(relpath), Mode: uint32(fi.Mode()), ModTime: fi.ModTime().UnixNano(), } diff --git a/validator.go b/validator.go index 9bd7d94d..8b0dd91e 100644 --- a/validator.go +++ b/validator.go @@ -2,8 +2,7 @@ package fsutil import ( "os" - "path" - "runtime" + "path/filepath" "sort" "strings" "syscall" @@ -28,21 +27,18 @@ func (v *Validator) HandleChange(kind ChangeKind, p string, fi os.FileInfo, err if v.parentDirs == nil { v.parentDirs = make([]parent, 1, 10) } - if runtime.GOOS == "windows" { - p = strings.Replace(p, "\\", "", -1) - } - if p != path.Clean(p) { + if p != filepath.Clean(p) { return errors.WithStack(&os.PathError{Path: p, Err: syscall.EINVAL, Op: "unclean path"}) } - if path.IsAbs(p) { + if filepath.IsAbs(p) { return errors.WithStack(&os.PathError{Path: p, Err: syscall.EINVAL, Op: "absolute path"}) } - dir := path.Dir(p) - base := path.Base(p) + dir := filepath.Dir(p) + base := filepath.Base(p) if dir == "." { dir = "" } - if dir == ".." || strings.HasPrefix(p, "../") { + if dir == ".." || strings.HasPrefix(p, filepath.FromSlash("../")) { return errors.WithStack(&os.PathError{Path: p, Err: syscall.EINVAL, Op: "escape check"}) } @@ -56,12 +52,12 @@ func (v *Validator) HandleChange(kind ChangeKind, p string, fi os.FileInfo, err } if dir != v.parentDirs[len(v.parentDirs)-1].dir || v.parentDirs[i].last >= base { - return errors.Errorf("changes out of order: %q %q", p, path.Join(v.parentDirs[i].dir, v.parentDirs[i].last)) + return errors.Errorf("changes out of order: %q %q", p, filepath.Join(v.parentDirs[i].dir, v.parentDirs[i].last)) } v.parentDirs[i].last = base if kind != ChangeKindDelete && fi.IsDir() { v.parentDirs = append(v.parentDirs, parent{ - dir: path.Join(dir, base), + dir: filepath.Join(dir, base), last: "", }) } @@ -76,7 +72,7 @@ func ComparePath(p1, p2 string) int { switch { case p1[i] == p2[i]: continue - case p2[i] != '/' && p1[i] < p2[i] || p1[i] == '/': + case p2[i] != filepath.Separator && p1[i] < p2[i] || p1[i] == filepath.Separator: return -1 default: return 1 diff --git a/validator_test.go b/validator_test.go index aabb0732..b3acf688 100644 --- a/validator_test.go +++ b/validator_test.go @@ -3,6 +3,7 @@ package fsutil import ( "fmt" "os" + "path/filepath" "strings" "testing" @@ -210,7 +211,7 @@ func parseChange(str string) *change { default: panic(errStr) } - c.path = f[1] + c.path = filepath.FromSlash(f[1]) st := &types.Stat{} switch f[2] { case "file": From 3e13e95f2b94dec4bc7c632d5bcb31ff3010b14b Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Wed, 7 Feb 2024 13:25:23 +0100 Subject: [PATCH 17/39] test with coverage and send to codecov Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- .github/workflows/ci.yml | 39 ++++++++++++++++++++++++++++++++++----- .gitignore | 6 +----- Dockerfile | 10 ++++++++-- docker-bake.hcl | 10 ++++++++-- 4 files changed, 51 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fa86cf6e..e576defe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +env: + DESTDIR: ./bin + on: schedule: - cron: '0 8 */6 * *' # every 6 days @@ -61,11 +64,14 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go_version: + go: - "1.19" - "1.20" + mode: + - "root" + - "noroot" env: - GO_VERSION: ${{ matrix.go_version }} + GO_VERSION: ${{ matrix.go }} steps: - name: Checkout @@ -77,7 +83,13 @@ jobs: name: Test uses: docker/bake-action@v3 with: - targets: test + targets: test-${{ matrix.mode }} + - + name: Upload coverage + uses: codecov/codecov-action@v4 + with: + directory: ${{ env.DESTDIR }}/coverage + flags: unit,${{ matrix.mode }},go-${{ matrix.go }} test-macos: runs-on: macos-latest @@ -99,7 +111,16 @@ jobs: - name: Test run: | - go test ./... + go test -coverprofile=coverage.txt -covermode=atomic ./... + go tool cover -func=coverage.txt + - + name: Upload coverage + if: always() + uses: codecov/codecov-action@v4 + with: + file: ./coverage.txt + env_vars: RUNNER_OS + flags: unit,go-${{ matrix.go }} test-freebsd-amd64: runs-on: macos-13 @@ -144,4 +165,12 @@ jobs: timeout_minutes: 20 max_attempts: 5 command: | - vagrant ssh -- "cd /vagrant; go test -buildvcs=false ./..." + vagrant ssh -- "cd /vagrant; go test -buildvcs=false -coverprofile=coverage.txt -covermode=atomic ./..." + vagrant ssh -c "sudo cat /vagrant/coverage.txt" > coverage.txt + - + name: Upload coverage + if: always() + uses: codecov/codecov-action@v4 + with: + file: ./coverage.txt + flags: unit,freebsd diff --git a/.gitignore b/.gitignore index 51b9602c..e5c343e2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,5 @@ # if you want to ignore files created by your editor/tools, consider using a # global .gitignore or .git/info/exclude see https://help.github.com/articles/ignoring-files -.* -!.github -!.gitignore -!.travis.yml -*.prof +bin/ # support running go modules in vendor mode for local development vendor/ diff --git a/Dockerfile b/Dockerfile index 9584648d..5fd39502 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,13 +18,19 @@ FROM base AS test ARG TESTFLAGS RUN --mount=target=. --mount=target=/go/pkg/mod,type=cache \ --mount=target=/root/.cache,type=cache \ - CGO_ENABLED=0 xx-go test -test.v ${TESTFLAGS} ./... + CGO_ENABLED=0 xx-go test -v -coverprofile=/tmp/coverage.txt -covermode=atomic ${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 \ - CGO_ENABLED=0 GOCACHE=/tmp/gocache xx-go test -test.v ./... + CGO_ENABLED=0 GOCACHE=/tmp/gocache xx-go test -v -coverprofile=/tmp/coverage.txt -covermode=atomic ./... + +FROM scratch AS test-coverage +COPY --from=test /tmp/coverage.txt /coverage-root.txt + +FROM scratch AS test-noroot-coverage +COPY --from=test-noroot /tmp/coverage.txt /coverage-noroot.txt FROM build diff --git a/docker-bake.hcl b/docker-bake.hcl index 6ba3c867..7df6d209 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -2,6 +2,10 @@ variable "GO_VERSION" { default = "1.20" } +variable "DESTDIR" { + default = "./bin" +} + group "default" { targets = ["build"] } @@ -18,12 +22,14 @@ group "test" { target "test-root" { inherits = ["build"] - target = "test" + target = "test-coverage" + output = ["${DESTDIR}/coverage"] } target "test-noroot" { inherits = ["build"] - target = "test-noroot" + target = "test-noroot-coverage" + output = ["${DESTDIR}/coverage"] } target "lint" { From e5c2801fc5869a3b54b0e03b3a084b8a9a9df83c Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Mon, 29 Jan 2024 09:35:17 +0100 Subject: [PATCH 18/39] ci: align events Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- .github/workflows/ci.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e576defe..1161cb3c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,10 +13,7 @@ on: push: branches: - master - - gh_test_ci pull_request: - branches: - - master jobs: validate: From 5bc23465dd69d75e2a44751e74a773e9b8ce60ea Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Mon, 29 Jan 2024 09:32:38 +0100 Subject: [PATCH 19/39] readme badges Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- readme.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/readme.md b/readme.md index 5ce685b7..c4171688 100644 --- a/readme.md +++ b/readme.md @@ -1,3 +1,8 @@ +[![PkgGoDev](https://img.shields.io/badge/go.dev-docs-007d9c?style=flat-square&logo=go&logoColor=white)](https://pkg.go.dev/github.com/tonistiigi/fsutil) +[![CI Status](https://img.shields.io/github/actions/workflow/status/tonistiigi/fsutil/ci.yml?label=ci&logo=github&style=flat-square)](https://github.com/tonistiigi/fsutil/actions?query=workflow%3Aci) +[![Go Report Card](https://goreportcard.com/badge/github.com/tonistiigi/fsutil?style=flat-square)](https://goreportcard.com/report/github.com/tonistiigi/fsutil) +[![Codecov](https://img.shields.io/codecov/c/github/tonistiigi/fsutil?logo=codecov&style=flat-square)](https://codecov.io/gh/tonistiigi/fsutil) + Incremental file directory sync tools in golang. ``` From bf5b720f9d7ca4081234bcffe8645bcc511f8aff Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Thu, 8 Feb 2024 19:20:05 +0100 Subject: [PATCH 20/39] ci: fix test step for windows Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d4280659..9cc301ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -111,8 +111,8 @@ jobs: - name: Test run: | - go test -coverprofile=coverage.txt -covermode=atomic ./... - go tool cover -func=coverage.txt + go test -coverprofile="coverage.txt" -covermode="atomic" ./... + go tool cover -func="coverage.txt" - name: Upload coverage if: always() From fb90e5f3085034f83d84dd315c7bbfe7ec34dbcd Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Mon, 12 Feb 2024 09:51:41 +0100 Subject: [PATCH 21/39] ci: bump actions to latest stable Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- .github/workflows/ci.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9cc301ef..49bf7fc7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,13 +27,13 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Run - uses: docker/bake-action@v3 + uses: docker/bake-action@v4 with: targets: ${{ matrix.target }} @@ -47,13 +47,13 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Run - uses: docker/bake-action@v3 + uses: docker/bake-action@v4 with: targets: ${{ matrix.target }} @@ -72,13 +72,13 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Test - uses: docker/bake-action@v3 + uses: docker/bake-action@v4 with: targets: test-${{ matrix.mode }} - @@ -102,10 +102,10 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} - @@ -129,10 +129,10 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Cache Vagrant boxes - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.vagrant.d/boxes key: ${{ runner.os }}-vagrant-${{ hashFiles('hack/Vagrantfile.freebsd13') }} @@ -160,7 +160,7 @@ jobs: vagrant up --no-tty - name: Test - uses: nick-fields/retry@14672906e672a08bd6eeb15720e9ed3ce869cdd4 # v2.9.0 + uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e # v3.0.0 with: timeout_minutes: 20 max_attempts: 5 From 7f115c28d7f19c9332777087688fdb760352d82c Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Mon, 12 Feb 2024 09:52:01 +0100 Subject: [PATCH 22/39] chore: dependabot to keep gha up to date Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- .github/dependabot.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..8d77e584 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + open-pull-requests-limit: 10 + directory: "/" + schedule: + interval: "daily" + labels: + - "dependencies" + - "bot" From cb7e6e613e8f25020d4a97cf3d2428dbbc40dd08 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Mon, 12 Feb 2024 09:38:32 +0100 Subject: [PATCH 23/39] update to go 1.21 Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- .github/workflows/ci.yml | 4 ++-- Dockerfile | 5 +++-- bench/go.mod | 2 +- docker-bake.hcl | 2 +- go.mod | 2 +- hack/Vagrantfile.freebsd13 | 3 ++- hack/dockerfiles/gomod.Dockerfile | 3 ++- hack/dockerfiles/lint.Dockerfile | 3 ++- 8 files changed, 14 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9cc301ef..b239d2c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,7 +62,7 @@ jobs: strategy: matrix: go: - - "1.19" + - "1.21" - "1.20" mode: - "root" @@ -97,7 +97,7 @@ jobs: - macos-latest - windows-latest go: - - "1.19" + - "1.21" - "1.20" steps: - diff --git a/Dockerfile b/Dockerfile index 5fd39502..b7eecd20 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,6 @@ -#syntax=docker/dockerfile:1 -ARG GO_VERSION=1.20 +# syntax=docker/dockerfile:1 + +ARG GO_VERSION=1.21 FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.1.0 AS xx diff --git a/bench/go.mod b/bench/go.mod index 6ca11879..805f3a38 100644 --- a/bench/go.mod +++ b/bench/go.mod @@ -1,6 +1,6 @@ module github.com/tonistiigi/fsutil/bench -go 1.19 +go 1.20 require ( github.com/containerd/continuity v0.4.1 diff --git a/docker-bake.hcl b/docker-bake.hcl index 7df6d209..48127d4f 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -1,5 +1,5 @@ variable "GO_VERSION" { - default = "1.20" + default = null } variable "DESTDIR" { diff --git a/go.mod b/go.mod index 3dceea97..93977f05 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/tonistiigi/fsutil -go 1.19 +go 1.20 require ( github.com/Microsoft/go-winio v0.5.2 diff --git a/hack/Vagrantfile.freebsd13 b/hack/Vagrantfile.freebsd13 index 7885e148..9cd7e062 100644 --- a/hack/Vagrantfile.freebsd13 +++ b/hack/Vagrantfile.freebsd13 @@ -13,7 +13,8 @@ Vagrant.configure("2") do |config| config.vm.provision "init", type: "shell", run: "once" do |sh| sh.inline = <<~SHELL pkg bootstrap - pkg install -y go + pkg install -y go121 + ln -s /usr/local/bin/go121 /usr/local/bin/go SHELL end end diff --git a/hack/dockerfiles/gomod.Dockerfile b/hack/dockerfiles/gomod.Dockerfile index a7c5101c..d9f1d6d9 100644 --- a/hack/dockerfiles/gomod.Dockerfile +++ b/hack/dockerfiles/gomod.Dockerfile @@ -1,5 +1,6 @@ # syntax=docker/dockerfile:1 -ARG GO_VERSION=1.20 + +ARG GO_VERSION=1.21 FROM golang:${GO_VERSION}-alpine AS gomod RUN apk add --no-cache git diff --git a/hack/dockerfiles/lint.Dockerfile b/hack/dockerfiles/lint.Dockerfile index 2561db30..74acfb44 100644 --- a/hack/dockerfiles/lint.Dockerfile +++ b/hack/dockerfiles/lint.Dockerfile @@ -1,5 +1,6 @@ # syntax=docker/dockerfile:1 -ARG GO_VERSION=1.20 + +ARG GO_VERSION=1.21 FROM golang:${GO_VERSION}-alpine RUN apk add --no-cache git gcc musl-dev From 59183f0692f27fe2b30bc8aec517f2911ebdb343 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Mon, 12 Feb 2024 09:41:28 +0100 Subject: [PATCH 24/39] update golangci-lint to 1.54.2 also update config to show all issues at once and set linter setting for io/ioutil. Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- .golangci.yml | 14 ++++++++++++++ hack/dockerfiles/lint.Dockerfile | 11 ++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 2a8b86ae..7300a0e5 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -14,3 +14,17 @@ linters: - staticcheck - typecheck disable-all: true + +linters-settings: + depguard: + rules: + main: + deny: + # The io/ioutil package has been deprecated. + # https://go.dev/doc/go1.16#ioutil + - pkg: "io/ioutil" + desc: The io/ioutil package has been deprecated. + +# show all +max-issues-per-linter: 0 +max-same-issues: 0 diff --git a/hack/dockerfiles/lint.Dockerfile b/hack/dockerfiles/lint.Dockerfile index 74acfb44..d6d173dc 100644 --- a/hack/dockerfiles/lint.Dockerfile +++ b/hack/dockerfiles/lint.Dockerfile @@ -1,10 +1,15 @@ # syntax=docker/dockerfile:1 ARG GO_VERSION=1.21 +ARG GOLANGCI_LINT_VERSION=1.54.2 FROM golang:${GO_VERSION}-alpine RUN apk add --no-cache git gcc musl-dev -RUN wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.53.3 +ENV GOFLAGS="-buildvcs=false" +ARG GOLANGCI_LINT_VERSION +RUN wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v${GOLANGCI_LINT_VERSION} WORKDIR /go/src/github.com/tonistiigi/fsutil -RUN --mount=target=/go/src/github.com/tonistiigi/fsutil --mount=target=/root/.cache,type=cache --mount=target=/go/pkg/mod,type=cache \ - golangci-lint run +RUN --mount=target=. \ + --mount=target=/root/.cache,type=cache \ + --mount=target=/go/pkg/mod,type=cache \ + golangci-lint run From ca0190e3595a6a79a23ac3c78ef1ac6431feb5e3 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Mon, 12 Feb 2024 09:45:24 +0100 Subject: [PATCH 25/39] dockerfile: update xx to 1.4.0 Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index b7eecd20..21c1f39f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,9 @@ # syntax=docker/dockerfile:1 ARG GO_VERSION=1.21 +ARG XX_VERSION=1.4.0 -FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.1.0 AS xx +FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS base RUN apk add --no-cache git From ab7803b639a56f5ac412021489215886fc54f5b6 Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Mon, 19 Feb 2024 13:58:50 +0000 Subject: [PATCH 26/39] filter: allow SkipDir return with lazy parents When using include/exclude options in a file walk, we lazily store the parents, and only later call the target function with them when we find a file inside. This handles the case where `**/*.go` should only walk directories that contain `.go` files, and not every directory in the tree. However, when storing lazy parents, we weren't appropriately handling the `SkipDir` value that could be returned. We were making two incorrect assumptions: 1. An error value from `fn()` was always an error case - this isn't true, since we have control error values like `SkipDir`. Because of this, we need to ensure that `calledFn` is *always* set to true when calling `fn` (even in an error case). 2. We can return `SkipDir` to skip this parent directory. This isn't *quite* right, since returning `SkipDir` would return it only for the child directory - we need to make sure it's called everytime for each child directory of this parent directory, by storing it as a boolean `skipFn` field. With these changes, the new test passes, and we don't end up with duplicate parent calls, as was happening before: Error: Not equal: expected: []string{"foo"} actual : []string{"foo", "foo"} Signed-off-by: Justin Chadwell --- filter.go | 11 +++++++++-- filter_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/filter.go b/filter.go index 5257c285..989dd178 100644 --- a/filter.go +++ b/filter.go @@ -176,6 +176,7 @@ func (fs *filterFS) Walk(ctx context.Context, target string, fn gofs.WalkDirFunc includeMatchInfo patternmatcher.MatchInfo excludeMatchInfo patternmatcher.MatchInfo calledFn bool + skipFn bool } // used only for include/exclude handling @@ -333,6 +334,9 @@ func (fs *filterFS) Walk(ctx context.Context, target string, fn gofs.WalkDirFunc } } for i, parentDir := range parentDirs { + if parentDir.skipFn { + return filepath.SkipDir + } if parentDir.calledFn { continue } @@ -357,10 +361,13 @@ func (fs *filterFS) Walk(ctx context.Context, target string, fn gofs.WalkDirFunc } } - if err := fn(parentStat.Path, &DirEntryInfo{Stat: parentStat}, nil); err != nil { + parentDirs[i].calledFn = true + if err := fn(parentStat.Path, &DirEntryInfo{Stat: parentStat}, nil); err == filepath.SkipDir { + parentDirs[i].skipFn = true + return filepath.SkipDir + } else if err != nil { return err } - parentDirs[i].calledFn = true } if err := fn(stat.Path, &DirEntryInfo{Stat: stat}, nil); err != nil { return err diff --git a/filter_test.go b/filter_test.go index 2e0619be..d3231675 100644 --- a/filter_test.go +++ b/filter_test.go @@ -143,6 +143,30 @@ file bar/foo `), b.String()) } +func TestWalkerIncludeReturnSkipDir(t *testing.T) { + d, err := tmpDir(changeStream([]string{ + "ADD foo dir", + "ADD foo/x dir", + "ADD foo/y dir", + "ADD foo/x/a.txt file", + "ADD foo/y/b.txt file", + })) + assert.NoError(t, err) + defer os.RemoveAll(d) + + found := []string{} + + err = Walk(context.Background(), d, &FilterOpt{ + IncludePatterns: []string{"**/*.txt"}, + }, func(path string, info gofs.FileInfo, err error) error { + found = append(found, path) + return filepath.SkipDir + }) + assert.NoError(t, err) + + assert.Equal(t, []string{"foo"}, found) +} + func TestWalkerExclude(t *testing.T) { d, err := tmpDir(changeStream([]string{ "ADD bar file", From d9cfebb97482769495b38d8e14920a4b79b17c95 Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Mon, 19 Feb 2024 14:12:50 +0000 Subject: [PATCH 27/39] filter: ensure MapResult is followed for parent directories Similar to the previous commit, we weren't properly respecting MapResult return values for parent directories. To resolve this, we just store into `skipFn` just like previously, and return `SkipDir` instead of `continue`ing on. After these changes, the new test passes, and we don't end up including entries that were in excluded directories, as was happening before: Error: Not equal: expected: "dir y\nfile y/b.txt\n" actual : "file x/a.txt\ndir y\nfile y/b.txt\n" Signed-off-by: Justin Chadwell --- filter.go | 5 ++++- filter_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/filter.go b/filter.go index 989dd178..9cee8ad2 100644 --- a/filter.go +++ b/filter.go @@ -356,8 +356,11 @@ func (fs *filterFS) Walk(ctx context.Context, target string, fn gofs.WalkDirFunc } if fs.mapFn != nil { result := fs.mapFn(parentStat.Path, parentStat) - if result == MapResultSkipDir || result == MapResultExclude { + if result == MapResultExclude { continue + } else if result == MapResultSkipDir { + parentDirs[i].skipFn = true + return filepath.SkipDir } } diff --git a/filter_test.go b/filter_test.go index d3231675..406425da 100644 --- a/filter_test.go +++ b/filter_test.go @@ -332,6 +332,33 @@ file includeDir/a.txt assert.Equal(t, []string{"excludeDir", "includeDir", filepath.FromSlash("includeDir/a.txt")}, walked) } +func TestWalkerMapSkipDirWithPattern(t *testing.T) { + d, err := tmpDir(changeStream([]string{ + "ADD x dir", + "ADD x/a.txt file", + "ADD y dir", + "ADD y/b.txt file", + })) + assert.NoError(t, err) + defer os.RemoveAll(d) + + b := &bytes.Buffer{} + err = Walk(context.Background(), d, &FilterOpt{ + IncludePatterns: []string{"**/*.txt"}, + Map: func(_ string, s *types.Stat) MapResult { + if filepath.Base(s.Path) == "x" { + return MapResultSkipDir + } + return MapResultKeep + }, + }, bufWalk(b)) + assert.NoError(t, err) + + assert.Equal(t, filepath.FromSlash(`dir y +file y/b.txt +`), b.String()) +} + func TestWalkerPermissionDenied(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("os.Chmod not fully supported on Windows") From b98da8a0b4f2b621dde34d246bd768b8e051c80e Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Tue, 20 Feb 2024 15:12:15 +0100 Subject: [PATCH 28/39] chore: codecov config Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- codecov.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 codecov.yml diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000..f5f9e02c --- /dev/null +++ b/codecov.yml @@ -0,0 +1,12 @@ +comment: false + +coverage: + status: + project: # settings affecting project coverage + default: + target: auto # auto % coverage target + threshold: 1% # allow for 1% reduction of coverage without failing + patch: off + +github_checks: + annotations: false From 34ca0de8fac74238a2922904292e86f98888739c Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Tue, 20 Feb 2024 15:13:31 +0100 Subject: [PATCH 29/39] ci: set codecov token Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b239d2c1..850b7e95 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -87,6 +87,7 @@ jobs: with: directory: ${{ env.DESTDIR }}/coverage flags: unit,${{ matrix.mode }},go-${{ matrix.go }} + token: ${{ secrets.CODECOV_TOKEN }} test-os: runs-on: ${{ matrix.os }} @@ -121,6 +122,7 @@ jobs: file: ./coverage.txt env_vars: RUNNER_OS flags: unit,go-${{ matrix.go }} + token: ${{ secrets.CODECOV_TOKEN }} test-freebsd-amd64: runs-on: macos-13 @@ -174,3 +176,4 @@ jobs: with: file: ./coverage.txt flags: unit,freebsd + token: ${{ secrets.CODECOV_TOKEN }} From 757927127c1bf10348e3c4264e09e858c66da236 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Fri, 23 Feb 2024 17:23:09 +0100 Subject: [PATCH 30/39] ci: set workflow_dispatch event this is for manual testing from a fork Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b239d2c1..3477ee56 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,7 @@ env: DESTDIR: ./bin on: + workflow_dispatch: schedule: - cron: '0 8 */6 * *' # every 6 days push: From 3092598e76277544baa64f8d501a8b475e88a6ba Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Fri, 23 Feb 2024 19:11:28 +0100 Subject: [PATCH 31/39] send: ensure file path to unix form when sending files Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- send.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/send.go b/send.go index a044d04d..6a935ed0 100644 --- a/send.go +++ b/send.go @@ -4,6 +4,7 @@ import ( "context" "io" "os" + "path/filepath" "sync" "syscall" @@ -156,7 +157,7 @@ func (s *sender) walk(ctx context.Context) error { if !ok { return errors.WithStack(&os.PathError{Path: path, Err: syscall.EBADMSG, Op: "fileinfo without stat info"}) } - + stat.Path = filepath.ToSlash(stat.Path) p := &types.Packet{ Type: types.PACKET_STAT, Stat: stat, From 216a08f9487cab4ba35e1c1c598ac3852f6ad3b4 Mon Sep 17 00:00:00 2001 From: Alex Couture-Beil Date: Mon, 10 Jul 2023 16:16:06 -0700 Subject: [PATCH 32/39] fix data race in progressCb Fixes a silly data race where progressCurrent is incremented (and later accessed) by 1 of 4 threads. Signed-off-by: Alex Couture-Beil --- receive_test.go | 8 +++++++- send.go | 17 ++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/receive_test.go b/receive_test.go index 7fc90bbb..fdd69a14 100644 --- a/receive_test.go +++ b/receive_test.go @@ -250,9 +250,14 @@ func TestCopySimple(t *testing.T) { tm := time.Now().Truncate(time.Hour) + var processCbWasCalled bool + progressCb := func(size int, last bool) { + processCbWasCalled = true + } + eg.Go(func() error { defer s1.(*fakeConnProto).closeSend() - return Send(ctx, s1, fs, nil) + return Send(ctx, s1, fs, progressCb) }) eg.Go(func() error { return Receive(ctx, s2, dest, ReceiveOpt{ @@ -273,6 +278,7 @@ func TestCopySimple(t *testing.T) { }) assert.NoError(t, eg.Wait()) + assert.True(t, processCbWasCalled) b := &bytes.Buffer{} err = Walk(context.Background(), dest, nil, bufWalk(b)) diff --git a/send.go b/send.go index 6a935ed0..ba97ef7a 100644 --- a/send.go +++ b/send.go @@ -43,13 +43,14 @@ type sendHandle struct { } type sender struct { - conn Stream - fs FS - files map[uint32]string - mu sync.RWMutex - progressCb func(int, bool) - progressCurrent int - sendpipeline chan *sendHandle + conn Stream + fs FS + files map[uint32]string + mu sync.RWMutex + progressCb func(int, bool) + progressCurrent int + progressCurrentMu sync.Mutex + sendpipeline chan *sendHandle } func (s *sender) run(ctx context.Context) error { @@ -112,6 +113,8 @@ func (s *sender) run(ctx context.Context) error { func (s *sender) updateProgress(size int, last bool) { if s.progressCb != nil { + s.progressCurrentMu.Lock() + defer s.progressCurrentMu.Unlock() s.progressCurrent += size s.progressCb(s.progressCurrent, last) } From 78f10b41c9aa8a5a90858c47a44d3c73818df28f Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Wed, 3 Apr 2024 15:22:22 +0100 Subject: [PATCH 33/39] receive: ensure callback errors are propagated Previously, it was possible for errors returned by an asyncronous callback function to not be propagated, and to instead return "context canceled" from `Receive`. This could happen based on this specific sequence of events: - An asyncronous callback is called from `HandleChange`, and returns an error, completing the async errorgroup context with an error state. - Another call to `HandleChange` completes, and uses the context syncronously, returning `context.Canceled`. - This in turn propagates to `doubleWalkDiff`, which returns the canceled error, instead of calling `Wait` which would contain the actual error group error. This behavior is racy, based on exactly how and when the asyncronous callback is called. The fix to this is relatively straightforward: we just need to make sure that syncronous and asyncronous function calls are kept separate, and don't reuse the same context. This means that a failure in an async callback, doesn't cause later sync ones to fail. Signed-off-by: Justin Chadwell --- diskwriter.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/diskwriter.go b/diskwriter.go index 10b60851..86ecb489 100644 --- a/diskwriter.go +++ b/diskwriter.go @@ -37,6 +37,7 @@ type DiskWriter struct { ctx context.Context cancel func() eg *errgroup.Group + egCtx context.Context filter FilterFunc dirModTimes map[string]int64 } @@ -50,13 +51,14 @@ func NewDiskWriter(ctx context.Context, dest string, opt DiskWriterOpt) (*DiskWr } ctx, cancel := context.WithCancel(ctx) - eg, ctx := errgroup.WithContext(ctx) + eg, egCtx := errgroup.WithContext(ctx) return &DiskWriter{ opt: opt, dest: dest, eg: eg, ctx: ctx, + egCtx: egCtx, cancel: cancel, filter: opt.Filter, dirModTimes: map[string]int64{}, @@ -188,7 +190,7 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er return errors.Wrapf(err, "failed to create %s", newPath) } if dw.opt.SyncDataCb != nil { - if err := dw.processChange(ChangeKindAdd, p, fi, file); err != nil { + if err := dw.processChange(dw.ctx, ChangeKindAdd, p, fi, file); err != nil { file.Close() return err } @@ -219,7 +221,7 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er dw.requestAsyncFileData(p, destPath, fi, &statCopy) } } else { - return dw.processChange(kind, p, fi, nil) + return dw.processChange(dw.ctx, kind, p, fi, nil) } return nil @@ -228,7 +230,7 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er func (dw *DiskWriter) requestAsyncFileData(p, dest string, fi os.FileInfo, st *types.Stat) { // todo: limit worker threads dw.eg.Go(func() error { - if err := dw.processChange(ChangeKindAdd, p, fi, &lazyFileWriter{ + if err := dw.processChange(dw.egCtx, ChangeKindAdd, p, fi, &lazyFileWriter{ dest: dest, }); err != nil { return err @@ -237,7 +239,7 @@ func (dw *DiskWriter) requestAsyncFileData(p, dest string, fi os.FileInfo, st *t }) } -func (dw *DiskWriter) processChange(kind ChangeKind, p string, fi os.FileInfo, w io.WriteCloser) error { +func (dw *DiskWriter) processChange(ctx context.Context, kind ChangeKind, p string, fi os.FileInfo, w io.WriteCloser) error { origw := w var hw *hashedWriter if dw.opt.NotifyCb != nil { @@ -252,7 +254,7 @@ func (dw *DiskWriter) processChange(kind ChangeKind, p string, fi os.FileInfo, w if fn == nil && dw.opt.AsyncDataCb != nil { fn = dw.opt.AsyncDataCb } - if err := fn(dw.ctx, p, w); err != nil { + if err := fn(ctx, p, w); err != nil { return err } } else { From e71925329f0fefc13ca47b0feab2b668b85bc6ff Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Mon, 15 Apr 2024 16:12:01 +0100 Subject: [PATCH 34/39] receive: translate unix-paths off the wire to native Then, inside all our validations, and our double walking, we don't have to worry about translating between different path formats. Additionally, we ensure that all received paths can be represented on the target host system. A path like `foo/bar\baz` can only be stored on linux (a dir with `foo`, containing a file `bar\baz`). On windows, this would later be interpreted to be `foo\bar\baz` (a dir with `foo`, containing a dir `bar`, containing a file `baz`). Signed-off-by: Justin Chadwell --- diff_containerd.go | 2 +- diskwriter.go | 2 +- receive.go | 14 +++++++++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/diff_containerd.go b/diff_containerd.go index 84fdc89d..86d64602 100644 --- a/diff_containerd.go +++ b/diff_containerd.go @@ -111,7 +111,7 @@ func doubleWalkDiff(ctx context.Context, changeFn ChangeFunc, a, b walkerFn, fil if filter != nil { filter(f2.path, &statCopy) } - f2copy = ¤tPath{path: filepath.FromSlash(f2.path), stat: &statCopy} + f2copy = ¤tPath{path: f2.path, stat: &statCopy} } k, p := pathChange(f1, f2copy) switch k { diff --git a/diskwriter.go b/diskwriter.go index 10b60851..36155725 100644 --- a/diskwriter.go +++ b/diskwriter.go @@ -98,7 +98,7 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er } }() - destPath := filepath.Join(dw.dest, filepath.FromSlash(p)) + destPath := filepath.Join(dw.dest, p) if kind == ChangeKindDelete { if dw.filter != nil { diff --git a/receive.go b/receive.go index 209d1d2f..3a42dfc0 100644 --- a/receive.go +++ b/receive.go @@ -4,7 +4,9 @@ import ( "context" "io" "os" + "path/filepath" "sync" + "syscall" "github.com/pkg/errors" "github.com/tonistiigi/fsutil/types" @@ -184,13 +186,23 @@ func (r *receiver) run(ctx context.Context) error { } break } + + // normalize unix wire-specific paths to platform-specific paths + path := filepath.FromSlash(p.Stat.Path) + if filepath.ToSlash(path) != p.Stat.Path { + // e.g. a linux path foo/bar\baz cannot be represented on windows + return errors.WithStack(&os.PathError{Path: p.Stat.Path, Err: syscall.EINVAL, Op: "unrepresentable path"}) + } + p.Stat.Path = path + if fileCanRequestData(os.FileMode(p.Stat.Mode)) { r.mu.Lock() r.files[p.Stat.Path] = i r.mu.Unlock() } i++ - cp := ¤tPath{path: p.Stat.Path, stat: p.Stat} + + cp := ¤tPath{path: path, stat: p.Stat} if err := r.orderValidator.HandleChange(ChangeKindAdd, cp.path, &StatInfo{cp.stat}, nil); err != nil { return err } From 86c9caf572e59c50eaaf9c1dc085378a8901340d Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Mon, 15 Apr 2024 16:17:37 +0100 Subject: [PATCH 35/39] chore: remove old windows todo comments Signed-off-by: Justin Chadwell --- diskwriter.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/diskwriter.go b/diskwriter.go index 36155725..6e9710ac 100644 --- a/diskwriter.go +++ b/diskwriter.go @@ -183,7 +183,7 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er } default: isRegularFile = true - file, err := os.OpenFile(newPath, os.O_CREATE|os.O_WRONLY, fi.Mode()) //todo: windows + file, err := os.OpenFile(newPath, os.O_CREATE|os.O_WRONLY, fi.Mode()) if err != nil { return errors.Wrapf(err, "failed to create %s", newPath) } @@ -313,7 +313,7 @@ type lazyFileWriter struct { func (lfw *lazyFileWriter) Write(dt []byte) (int, error) { if lfw.f == nil { - file, err := os.OpenFile(lfw.dest, os.O_WRONLY, 0) //todo: windows + file, err := os.OpenFile(lfw.dest, os.O_WRONLY, 0) if os.IsPermission(err) { // retry after chmod fi, er := os.Stat(lfw.dest) From 423880fb4055585a0f71731fbed2f1e38fba11c5 Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Tue, 16 Apr 2024 12:21:32 +0100 Subject: [PATCH 36/39] chore: add fsutil protocol description Signed-off-by: Justin Chadwell --- receive.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/receive.go b/receive.go index 3a42dfc0..2be245d7 100644 --- a/receive.go +++ b/receive.go @@ -1,3 +1,33 @@ +// send.go and receive.go describe the fsutil file-transfer protocol, which +// allows transferring file trees across a network connection. +// +// The protocol operates as follows: +// - The client (the receiver) connects to the server (the sender). +// - The sender walks the target tree lexicographically and sends a series of +// STAT packets that describe each file (an empty stat indicates EOF). +// - The receiver sends a REQ packet for each file it requires the contents for, +// using the ID for the file (determined as its index in the STAT sequence). +// - The sender sends a DATA packet with byte arrays for the contents of the +// file, associated with an ID (an empty array indicates EOF). +// - Once the receiver has received all files it wants, it sends a FIN packet, +// and the file transfer is complete. +// If an error is encountered on either side, an ERR packet is sent containing +// a human-readable error. +// +// All paths transferred over the protocol are normalized to unix-style paths, +// regardless of which platforms are present on either side. These path +// conversions are performed right before sending a STAT packet (for the +// sender) or right after receiving the corresponding STAT packet (for the +// receiver); this abstraction doesn't leak into the rest of fsutil, which +// operates on native platform-specific paths. +// +// Note that in the case of cross-platform file transfers, the transfer is +// best-effort. Some filenames that are valid on a unix sender would not be +// valid on a windows receiver, so these paths are rejected as they are +// received. Additionally, file metadata, like user/group owners and xattrs do +// not have an exact correspondence on windows, and so would be discarded by +// a windows receiver. + package fsutil import ( From 0c628a73427a72c903a9c616f87a48b56ee715ad Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Thu, 18 Apr 2024 17:01:18 +0100 Subject: [PATCH 37/39] recv: translate linkname to wire format Signed-off-by: Justin Chadwell --- receive.go | 1 + send.go | 1 + 2 files changed, 2 insertions(+) diff --git a/receive.go b/receive.go index 2be245d7..6a82d205 100644 --- a/receive.go +++ b/receive.go @@ -224,6 +224,7 @@ func (r *receiver) run(ctx context.Context) error { return errors.WithStack(&os.PathError{Path: p.Stat.Path, Err: syscall.EINVAL, Op: "unrepresentable path"}) } p.Stat.Path = path + p.Stat.Linkname = filepath.FromSlash(p.Stat.Linkname) if fileCanRequestData(os.FileMode(p.Stat.Mode)) { r.mu.Lock() diff --git a/send.go b/send.go index ba97ef7a..43bf1717 100644 --- a/send.go +++ b/send.go @@ -161,6 +161,7 @@ func (s *sender) walk(ctx context.Context) error { return errors.WithStack(&os.PathError{Path: path, Err: syscall.EBADMSG, Op: "fileinfo without stat info"}) } stat.Path = filepath.ToSlash(stat.Path) + stat.Linkname = filepath.ToSlash(stat.Linkname) p := &types.Packet{ Type: types.PACKET_STAT, Stat: stat, From 16fccd4daeb2a041a8729e72637e3483f349d91b Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Fri, 19 Apr 2024 16:25:41 -0700 Subject: [PATCH 38/39] fix hardlink filter regression With the refactor to FilterFS the hardlink handling was changed so that the hardlink detection is in a separate FS instance and then FilterFS is layered on top of it. This means that the file that was the source for the hardlink could be filtered out, but the Stat pointing to that link is still sent as is. New function validates if source files are not present in FS anymore and correct the linking. It could be better if all the FS implementation did this automatically, but there is quite a lot of layering going on atm. with multiple layers of FilterFS that would all need to keep own hardlink memory, so atm. the new function is only called before send. Signed-off-by: Tonis Tiigi --- hardlinks.go | 68 +++++++++++++++++++++++++++++++++++++++++++++++++ receive_test.go | 66 +++++++++++++++++++++++++++++++++++++++++++++++ send.go | 2 +- 3 files changed, 135 insertions(+), 1 deletion(-) diff --git a/hardlinks.go b/hardlinks.go index ef8bbfb5..d9bf2fc1 100644 --- a/hardlinks.go +++ b/hardlinks.go @@ -1,6 +1,9 @@ package fsutil import ( + "context" + "io" + gofs "io/fs" "os" "syscall" @@ -46,3 +49,68 @@ func (v *Hardlinks) HandleChange(kind ChangeKind, p string, fi os.FileInfo, err return nil } + +// WithHardlinkReset returns a FS that fixes hardlinks for FS that has been filtered +// so that original hardlink sources might be missing +func WithHardlinkReset(fs FS) FS { + return &hardlinkFilter{fs: fs} +} + +type hardlinkFilter struct { + fs FS +} + +var _ FS = &hardlinkFilter{} + +func (r *hardlinkFilter) Walk(ctx context.Context, target string, fn gofs.WalkDirFunc) error { + seenFiles := make(map[string]string) + return r.fs.Walk(ctx, target, func(path string, entry gofs.DirEntry, err error) error { + if err != nil { + return err + } + + fi, err := entry.Info() + if err != nil { + return err + } + + if fi.IsDir() || fi.Mode()&os.ModeSymlink != 0 { + return fn(path, entry, nil) + } + + stat, ok := fi.Sys().(*types.Stat) + if !ok { + return errors.WithStack(&os.PathError{Path: path, Err: syscall.EBADMSG, Op: "fileinfo without stat info"}) + } + + if stat.Linkname != "" { + if v, ok := seenFiles[stat.Linkname]; !ok { + seenFiles[stat.Linkname] = stat.Path + stat.Linkname = "" + entry = &dirEntryWithStat{DirEntry: entry, stat: stat} + } else { + if v != stat.Path { + stat.Linkname = v + entry = &dirEntryWithStat{DirEntry: entry, stat: stat} + } + } + } + + seenFiles[path] = stat.Path + + return fn(path, entry, nil) + }) +} + +func (r *hardlinkFilter) Open(p string) (io.ReadCloser, error) { + return r.fs.Open(p) +} + +type dirEntryWithStat struct { + gofs.DirEntry + stat *types.Stat +} + +func (d *dirEntryWithStat) Info() (gofs.FileInfo, error) { + return &StatInfo{d.stat}, nil +} diff --git a/receive_test.go b/receive_test.go index fdd69a14..d6988d81 100644 --- a/receive_test.go +++ b/receive_test.go @@ -216,6 +216,72 @@ func TestCopySwitchDirToFile(t *testing.T) { `, b.String()) } +func TestHardlinkFilter(t *testing.T) { + d, err := tmpDir(changeStream([]string{ + "ADD bar file data1", + "ADD foo file >bar", + "ADD foo2 file >bar", + })) + assert.NoError(t, err) + defer os.RemoveAll(d) + + assert.NoError(t, err) + defer os.RemoveAll(d) + fs, err := NewFS(d) + assert.NoError(t, err) + fs, err = NewFilterFS(fs, &FilterOpt{}) + assert.NoError(t, err) + fs, err = NewFilterFS(fs, &FilterOpt{ + IncludePatterns: []string{"foo*"}, + Map: func(_ string, s *types.Stat) MapResult { + s.Uid = 0 + s.Gid = 0 + return MapResultKeep + }, + }) + assert.NoError(t, err) + + dest := t.TempDir() + + eg, ctx := errgroup.WithContext(context.Background()) + s1, s2 := sockPairProto(ctx) + + eg.Go(func() error { + defer s1.(*fakeConnProto).closeSend() + return Send(ctx, s1, fs, nil) + }) + eg.Go(func() error { + return Receive(ctx, s2, dest, ReceiveOpt{ + Filter: func(p string, s *types.Stat) bool { + if p == "foo2" { + require.Equal(t, "foo", s.Linkname) + } + if runtime.GOOS != "windows" { + // On Windows, Getuid() and Getgid() always return -1 + // See: https://pkg.go.dev/os#Getgid + // See: https://pkg.go.dev/os#Geteuid + s.Uid = uint32(os.Getuid()) + s.Gid = uint32(os.Getgid()) + } + return true + }, + }) + }) + assert.NoError(t, eg.Wait()) + + dt, err := os.ReadFile(filepath.Join(dest, "foo")) + assert.NoError(t, err) + assert.Equal(t, "data1", string(dt)) + + st1, err := os.Stat(filepath.Join(dest, "foo")) + assert.NoError(t, err) + + st2, err := os.Stat(filepath.Join(dest, "foo2")) + assert.NoError(t, err) + + assert.True(t, os.SameFile(st1, st2)) +} + func TestCopySimple(t *testing.T) { d, err := tmpDir(changeStream([]string{ "ADD foo file data1", diff --git a/send.go b/send.go index 43bf1717..e4a31563 100644 --- a/send.go +++ b/send.go @@ -29,7 +29,7 @@ type Stream interface { func Send(ctx context.Context, conn Stream, fs FS, progressCb func(int, bool)) error { s := &sender{ conn: &syncStream{Stream: conn}, - fs: fs, + fs: WithHardlinkReset(fs), files: make(map[uint32]string), progressCb: progressCb, sendpipeline: make(chan *sendHandle, 128), From 235d76caf236618ba6b8255269f4bf764328dea3 Mon Sep 17 00:00:00 2001 From: Kieran Mann Date: Sat, 10 Oct 2026 00:06:33 -0700 Subject: [PATCH 39/39] ci: make the workflow run again (pinning, Docker Hub auth, FreeBSD runner) (#6) * ci: run CI for pushes to earthly-main and PRs into any branch The upstream merge took upstream's trigger block. That dropped the fork's push trigger (earthly-main, gh_test_ci) and limited pull_request to master/main. So CI no longer ran for PRs into earthly-main, including #3 itself. This brings back the push trigger from earthly-main (644b083). It also removes the branch filter from pull_request, so the workflow runs for every PR, whatever its base. A PR into earthly-main would have been enough, but stacked PRs (like this one, based on chore-merge-v0.14.1) need CI too. We keep upstream's workflow_dispatch and schedule triggers. Co-Authored-By: Claude Opus 5.5 (cherry picked from commit f2e55f54a795f34aa3e5dc34a8377c7f0df4b6fb) * ci: pin actions to full-length commit SHAs The EarthBuild org requires every action to be pinned to a full commit SHA; with tag pins every job failed at "Set up job" before running. Co-Authored-By: Claude Opus 5.5 (cherry picked from commit e66a226757f77ce33b857dccf7a9e8fb8ee7c2de) * ci: bump docker/bake-action to v5 for buildx >= 0.20 bake-action < v5 refuses to run with the buildx now shipped on ubuntu-latest runners. Co-Authored-By: Claude Opus 5.5 (cherry picked from commit e2fbb01d11b911a227279f31b9c6c853704609a0) * ci: log in to Docker Hub before bake to avoid pull rate limits Anonymous pulls of tonistiigi/xx and mvdan/shfmt hit Docker Hub's 429 rate limit on shared runners. Use the EarthBuild org's CI account (vars.DOCKERHUB_USERNAME / secrets.DOCKERHUB_TOKEN); skipped for fork PRs, which don't receive secrets. Co-Authored-By: Claude Opus 5.5 * ci: run FreeBSD tests in a vmactions VM on ubuntu-latest test-freebsd-amd64 targeted the retired macos-13 runner, so it queued forever and the workflow run never completed. Port upstream tonistiigi/fsutil's approach (vmactions/freebsd-vm on Linux) for the FreeBSD leg, keeping the fork's Go 1.21 and actions pinned by SHA. Co-Authored-By: Claude Opus 5.5 * ci: skip Go 1.21 on macOS where its test binaries cannot load Go 1.21 binaries abort with "dyld: missing LC_UUID load command" on the current macos-latest image before any test runs. Go 1.20 keeps macOS coverage; Windows and Linux still run 1.21. Co-Authored-By: Claude Opus 5.5 * ci: run OS tests with bash Force the test-os workflow step to use bash so a failing go test command is not masked by a later coverage command on Windows PowerShell. Signed-off-by: Tonis Tiigi (cherry picked from commit 818582ba80913f067501e4182f1f2b8da81edb99) Without a shell, windows-latest runs this step in PowerShell, where a failing native command does not stop the script: `go tool cover` succeeding overwrote `go test`'s exit code. Run 37996598366 (jobs 114044153320, 114044153527) logged `--- FAIL: TestHardlinkFilter` yet both Windows jobs concluded success. The other multi-command run steps already stop on failure: the FreeBSD Prepare step sets `shell: bash`, and the FreeBSD Test step runs on ubuntu-latest (default `bash -e`) with `set -ex` inside the VM script. Co-Authored-By: Claude Opus 5.5 * test: skip hardlink filter on Windows Windows stat handling does not populate hardlink linknames, so TestHardlinkFilter cannot assert the Unix hardlink normalization behavior there. Signed-off-by: Tonis Tiigi (cherry picked from commit a0b4937f7b24059165ad6cc9e149fe405feb3f5d) Cherry-picked rather than fixed: the failure is pre-existing upstream behaviour, not something the merge or #4 introduced. TestHardlinkFilter arrived with upstream 91a3fc4 (tonistiigi/fsutil#198), and setUnixOpt in stat_windows.go is a no-op, so the inode map that fills Stat.Linkname for hardlinks never runs on Windows. Upstream's own CI masked the failure the same way until tonistiigi/fsutil#259, which added `shell: bash` and this skip together (its bash-only run 26987953480 failed all three Windows test-os jobs). With the bash fix alone, workflow_dispatch probes failed the same way on Windows (Go 1.20 and 1.21) on upstream 91a3fc4 (run 38000549628) and on #4's code e66a226 (run 38000552489), with `foo2: The system cannot find the file specified.` and an empty Linkname where "foo" was expected. Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Claude Opus 5.5 Co-authored-by: Tonis Tiigi --- .github/workflows/ci.yml | 142 ++++++++++++++++++++++++--------------- receive_test.go | 4 ++ 2 files changed, 93 insertions(+), 53 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 283c4d4e..0af4d51d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,10 +11,13 @@ on: workflow_dispatch: schedule: - cron: '0 8 */6 * *' # every 6 days - pull_request: + push: branches: - - master - - main + - earthly-main + - gh_test_ci + # No branch filter, so PRs stacked on other PR branches + # (not just PRs into earthly-main) get CI too. + pull_request: jobs: validate: @@ -28,13 +31,22 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - + name: Login to Docker Hub + # Authenticated pulls avoid Docker Hub's anonymous rate limit (429). + # Secrets are unavailable to fork PRs, so skip the login there. + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + with: + username: ${{ vars.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 - name: Run - uses: docker/bake-action@v4 + uses: docker/bake-action@4a9a8d494466d37134e2bfca2d3a8de8fb2681ad # v5.13.0 with: targets: ${{ matrix.target }} @@ -48,13 +60,22 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - + name: Login to Docker Hub + # Authenticated pulls avoid Docker Hub's anonymous rate limit (429). + # Secrets are unavailable to fork PRs, so skip the login there. + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + with: + username: ${{ vars.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 - name: Run - uses: docker/bake-action@v4 + uses: docker/bake-action@4a9a8d494466d37134e2bfca2d3a8de8fb2681ad # v5.13.0 with: targets: ${{ matrix.target }} @@ -73,18 +94,27 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - + name: Login to Docker Hub + # Authenticated pulls avoid Docker Hub's anonymous rate limit (429). + # Secrets are unavailable to fork PRs, so skip the login there. + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + with: + username: ${{ vars.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 - name: Test - uses: docker/bake-action@v4 + uses: docker/bake-action@4a9a8d494466d37134e2bfca2d3a8de8fb2681ad # v5.13.0 with: targets: test-${{ matrix.mode }} - name: Upload coverage - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0 with: directory: ${{ env.DESTDIR }}/coverage flags: unit,${{ matrix.mode }},go-${{ matrix.go }} @@ -101,24 +131,30 @@ jobs: go: - "1.21" - "1.20" + exclude: + # Go 1.21 test binaries abort on current macOS runner images + # ("dyld: missing LC_UUID load command"); Go 1.20 still covers macOS. + - os: macos-latest + go: "1.21" steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 with: go-version: ${{ matrix.go }} - name: Test + shell: bash run: | go test -coverprofile="coverage.txt" -covermode="atomic" ./... go tool cover -func="coverage.txt" - name: Upload coverage if: always() - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0 with: file: ./coverage.txt env_vars: RUNNER_OS @@ -126,54 +162,54 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} test-freebsd-amd64: - runs-on: macos-13 + # macos-13 (and Vagrant/VirtualBox on it) is retired; run FreeBSD in a VM + # on Linux the way upstream tonistiigi/fsutil does. + runs-on: ubuntu-latest env: - VAGRANT_VAGRANTFILE: hack/Vagrantfile.freebsd13 + GO_VERSION: "1.21" steps: + - + name: Prepare + shell: bash + run: | + goVersion=$(curl --fail --silent --show-error "https://go.dev/dl/?mode=json&include=all" | jq -er --arg version "go${GO_VERSION}." '[.[] | select(.stable and (.version | startswith($version)))][0].version') + echo "GO_VERSION=${goVersion#go}" >> "$GITHUB_ENV" - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - - name: Cache Vagrant boxes - uses: actions/cache@v4 + name: Set up FreeBSD + uses: vmactions/freebsd-vm@c46abacb49f09938ca4e1702d15d836285d694cc # v1.5.9 with: - path: ~/.vagrant.d/boxes - key: ${{ runner.os }}-vagrant-${{ hashFiles('hack/Vagrantfile.freebsd13') }} - restore-keys: | - ${{ runner.os }}-vagrant- - - - name: Install Vagrant and VirtualBox - run: | - set -x - brew tap hashicorp/tap - brew install hashicorp/tap/hashicorp-vagrant - brew install --cask virtualbox - env: - HOMEBREW_NO_AUTO_UPDATE: 1 - HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1 - - - name: Check versions - run: | - set -x - vagrant --version - VBoxManage -v - - - name: Set up vagrant - run: | - vagrant up --no-tty + release: "14.5" + envs: GO_VERSION + custom-shell-name: bsd + prepare: | + set -ex + mkdir -p /var/tmp + chmod 1777 /var/tmp + archive="go${GO_VERSION}.freebsd-amd64.tar.gz" + fetch -o "/var/tmp/$archive" "https://go.dev/dl/$archive" + tar -C /var/tmp -xzf "/var/tmp/$archive" + pw useradd -n fsutil -m -s /bin/sh - name: Test - uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e # v3.0.0 - with: - timeout_minutes: 20 - max_attempts: 5 - command: | - vagrant ssh -- "cd /vagrant; go test -buildvcs=false -coverprofile=coverage.txt -covermode=atomic ./..." - vagrant ssh -c "sudo cat /vagrant/coverage.txt" > coverage.txt + run: | + bsd /dev/stdin <<'EOF' + set -ex + chown -R fsutil "$GITHUB_WORKSPACE" + su - fsutil -c ' + set -ex + cd "$1" + export TMPDIR=/var/tmp + export PATH="/var/tmp/go/bin:$PATH" + go test -buildvcs=false -coverprofile=coverage.txt -covermode=atomic ./... + ' sh "$GITHUB_WORKSPACE" + EOF - name: Upload coverage if: always() - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0 with: file: ./coverage.txt flags: unit,freebsd diff --git a/receive_test.go b/receive_test.go index 4f3d8e39..4ffff200 100644 --- a/receive_test.go +++ b/receive_test.go @@ -217,6 +217,10 @@ func TestCopySwitchDirToFile(t *testing.T) { } func TestHardlinkFilter(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("hardlink stat linknames are not populated on windows") + } + d, err := tmpDir(changeStream([]string{ "ADD bar file data1", "ADD foo file >bar",