pax_global_header00006660000000000000000000000064145664534740014534gustar00rootroot0000000000000052 comment=f73b49ed24a29ee1d6dbb0dd545f30a1f125fb31 golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/000077500000000000000000000000001456645347400224125ustar00rootroot00000000000000golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/.gitignore000066400000000000000000000004401456645347400244000ustar00rootroot00000000000000# 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 # support running go modules in vendor mode for local development vendor/ golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/.golangci.yml000066400000000000000000000003301456645347400247720ustar00rootroot00000000000000run: timeout: 10m skip-files: - ".*\\.pb\\.go$" linters: enable: - gofmt - govet - goimports - ineffassign - misspell - unused - staticcheck - typecheck disable-all: true golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/Dockerfile000066400000000000000000000014631456645347400244100ustar00rootroot00000000000000#syntax=docker/dockerfile:1 ARG GO_VERSION=1.20 FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.1.0 AS xx FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS base RUN apk add --no-cache git COPY --from=xx / / WORKDIR /src FROM base AS build ARG TARGETPLATFORM RUN --mount=target=. --mount=target=/go/pkg/mod,type=cache \ --mount=target=/root/.cache,type=cache \ xx-go build ./... 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} ./... 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 ./... FROM build golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/LICENSE000066400000000000000000000020671456645347400234240ustar00rootroot00000000000000MIT Copyright 2017 Tõnis Tiigi Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/bench/000077500000000000000000000000001456645347400234715ustar00rootroot00000000000000golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/bench/archive.go000066400000000000000000000000161456645347400254360ustar00rootroot00000000000000package bench golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/bench/bench_test.go000066400000000000000000000156211456645347400261430ustar00rootroot00000000000000package bench import ( "os" "os/exec" "testing" "github.com/containerd/continuity" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/reexec" ) func init() { reexec.Init() } func benchmarkInitialCopy(b *testing.B, fn func(string, string) error, size int) { baseDir := os.Getenv("BENCH_BASE_DIR") verify := os.Getenv("BENCH_VERIFY") == "1" for i := 0; i < b.N; i++ { b.StopTimer() tmpdir, err := createTestDir(size) if err != nil { b.Error(err) } destdir, err := os.MkdirTemp(baseDir, "destdir") if err != nil { os.RemoveAll(tmpdir) b.Error(err) } var m *continuity.Manifest if verify { ctx, err := continuity.NewContext(tmpdir) if err != nil { b.Error(err) } m, err = continuity.BuildManifest(ctx) if err != nil { b.Error(err) } } b.StartTimer() err = fn(tmpdir, destdir) if err != nil { b.Error(err) } b.StopTimer() if verify { ctx2, err := continuity.NewContext(destdir) if err != nil { b.Fatal(err) } err = continuity.VerifyManifest(ctx2, m) if err != nil { b.Error(err) } } os.RemoveAll(tmpdir) os.RemoveAll(destdir) } } func benchmarkIncrementalCopy(b *testing.B, fn func(string, string) error, size int) { b.StopTimer() baseDir := os.Getenv("BENCH_BASE_DIR") verify := os.Getenv("BENCH_VERIFY") == "1" tmpdir, err := createTestDir(size) if err != nil { b.Error(err) } destdir, err := os.MkdirTemp(baseDir, "destdir") if err != nil { os.RemoveAll(tmpdir) b.Error(err) } err = fn(tmpdir, destdir) if err != nil { b.Error(err) } defer os.RemoveAll(tmpdir) defer os.RemoveAll(destdir) for i := 0; i < b.N; i++ { if err := mutate(tmpdir, 2); err != nil { b.Error(err) } var m *continuity.Manifest if verify { ctx, err := continuity.NewContext(tmpdir) if err != nil { b.Error(err) } m, err = continuity.BuildManifest(ctx) if err != nil { b.Error(err) } } b.StartTimer() err = fn(tmpdir, destdir) if err != nil { b.Error(err) } b.StopTimer() if verify { ctx2, err := continuity.NewContext(destdir) if err != nil { b.Fatal(err) } err = continuity.VerifyManifest(ctx2, m) if err != nil { b.Error(err) } } } } func copyWithTar(src, dest string) error { return archive.NewDefaultArchiver().CopyWithTar(src, dest) } func cpa(src, dest string) error { cmd := exec.Command("cp", "-a", src+"/.", dest) return cmd.Run() } func rsync(src, dest string) error { cmd := exec.Command("rsync", "-a", "--del", src+"/.", dest) return cmd.Run() } func gnutar(src, dest string) error { tar := exec.Command("tar", "-cf", "-", "-C", src, ".") unpack := exec.Command("tar", "xf", "-", "-C", dest) stdout, err := tar.StdoutPipe() if err != nil { return err } unpack.Stdin = stdout go tar.Run() return unpack.Run() } func BenchmarkCopyWithTar10(b *testing.B) { benchmarkInitialCopy(b, copyWithTar, 10) } func BenchmarkCopyWithTar50(b *testing.B) { benchmarkInitialCopy(b, copyWithTar, 50) } func BenchmarkCopyWithTar200(b *testing.B) { benchmarkInitialCopy(b, copyWithTar, 200) } func BenchmarkCopyWithTar1000(b *testing.B) { benchmarkInitialCopy(b, copyWithTar, 1000) } // func chrootCopyWithTar(src, dest string) error { // return chrootarchive.NewArchiver(nil).CopyWithTar(src, dest) // } // func BenchmarkChrootCopyWithTar10(b *testing.B) { // benchmarkInitialCopy(b, chrootCopyWithTar, 10) // } // // func BenchmarkChrootCopyWithTar50(b *testing.B) { // benchmarkInitialCopy(b, chrootCopyWithTar, 50) // } // // func BenchmarkChrootCopyWithTar200(b *testing.B) { // benchmarkInitialCopy(b, chrootCopyWithTar, 200) // } // // func BenchmarkChrootCopyWithTar1000(b *testing.B) { // benchmarkInitialCopy(b, chrootCopyWithTar, 1000) // } func BenchmarkCPA10(b *testing.B) { benchmarkInitialCopy(b, cpa, 10) } func BenchmarkCPA50(b *testing.B) { benchmarkInitialCopy(b, cpa, 50) } func BenchmarkCPA200(b *testing.B) { benchmarkInitialCopy(b, cpa, 200) } func BenchmarkCPA1000(b *testing.B) { benchmarkInitialCopy(b, cpa, 1000) } func BenchmarkDiffCopy10(b *testing.B) { benchmarkInitialCopy(b, diffCopyReg, 10) } func BenchmarkDiffCopy50(b *testing.B) { benchmarkInitialCopy(b, diffCopyReg, 50) } func BenchmarkDiffCopy200(b *testing.B) { benchmarkInitialCopy(b, diffCopyReg, 200) } func BenchmarkDiffCopy1000(b *testing.B) { benchmarkInitialCopy(b, diffCopyReg, 1000) } func BenchmarkDiffCopyProto10(b *testing.B) { benchmarkInitialCopy(b, diffCopyProto, 10) } func BenchmarkDiffCopyProto50(b *testing.B) { benchmarkInitialCopy(b, diffCopyProto, 50) } func BenchmarkDiffCopyProto200(b *testing.B) { benchmarkInitialCopy(b, diffCopyProto, 200) } func BenchmarkDiffCopyProto1000(b *testing.B) { benchmarkInitialCopy(b, diffCopyProto, 1000) } func BenchmarkIncrementalDiffCopy10(b *testing.B) { benchmarkIncrementalCopy(b, diffCopyReg, 10) } func BenchmarkIncrementalDiffCopy50(b *testing.B) { benchmarkIncrementalCopy(b, diffCopyReg, 50) } func BenchmarkIncrementalDiffCopy200(b *testing.B) { benchmarkIncrementalCopy(b, diffCopyReg, 200) } func BenchmarkIncrementalDiffCopy1000(b *testing.B) { benchmarkIncrementalCopy(b, diffCopyReg, 1000) } func BenchmarkIncrementalDiffCopy5000(b *testing.B) { benchmarkIncrementalCopy(b, diffCopyReg, 5000) } func BenchmarkIncrementalDiffCopy10000(b *testing.B) { benchmarkIncrementalCopy(b, diffCopyReg, 10000) } func BenchmarkIncrementalCopyWithTar10(b *testing.B) { benchmarkIncrementalCopy(b, copyWithTar, 10) } func BenchmarkIncrementalCopyWithTar50(b *testing.B) { benchmarkIncrementalCopy(b, copyWithTar, 50) } func BenchmarkIncrementalCopyWithTar200(b *testing.B) { benchmarkIncrementalCopy(b, copyWithTar, 200) } func BenchmarkIncrementalCopyWithTar1000(b *testing.B) { benchmarkIncrementalCopy(b, copyWithTar, 1000) } func BenchmarkIncrementalRsync10(b *testing.B) { benchmarkIncrementalCopy(b, rsync, 10) } func BenchmarkIncrementalRsync50(b *testing.B) { benchmarkIncrementalCopy(b, rsync, 50) } func BenchmarkIncrementalRsync200(b *testing.B) { benchmarkIncrementalCopy(b, rsync, 200) } func BenchmarkIncrementalRsync1000(b *testing.B) { benchmarkIncrementalCopy(b, rsync, 1000) } func BenchmarkIncrementalRsync5000(b *testing.B) { benchmarkIncrementalCopy(b, rsync, 5000) } func BenchmarkIncrementalRsync10000(b *testing.B) { benchmarkIncrementalCopy(b, rsync, 10000) } func BenchmarkRsync10(b *testing.B) { benchmarkInitialCopy(b, rsync, 10) } func BenchmarkRsync50(b *testing.B) { benchmarkInitialCopy(b, rsync, 50) } func BenchmarkRsync200(b *testing.B) { benchmarkInitialCopy(b, rsync, 200) } func BenchmarkRsync1000(b *testing.B) { benchmarkInitialCopy(b, rsync, 1000) } func BenchmarkGnuTar10(b *testing.B) { benchmarkInitialCopy(b, gnutar, 10) } func BenchmarkGnuTar50(b *testing.B) { benchmarkInitialCopy(b, gnutar, 50) } func BenchmarkGnuTar200(b *testing.B) { benchmarkInitialCopy(b, gnutar, 200) } func BenchmarkGnuTar1000(b *testing.B) { benchmarkInitialCopy(b, gnutar, 1000) } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/bench/diffcopy.go000066400000000000000000000050441456645347400256260ustar00rootroot00000000000000package bench import ( "context" "github.com/pkg/errors" "github.com/tonistiigi/fsutil" fstypes "github.com/tonistiigi/fsutil/types" "golang.org/x/sync/errgroup" ) func diffCopy(proto bool, src, dest string) error { var s1, s2 fsutil.Stream eg, ctx := errgroup.WithContext(context.Background()) if proto { s1, s2 = sockPairProto(ctx) } else { s1, s2 = sockPair(ctx) } eg.Go(func() error { return fsutil.Send(ctx, s1, fsutil.NewFS(src, nil), nil) }) eg.Go(func() error { return fsutil.Receive(ctx, s2, dest, fsutil.ReceiveOpt{}) }) return eg.Wait() } func diffCopyProto(src, dest string) error { return diffCopy(true, src, dest) } func diffCopyReg(src, dest string) error { return diffCopy(false, src, dest) } func sockPair(ctx context.Context) (fsutil.Stream, fsutil.Stream) { c1 := make(chan *fstypes.Packet, 64) c2 := make(chan *fstypes.Packet, 64) return &fakeConn{ctx, c1, c2}, &fakeConn{ctx, c2, c1} } func sockPairProto(ctx context.Context) (fsutil.Stream, fsutil.Stream) { c1 := make(chan []byte, 64) c2 := make(chan []byte, 64) return &fakeConnProto{ctx, c1, c2}, &fakeConnProto{ctx, c2, c1} } type fakeConn struct { ctx context.Context recvChan chan *fstypes.Packet sendChan chan *fstypes.Packet } func (fc *fakeConn) Context() context.Context { return fc.ctx } func (fc *fakeConn) RecvMsg(m interface{}) error { p, ok := m.(*fstypes.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 return nil } } func (fc *fakeConn) SendMsg(m interface{}) error { p, ok := m.(*fstypes.Packet) if !ok { return errors.Errorf("invalid msg: %#v", m) } p2 := *p p2.Data = append([]byte{}, p2.Data...) select { case <-fc.ctx.Done(): return fc.ctx.Err() case fc.sendChan <- &p2: return nil } } type fakeConnProto struct { ctx context.Context recvChan chan []byte sendChan chan []byte } func (fc *fakeConnProto) Context() context.Context { return fc.ctx } func (fc *fakeConnProto) RecvMsg(m interface{}) error { p, ok := m.(*fstypes.Packet) if !ok { return errors.Errorf("invalid msg: %#v", m) } select { case <-fc.ctx.Done(): return fc.ctx.Err() case dt := <-fc.recvChan: return p.Unmarshal(dt) } } func (fc *fakeConnProto) SendMsg(m interface{}) error { p, ok := m.(*fstypes.Packet) if !ok { return errors.Errorf("invalid msg: %#v", m) } dt, err := p.Marshal() if err != nil { return err } select { case <-fc.ctx.Done(): return fc.ctx.Err() case fc.sendChan <- dt: return nil } } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/bench/go.mod000066400000000000000000000023751456645347400246060ustar00rootroot00000000000000module github.com/tonistiigi/fsutil/bench go 1.19 require ( github.com/containerd/continuity v0.3.1-0.20230206214859-2a963a2f56e8 github.com/docker/docker v20.10.18+incompatible github.com/pkg/errors v0.9.1 github.com/tonistiigi/fsutil v0.0.0-00010101000000-000000000000 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c ) require ( github.com/Microsoft/go-winio v0.5.2 // indirect github.com/Microsoft/hcsshim v0.8.17 // indirect github.com/containerd/cgroups v1.0.1 // indirect github.com/containerd/containerd v1.5.2 // indirect github.com/docker/go-units v0.4.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/moby/patternmatcher v0.5.0 // indirect github.com/moby/sys/mount v0.2.0 // indirect github.com/moby/sys/mountinfo v0.4.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.1 // indirect github.com/opencontainers/runc v1.0.0-rc93 // indirect github.com/sirupsen/logrus v1.8.1 // indirect go.opencensus.io v0.22.3 // indirect golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect google.golang.org/protobuf v1.26.0 // indirect ) replace github.com/tonistiigi/fsutil => ../ golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/bench/go.sum000066400000000000000000002716101456645347400246330ustar00rootroot00000000000000bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= github.com/Microsoft/hcsshim v0.8.17 h1:yFHH5bghP9ij5Y34PPaMOE8g//oXZ0uJQeMENVo2zcI= github.com/Microsoft/hcsshim v0.8.17/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= github.com/containerd/cgroups v1.0.1 h1:iJnMvco9XGvKUvNQkv88bE4uJXxRQH18efbKo9w5vHQ= github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= github.com/containerd/containerd v1.5.2 h1:MG/Bg1pbmMb61j3wHCFWPxESXHieiKr2xG64px/k8zQ= github.com/containerd/containerd v1.5.2/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= github.com/containerd/continuity v0.3.1-0.20230206214859-2a963a2f56e8 h1:EdSQb65ohzz4jsyPOhxfu3/+c9nnU0euk0otferwl9A= github.com/containerd/continuity v0.3.1-0.20230206214859-2a963a2f56e8/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v20.10.18+incompatible h1:SN84VYXTBNGn92T/QwIRPlum9zfemfitN7pbsp26WSc= github.com/docker/docker v20.10.18+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 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/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo= github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/sys/mount v0.2.0 h1:WhCW5B355jtxndN5ovugJlMFJawbUODuW8fSnEH6SSM= github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.4.1 h1:1O+1cHA1aujwEwwVMa2Xm2l+gIpUHyd3+D+d7LZh1kM= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc93 h1:x2UMpOOVf3kQ8arv/EsDGwim8PTNqzL1/EYDr/+scOM= github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 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-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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-20200625203802-6e8e738ad208/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.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 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= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/bench/util.go000066400000000000000000000044741456645347400250060ustar00rootroot00000000000000package bench import ( "crypto/rand" "encoding/hex" "errors" "math" mathrand "math/rand" "os" "path/filepath" "strconv" "sync" ) func createTestDir(n int) (string, error) { const nesting = 1.0 / 3.0 rootDir, err := os.MkdirTemp(os.Getenv("BENCH_BASE_DIR"), "diffcopy") if err != nil { return "", err } dirs := int(math.Ceil(math.Pow(float64(n), nesting))) if err := fillTestDir(rootDir, dirs, n); err != nil { os.RemoveAll(rootDir) return "", err } return rootDir, nil } func fillTestDir(root string, items, n int) error { if n <= items { for i := 0; i < items; i++ { fp := filepath.Join(root, randomID()) if err := writeFile(fp); err != nil { return err } } } else { sub := n / items for n > 0 { fp := filepath.Join(root, randomID()) if err := os.MkdirAll(fp, 0700); err != nil { return err } if n < sub { sub = n } if err := fillTestDir(fp, items, sub); err != nil { return err } n -= sub } } return nil } func randomID() string { b := make([]byte, 10) rand.Read(b) return hex.EncodeToString(b) } var buf []byte var once sync.Once func randBuf() []byte { once.Do(func() { var size int64 = 64 * 1024 if s, err := strconv.ParseInt(os.Getenv("BENCH_FILE_SIZE"), 10, 64); err == nil { size = s } buf = make([]byte, size) rand.Read(buf) }) return buf } func writeFile(p string) error { tf, err := os.Create(p) if err != nil { return err } if _, err := tf.Write(randBuf()); err != nil { return err } return tf.Close() } func mutate(root string, n int) error { del := n add := n mod := n stop := errors.New("") for { if err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { if !info.IsDir() { if mathrand.Intn(3) == 0 { switch mathrand.Intn(3) { case 0: if del > 0 { del-- os.RemoveAll(path) } case 1: if add > 0 { add-- fp := filepath.Join(filepath.Dir(path), randomID()) if err := writeFile(fp); err != nil { return err } } case 2: if mod > 0 { mod-- if err := writeFile(path); err != nil { return err } } } } } if add+mod+del == 0 { return stop } return nil }); err != nil { if err == stop { return nil } return err } } } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/chtimes_linux.go000066400000000000000000000006471456645347400256230ustar00rootroot00000000000000//go:build linux // +build linux package fsutil import ( "github.com/pkg/errors" "golang.org/x/sys/unix" ) func chtimes(path string, un int64) error { var utimes [2]unix.Timespec utimes[0] = unix.NsecToTimespec(un) utimes[1] = utimes[0] if err := unix.UtimesNanoAt(unix.AT_FDCWD, path, utimes[0:], unix.AT_SYMLINK_NOFOLLOW); err != nil { return errors.Wrap(err, "failed call to UtimesNanoAt") } return nil } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/chtimes_nolinux.go000066400000000000000000000005321456645347400261510ustar00rootroot00000000000000// +build !linux package fsutil import ( "os" "time" "github.com/pkg/errors" ) func chtimes(path string, un int64) error { mtime := time.Unix(0, un) fi, err := os.Lstat(path) if err != nil { return errors.WithStack(err) } if fi.Mode()&os.ModeSymlink != 0 { return nil } return errors.WithStack(os.Chtimes(path, mtime, mtime)) } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/cmd/000077500000000000000000000000001456645347400231555ustar00rootroot00000000000000golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/cmd/receive/000077500000000000000000000000001456645347400245775ustar00rootroot00000000000000golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/cmd/receive/receive.go000066400000000000000000000006131456645347400265500ustar00rootroot00000000000000package main import ( "context" "flag" "os" "github.com/tonistiigi/fsutil" "github.com/tonistiigi/fsutil/util" ) func main() { flag.Parse() if len(flag.Args()) == 0 { panic("dest path not set") } ctx := context.Background() s := util.NewProtoStream(ctx, os.Stdin, os.Stdout) if err := fsutil.Receive(ctx, s, flag.Args()[0], fsutil.ReceiveOpt{}); err != nil { panic(err) } } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/cmd/send/000077500000000000000000000000001456645347400241065ustar00rootroot00000000000000golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/cmd/send/send.go000066400000000000000000000006151456645347400253700ustar00rootroot00000000000000package main import ( "context" "flag" "os" "github.com/tonistiigi/fsutil" "github.com/tonistiigi/fsutil/util" ) func main() { flag.Parse() if len(flag.Args()) == 0 { panic("source path not set") } ctx := context.Background() s := util.NewProtoStream(ctx, os.Stdin, os.Stdout) if err := fsutil.Send(ctx, s, fsutil.NewFS(flag.Args()[0], nil), nil); err != nil { panic(err) } } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/cmd/walk/000077500000000000000000000000001456645347400241135ustar00rootroot00000000000000golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/cmd/walk/walk.go000066400000000000000000000011441456645347400254000ustar00rootroot00000000000000package main import ( "context" "flag" "os" "strings" "github.com/tonistiigi/fsutil" ) func main() { flag.Parse() if len(flag.Args()) == 0 { panic("source path not set") } var excludes []string if len(flag.Args()) > 1 { dt, err := os.ReadFile(flag.Args()[1]) if err != nil { panic(err) } excludes = strings.Split(string(dt), "\n") } if err := fsutil.Walk(context.Background(), flag.Args()[0], &fsutil.WalkOpt{ ExcludePatterns: excludes, }, func(path string, fi os.FileInfo, err error) error { if err != nil { return err } return nil }); err != nil { panic(err) } } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/copy/000077500000000000000000000000001456645347400233645ustar00rootroot00000000000000golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/copy/copy.go000066400000000000000000000400431456645347400246660ustar00rootroot00000000000000package fs import ( "context" "os" "path" "path/filepath" "runtime" "strings" "sync" "time" "github.com/containerd/continuity/fs" "github.com/moby/patternmatcher" "github.com/pkg/errors" "github.com/tonistiigi/fsutil" ) var bufferPool = &sync.Pool{ New: func() interface{} { buffer := make([]byte, 32*1024) return &buffer }, } func rootPath(root, p string, followLinks bool) (string, error) { p = filepath.Join("/", p) if p == "/" { return root, nil } if followLinks { return fs.RootPath(root, p) } d, f := filepath.Split(p) ppath, err := fs.RootPath(root, d) if err != nil { return "", err } return filepath.Join(ppath, f), nil } func ResolveWildcards(root, src string, followLinks bool) ([]string, error) { d1, d2 := splitWildcards(src) if d2 != "" { p, err := rootPath(root, d1, followLinks) if err != nil { return nil, err } matches, err := resolveWildcards(p, d2) if err != nil { return nil, err } for i, m := range matches { p, err := rel(root, m) if err != nil { return nil, err } matches[i] = p } return matches, nil } return []string{d1}, nil } // Copy copies files using `cp -a` semantics. // Copy is likely unsafe to be used in non-containerized environments. func Copy(ctx context.Context, srcRoot, src, dstRoot, dst string, opts ...Opt) error { var ci CopyInfo for _, o := range opts { o(&ci) } ensureDstPath := dst if d, f := filepath.Split(dst); f != "" && f != "." { ensureDstPath = d } if ensureDstPath != "" { ensureDstPath, err := fs.RootPath(dstRoot, ensureDstPath) if err != nil { return err } if err := MkdirAll(ensureDstPath, 0755, ci.Chown, ci.Utime); err != nil { return err } } dst, err := fs.RootPath(dstRoot, filepath.Clean(dst)) if err != nil { return err } c, err := newCopier(dstRoot, ci.Chown, ci.Utime, ci.Mode, ci.XAttrErrorHandler, ci.IncludePatterns, ci.ExcludePatterns, ci.ChangeFunc) if err != nil { return err } srcs := []string{src} if ci.AllowWildcards { matches, err := ResolveWildcards(srcRoot, src, ci.FollowLinks) if err != nil { return err } if len(matches) == 0 { return errors.Errorf("no matches found: %s", src) } srcs = matches } for _, src := range srcs { srcFollowed, err := rootPath(srcRoot, src, ci.FollowLinks) if err != nil { return err } dst, err := c.prepareTargetDir(srcFollowed, src, dst, ci.CopyDirContents) if err != nil { return err } if err := c.copy(ctx, srcFollowed, "", dst, false, patternmatcher.MatchInfo{}, patternmatcher.MatchInfo{}); err != nil { return err } } return nil } func (c *copier) prepareTargetDir(srcFollowed, src, destPath string, copyDirContents bool) (string, error) { fiSrc, err := os.Lstat(srcFollowed) if err != nil { return "", err } fiDest, err := os.Stat(destPath) if err != nil { if !os.IsNotExist(err) { return "", errors.Wrap(err, "failed to lstat destination path") } } if (!copyDirContents && fiSrc.IsDir() && fiDest != nil) || (!fiSrc.IsDir() && fiDest != nil && fiDest.IsDir()) { destPath = filepath.Join(destPath, filepath.Base(src)) } target := filepath.Dir(destPath) if copyDirContents && fiSrc.IsDir() && fiDest == nil { target = destPath } if err := MkdirAll(target, 0755, c.chown, c.utime); err != nil { return "", err } return destPath, nil } type User struct { UID, GID int SID string } type Chowner func(*User) (*User, error) type XAttrErrorHandler func(dst, src, xattrKey string, err error) error type CopyInfo struct { Chown Chowner Utime *time.Time AllowWildcards bool Mode *int XAttrErrorHandler XAttrErrorHandler CopyDirContents bool FollowLinks bool // Include only files/dirs matching at least one of these patterns IncludePatterns []string // Exclude files/dir matching any of these patterns (even if they match an include pattern) ExcludePatterns []string ChangeFunc fsutil.ChangeFunc } type Opt func(*CopyInfo) func WithCopyInfo(ci CopyInfo) func(*CopyInfo) { return func(c *CopyInfo) { *c = ci } } func WithChown(uid, gid int) Opt { return func(ci *CopyInfo) { ci.Chown = func(*User) (*User, error) { return &User{UID: uid, GID: gid}, nil } } } func AllowWildcards(ci *CopyInfo) { ci.AllowWildcards = true } func WithXAttrErrorHandler(h XAttrErrorHandler) Opt { return func(ci *CopyInfo) { ci.XAttrErrorHandler = h } } func AllowXAttrErrors(ci *CopyInfo) { h := func(string, string, string, error) error { return nil } WithXAttrErrorHandler(h)(ci) } func WithIncludePattern(includePattern string) Opt { return func(ci *CopyInfo) { ci.IncludePatterns = append(ci.IncludePatterns, includePattern) } } func WithExcludePattern(excludePattern string) Opt { return func(ci *CopyInfo) { ci.ExcludePatterns = append(ci.ExcludePatterns, excludePattern) } } func WithChangeNotifier(fn fsutil.ChangeFunc) Opt { return func(ci *CopyInfo) { ci.ChangeFunc = fn } } 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 } type parentDir struct { srcPath string dstPath string copied bool } func newCopier(root string, chown Chowner, tm *time.Time, mode *int, xeh XAttrErrorHandler, includePatterns, excludePatterns []string, changeFunc fsutil.ChangeFunc) (*copier, error) { if xeh == nil { xeh = func(dst, src, key string, err error) error { return err } } var includePatternMatcher *patternmatcher.PatternMatcher if len(includePatterns) != 0 { var err error includePatternMatcher, err = patternmatcher.New(includePatterns) if err != nil { return nil, errors.Wrapf(err, "invalid includepatterns: %s", includePatterns) } } var excludePatternMatcher *patternmatcher.PatternMatcher if len(excludePatterns) != 0 { var err error excludePatternMatcher, err = patternmatcher.New(excludePatterns) if err != nil { return nil, errors.Wrapf(err, "invalid excludepatterns: %s", excludePatterns) } } return &copier{ root: root, inodes: map[uint64]string{}, chown: chown, utime: tm, xattrErrorHandler: xeh, mode: mode, includePatternMatcher: includePatternMatcher, excludePatternMatcher: excludePatternMatcher, changefn: changeFunc, }, nil } // dest is always clean func (c *copier) copy(ctx context.Context, src, srcComponents, target string, overwriteTargetMetadata bool, parentIncludeMatchInfo, parentExcludeMatchInfo patternmatcher.MatchInfo) error { select { case <-ctx.Done(): return ctx.Err() default: } fi, err := os.Lstat(src) if err != nil { return errors.Wrapf(err, "failed to stat %s", src) } targetFi, err := os.Lstat(target) if err != nil && !os.IsNotExist(err) { return errors.Wrapf(err, "failed to stat %s", src) } include := true var ( includeMatchInfo patternmatcher.MatchInfo excludeMatchInfo patternmatcher.MatchInfo ) if srcComponents != "" { matchesIncludePattern := false matchesExcludePattern := false matchesIncludePattern, includeMatchInfo, err = c.include(srcComponents, fi, parentIncludeMatchInfo) if err != nil { return err } include = matchesIncludePattern matchesExcludePattern, excludeMatchInfo, err = c.exclude(srcComponents, fi, parentExcludeMatchInfo) if err != nil { return err } if matchesExcludePattern { include = false } } if include { if err := c.createParentDirs(src, srcComponents, target, overwriteTargetMetadata); err != nil { return err } } if !fi.IsDir() { if !include { return nil } if err := ensureEmptyFileTarget(target); err != nil { return err } } copyFileInfo := include restoreFileTimestamp := false notify := true switch { case fi.IsDir(): if created, err := c.copyDirectory( ctx, src, srcComponents, target, fi, overwriteTargetMetadata, include, includeMatchInfo, excludeMatchInfo, ); err != nil { return err } else if !overwriteTargetMetadata { // if we aren't supposed to overwrite existing target metadata, // then we only need to copy the new file info if we newly created // it, or restore the previous file timestamp if not copyFileInfo = created restoreFileTimestamp = !created } notify = false case (fi.Mode() & os.ModeType) == 0: link, err := getLinkSource(target, fi, c.inodes) if err != nil { return errors.Wrap(err, "failed to get hardlink") } if link != "" { if err := os.Link(link, target); err != nil { return errors.Wrap(err, "failed to create hard link") } } else if err := copyFile(src, target); err != nil { return errors.Wrap(err, "failed to copy files") } case (fi.Mode() & os.ModeSymlink) == os.ModeSymlink: link, err := os.Readlink(src) if err != nil { return errors.Wrapf(err, "failed to read link: %s", src) } if err := os.Symlink(link, target); err != nil { return errors.Wrapf(err, "failed to create symlink: %s", target) } case (fi.Mode() & os.ModeDevice) == os.ModeDevice, (fi.Mode() & os.ModeNamedPipe) == os.ModeNamedPipe, (fi.Mode() & os.ModeSocket) == os.ModeSocket: if err := copyDevice(target, fi); err != nil { return errors.Wrapf(err, "failed to create device") } } if copyFileInfo { if err := c.copyFileInfo(fi, src, target); err != nil { return errors.Wrap(err, "failed to copy file info") } if err := copyXAttrs(target, src, c.xattrErrorHandler); err != nil { return errors.Wrap(err, "failed to copy xattrs") } } else if restoreFileTimestamp && targetFi != nil { if err := c.copyFileTimestamp(fi, target); err != nil { return errors.Wrap(err, "failed to restore file timestamp") } } if notify { if err := c.notifyChange(target, fi); err != nil { return err } } return nil } func (c *copier) notifyChange(target string, fi os.FileInfo) error { if c.changefn != nil { if err := c.changefn(fsutil.ChangeKindAdd, path.Clean(strings.TrimPrefix(target, c.root)), fi, nil); err != nil { return errors.Wrap(err, "failed to notify file change") } } return nil } func (c *copier) include(path string, fi os.FileInfo, parentIncludeMatchInfo patternmatcher.MatchInfo) (bool, patternmatcher.MatchInfo, error) { if c.includePatternMatcher == nil { return true, patternmatcher.MatchInfo{}, nil } m, matchInfo, err := c.includePatternMatcher.MatchesUsingParentResults(path, parentIncludeMatchInfo) if err != nil { return false, matchInfo, errors.Wrap(err, "failed to match includepatterns") } return m, matchInfo, nil } func (c *copier) exclude(path string, fi os.FileInfo, parentExcludeMatchInfo patternmatcher.MatchInfo) (bool, patternmatcher.MatchInfo, error) { if c.excludePatternMatcher == nil { return false, patternmatcher.MatchInfo{}, nil } m, matchInfo, err := c.excludePatternMatcher.MatchesUsingParentResults(path, parentExcludeMatchInfo) if err != nil { return false, matchInfo, errors.Wrap(err, "failed to match excludepatterns") } return m, matchInfo, nil } // Delayed creation of parent directories when a file or dir matches an include // pattern. func (c *copier) createParentDirs(src, srcComponents, target string, overwriteTargetMetadata bool) error { for i, parentDir := range c.parentDirs { if parentDir.copied { continue } fi, err := os.Stat(parentDir.srcPath) if err != nil { return errors.Wrapf(err, "failed to stat %s", src) } if !fi.IsDir() { return errors.Errorf("%s is not a directory", parentDir.srcPath) } created, err := copyDirectoryOnly(parentDir.srcPath, parentDir.dstPath, fi, overwriteTargetMetadata) if err != nil { return err } if created { if err := c.copyFileInfo(fi, parentDir.srcPath, parentDir.dstPath); err != nil { return errors.Wrap(err, "failed to copy file info") } if err := copyXAttrs(parentDir.dstPath, parentDir.srcPath, c.xattrErrorHandler); err != nil { return errors.Wrap(err, "failed to copy xattrs") } } c.parentDirs[i].copied = true } return nil } func (c *copier) copyDirectory( ctx context.Context, src string, srcComponents string, dst string, stat os.FileInfo, overwriteTargetMetadata bool, include bool, includeMatchInfo patternmatcher.MatchInfo, excludeMatchInfo patternmatcher.MatchInfo, ) (bool, error) { if !stat.IsDir() { return false, errors.Errorf("source is not directory") } created := false parentDir := parentDir{ srcPath: src, dstPath: dst, } // If this directory passed include/exclude matching directly, go ahead // and create the directory. Otherwise, delay to handle include // patterns like a/*/c where we do not want to create a/b until we // encounter a/b/c. if include { var err error created, err = copyDirectoryOnly(src, dst, stat, overwriteTargetMetadata) if err != nil { return created, err } if created || overwriteTargetMetadata { if err := c.notifyChange(dst, stat); err != nil { return created, err } } parentDir.copied = true } c.parentDirs = append(c.parentDirs, parentDir) defer func() { c.parentDirs = c.parentDirs[:len(c.parentDirs)-1] }() fis, err := os.ReadDir(src) if err != nil { return false, errors.Wrapf(err, "failed to read %s", src) } for _, fi := range fis { if err := c.copy( ctx, filepath.Join(src, fi.Name()), filepath.Join(srcComponents, fi.Name()), filepath.Join(dst, fi.Name()), true, includeMatchInfo, excludeMatchInfo, ); err != nil { return false, err } } return created, nil } func copyDirectoryOnly(src, dst string, stat os.FileInfo, overwriteTargetMetadata bool) (bool, error) { if st, err := os.Lstat(dst); err != nil { if !os.IsNotExist(err) { return false, err } if err := os.Mkdir(dst, stat.Mode()); err != nil { return false, errors.Wrapf(err, "failed to mkdir %s", dst) } return true, nil } else if !st.IsDir() { return false, errors.Errorf("cannot copy to non-directory: %s", dst) } else if overwriteTargetMetadata { if err := os.Chmod(dst, stat.Mode()); err != nil { return false, errors.Wrapf(err, "failed to chmod on %s", dst) } } return false, nil } func ensureEmptyFileTarget(dst string) error { fi, err := os.Lstat(dst) if err != nil { if os.IsNotExist(err) { return nil } return errors.Wrap(err, "failed to lstat file target") } if fi.IsDir() { return errors.Errorf("cannot replace to directory %s with file", dst) } return os.Remove(dst) } func containsWildcards(name string) bool { isWindows := runtime.GOOS == "windows" for i := 0; i < len(name); i++ { ch := name[i] if ch == '\\' && !isWindows { i++ } else if ch == '*' || ch == '?' || ch == '[' { return true } } return false } func splitWildcards(p string) (d1, d2 string) { parts := strings.Split(filepath.Join(p), string(filepath.Separator)) var p1, p2 []string var found bool for _, p := range parts { if !found && containsWildcards(p) { found = true } if p == "" { p = "/" } if !found { p1 = append(p1, p) } else { p2 = append(p2, p) } } return filepath.Join(p1...), filepath.Join(p2...) } func resolveWildcards(basePath, comp string) ([]string, error) { var out []string err := filepath.Walk(basePath, func(path string, info os.FileInfo, err error) error { if err != nil { return err } rel, err := rel(basePath, path) if err != nil { return err } if rel == "." { return nil } if match, _ := filepath.Match(comp, rel); !match { return nil } out = append(out, path) if info.IsDir() { return filepath.SkipDir } return nil }) if err != nil { return nil, err } return out, nil } // rel makes a path relative to base path. Same as `filepath.Rel` but can also // handle UUID paths in windows. func rel(basepath, targpath string) (string, error) { // filepath.Rel can't handle UUID paths in windows if runtime.GOOS == "windows" { pfx := basepath + `\` if strings.HasPrefix(targpath, pfx) { p := strings.TrimPrefix(targpath, pfx) if p == "" { p = "." } return p, nil } } return filepath.Rel(basepath, targpath) } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/copy/copy_darwin.go000066400000000000000000000016171456645347400262360ustar00rootroot00000000000000//go:build darwin // +build darwin package fs import ( "io" "os" "github.com/pkg/errors" "golang.org/x/sys/unix" ) 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 { return err } } else { return nil } src, err := os.Open(source) if err != nil { return errors.Wrapf(err, "failed to open source %s", source) } defer src.Close() tgt, err := os.Create(target) if err != nil { return errors.Wrapf(err, "failed to open target %s", target) } defer tgt.Close() return copyFileContent(tgt, src) } func copyFileContent(dst, src *os.File) error { buf := bufferPool.Get().(*[]byte) _, err := io.CopyBuffer(dst, src, *buf) bufferPool.Put(buf) return err } func mknod(dst string, mode uint32, rDev int) error { return unix.Mknod(dst, uint32(mode), rDev) } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/copy/copy_freebsd.go000066400000000000000000000013441456645347400263610ustar00rootroot00000000000000//go:build freebsd // +build freebsd package fs import ( "io" "os" "github.com/pkg/errors" "golang.org/x/sys/unix" ) func copyFile(source, target string) error { src, err := os.Open(source) if err != nil { return errors.Wrapf(err, "failed to open source %s", source) } defer src.Close() tgt, err := os.Create(target) if err != nil { return errors.Wrapf(err, "failed to open target %s", target) } defer tgt.Close() return copyFileContent(tgt, src) } func copyFileContent(dst, src *os.File) error { buf := bufferPool.Get().(*[]byte) _, err := io.CopyBuffer(dst, src, *buf) bufferPool.Put(buf) return err } func mknod(dst string, mode uint32, rDev int) error { return unix.Mknod(dst, uint32(mode), uint64(rDev)) } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/copy/copy_linux.go000066400000000000000000000053171456645347400261120ustar00rootroot00000000000000package fs import ( "io" "math" "os" "syscall" "github.com/pkg/errors" "golang.org/x/sys/unix" ) func getUIDGID(fi os.FileInfo) (uid, gid int) { st := fi.Sys().(*syscall.Stat_t) return int(st.Uid), int(st.Gid) } func (c *copier) copyFileInfo(fi os.FileInfo, src, name string) error { chown := c.chown uid, gid := getUIDGID(fi) old := &User{UID: uid, GID: gid} if chown == nil { chown = func(u *User) (*User, error) { return u, nil } } if err := Chown(name, old, chown); err != nil { return errors.Wrapf(err, "failed to chown %s", name) } m := fi.Mode() if c.mode != nil { m = (m & ^os.FileMode(0777)) | os.FileMode(*c.mode&0777) } if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink { if err := os.Chmod(name, m); err != nil { return errors.Wrapf(err, "failed to chmod %s", name) } } if err := c.copyFileTimestamp(fi, name); err != nil { return err } return nil } func (c *copier) copyFileTimestamp(fi os.FileInfo, name string) error { if c.utime != nil { return Utimes(name, c.utime) } st := fi.Sys().(*syscall.Stat_t) timespec := []unix.Timespec{unix.Timespec(StatAtime(st)), unix.Timespec(StatMtime(st))} if err := unix.UtimesNanoAt(unix.AT_FDCWD, name, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil { return errors.Wrapf(err, "failed to utime %s", name) } return nil } func copyFile(source, target string) error { src, err := os.Open(source) if err != nil { return errors.Wrapf(err, "failed to open source %s", source) } defer src.Close() tgt, err := os.Create(target) if err != nil { return errors.Wrapf(err, "failed to open target %s", target) } defer tgt.Close() return copyFileContent(tgt, src) } func copyFileContent(dst, src *os.File) error { st, err := src.Stat() if err != nil { return errors.Wrap(err, "unable to stat source") } var written int64 size := st.Size() first := true for written < size { var desired int if size-written > math.MaxInt32 { desired = int(math.MaxInt32) } else { desired = int(size - written) } n, err := unix.CopyFileRange(int(src.Fd()), nil, int(dst.Fd()), nil, desired, 0) if err != nil { // matches go/src/internal/poll/copy_file_range_linux.go if (err != unix.ENOSYS && err != unix.EXDEV && err != unix.EPERM && err != syscall.EIO && err != unix.EOPNOTSUPP && err != syscall.EINVAL) || !first { return errors.Wrap(err, "copy file range failed") } buf := bufferPool.Get().(*[]byte) _, err = io.CopyBuffer(dst, src, *buf) bufferPool.Put(buf) if err != nil { return errors.Wrap(err, "userspace copy failed") } return nil } first = false written += int64(n) } return nil } func mknod(dst string, mode uint32, rDev int) error { return unix.Mknod(dst, uint32(mode), rDev) } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/copy/copy_nowindows.go000066400000000000000000000022141456645347400267730ustar00rootroot00000000000000//go:build !windows // +build !windows package fs import ( "os" "syscall" "github.com/pkg/errors" "github.com/containerd/continuity/sysx" ) // copyXAttrs requires xeh to be non-nil func copyXAttrs(dst, src string, xeh XAttrErrorHandler) error { xattrKeys, err := sysx.LListxattr(src) if err != nil { return xeh(dst, src, "", errors.Wrapf(err, "failed to list xattrs on %s", src)) } for _, xattr := range xattrKeys { data, err := sysx.LGetxattr(src, xattr) if err != nil { return xeh(dst, src, xattr, errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src)) } if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil { return xeh(dst, src, xattr, errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst)) } } return nil } func copyDevice(dst string, fi os.FileInfo) error { st, ok := fi.Sys().(*syscall.Stat_t) if !ok { return errors.New("unsupported stat type") } var rDev int if fi.Mode()&os.ModeDevice == os.ModeDevice || fi.Mode()&os.ModeCharDevice == os.ModeCharDevice { rDev = int(st.Rdev) } mode := st.Mode mode &^= syscall.S_IFSOCK // socket copied as stub return mknod(dst, uint32(mode), rDev) } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/copy/copy_test.go000066400000000000000000000400751456645347400257320ustar00rootroot00000000000000package fs import ( "context" _ "crypto/sha256" "fmt" "os" "path/filepath" "runtime" "sort" "strings" "testing" "time" "github.com/containerd/continuity/fs/fstest" "github.com/pkg/errors" "github.com/stretchr/testify/require" "github.com/tonistiigi/fsutil" ) // requiresRoot skips tests that require root func requiresRoot(t *testing.T) { t.Helper() if os.Getuid() != 0 { t.Skip("skipping test that requires root") return } } // TODO: Create copy directory which requires privilege // chown // mknod // setxattr fstest.SetXAttr("/home", "trusted.overlay.opaque", "y"), func TestCopyDirectory(t *testing.T) { apply := fstest.Apply( fstest.CreateDir("/etc/", 0755), fstest.CreateFile("/etc/hosts", []byte("localhost 127.0.0.1"), 0644), fstest.Link("/etc/hosts", "/etc/hosts.allow"), fstest.CreateDir("/usr/local/lib", 0755), fstest.CreateFile("/usr/local/lib/libnothing.so", []byte{0x00, 0x00}, 0755), fstest.Symlink("libnothing.so", "/usr/local/lib/libnothing.so.2"), fstest.CreateDir("/home", 0755), ) exp := filepath.FromSlash("add:/etc,add:/etc/hosts,add:/etc/hosts.allow,add:/home,add:/usr,add:/usr/local,add:/usr/local/lib,add:/usr/local/lib/libnothing.so,add:/usr/local/lib/libnothing.so.2") if err := testCopy(t, apply, exp); err != nil { t.Fatalf("Copy test failed: %+v", err) } } // This test used to fail because link-no-nothing.txt would be copied first, // then file operations in dst during the CopyDir would follow the symlink and // fail. func TestCopyDirectoryWithLocalSymlink(t *testing.T) { apply := fstest.Apply( fstest.CreateFile("nothing.txt", []byte{0x00, 0x00}, 0755), fstest.Symlink("nothing.txt", "link-no-nothing.txt"), ) exp := filepath.FromSlash("add:/link-no-nothing.txt,add:/nothing.txt") if err := testCopy(t, apply, exp); err != nil { t.Fatalf("Copy test failed: %+v", err) } } func TestCopyToWorkDir(t *testing.T) { t1 := t.TempDir() apply := fstest.Apply( fstest.CreateFile("foo.txt", []byte("contents"), 0755), ) require.NoError(t, apply.Apply(t1)) t2 := t.TempDir() err := Copy(context.TODO(), t1, "foo.txt", t2, "foo.txt") require.NoError(t, err) err = fstest.CheckDirectoryEqual(t1, t2) require.NoError(t, err) } func TestCopySingleFile(t *testing.T) { t1 := t.TempDir() apply := fstest.Apply( fstest.CreateFile("foo.txt", []byte("contents"), 0755), ) require.NoError(t, apply.Apply(t1)) t2 := t.TempDir() err := Copy(context.TODO(), t1, "foo.txt", t2, "/") require.NoError(t, err) err = fstest.CheckDirectoryEqual(t1, t2) require.NoError(t, err) t3 := t.TempDir() err = Copy(context.TODO(), t1, "foo.txt", t3, "foo.txt") require.NoError(t, err) err = fstest.CheckDirectoryEqual(t1, t2) require.NoError(t, err) t4 := t.TempDir() err = Copy(context.TODO(), t1, "foo.txt", t4, "foo2.txt") require.NoError(t, err) _, err = os.Stat(filepath.Join(t4, "foo2.txt")) require.NoError(t, err) ch := &changeCollector{} err = Copy(context.TODO(), t1, "foo.txt", t4, "a/b/c/foo2.txt", WithChangeNotifier(ch.onChange)) require.NoError(t, err) _, err = os.Stat(filepath.Join(t4, "a/b/c/foo2.txt")) require.NoError(t, err) require.Equal(t, filepath.FromSlash("add:/a/b/c/foo2.txt"), ch.String()) } func TestCopyOverrideFile(t *testing.T) { t1 := t.TempDir() apply := fstest.Apply( fstest.CreateFile("foo.txt", []byte("contents"), 0755), ) require.NoError(t, apply.Apply(t1)) t2 := t.TempDir() err := Copy(context.TODO(), t1, "foo.txt", t2, "foo.txt") require.NoError(t, err) err = fstest.CheckDirectoryEqual(t1, t2) require.NoError(t, err) err = Copy(context.TODO(), t1, "foo.txt", t2, "foo.txt") require.NoError(t, err) err = fstest.CheckDirectoryEqual(t1, t2) require.NoError(t, err) err = Copy(context.TODO(), t1, "/.", t2, "/") require.NoError(t, err) err = fstest.CheckDirectoryEqual(t1, t2) require.NoError(t, err) } func TestCopyDirectoryBasename(t *testing.T) { t1 := t.TempDir() apply := fstest.Apply( fstest.CreateDir("foo", 0755), fstest.CreateDir("foo/bar", 0755), fstest.CreateFile("foo/bar/baz.txt", []byte("contents"), 0755), ) require.NoError(t, apply.Apply(t1)) t2 := t.TempDir() ch := &changeCollector{} err := Copy(context.TODO(), t1, "foo", t2, "foo", WithChangeNotifier(ch.onChange)) require.NoError(t, err) err = fstest.CheckDirectoryEqual(t1, t2) require.NoError(t, err) require.Equal(t, filepath.FromSlash("add:/foo,add:/foo/bar,add:/foo/bar/baz.txt"), ch.String()) ch = &changeCollector{} err = Copy(context.TODO(), t1, "foo", t2, "foo", WithCopyInfo(CopyInfo{ CopyDirContents: true, ChangeFunc: ch.onChange, })) require.NoError(t, err) require.Equal(t, filepath.FromSlash("add:/foo/bar,add:/foo/bar/baz.txt"), ch.String()) err = fstest.CheckDirectoryEqual(t1, t2) require.NoError(t, err) } func TestCopyWildcards(t *testing.T) { t1 := t.TempDir() apply := fstest.Apply( fstest.CreateFile("foo.txt", []byte("foo-contents"), 0755), fstest.CreateFile("foo.go", []byte("go-contents"), 0755), fstest.CreateFile("bar.txt", []byte("bar-contents"), 0755), ) require.NoError(t, apply.Apply(t1)) t2 := t.TempDir() err := Copy(context.TODO(), t1, "foo*", t2, "/") require.Error(t, err) err = Copy(context.TODO(), t1, "foo*", t2, "/", AllowWildcards) require.NoError(t, err) _, err = os.Stat(filepath.Join(t2, "foo.txt")) require.NoError(t, err) _, err = os.Stat(filepath.Join(t2, "foo.go")) require.NoError(t, err) _, err = os.Stat(filepath.Join(t2, "bar.txt")) require.Error(t, err) require.True(t, os.IsNotExist(err)) t3 := t.TempDir() err = Copy(context.TODO(), t1, "bar*", t3, "foo.txt", AllowWildcards) require.NoError(t, err) dt, err := os.ReadFile(filepath.Join(t3, "foo.txt")) require.NoError(t, err) require.Equal(t, "bar-contents", string(dt)) } func TestCopyExistingDirDest(t *testing.T) { if os.Getuid() != 0 { t.Skip() } t1 := t.TempDir() apply := fstest.Apply( fstest.CreateDir("dir", 0755), fstest.CreateFile("dir/foo.txt", []byte("foo-contents"), 0644), fstest.CreateFile("dir/bar.txt", []byte("bar-contents"), 0644), ) require.NoError(t, apply.Apply(t1)) t2 := t.TempDir() apply = fstest.Apply( // notice how perms for destination and source are different fstest.CreateDir("dir", 0700), // dir/foo.txt does not exist, but dir/bar.txt does // notice how both perms and contents for destination and source are different fstest.CreateFile("dir/bar.txt", []byte("old-bar-contents"), 0600), ) require.NoError(t, apply.Apply(t2)) for _, x := range []string{"dir", "dir/bar.txt"} { err := os.Chown(filepath.Join(t2, x), 1, 1) require.NoErrorf(t, err, "x=%s", x) } err := Copy(context.TODO(), t1, "dir", t2, "dir", WithCopyInfo(CopyInfo{ CopyDirContents: true, })) require.NoError(t, err) // verify that existing destination dir's metadata was not overwritten st, err := os.Lstat(filepath.Join(t2, "dir")) require.NoError(t, err) require.Equal(t, st.Mode()&os.ModePerm, os.FileMode(0700)) var uid, gid int if runtime.GOOS != "windows" { uid, gid = getUIDGID(st) require.Equal(t, 1, uid) require.Equal(t, 1, gid) } // verify that non-existing file was created _, err = os.Lstat(filepath.Join(t2, "dir/foo.txt")) require.NoError(t, err) // verify that existing file's content and metadata was overwritten st, err = os.Lstat(filepath.Join(t2, "dir/bar.txt")) require.NoError(t, err) require.Equal(t, os.FileMode(0644), st.Mode()&os.ModePerm) if runtime.GOOS != "windows" { uid, gid = getUIDGID(st) require.Equal(t, 0, uid) require.Equal(t, 0, gid) } dt, err := os.ReadFile(filepath.Join(t2, "dir/bar.txt")) require.NoError(t, err) require.Equal(t, "bar-contents", string(dt)) } func TestCopyDirectoryContentsTimestamp(t *testing.T) { timestamp := time.Unix(0, 0) apply := fstest.Apply( fstest.CreateDir("/foo/", 0755), fstest.CreateDir("/foo/bar", 0755), fstest.CreateFile("/foo/bar/baz", []byte{}, 0644), ) t1 := t.TempDir() t2 := t.TempDir() require.NoError(t, apply.Apply(t1)) require.NoError(t, Copy(context.TODO(), t1, "/foo", t2, "/foo", WithCopyInfo(CopyInfo{ CopyDirContents: true, Utime: ×tamp, }))) require.NoError(t, fstest.CheckDirectoryEqual(t1, t2)) for _, s := range []string{"/foo/bar/baz", "/foo/bar", "/foo"} { stat, _ := os.Stat(filepath.Join(t2, s)) require.Equal(t, timestamp, stat.ModTime()) } } func TestCopySymlinks(t *testing.T) { t1 := t.TempDir() apply := fstest.Apply( fstest.CreateDir("testdir", 0755), fstest.CreateFile("testdir/foo.txt", []byte("foo-contents"), 0644), fstest.Symlink("foo.txt", "testdir/link2"), fstest.Symlink("/testdir", "link"), ) require.NoError(t, apply.Apply(t1)) t2 := t.TempDir() err := Copy(context.TODO(), t1, "link/link2", t2, "foo", WithCopyInfo(CopyInfo{ FollowLinks: true, })) require.NoError(t, err) // verify that existing destination dir's metadata was not overwritten st, err := os.Lstat(filepath.Join(t2, "foo")) require.NoError(t, err) if runtime.GOOS != "windows" { require.Equal(t, os.FileMode(0644), st.Mode()&os.ModePerm) } require.Equal(t, 0, int(st.Mode()&os.ModeSymlink)) dt, err := os.ReadFile(filepath.Join(t2, "foo")) require.NoError(t, err) require.Equal(t, "foo-contents", string(dt)) t3 := t.TempDir() err = Copy(context.TODO(), t1, "link/link2", t3, "foo", WithCopyInfo(CopyInfo{})) require.NoError(t, err) // verify that existing destination dir's metadata was not overwritten st, err = os.Lstat(filepath.Join(t3, "foo")) require.NoError(t, err) require.Equal(t, os.ModeSymlink, st.Mode()&os.ModeSymlink) link, err := os.Readlink(filepath.Join(t3, "foo")) require.NoError(t, err) require.Equal(t, "foo.txt", link) } func testCopy(t *testing.T, apply fstest.Applier, exp string) error { t1 := t.TempDir() t2 := t.TempDir() if err := apply.Apply(t1); err != nil { return errors.Wrap(err, "failed to apply changes") } ch := &changeCollector{} if err := Copy(context.TODO(), t1, "/.", t2, "/", WithChangeNotifier(ch.onChange)); err != nil { return errors.Wrap(err, "failed to copy") } if exp != ch.String() { return errors.Errorf("unexpected changes: %s", ch) } return fstest.CheckDirectoryEqual(t1, t2) } func TestCopyIncludeExclude(t *testing.T) { t1 := t.TempDir() apply := fstest.Apply( fstest.CreateDir("bar", 0755), fstest.CreateFile("bar/foo", []byte("foo-contents"), 0755), fstest.CreateDir("bar/baz", 0755), fstest.CreateFile("bar/baz/foo3", []byte("foo3-contents"), 0755), fstest.CreateFile("foo2", []byte("foo2-contents"), 0755), ) require.NoError(t, apply.Apply(t1)) testCases := []struct { name string opts []Opt expectedResults []string expectedChanges string }{ { name: "include bar", opts: []Opt{WithIncludePattern("bar")}, expectedResults: []string{"bar", "bar/foo", "bar/baz", "bar/baz/foo3"}, expectedChanges: filepath.FromSlash("add:/bar,add:/bar/baz,add:/bar/baz/foo3,add:/bar/foo"), }, { name: "include *", opts: []Opt{WithIncludePattern("*")}, expectedResults: []string{"bar", "bar/foo", "bar/baz", "bar/baz/foo3", "foo2"}, expectedChanges: filepath.FromSlash("add:/bar,add:/bar/baz,add:/bar/baz/foo3,add:/bar/foo,add:/foo2"), }, { name: "include bar/foo", opts: []Opt{WithIncludePattern("bar/foo")}, expectedResults: []string{"bar", "bar/foo"}, expectedChanges: filepath.FromSlash("add:/bar/foo"), }, { name: "include bar except bar/foo", opts: []Opt{WithIncludePattern("bar"), WithIncludePattern("!bar/foo")}, expectedResults: []string{"bar", "bar/baz", "bar/baz/foo3"}, expectedChanges: filepath.FromSlash("add:/bar,add:/bar/baz,add:/bar/baz/foo3"), }, { name: "include bar/foo and foo*", opts: []Opt{WithIncludePattern("bar/foo"), WithIncludePattern("foo*")}, expectedResults: []string{"bar", "bar/foo", "foo2"}, expectedChanges: filepath.FromSlash("add:/bar/foo,add:/foo2"), }, { name: "include b*", opts: []Opt{WithIncludePattern("b*")}, expectedResults: []string{"bar", "bar/foo", "bar/baz", "bar/baz/foo3"}, expectedChanges: filepath.FromSlash("add:/bar,add:/bar/baz,add:/bar/baz/foo3,add:/bar/foo"), }, { name: "include bar/f*", opts: []Opt{WithIncludePattern("bar/f*")}, expectedResults: []string{"bar", "bar/foo"}, }, { name: "include bar/g*", opts: []Opt{WithIncludePattern("bar/g*")}, expectedResults: nil, }, { name: "include b*/f*", opts: []Opt{WithIncludePattern("b*/f*")}, expectedResults: []string{"bar", "bar/foo"}, }, { name: "include b*/foo", opts: []Opt{WithIncludePattern("b*/foo")}, expectedResults: []string{"bar", "bar/foo"}, }, { name: "include b*/", opts: []Opt{WithIncludePattern("b*/")}, expectedResults: []string{"bar", "bar/foo", "bar/baz", "bar/baz/foo3"}, }, { name: "include bar/*/foo3", opts: []Opt{WithIncludePattern("bar/*/foo3")}, expectedResults: []string{"bar", "bar/baz", "bar/baz/foo3"}, }, { name: "exclude bar*, !bar/baz", opts: []Opt{WithExcludePattern("bar*"), WithExcludePattern("!bar/baz")}, expectedResults: []string{"bar", "bar/baz", "bar/baz/foo3", "foo2"}, }, { name: "exclude **, !bar/baz", opts: []Opt{WithExcludePattern("**"), WithExcludePattern("!bar/baz")}, expectedResults: []string{"bar", "bar/baz", "bar/baz/foo3"}, }, { name: "exclude **, !bar/baz, bar/baz/foo3", opts: []Opt{WithExcludePattern("**"), WithExcludePattern("!bar/baz"), WithExcludePattern("bar/baz/foo3")}, expectedResults: []string{"bar", "bar/baz"}, }, { name: "include bar, exclude bar/baz", opts: []Opt{WithIncludePattern("bar"), WithExcludePattern("bar/baz")}, expectedResults: []string{"bar", "bar/foo"}, }, { name: "doublestar include", opts: []Opt{WithIncludePattern("**/foo3")}, expectedResults: []string{"bar", "bar/baz", "bar/baz/foo3"}, }, { name: "doublestar matching second item in path", opts: []Opt{WithIncludePattern("**/baz")}, expectedResults: []string{"bar", "bar/baz", "bar/baz/foo3"}, expectedChanges: filepath.FromSlash("add:/bar/baz,add:/bar/baz/foo3"), }, { name: "doublestar matching first item in path", opts: []Opt{WithIncludePattern("**/bar")}, expectedResults: []string{"bar", "bar/foo", "bar/baz", "bar/baz/foo3"}, expectedChanges: filepath.FromSlash("add:/bar,add:/bar/baz,add:/bar/baz/foo3,add:/bar/foo"), }, { name: "doublestar exclude", opts: []Opt{WithIncludePattern("bar"), WithExcludePattern("**/foo3")}, expectedResults: []string{"bar", "bar/foo", "bar/baz"}, expectedChanges: filepath.FromSlash("add:/bar,add:/bar/baz,add:/bar/foo"), }, { name: "exclude bar/baz", opts: []Opt{WithExcludePattern("bar/baz")}, expectedResults: []string{"bar", "bar/foo", "foo2"}, expectedChanges: filepath.FromSlash("add:/bar,add:/bar/foo,add:/foo2"), }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { t2 := t.TempDir() ch := &changeCollector{} tc.opts = append(tc.opts, WithChangeNotifier(ch.onChange)) err := Copy(context.Background(), t1, "/", t2, "/", tc.opts...) require.NoError(t, err, tc.name) var results []string for _, path := range []string{"bar", "bar/foo", "bar/baz", "bar/baz/asdf", "bar/baz/asdf/x", "bar/baz/foo3", "foo2"} { _, err := os.Stat(filepath.Join(t2, path)) if err == nil { results = append(results, path) } } require.Equal(t, tc.expectedResults, results, tc.name) if tc.expectedChanges != "" { require.Equal(t, tc.expectedChanges, ch.String()) } }) } } type changeCollector struct { changes []string } func (c *changeCollector) onChange(kind fsutil.ChangeKind, path string, _ os.FileInfo, _ error) error { c.changes = append(c.changes, fmt.Sprintf("%s:%s", kind, path)) return nil } func (c *changeCollector) String() string { sort.Strings(c.changes) return strings.Join(c.changes, ",") } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/copy/copy_unix.go000066400000000000000000000025631456645347400257360ustar00rootroot00000000000000//go:build solaris || darwin || freebsd // +build solaris darwin freebsd package fs import ( "os" "syscall" "github.com/pkg/errors" "golang.org/x/sys/unix" ) func getUIDGID(fi os.FileInfo) (uid, gid int) { st := fi.Sys().(*syscall.Stat_t) return int(st.Uid), int(st.Gid) } func (c *copier) copyFileInfo(fi os.FileInfo, src, name string) error { chown := c.chown uid, gid := getUIDGID(fi) old := &User{UID: uid, GID: gid} if chown == nil { chown = func(u *User) (*User, error) { return u, nil } } if err := Chown(name, old, chown); err != nil { return errors.Wrapf(err, "failed to chown %s", name) } m := fi.Mode() if c.mode != nil { m = (m & ^os.FileMode(0777)) | os.FileMode(*c.mode&0777) } if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink { if err := os.Chmod(name, m); err != nil { return errors.Wrapf(err, "failed to chmod %s", name) } } if err := c.copyFileTimestamp(fi, name); err != nil { return err } return nil } func (c *copier) copyFileTimestamp(fi os.FileInfo, name string) error { if c.utime != nil { return Utimes(name, c.utime) } st := fi.Sys().(*syscall.Stat_t) timespec := []unix.Timespec{unix.Timespec(StatAtime(st)), unix.Timespec(StatMtime(st))} if err := unix.UtimesNanoAt(unix.AT_FDCWD, name, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil { return errors.Wrapf(err, "failed to utime %s", name) } return nil } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/copy/copy_unix_test.go000066400000000000000000000030141456645347400267650ustar00rootroot00000000000000//go:build !windows // +build !windows package fs import ( "context" "os" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/sys/unix" ) func TestCopyDevicesAndFifo(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) err = mknod(filepath.Join(t1, "block"), unix.S_IFBLK|0441, int(unix.Mkdev(3, 2))) require.NoError(t, err) err = mknod(filepath.Join(t1, "socket"), unix.S_IFSOCK|0555, 0) require.NoError(t, err) err = unix.Mkfifo(filepath.Join(t1, "fifo"), 0555) 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.ModeCharDevice, fi.Mode()&os.ModeCharDevice) assert.Equal(t, os.FileMode(0444), fi.Mode()&0777) fi, err = os.Lstat(filepath.Join(t2, "block")) require.NoError(t, err) assert.Equal(t, os.ModeDevice, fi.Mode()&os.ModeDevice) assert.Equal(t, os.FileMode(0441), fi.Mode()&0777) fi, err = os.Lstat(filepath.Join(t2, "fifo")) require.NoError(t, err) assert.Equal(t, os.ModeNamedPipe, fi.Mode()&os.ModeNamedPipe) assert.Equal(t, os.FileMode(0555), fi.Mode()&0777) fi, err = os.Lstat(filepath.Join(t2, "socket")) require.NoError(t, err) assert.NotEqual(t, os.ModeSocket, fi.Mode()&os.ModeSocket) // socket copied as stub assert.Equal(t, os.FileMode(0555), fi.Mode()&0777) } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/copy/copy_windows.go000066400000000000000000000060351456645347400264430ustar00rootroot00000000000000package fs import ( "io" "os" "github.com/Microsoft/go-winio" "github.com/pkg/errors" "golang.org/x/sys/windows" ) const ( seTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege" ) func getUIDGID(fi os.FileInfo) (uid, gid int) { return 0, 0 } func getFileSecurityInfo(name string) (*windows.SID, *windows.ACL, error) { secInfo, err := windows.GetNamedSecurityInfo( name, windows.SE_FILE_OBJECT, windows.OWNER_SECURITY_INFORMATION|windows.DACL_SECURITY_INFORMATION) if err != nil { return nil, nil, errors.Wrap(err, "fetching security info") } sid, _, err := secInfo.Owner() if err != nil { return nil, nil, errors.Wrap(err, "fetching owner SID") } dacl, _, err := secInfo.DACL() if err != nil { return nil, nil, errors.Wrap(err, "fetching dacl") } return sid, dacl, nil } func (c *copier) copyFileInfo(fi os.FileInfo, src, name string) error { if err := os.Chmod(name, fi.Mode()); err != nil { return errors.Wrapf(err, "failed to chmod %s", name) } sid, dacl, err := getFileSecurityInfo(src) if err != nil { return errors.Wrap(err, "getting file info") } if c.chown != nil { // Use the defined chowner. usr := &User{SID: sid.String()} if err := Chown(name, usr, c.chown); err != nil { return errors.Wrapf(err, "failed to chown %s", name) } return nil } else { // Copy file ownership and ACL from the source file. // We need SeRestorePrivilege and SeTakeOwnershipPrivilege in order // to restore security info on a file, especially if we're trying to // apply security info which includes SIDs not necessarily present on // the host. privileges := []string{winio.SeRestorePrivilege, seTakeOwnershipPrivilege} if err := winio.EnableProcessPrivileges(privileges); err != nil { return err } defer winio.DisableProcessPrivileges(privileges) if err := windows.SetNamedSecurityInfo( name, windows.SE_FILE_OBJECT, windows.OWNER_SECURITY_INFORMATION|windows.DACL_SECURITY_INFORMATION, sid, nil, dacl, nil); err != nil { return err } } if err := c.copyFileTimestamp(fi, name); err != nil { return err } return nil } func (c *copier) copyFileTimestamp(fi os.FileInfo, name string) error { if c.utime != nil { return Utimes(name, c.utime) } if fi.Mode()&os.ModeSymlink == 0 { if err := os.Chtimes(name, fi.ModTime(), fi.ModTime()); err != nil { return errors.Wrap(err, "changing mtime") } } return nil } func copyFile(source, target string) error { src, err := os.Open(source) if err != nil { return errors.Wrapf(err, "failed to open source %s", source) } defer src.Close() tgt, err := os.Create(target) if err != nil { return errors.Wrapf(err, "failed to open target %s", target) } defer tgt.Close() return copyFileContent(tgt, src) } func copyFileContent(dst, src *os.File) error { buf := bufferPool.Get().(*[]byte) _, err := io.CopyBuffer(dst, src, *buf) bufferPool.Put(buf) return err } func copyXAttrs(dst, src string, xeh XAttrErrorHandler) error { return nil } func copyDevice(dst string, fi os.FileInfo) error { return errors.New("device copy not supported") } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/copy/hardlink.go000066400000000000000000000013621456645347400255110ustar00rootroot00000000000000package fs import "os" // GetLinkInfo returns an identifier representing the node a hardlink is pointing // to. If the file is not hard linked then 0 will be returned. func GetLinkInfo(fi os.FileInfo) (uint64, bool) { return getLinkInfo(fi) } // getLinkSource returns a path for the given name and // file info to its link source in the provided inode // map. If the given file name is not in the map and // has other links, it is added to the inode map // to be a source for other link locations. func getLinkSource(name string, fi os.FileInfo, inodes map[uint64]string) (string, error) { inode, isHardlink := getLinkInfo(fi) if !isHardlink { return "", nil } path, ok := inodes[inode] if !ok { inodes[inode] = name } return path, nil } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/copy/hardlink_unix.go000066400000000000000000000003741456645347400265560ustar00rootroot00000000000000//go:build !windows // +build !windows package fs import ( "os" "syscall" ) func getLinkInfo(fi os.FileInfo) (uint64, bool) { s, ok := fi.Sys().(*syscall.Stat_t) if !ok { return 0, false } return uint64(s.Ino), !fi.IsDir() && s.Nlink > 1 } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/copy/hardlink_windows.go000066400000000000000000000001361456645347400272610ustar00rootroot00000000000000package fs import "os" func getLinkInfo(fi os.FileInfo) (uint64, bool) { return 0, false } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/copy/mkdir.go000066400000000000000000000024561456645347400250300ustar00rootroot00000000000000package fs import ( "os" "syscall" "time" ) // MkdirAll is forked os.MkdirAll func MkdirAll(path string, perm os.FileMode, user Chowner, tm *time.Time) error { // Fast path: if we can tell whether path is a directory or file, stop with success or error. dir, err := os.Stat(path) if err == nil { if dir.IsDir() { return nil } return &os.PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR} } // Slow path: make sure parent exists and then call Mkdir for path. i := len(path) for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator. i-- } j := i for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element. j-- } if j > 1 { // Create parent. err = MkdirAll(fixRootDirectory(path[:j-1]), perm, user, tm) if err != nil { return err } } dir, err1 := os.Lstat(path) if err1 == nil && dir.IsDir() { return nil } // Parent now exists; invoke Mkdir and use its result. err = os.Mkdir(path, perm) if err != nil { // Handle arguments like "foo/." by // double-checking that directory doesn't exist. dir, err1 := os.Lstat(path) if err1 == nil && dir.IsDir() { return nil } return err } if err := Chown(path, nil, user); err != nil { return err } if err := Utimes(path, tm); err != nil { return err } return nil } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/copy/mkdir_unix.go000066400000000000000000000014461456645347400260710ustar00rootroot00000000000000//go:build !windows // +build !windows package fs import ( "os" "time" "github.com/pkg/errors" "golang.org/x/sys/unix" ) func fixRootDirectory(p string) string { return p } func Utimes(p string, tm *time.Time) error { if tm == nil { return nil } ts, err := unix.TimeToTimespec(*tm) if err != nil { return err } timespec := []unix.Timespec{ts, ts} if err := unix.UtimesNanoAt(unix.AT_FDCWD, p, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil { return errors.Wrapf(err, "failed to utime %s", p) } return nil } func Chown(p string, old *User, fn Chowner) error { if fn == nil { return nil } user, err := fn(old) if err != nil { return errors.WithStack(err) } if user != nil { if err := os.Lchown(p, user.UID, user.GID); err != nil { return err } } return nil } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/copy/mkdir_windows.go000066400000000000000000000047151456645347400266020ustar00rootroot00000000000000//go:build windows // +build windows package fs import ( "fmt" "os" "syscall" "time" "github.com/Microsoft/go-winio" "github.com/pkg/errors" "golang.org/x/sys/windows" ) const ( containerAdministratorSidString = "S-1-5-93-2-1" ) func fixRootDirectory(p string) string { if len(p) == len(`\\?\c:`) { if os.IsPathSeparator(p[0]) && os.IsPathSeparator(p[1]) && p[2] == '?' && os.IsPathSeparator(p[3]) && p[5] == ':' { return p + `\` } } return p } func Utimes(p string, tm *time.Time) error { info, err := os.Lstat(p) if err != nil { return errors.Wrap(err, "fetching file info") } if tm != nil && info.Mode()&os.ModeSymlink == 0 { if err := os.Chtimes(p, *tm, *tm); err != nil { return errors.Wrap(err, "changing times") } } return nil } func Chown(p string, old *User, fn Chowner) error { if fn == nil { return nil } user, err := fn(old) if err != nil { return errors.WithStack(err) } var userSIDstring string if user != nil && user.SID != "" { userSIDstring = user.SID } if userSIDstring == "" { userSIDstring = containerAdministratorSidString } sidPtr, err := syscall.UTF16PtrFromString(userSIDstring) if err != nil { return errors.Wrap(err, "converting to utf16 ptr") } var userSID *windows.SID if err := windows.ConvertStringSidToSid(sidPtr, &userSID); err != nil { return errors.Wrap(err, "converting to windows SID") } var dacl *windows.ACL newEntries := []windows.EXPLICIT_ACCESS{ { AccessPermissions: windows.GENERIC_ALL, AccessMode: windows.GRANT_ACCESS, Inheritance: windows.SUB_CONTAINERS_AND_OBJECTS_INHERIT, Trustee: windows.TRUSTEE{ TrusteeForm: windows.TRUSTEE_IS_SID, TrusteeValue: windows.TrusteeValueFromSID(userSID), }, }, } newAcl, err := windows.ACLFromEntries(newEntries, dacl) if err != nil { return fmt.Errorf("adding acls: %w", err) } // Copy file ownership and ACL // We need SeRestorePrivilege and SeTakeOwnershipPrivilege in order // to restore security info on a file, especially if we're trying to // apply security info which includes SIDs not necessarily present on // the host. privileges := []string{winio.SeRestorePrivilege, seTakeOwnershipPrivilege} err = winio.RunWithPrivileges(privileges, func() error { if err := windows.SetNamedSecurityInfo( p, windows.SE_FILE_OBJECT, windows.OWNER_SECURITY_INFORMATION|windows.DACL_SECURITY_INFORMATION, userSID, nil, newAcl, nil); err != nil { return err } return nil }) return err } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/copy/stat_bsd.go000066400000000000000000000004531456645347400255200ustar00rootroot00000000000000// +build darwin freebsd netbsd openbsd package fs import ( "syscall" ) // Returns the last-accessed time func StatAtime(st *syscall.Stat_t) syscall.Timespec { return st.Atimespec } // Returns the last-modified time func StatMtime(st *syscall.Stat_t) syscall.Timespec { return st.Mtimespec } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/copy/stat_sysv.go000066400000000000000000000005041456645347400257510ustar00rootroot00000000000000//go:build dragonfly || linux || solaris // +build dragonfly linux solaris package fs import ( "syscall" ) // Returns the last-accessed time func StatAtime(st *syscall.Stat_t) syscall.Timespec { return st.Atim } // Returns the last-modified time func StatMtime(st *syscall.Stat_t) syscall.Timespec { return st.Mtim } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/diff.go000066400000000000000000000020461456645347400236530ustar00rootroot00000000000000package fsutil import ( "context" "hash" "os" "github.com/pkg/errors" "github.com/tonistiigi/fsutil/types" ) type walkerFn func(ctx context.Context, pathC chan<- *currentPath) error func Changes(ctx context.Context, a, b walkerFn, changeFn ChangeFunc) error { return nil } type HandleChangeFn func(ChangeKind, string, os.FileInfo, error) error type ContentHasher func(*types.Stat) (hash.Hash, error) func getWalkerFn(root string) walkerFn { return func(ctx context.Context, pathC chan<- *currentPath) error { return errors.Wrap(Walk(ctx, root, nil, func(path string, f os.FileInfo, err error) error { if err != nil { return err } stat, ok := f.Sys().(*types.Stat) if !ok { return errors.Errorf("%T invalid file without stat information", f.Sys()) } p := ¤tPath{ path: path, stat: stat, } select { case <-ctx.Done(): return ctx.Err() case pathC <- p: return nil } }), "failed to walk") } } func emptyWalker(ctx context.Context, pathC chan<- *currentPath) error { return nil } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/diff_containerd.go000066400000000000000000000124501456645347400260610ustar00rootroot00000000000000package fsutil import ( "bytes" "context" "io" "os" "strings" "github.com/tonistiigi/fsutil/types" "golang.org/x/sync/errgroup" ) // Everything below is copied from containerd/fs. TODO: remove duplication @dmcgowan // Const redefined because containerd/fs doesn't build on !linux // ChangeKind is the type of modification that // a change is making. type ChangeKind int const ( // ChangeKindAdd represents an addition of // a file ChangeKindAdd ChangeKind = iota // ChangeKindModify represents a change to // an existing file ChangeKindModify // ChangeKindDelete represents a delete of // a file ChangeKindDelete ) func (k ChangeKind) String() string { switch k { case ChangeKindAdd: return "add" case ChangeKindModify: return "modify" case ChangeKindDelete: return "delete" default: return "unknown" } } // ChangeFunc is the type of function called for each change // computed during a directory changes calculation. type ChangeFunc func(ChangeKind, string, os.FileInfo, error) error const compareChunkSize = 32 * 1024 type currentPath struct { path string stat *types.Stat // fullPath string } // doubleWalkDiff walks both directories to create a diff func doubleWalkDiff(ctx context.Context, changeFn ChangeFunc, a, b walkerFn, filter FilterFunc, differ DiffType) (err error) { g, ctx := errgroup.WithContext(ctx) var ( c1 = make(chan *currentPath, 128) c2 = make(chan *currentPath, 128) f1, f2 *currentPath rmdir string ) g.Go(func() error { defer close(c1) return a(ctx, c1) }) g.Go(func() error { defer close(c2) return b(ctx, c2) }) g.Go(func() error { loop0: for c1 != nil || c2 != nil { if f1 == nil && c1 != nil { f1, err = nextPath(ctx, c1) if err != nil { return err } if f1 == nil { c1 = nil } } if f2 == nil && c2 != nil { f2, err = nextPath(ctx, c2) if err != nil { return err } if f2 == nil { c2 = nil } } if f1 == nil && f2 == nil { continue } var f *types.Stat var f2copy *currentPath if f2 != nil { statCopy := *f2.stat if filter != nil { filter(f2.path, &statCopy) } f2copy = ¤tPath{path: f2.path, stat: &statCopy} } k, p := pathChange(f1, f2copy) switch k { case ChangeKindAdd: if rmdir != "" { rmdir = "" } f = f2.stat f2 = nil case ChangeKindDelete: // Check if this file is already removed by being // under of a removed directory if rmdir != "" && strings.HasPrefix(f1.path, rmdir) { f1 = nil continue } else if rmdir == "" && f1.stat.IsDir() { rmdir = f1.path + string(os.PathSeparator) } else if rmdir != "" { rmdir = "" } f1 = nil case ChangeKindModify: same, err := sameFile(f1, f2copy, differ) if err != nil { return err } if f1.stat.IsDir() && !f2copy.stat.IsDir() { rmdir = f1.path + string(os.PathSeparator) } else if rmdir != "" { rmdir = "" } f = f2.stat f1 = nil f2 = nil if same { continue loop0 } } if err := changeFn(k, p, &StatInfo{f}, nil); err != nil { return err } } return nil }) return g.Wait() } func pathChange(lower, upper *currentPath) (ChangeKind, string) { if lower == nil { if upper == nil { panic("cannot compare nil paths") } return ChangeKindAdd, upper.path } if upper == nil { return ChangeKindDelete, lower.path } switch i := ComparePath(lower.path, upper.path); { case i < 0: // File in lower that is not in upper return ChangeKindDelete, lower.path case i > 0: // File in upper that is not in lower return ChangeKindAdd, upper.path default: return ChangeKindModify, upper.path } } func sameFile(f1, f2 *currentPath, differ DiffType) (same bool, retErr error) { if differ == DiffNone { return false, nil } // If not a directory also check size, modtime, and content if !f1.stat.IsDir() { if f1.stat.Size_ != f2.stat.Size_ { return false, nil } if f1.stat.ModTime != f2.stat.ModTime { return false, nil } } same, err := compareStat(f1.stat, f2.stat) if err != nil || !same || differ == DiffMetadata { return same, err } return compareFileContent(f1.path, f2.path) } func compareFileContent(p1, p2 string) (bool, error) { f1, err := os.Open(p1) if err != nil { return false, err } defer f1.Close() f2, err := os.Open(p2) if err != nil { return false, err } defer f2.Close() b1 := make([]byte, compareChunkSize) b2 := make([]byte, compareChunkSize) for { n1, err1 := f1.Read(b1) if err1 != nil && err1 != io.EOF { return false, err1 } n2, err2 := f2.Read(b2) if err2 != nil && err2 != io.EOF { return false, err2 } if n1 != n2 || !bytes.Equal(b1[:n1], b2[:n2]) { return false, nil } if err1 == io.EOF && err2 == io.EOF { return true, nil } } } // compareStat returns whether the stats are equivalent, // whether the files are considered the same file, and // an error func compareStat(ls1, ls2 *types.Stat) (bool, error) { return ls1.Mode == ls2.Mode && ls1.Uid == ls2.Uid && ls1.Gid == ls2.Gid && ls1.Devmajor == ls2.Devmajor && ls1.Devminor == ls2.Devminor && ls1.Linkname == ls2.Linkname, nil } func nextPath(ctx context.Context, pathC <-chan *currentPath) (*currentPath, error) { select { case <-ctx.Done(): return nil, ctx.Err() case p := <-pathC: return p, nil } } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/diskwriter.go000066400000000000000000000204171456645347400251340ustar00rootroot00000000000000package fsutil import ( "context" "hash" "io" gofs "io/fs" "os" "path/filepath" "strconv" "sync" "syscall" "time" "github.com/opencontainers/go-digest" "github.com/pkg/errors" "github.com/tonistiigi/fsutil/types" "golang.org/x/sync/errgroup" ) type WriteToFunc func(context.Context, string, io.WriteCloser) error type DiskWriterOpt struct { AsyncDataCb WriteToFunc SyncDataCb WriteToFunc NotifyCb func(ChangeKind, string, os.FileInfo, error) error ContentHasher ContentHasher Filter FilterFunc } type FilterFunc func(string, *types.Stat) bool type DiskWriter struct { opt DiskWriterOpt dest string ctx context.Context cancel func() eg *errgroup.Group filter FilterFunc dirModTimes map[string]int64 } func NewDiskWriter(ctx context.Context, dest string, opt DiskWriterOpt) (*DiskWriter, error) { if opt.SyncDataCb == nil && opt.AsyncDataCb == nil { return nil, errors.New("no data callback specified") } if opt.SyncDataCb != nil && opt.AsyncDataCb != nil { return nil, errors.New("can't specify both sync and async data callbacks") } ctx, cancel := context.WithCancel(ctx) eg, ctx := errgroup.WithContext(ctx) return &DiskWriter{ opt: opt, dest: dest, eg: eg, ctx: ctx, cancel: cancel, filter: opt.Filter, dirModTimes: map[string]int64{}, }, nil } func (dw *DiskWriter) Wait(ctx context.Context) error { if err := dw.eg.Wait(); err != nil { return err } return filepath.WalkDir(dw.dest, func(path string, d gofs.DirEntry, prevErr error) error { if prevErr != nil { return prevErr } if !d.IsDir() { return nil } if mtime, ok := dw.dirModTimes[path]; ok { return chtimes(path, mtime) } return nil }) } func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, err error) (retErr error) { if err != nil { return err } select { case <-dw.ctx.Done(): return dw.ctx.Err() default: } defer func() { if retErr != nil { dw.cancel() } }() destPath := filepath.Join(dw.dest, filepath.FromSlash(p)) if kind == ChangeKindDelete { if dw.filter != nil { var empty types.Stat if ok := dw.filter(p, &empty); !ok { return nil } } // todo: no need to validate if diff is trusted but is it always? if err := os.RemoveAll(destPath); err != nil { return errors.Wrapf(err, "failed to remove: %s", destPath) } if dw.opt.NotifyCb != nil { if err := dw.opt.NotifyCb(kind, p, nil, nil); err != nil { return err } } return nil } stat, ok := fi.Sys().(*types.Stat) if !ok { return errors.WithStack(&os.PathError{Path: p, Err: syscall.EBADMSG, Op: "change without stat info"}) } statCopy := *stat if dw.filter != nil { if ok := dw.filter(p, &statCopy); !ok { return nil } } rename := true oldFi, err := os.Lstat(destPath) if err != nil { if errors.Is(err, os.ErrNotExist) { if kind != ChangeKindAdd { return errors.Wrap(err, "modify/rm") } rename = false } else { return errors.WithStack(err) } } if oldFi != nil && fi.IsDir() && oldFi.IsDir() { if err := rewriteMetadata(destPath, &statCopy); err != nil { return errors.Wrapf(err, "error setting dir metadata for %s", destPath) } return nil } newPath := destPath if rename { newPath = filepath.Join(filepath.Dir(destPath), ".tmp."+nextSuffix()) } isRegularFile := false switch { case fi.IsDir(): if err := os.Mkdir(newPath, fi.Mode()); err != nil { if errors.Is(err, syscall.EEXIST) { // we saw a race to create this directory, so try again return dw.HandleChange(kind, p, fi, nil) } return errors.Wrapf(err, "failed to create dir %s", newPath) } dw.dirModTimes[destPath] = statCopy.ModTime case fi.Mode()&os.ModeDevice != 0 || fi.Mode()&os.ModeNamedPipe != 0: if err := handleTarTypeBlockCharFifo(newPath, &statCopy); err != nil { return errors.Wrapf(err, "failed to create device %s", newPath) } case fi.Mode()&os.ModeSymlink != 0: if err := os.Symlink(statCopy.Linkname, newPath); err != nil { return errors.Wrapf(err, "failed to symlink %s", newPath) } case statCopy.Linkname != "": if err := os.Link(filepath.Join(dw.dest, statCopy.Linkname), newPath); err != nil { return errors.Wrapf(err, "failed to link %s to %s", newPath, statCopy.Linkname) } default: isRegularFile = true file, err := os.OpenFile(newPath, os.O_CREATE|os.O_WRONLY, fi.Mode()) //todo: windows if err != nil { return errors.Wrapf(err, "failed to create %s", newPath) } if dw.opt.SyncDataCb != nil { if err := dw.processChange(ChangeKindAdd, p, fi, file); err != nil { file.Close() return err } } if err := file.Close(); err != nil { return errors.Wrapf(err, "failed to close %s", newPath) } } if err := rewriteMetadata(newPath, &statCopy); err != nil { return errors.Wrapf(err, "error setting metadata for %s", newPath) } if rename { if oldFi.IsDir() != fi.IsDir() { if err := os.RemoveAll(destPath); err != nil { return errors.Wrapf(err, "failed to remove %s", destPath) } } if err := renameFile(newPath, destPath); err != nil { return errors.Wrapf(err, "failed to rename %s to %s", newPath, destPath) } } if isRegularFile { if dw.opt.AsyncDataCb != nil { dw.requestAsyncFileData(p, destPath, fi, &statCopy) } } else { return dw.processChange(kind, p, fi, nil) } return nil } 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{ dest: dest, }); err != nil { return err } return chtimes(dest, st.ModTime) // TODO: parent dirs }) } func (dw *DiskWriter) processChange(kind ChangeKind, p string, fi os.FileInfo, w io.WriteCloser) error { origw := w var hw *hashedWriter if dw.opt.NotifyCb != nil { var err error if hw, err = newHashWriter(dw.opt.ContentHasher, fi, w); err != nil { return err } w = hw } if origw != nil { fn := dw.opt.SyncDataCb if fn == nil && dw.opt.AsyncDataCb != nil { fn = dw.opt.AsyncDataCb } if err := fn(dw.ctx, p, w); err != nil { return err } } else { if hw != nil { hw.Close() } } if hw != nil { return dw.opt.NotifyCb(kind, p, hw, nil) } return nil } type hashedWriter struct { os.FileInfo io.Writer h hash.Hash w io.WriteCloser dgst digest.Digest } func newHashWriter(ch ContentHasher, fi os.FileInfo, w io.WriteCloser) (*hashedWriter, error) { stat, ok := fi.Sys().(*types.Stat) if !ok { return nil, errors.Errorf("invalid change without stat information") } h, err := ch(stat) if err != nil { return nil, err } hw := &hashedWriter{ FileInfo: fi, Writer: io.MultiWriter(w, h), h: h, w: w, } return hw, nil } func (hw *hashedWriter) Close() error { hw.dgst = digest.NewDigest(digest.SHA256, hw.h) if hw.w != nil { return hw.w.Close() } return nil } func (hw *hashedWriter) Digest() digest.Digest { return hw.dgst } type lazyFileWriter struct { dest string f *os.File fileMode *os.FileMode } func (lfw *lazyFileWriter) Write(dt []byte) (int, error) { if lfw.f == nil { file, err := os.OpenFile(lfw.dest, os.O_WRONLY, 0) //todo: windows if os.IsPermission(err) { // retry after chmod fi, er := os.Stat(lfw.dest) if er == nil { mode := fi.Mode() lfw.fileMode = &mode er = os.Chmod(lfw.dest, mode|0222) if er == nil { file, err = os.OpenFile(lfw.dest, os.O_WRONLY, 0) } } } if err != nil { return 0, errors.Wrapf(err, "failed to open %s", lfw.dest) } lfw.f = file } return lfw.f.Write(dt) } func (lfw *lazyFileWriter) Close() error { var err error if lfw.f != nil { err = lfw.f.Close() } if err == nil && lfw.fileMode != nil { err = os.Chmod(lfw.dest, *lfw.fileMode) } return err } // Random number state. // We generate random temporary file names so that there's a good // chance the file doesn't exist yet - keeps the number of tries in // TempFile to a minimum. var rand uint32 var randmu sync.Mutex func reseed() uint32 { return uint32(time.Now().UnixNano() + int64(os.Getpid())) } func nextSuffix() string { randmu.Lock() r := rand if r == 0 { r = reseed() } r = r*1664525 + 1013904223 // constants from Numerical Recipes rand = r randmu.Unlock() return strconv.Itoa(int(1e9 + r%1e9))[1:] } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/diskwriter_freebsd.go000066400000000000000000000005641456645347400266270ustar00rootroot00000000000000//go:build freebsd // +build freebsd package fsutil import ( "github.com/tonistiigi/fsutil/types" "golang.org/x/sys/unix" ) func createSpecialFile(path string, mode uint32, stat *types.Stat) error { return unix.Mknod(path, mode, mkdev(stat.Devmajor, stat.Devminor)) } func mkdev(major int64, minor int64) uint64 { return unix.Mkdev(uint32(major), uint32(minor)) } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/diskwriter_test.go000066400000000000000000000160521456645347400261730ustar00rootroot00000000000000package fsutil import ( "bytes" "context" "io" "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 func requiresRoot(t *testing.T) { t.Helper() if os.Getuid() != 0 { t.Skip("skipping test that requires root") return } } func TestWriterSimple(t *testing.T) { requiresRoot(t) changes := changeStream([]string{ "ADD bar dir", "ADD bar/foo file", "ADD bar/foo2 symlink ../foo", "ADD foo file", "ADD foo2 file >foo", }) dest := t.TempDir() dw, err := NewDiskWriter(context.TODO(), dest, DiskWriterOpt{ SyncDataCb: noOpWriteTo, }) assert.NoError(t, err) for _, c := range changes { err := dw.HandleChange(c.kind, c.path, c.fi, nil) assert.NoError(t, err) } b := &bytes.Buffer{} err = Walk(context.Background(), dest, nil, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, string(b.Bytes()), `dir bar file bar/foo symlink:../foo bar/foo2 file foo file foo2 >foo `) } func TestWriterFileToDir(t *testing.T) { requiresRoot(t) changes := changeStream([]string{ "ADD foo dir", "ADD foo/bar file data2", }) dest, err := tmpDir(changeStream([]string{ "ADD foo file data1", })) assert.NoError(t, err) defer os.RemoveAll(dest) dw, err := NewDiskWriter(context.TODO(), dest, DiskWriterOpt{ SyncDataCb: noOpWriteTo, }) assert.NoError(t, err) for _, c := range changes { err := dw.HandleChange(c.kind, c.path, c.fi, nil) assert.NoError(t, err) } b := &bytes.Buffer{} err = Walk(context.Background(), dest, nil, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, string(b.Bytes()), `dir foo file foo/bar `) } func TestWriterDirToFile(t *testing.T) { requiresRoot(t) changes := changeStream([]string{ "ADD foo file data1", }) dest, err := tmpDir(changeStream([]string{ "ADD foo dir", "ADD foo/bar file data2", })) assert.NoError(t, err) defer os.RemoveAll(dest) dw, err := NewDiskWriter(context.TODO(), dest, DiskWriterOpt{ SyncDataCb: noOpWriteTo, }) assert.NoError(t, err) for _, c := range changes { err := dw.HandleChange(c.kind, c.path, c.fi, nil) assert.NoError(t, err) } b := &bytes.Buffer{} err = Walk(context.Background(), dest, nil, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, string(b.Bytes()), `file foo `) } func TestWalkerWriterSimple(t *testing.T) { d, err := tmpDir(changeStream([]string{ "ADD bar dir", "ADD bar/foo file", "ADD bar/foo2 symlink ../foo", "ADD foo file mydata", "ADD foo2 file", })) assert.NoError(t, err) defer os.RemoveAll(d) 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) b := &bytes.Buffer{} err = Walk(context.Background(), dest, nil, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, string(b.Bytes()), `dir bar file bar/foo symlink:../foo bar/foo2 file foo 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 { return func(path string, fi os.FileInfo, err error) error { return f(ChangeKindAdd, path, fi, err) } } func noOpWriteTo(_ context.Context, _ string, _ io.WriteCloser) error { return nil } func newWriteToFunc(baseDir string, delay time.Duration) WriteToFunc { return func(ctx context.Context, path string, wc io.WriteCloser) error { if delay > 0 { time.Sleep(delay) } f, err := os.Open(filepath.Join(baseDir, path)) if err != nil { return err } if _, err := io.Copy(wc, f); err != nil { return err } if err := f.Close(); err != nil { return err } return nil } } type notificationBuffer struct { items map[string]digest.Digest sync.Mutex } func newNotificationBuffer() *notificationBuffer { nb := ¬ificationBuffer{ items: map[string]digest.Digest{}, } return nb } type hashed interface { Digest() digest.Digest } func (nb *notificationBuffer) HandleChange(kind ChangeKind, p string, fi os.FileInfo, err error) (retErr error) { nb.Lock() defer nb.Unlock() if kind == ChangeKindDelete { delete(nb.items, p) } else { h, ok := fi.(hashed) if !ok { return errors.Errorf("invalid FileInfo: %s", p) } nb.items[p] = h.Digest() } return nil } func (nb *notificationBuffer) Hash(p string) (digest.Digest, bool) { nb.Lock() v, ok := nb.items[p] nb.Unlock() return v, ok } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/diskwriter_unix.go000066400000000000000000000025631456645347400262010ustar00rootroot00000000000000//go:build !windows // +build !windows package fsutil import ( "os" "syscall" "github.com/containerd/continuity/sysx" "github.com/pkg/errors" "github.com/tonistiigi/fsutil/types" ) func rewriteMetadata(p string, stat *types.Stat) error { for key, value := range stat.Xattrs { sysx.Setxattr(p, key, value, 0) } if err := os.Lchown(p, int(stat.Uid), int(stat.Gid)); err != nil { return errors.WithStack(err) } if os.FileMode(stat.Mode)&os.ModeSymlink == 0 { if err := os.Chmod(p, os.FileMode(stat.Mode)); err != nil { return errors.WithStack(err) } } if err := chtimes(p, stat.ModTime); err != nil { return err } return nil } // handleTarTypeBlockCharFifo is an OS-specific helper function used by // createTarFile to handle the following types of header: Block; Char; Fifo func handleTarTypeBlockCharFifo(path string, stat *types.Stat) error { mode := uint32(stat.Mode & 07777) if os.FileMode(stat.Mode)&os.ModeCharDevice != 0 { mode |= syscall.S_IFCHR } else if os.FileMode(stat.Mode)&os.ModeNamedPipe != 0 { mode |= syscall.S_IFIFO } else { mode |= syscall.S_IFBLK } if err := createSpecialFile(path, mode, stat); err != nil { return errors.WithStack(err) } return nil } func renameFile(src, dst string) error { if err := os.Rename(src, dst); err != nil { return errors.Wrapf(err, "failed to rename %s to %s", src, dst) } return nil } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/diskwriter_unixnobsd.go000066400000000000000000000006151456645347400272230ustar00rootroot00000000000000//go:build !windows && !freebsd // +build !windows,!freebsd package fsutil import ( "github.com/tonistiigi/fsutil/types" "golang.org/x/sys/unix" ) func createSpecialFile(path string, mode uint32, stat *types.Stat) error { return unix.Mknod(path, mode, mkdev(stat.Devmajor, stat.Devminor)) } func mkdev(major int64, minor int64) int { return int(unix.Mkdev(uint32(major), uint32(minor))) } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/diskwriter_windows.go000066400000000000000000000054241456645347400267070ustar00rootroot00000000000000//go:build windows // +build windows package fsutil import ( "fmt" iofs "io/fs" "os" "syscall" "github.com/Microsoft/go-winio" "github.com/pkg/errors" "github.com/tonistiigi/fsutil/types" ) func rewriteMetadata(p string, stat *types.Stat) error { return chtimes(p, stat.ModTime) } // handleTarTypeBlockCharFifo is an OS-specific helper function used by // createTarFile to handle the following types of header: Block; Char; Fifo func handleTarTypeBlockCharFifo(path string, stat *types.Stat) error { return errors.New("Not implemented on windows") } func getFileHandle(path string, info iofs.FileInfo) (syscall.Handle, error) { p, err := syscall.UTF16PtrFromString(path) if err != nil { return 0, errors.Wrap(err, "converting string to UTF-16") } attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS) if info.Mode()&os.ModeSymlink != 0 { // Use FILE_FLAG_OPEN_REPARSE_POINT, otherwise CreateFile will follow symlink. // See https://docs.microsoft.com/en-us/windows/desktop/FileIO/symbolic-link-effects-on-file-systems-functions#createfile-and-createfiletransacted attrs |= syscall.FILE_FLAG_OPEN_REPARSE_POINT } h, err := syscall.CreateFile(p, 0, 0, nil, syscall.OPEN_EXISTING, attrs, 0) if err != nil { return 0, errors.Wrap(err, "getting file handle") } return h, nil } func readlink(path string, info iofs.FileInfo) ([]byte, error) { h, err := getFileHandle(path, info) if err != nil { return nil, errors.Wrap(err, "getting file handle") } defer syscall.CloseHandle(h) rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE) var bytesReturned uint32 err = syscall.DeviceIoControl(h, syscall.FSCTL_GET_REPARSE_POINT, nil, 0, &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil) if err != nil { return nil, errors.Wrap(err, "sending I/O control command") } return rdbbuf[:bytesReturned], nil } func getReparsePoint(path string, info iofs.FileInfo) (*winio.ReparsePoint, error) { target, err := readlink(path, info) if err != nil { return nil, errors.Wrap(err, "fetching link") } rp, err := winio.DecodeReparsePoint(target) if err != nil { return nil, errors.Wrap(err, "decoding reparse point") } return rp, nil } func renameFile(src, dst string) error { info, err := os.Lstat(dst) if err != nil { if !os.IsNotExist(err) { return errors.Wrap(err, "getting file info") } } if info != nil && info.Mode()&os.ModeSymlink != 0 { dstInfoRp, err := getReparsePoint(dst, info) if err != nil { return errors.Wrap(err, "getting reparse point") } if dstInfoRp.IsMountPoint { return fmt.Errorf("%s is a mount point", dst) } if err := os.Remove(dst); err != nil { return errors.Wrapf(err, "removing %s", dst) } } if err := os.Rename(src, dst); err != nil { return errors.Wrapf(err, "failed to rename %s to %s", src, dst) } return nil } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/docker-bake.hcl000066400000000000000000000024331456645347400252530ustar00rootroot00000000000000variable "GO_VERSION" { default = "1.20" } group "default" { targets = ["build"] } target "build" { args = { GO_VERSION = "${GO_VERSION}" } } group "test" { targets = ["test-root", "test-noroot"] } target "test-root" { inherits = ["build"] target = "test" } target "test-noroot" { inherits = ["build"] target = "test-noroot" } target "lint" { dockerfile = "./hack/dockerfiles/lint.Dockerfile" args = { GO_VERSION = "${GO_VERSION}" } } target "validate-gomod" { dockerfile = "./hack/dockerfiles/gomod.Dockerfile" target = "validate" args = { # go mod may produce different results between go versions, # if this becomes a problem, this should be switched to use # a fixed go version. GO_VERSION = "${GO_VERSION}" } } target "gomod" { inherits = ["validate-gomod"] output = ["."] target = "update" } target "validate-shfmt" { dockerfile = "./hack/dockerfiles/shfmt.Dockerfile" target = "validate" } target "shfmt" { inherits = ["validate-shfmt"] output = ["."] target = "update" } target "cross" { inherits = ["build"] platforms = ["linux/amd64", "linux/386", "linux/arm64", "linux/arm", "linux/ppc64le", "linux/s390x", "darwin/amd64", "darwin/arm64", "windows/amd64", "windows/arm64", "freebsd/amd64", "freebsd/arm64"] } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/followlinks.go000066400000000000000000000062201456645347400253040ustar00rootroot00000000000000package fsutil import ( "os" "path/filepath" "runtime" "sort" strings "strings" "github.com/pkg/errors" ) func FollowLinks(root string, paths []string) ([]string, error) { r := &symlinkResolver{root: root, resolved: map[string]struct{}{}} for _, p := range paths { if err := r.append(p); err != nil { return nil, err } } res := make([]string, 0, len(r.resolved)) for r := range r.resolved { res = append(res, filepath.ToSlash(r)) } sort.Strings(res) return dedupePaths(res), nil } type symlinkResolver struct { root string resolved map[string]struct{} } func (r *symlinkResolver) append(p string) error { if runtime.GOOS == "windows" && filepath.IsAbs(filepath.FromSlash(p)) { absParts := strings.SplitN(p, ":", 2) if len(absParts) == 2 { p = absParts[1] } } p = filepath.Join(".", p) current := "." for { parts := strings.SplitN(p, string(filepath.Separator), 2) current = filepath.Join(current, parts[0]) targets, err := r.readSymlink(current, true) if err != nil { return err } p = "" if len(parts) == 2 { p = parts[1] } if p == "" || targets != nil { if _, ok := r.resolved[current]; ok { return nil } } if targets != nil { r.resolved[current] = struct{}{} for _, target := range targets { if err := r.append(filepath.Join(target, p)); err != nil { return err } } return nil } if p == "" { r.resolved[current] = struct{}{} return nil } } } func (r *symlinkResolver) readSymlink(p string, allowWildcard bool) ([]string, error) { realPath := filepath.Join(r.root, p) base := filepath.Base(p) if allowWildcard && containsWildcards(base) { fis, err := os.ReadDir(filepath.Dir(realPath)) if err != nil { if isNotFound(err) { return nil, nil } return nil, errors.Wrap(err, "readdir") } var out []string for _, f := range fis { if ok, _ := filepath.Match(base, f.Name()); ok { res, err := r.readSymlink(filepath.Join(filepath.Dir(p), f.Name()), false) if err != nil { return nil, err } out = append(out, res...) } } return out, nil } fi, err := os.Lstat(realPath) if err != nil { if isNotFound(err) { return nil, nil } return nil, errors.WithStack(err) } if fi.Mode()&os.ModeSymlink == 0 { return nil, nil } link, err := os.Readlink(realPath) if err != nil { return nil, errors.WithStack(err) } link = filepath.Clean(link) if filepath.IsAbs(link) { return []string{link}, nil } return []string{ filepath.Join(string(filepath.Separator), filepath.Join(filepath.Dir(p), link)), }, nil } func containsWildcards(name string) bool { isWindows := runtime.GOOS == "windows" for i := 0; i < len(name); i++ { ch := name[i] if ch == '\\' && !isWindows { i++ } else if ch == '*' || ch == '?' || ch == '[' { return true } } return false } // dedupePaths expects input as a sorted list func dedupePaths(in []string) []string { out := make([]string, 0, len(in)) var last string for _, s := range in { // if one of the paths is root there is no filter if s == "." { return nil } if strings.HasPrefix(s, last+"/") { continue } out = append(out, s) last = s } return out } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/followlinks_test.go000066400000000000000000000106471456645347400263530ustar00rootroot00000000000000package fsutil import ( "path/filepath" "runtime" "testing" "github.com/containerd/continuity/fs/fstest" "github.com/stretchr/testify/require" ) func TestFollowLinks(t *testing.T) { tmpDir := t.TempDir() apply := fstest.Apply( fstest.CreateDir("dir", 0700), fstest.CreateFile("dir/foo", []byte("contents"), 0600), fstest.Symlink("foo", "dir/l1"), fstest.Symlink("dir/l1", "l2"), fstest.CreateFile("bar", nil, 0600), fstest.CreateFile("baz", nil, 0600), ) require.NoError(t, apply.Apply(tmpDir)) out, err := FollowLinks(tmpDir, []string{"l2", "bar"}) require.NoError(t, err) require.Equal(t, out, []string{"bar", "dir/foo", "dir/l1", "l2"}) } func TestFollowLinksLoop(t *testing.T) { tmpDir := t.TempDir() apply := fstest.Apply( fstest.Symlink("l1", "l1"), fstest.Symlink("l2", "l3"), fstest.Symlink("l3", "l2"), ) require.NoError(t, apply.Apply(tmpDir)) out, err := FollowLinks(tmpDir, []string{"l1", "l3"}) require.NoError(t, err) require.Equal(t, out, []string{"l1", "l2", "l3"}) } func TestFollowLinksAbsolute(t *testing.T) { tmpDir := t.TempDir() abslutePathForBaz := "/foo/bar/baz" if runtime.GOOS == "windows" { abslutePathForBaz = "C:/foo/bar/baz" } apply := fstest.Apply( fstest.CreateDir("dir", 0700), fstest.Symlink(abslutePathForBaz, "dir/l1"), fstest.CreateDir("foo", 0700), fstest.Symlink("../", "foo/bar"), fstest.CreateFile("baz", nil, 0600), ) require.NoError(t, apply.Apply(tmpDir)) out, err := FollowLinks(tmpDir, []string{"dir/l1"}) require.NoError(t, err) require.Equal(t, []string{"baz", "dir/l1", "foo/bar"}, out) // same but a link outside root tmpDir = t.TempDir() apply = fstest.Apply( fstest.CreateDir("dir", 0700), fstest.Symlink(abslutePathForBaz, "dir/l1"), fstest.CreateDir("foo", 0700), fstest.Symlink("../../../", "foo/bar"), fstest.CreateFile("baz", nil, 0600), ) require.NoError(t, apply.Apply(tmpDir)) out, err = FollowLinks(tmpDir, []string{"dir/l1"}) require.NoError(t, err) require.Equal(t, []string{"baz", "dir/l1", "foo/bar"}, out) } func TestFollowLinksNotExists(t *testing.T) { tmpDir := t.TempDir() out, err := FollowLinks(tmpDir, []string{"foo/bar/baz", "bar/baz"}) require.NoError(t, err) require.Equal(t, out, []string{"bar/baz", "foo/bar/baz"}) // root works fine with empty directory out, err = FollowLinks(tmpDir, []string{"."}) require.NoError(t, err) require.Equal(t, out, []string(nil)) out, err = FollowLinks(tmpDir, []string{"f*/foo/t*"}) require.NoError(t, err) require.Equal(t, []string{"f*/foo/t*"}, out) } func TestFollowLinksNormalized(t *testing.T) { tmpDir := t.TempDir() out, err := FollowLinks(tmpDir, []string{"foo/bar/baz", "foo/bar"}) require.NoError(t, err) require.Equal(t, out, []string{"foo/bar"}) rootPath := "/" if runtime.GOOS == "windows" { rootPath = "C:/" } apply := fstest.Apply( fstest.CreateDir("dir", 0700), fstest.Symlink(filepath.Join(rootPath, "foo"), "dir/l1"), fstest.Symlink(rootPath, "dir/l2"), fstest.CreateDir("foo", 0700), fstest.CreateFile("foo/bar", nil, 0600), ) require.NoError(t, apply.Apply(tmpDir)) out, err = FollowLinks(tmpDir, []string{"dir/l1", "foo/bar"}) require.NoError(t, err) require.Equal(t, out, []string{"dir/l1", "foo"}) out, err = FollowLinks(tmpDir, []string{"dir/l2", "foo", "foo/bar"}) require.NoError(t, err) require.Equal(t, out, []string(nil)) } func TestFollowLinksWildcard(t *testing.T) { tmpDir := t.TempDir() absolutePathForFoo := "/foo" if runtime.GOOS == "windows" { absolutePathForFoo = "C:/foo" } apply := fstest.Apply( fstest.CreateDir("dir", 0700), fstest.CreateDir("foo", 0700), fstest.Symlink(filepath.Join(absolutePathForFoo, "bar1"), "dir/l1"), fstest.Symlink(filepath.Join(absolutePathForFoo, "bar2"), "dir/l2"), fstest.Symlink(filepath.Join(absolutePathForFoo, "bar3"), "dir/anotherlink"), fstest.Symlink("../baz", "foo/bar2"), fstest.CreateFile("foo/bar1", nil, 0600), fstest.CreateFile("foo/bar3", nil, 0600), fstest.CreateFile("baz", nil, 0600), ) require.NoError(t, apply.Apply(tmpDir)) out, err := FollowLinks(tmpDir, []string{"dir/l*"}) require.NoError(t, err) require.Equal(t, []string{"baz", "dir/l*", "foo/bar1", "foo/bar2"}, out) out, err = FollowLinks(tmpDir, []string{"dir"}) require.NoError(t, err) require.Equal(t, out, []string{"dir"}) out, err = FollowLinks(tmpDir, []string{"dir", "dir/*link"}) require.NoError(t, err) require.Equal(t, out, []string{"dir", "foo/bar3"}) } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/followlinks_unix.go000066400000000000000000000002571456645347400263530ustar00rootroot00000000000000//go:build !windows // +build !windows package fsutil import ( "os" "github.com/pkg/errors" ) func isNotFound(err error) bool { return errors.Is(err, os.ErrNotExist) } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/followlinks_windows.go000066400000000000000000000003561456645347400270620ustar00rootroot00000000000000package fsutil import ( "os" "github.com/pkg/errors" "golang.org/x/sys/windows" ) func isNotFound(err error) bool { if errors.Is(err, os.ErrNotExist) || errors.Is(err, windows.ERROR_INVALID_NAME) { return true } return false } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/fs.go000066400000000000000000000053701456645347400233560ustar00rootroot00000000000000package fsutil import ( "context" "io" "os" "path" "path/filepath" "sort" "strings" "syscall" "github.com/pkg/errors" "github.com/tonistiigi/fsutil/types" ) type FS interface { Walk(context.Context, filepath.WalkFunc) error Open(string) (io.ReadCloser, error) } func NewFS(root string, opt *WalkOpt) FS { return &fs{ root: root, opt: opt, } } type fs struct { root string opt *WalkOpt } func (fs *fs) Walk(ctx context.Context, fn filepath.WalkFunc) error { return Walk(ctx, fs.root, fs.opt, fn) } func (fs *fs) Open(p string) (io.ReadCloser, error) { rc, err := os.Open(filepath.Join(fs.root, p)) return rc, errors.WithStack(err) } type Dir struct { Stat types.Stat FS FS } func SubDirFS(dirs []Dir) (FS, error) { sort.Slice(dirs, func(i, j int) bool { return dirs[i].Stat.Path < dirs[j].Stat.Path }) m := map[string]Dir{} for _, d := range dirs { if path.Base(d.Stat.Path) != d.Stat.Path { return nil, errors.WithStack(&os.PathError{Path: d.Stat.Path, Err: syscall.EISDIR, Op: "invalid path"}) } if _, ok := m[d.Stat.Path]; ok { return nil, errors.WithStack(&os.PathError{Path: d.Stat.Path, Err: syscall.EEXIST, Op: "duplicate path"}) } m[d.Stat.Path] = d } return &subDirFS{m: m, dirs: dirs}, nil } type subDirFS struct { m map[string]Dir dirs []Dir } func (fs *subDirFS) Walk(ctx context.Context, fn filepath.WalkFunc) error { for _, d := range fs.dirs { fi := &StatInfo{Stat: &d.Stat} if !fi.IsDir() { return errors.WithStack(&os.PathError{Path: d.Stat.Path, Err: syscall.ENOTDIR, Op: "walk subdir"}) } if err := fn(d.Stat.Path, fi, nil); err != nil { return err } if err := d.FS.Walk(ctx, func(p string, fi os.FileInfo, err error) error { stat, ok := fi.Sys().(*types.Stat) if !ok { return errors.WithStack(&os.PathError{Path: d.Stat.Path, Err: syscall.EBADMSG, Op: "fileinfo without stat info"}) } stat.Path = path.Join(d.Stat.Path, stat.Path) if stat.Linkname != "" { if fi.Mode()&os.ModeSymlink != 0 { if strings.HasPrefix(stat.Linkname, "/") { stat.Linkname = path.Join("/"+d.Stat.Path, stat.Linkname) } } else { stat.Linkname = path.Join(d.Stat.Path, stat.Linkname) } } return fn(filepath.Join(d.Stat.Path, p), &StatInfo{stat}, nil) }); err != nil { return err } } return nil } func (fs *subDirFS) Open(p string) (io.ReadCloser, error) { parts := strings.SplitN(filepath.Clean(p), string(filepath.Separator), 2) if len(parts) == 0 { return io.NopCloser(&emptyReader{}), nil } d, ok := fs.m[parts[0]] if !ok { return nil, errors.WithStack(&os.PathError{Path: parts[0], Err: syscall.ENOENT, Op: "open"}) } return d.FS.Open(parts[1]) } type emptyReader struct { } func (*emptyReader) Read([]byte) (int, error) { return 0, io.EOF } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/go.mod000066400000000000000000000015011456645347400235150ustar00rootroot00000000000000module github.com/tonistiigi/fsutil go 1.19 require ( github.com/Microsoft/go-winio v0.5.2 github.com/containerd/continuity v0.3.1-0.20230206214859-2a963a2f56e8 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.7.0 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20220412211240-33da011f77ad ) require ( github.com/davecgh/go-spew v1.1.1 // indirect 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 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/go.sum000066400000000000000000000161751456645347400235570ustar00rootroot00000000000000github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/containerd/continuity v0.3.1-0.20230206214859-2a963a2f56e8 h1:EdSQb65ohzz4jsyPOhxfu3/+c9nnU0euk0otferwl9A= github.com/containerd/continuity v0.3.1-0.20230206214859-2a963a2f56e8/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo= github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/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= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/hack/000077500000000000000000000000001456645347400233205ustar00rootroot00000000000000golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/hack/Vagrantfile.freebsd13000066400000000000000000000040551456645347400272660ustar00rootroot00000000000000# 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. 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.provision "shell", inline: <<-SHELL pkg bootstrap pkg install -y go SHELL end golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/hack/dockerfiles/000077500000000000000000000000001456645347400256125ustar00rootroot00000000000000golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/hack/dockerfiles/gomod.Dockerfile000066400000000000000000000010131456645347400307030ustar00rootroot00000000000000# syntax=docker/dockerfile:1 ARG GO_VERSION=1.20 FROM golang:${GO_VERSION}-alpine AS gomod RUN apk add --no-cache git WORKDIR /src RUN --mount=target=/src,rw \ --mount=target=/go/pkg/mod,type=cache \ go mod tidy && \ mkdir /out && cp -r go.mod go.sum /out && \ cd bench && go mod tidy && \ mkdir /out/bench && cp -r go.mod go.sum /out/bench FROM scratch AS update COPY --from=gomod /out / FROM gomod AS validate RUN --mount=target=.,rw \ git add -A && \ cp -rf /out/* . && \ ./hack/validate-gomod check golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/hack/dockerfiles/lint.Dockerfile000066400000000000000000000006571456645347400305610ustar00rootroot00000000000000# syntax=docker/dockerfile:1 ARG GO_VERSION=1.20 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 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 golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/hack/dockerfiles/shfmt.Dockerfile000066400000000000000000000005611456645347400307260ustar00rootroot00000000000000# syntax=docker/dockerfile:1 FROM mvdan/shfmt:v3.4.3-alpine AS shfmt WORKDIR /src ARG SHFMT_FLAGS="-i 2 -ci" FROM shfmt AS generate RUN --mount=target=/src,rw \ shfmt -l -w $SHFMT_FLAGS ./hack && \ mkdir -p /out && cp -r ./hack /out/ FROM scratch AS update COPY --from=generate /out / FROM shfmt AS validate RUN --mount=target=. \ shfmt $SHFMT_FLAGS -d ./hack golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/hack/lint000077500000000000000000000006211456645347400242130ustar00rootroot00000000000000#!/usr/bin/env bash set -eu -o pipefail -x : ${CONTINUOUS_INTEGRATION=} progressFlag="" if [ "$CONTINUOUS_INTEGRATION" == "true" ]; then progressFlag="--progress=plain"; fi cacheOnlyFlag="" if ! docker build --help 2>&1 | grep buildx >/dev/null; then cacheOnlyFlag="-o type=cacheonly"; fi export DOCKER_BUILDKIT=1 docker build $progressFlag $cacheOnlyFlag -f ./hack/dockerfiles/lint.Dockerfile . golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/hack/shfmt000077500000000000000000000004431456645347400243700ustar00rootroot00000000000000#!/usr/bin/env bash set -eu -o pipefail -x : ${CONTINUOUS_INTEGRATION=} progressFlag="" if [ "$CONTINUOUS_INTEGRATION" == "true" ]; then progressFlag="--progress=plain"; fi export DOCKER_BUILDKIT=1 docker build $progressFlag -f ./hack/dockerfiles/shfmt.Dockerfile --target update -o . . golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/hack/update-gomod000077500000000000000000000004431456645347400256340ustar00rootroot00000000000000#!/usr/bin/env bash set -eu -o pipefail -x : ${CONTINUOUS_INTEGRATION=} progressFlag="" if [ "$CONTINUOUS_INTEGRATION" == "true" ]; then progressFlag="--progress=plain"; fi export DOCKER_BUILDKIT=1 docker build $progressFlag -f ./hack/dockerfiles/gomod.Dockerfile --target update -o . . golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/hack/validate-gomod000077500000000000000000000017131456645347400261440ustar00rootroot00000000000000#!/usr/bin/env sh set -eu : ${CONTINUOUS_INTEGRATION=} : ${DOCKER_BUILDKIT=} progressFlag="" if [ "$CONTINUOUS_INTEGRATION" = "true" ]; then progressFlag="--progress=plain"; fi cacheOnlyFlag="" if ! docker build --help 2>&1 | grep buildx >/dev/null; then cacheOnlyFlag="-o type=cacheonly"; fi case ${1:-} in '') export DOCKER_BUILDKIT=1 docker build $progressFlag $cacheOnlyFlag -f ./hack/dockerfiles/gomod.Dockerfile --target validate . || exit 1 ;; check) status="$(git status --porcelain -- go.mod go.sum bench/go.mod bench/go.sum 2>/dev/null)" diffs=$(echo "$status" | grep -v '^[RAD] ' || true) if [ "$diffs" ]; then { set +x echo 'The result of "./hack/validate-gomod" differs' echo echo "$diffs" echo echo 'Please vendor your package with "./hack/update-gomod"' echo } >&2 exit 1 fi echo 'Congratulations! go.mod is done the right way.' ;; esac golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/hack/validate-shfmt000077500000000000000000000006501456645347400261570ustar00rootroot00000000000000#!/usr/bin/env sh set -eu : ${CONTINUOUS_INTEGRATION=} : ${DOCKER_BUILDKIT=} progressFlag="" if [ "$CONTINUOUS_INTEGRATION" = "true" ]; then progressFlag="--progress=plain"; fi cacheOnlyFlag="" if ! docker build --help 2>&1 | grep buildx >/dev/null; then cacheOnlyFlag="-o type=cacheonly"; fi export DOCKER_BUILDKIT=1 docker build $progressFlag $cacheOnlyFlag -f ./hack/dockerfiles/shfmt.Dockerfile --target validate . golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/hardlinks.go000066400000000000000000000016441456645347400247250ustar00rootroot00000000000000package fsutil import ( "os" "syscall" "github.com/pkg/errors" "github.com/tonistiigi/fsutil/types" ) // Hardlinks validates that all targets for links were part of the changes type Hardlinks struct { seenFiles map[string]struct{} } func (v *Hardlinks) HandleChange(kind ChangeKind, p string, fi os.FileInfo, err error) error { if err != nil { return err } if v.seenFiles == nil { v.seenFiles = make(map[string]struct{}) } if kind == ChangeKindDelete { return nil } stat, ok := fi.Sys().(*types.Stat) if !ok { return errors.WithStack(&os.PathError{Path: p, Err: syscall.EBADMSG, Op: "change without stat info"}) } if fi.IsDir() || fi.Mode()&os.ModeSymlink != 0 { return nil } if len(stat.Linkname) > 0 { if _, ok := v.seenFiles[stat.Linkname]; !ok { return errors.Errorf("invalid link %s to unknown path: %q", p, stat.Linkname) } } else { v.seenFiles[p] = struct{}{} } return nil } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/hardlinks_test.go000066400000000000000000000020621456645347400257570ustar00rootroot00000000000000package fsutil import ( "testing" "github.com/stretchr/testify/assert" ) func TestValidHardlinks(t *testing.T) { err := checkHardlinks(changeStream([]string{ "ADD foo file", "ADD foo2 file >foo", })) assert.NoError(t, err) } func TestInvalideHardlinks(t *testing.T) { err := checkHardlinks(changeStream([]string{ "ADD foo file >foo2", "ADD foo2 file", })) assert.Error(t, err) } func TestInvalideHardlinks2(t *testing.T) { err := checkHardlinks(changeStream([]string{ "ADD foo file", "ADD foo2 file >bar", })) assert.Error(t, err) } func TestHardlinkToDir(t *testing.T) { err := checkHardlinks(changeStream([]string{ "ADD foo dir", "ADD foo2 file >foo", })) assert.Error(t, err) } func TestHardlinkToSymlink(t *testing.T) { err := checkHardlinks(changeStream([]string{ "ADD foo symlink /", "ADD foo2 file >foo", })) assert.Error(t, err) } func checkHardlinks(inp []*change) error { h := &Hardlinks{} for _, c := range inp { if err := h.HandleChange(c.kind, c.path, c.fi, nil); err != nil { return err } } return nil } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/readme.md000066400000000000000000000053431456645347400241760ustar00rootroot00000000000000Incremental file directory sync tools in golang. ``` BENCH_FILE_SIZE=10000 ./bench.test --test.bench . BenchmarkCopyWithTar10-4 2000 995242 ns/op BenchmarkCopyWithTar50-4 300 4710021 ns/op BenchmarkCopyWithTar200-4 100 16627260 ns/op BenchmarkCopyWithTar1000-4 20 60031459 ns/op BenchmarkCPA10-4 1000 1678367 ns/op BenchmarkCPA50-4 500 3690306 ns/op BenchmarkCPA200-4 200 9495066 ns/op BenchmarkCPA1000-4 50 29769289 ns/op BenchmarkDiffCopy10-4 2000 943889 ns/op BenchmarkDiffCopy50-4 500 3285950 ns/op BenchmarkDiffCopy200-4 200 8563792 ns/op BenchmarkDiffCopy1000-4 50 29511340 ns/op BenchmarkDiffCopyProto10-4 2000 944615 ns/op BenchmarkDiffCopyProto50-4 500 3334940 ns/op BenchmarkDiffCopyProto200-4 200 9420038 ns/op BenchmarkDiffCopyProto1000-4 50 30632429 ns/op BenchmarkIncrementalDiffCopy10-4 2000 691993 ns/op BenchmarkIncrementalDiffCopy50-4 1000 1304253 ns/op BenchmarkIncrementalDiffCopy200-4 500 3306519 ns/op BenchmarkIncrementalDiffCopy1000-4 200 10211343 ns/op BenchmarkIncrementalDiffCopy5000-4 20 55194427 ns/op BenchmarkIncrementalDiffCopy10000-4 20 91759289 ns/op BenchmarkIncrementalCopyWithTar10-4 2000 1020258 ns/op BenchmarkIncrementalCopyWithTar50-4 300 5348786 ns/op BenchmarkIncrementalCopyWithTar200-4 100 19495000 ns/op BenchmarkIncrementalCopyWithTar1000-4 20 70338507 ns/op BenchmarkIncrementalRsync10-4 30 45215754 ns/op BenchmarkIncrementalRsync50-4 30 45837260 ns/op BenchmarkIncrementalRsync200-4 30 48780614 ns/op BenchmarkIncrementalRsync1000-4 20 54801892 ns/op BenchmarkIncrementalRsync5000-4 20 84782542 ns/op BenchmarkIncrementalRsync10000-4 10 103355108 ns/op BenchmarkRsync10-4 30 46776470 ns/op BenchmarkRsync50-4 30 48601555 ns/op BenchmarkRsync200-4 20 59642691 ns/op BenchmarkRsync1000-4 20 101343010 ns/op BenchmarkGnuTar10-4 500 3171448 ns/op BenchmarkGnuTar50-4 300 5030296 ns/op BenchmarkGnuTar200-4 100 10464313 ns/op BenchmarkGnuTar1000-4 50 30375257 ns/op ```golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/receive.go000066400000000000000000000133741456645347400243730ustar00rootroot00000000000000package fsutil import ( "context" "io" "os" "sync" "github.com/pkg/errors" "github.com/tonistiigi/fsutil/types" "golang.org/x/sync/errgroup" ) type DiffType int const ( DiffMetadata DiffType = iota DiffNone DiffContent ) type ReceiveOpt struct { NotifyHashed ChangeFunc ContentHasher ContentHasher ProgressCb func(int, bool) Merge bool Filter FilterFunc Differ DiffType } func Receive(ctx context.Context, conn Stream, dest string, opt ReceiveOpt) error { ctx, cancel := context.WithCancel(ctx) defer cancel() r := &receiver{ conn: &syncStream{Stream: conn}, dest: dest, files: make(map[string]uint32), pipes: make(map[uint32]io.WriteCloser), notifyHashed: opt.NotifyHashed, contentHasher: opt.ContentHasher, progressCb: opt.ProgressCb, merge: opt.Merge, filter: opt.Filter, differ: opt.Differ, } return r.run(ctx) } type receiver struct { dest string conn Stream files map[string]uint32 pipes map[uint32]io.WriteCloser mu sync.RWMutex muPipes sync.RWMutex progressCb func(int, bool) merge bool filter FilterFunc differ DiffType notifyHashed ChangeFunc contentHasher ContentHasher orderValidator Validator hlValidator Hardlinks } type dynamicWalker struct { walkChan chan *currentPath err error closeCh chan struct{} } func newDynamicWalker() *dynamicWalker { return &dynamicWalker{ walkChan: make(chan *currentPath, 128), closeCh: make(chan struct{}), } } func (w *dynamicWalker) update(p *currentPath) error { select { case <-w.closeCh: return errors.Wrap(w.err, "walker is closed") default: } if p == nil { close(w.walkChan) return nil } select { case w.walkChan <- p: return nil case <-w.closeCh: return errors.Wrap(w.err, "walker is closed") } } func (w *dynamicWalker) fill(ctx context.Context, pathC chan<- *currentPath) error { for { select { case p, ok := <-w.walkChan: if !ok { return nil } select { case pathC <- p: case <-ctx.Done(): w.err = ctx.Err() close(w.closeCh) return ctx.Err() } case <-ctx.Done(): w.err = ctx.Err() close(w.closeCh) return ctx.Err() } } } func (r *receiver) run(ctx context.Context) error { g, ctx := errgroup.WithContext(ctx) dw, err := NewDiskWriter(ctx, r.dest, DiskWriterOpt{ AsyncDataCb: r.asyncDataFunc, NotifyCb: r.notifyHashed, ContentHasher: r.contentHasher, Filter: r.filter, }) if err != nil { return err } w := newDynamicWalker() g.Go(func() (retErr error) { defer func() { if retErr != nil { r.conn.SendMsg(&types.Packet{Type: types.PACKET_ERR, Data: []byte(retErr.Error())}) } }() destWalker := emptyWalker if !r.merge { destWalker = getWalkerFn(r.dest) } err := doubleWalkDiff(ctx, dw.HandleChange, destWalker, w.fill, r.filter, r.differ) if err != nil { return err } if err := dw.Wait(ctx); err != nil { return err } r.conn.SendMsg(&types.Packet{Type: types.PACKET_FIN}) return nil }) g.Go(func() error { var i uint32 = 0 size := 0 if r.progressCb != nil { defer func() { r.progressCb(size, true) }() } var p types.Packet for { p = types.Packet{Data: p.Data[:0]} if err := r.conn.RecvMsg(&p); err != nil { return err } if r.progressCb != nil { size += p.Size() r.progressCb(size, false) } switch p.Type { case types.PACKET_ERR: return errors.Errorf("error from sender: %s", p.Data) case types.PACKET_STAT: if p.Stat == nil { if err := w.update(nil); err != nil { return err } break } 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} if err := r.orderValidator.HandleChange(ChangeKindAdd, cp.path, &StatInfo{cp.stat}, nil); err != nil { return err } if err := r.hlValidator.HandleChange(ChangeKindAdd, cp.path, &StatInfo{cp.stat}, nil); err != nil { return err } if err := w.update(cp); err != nil { return err } case types.PACKET_DATA: r.muPipes.Lock() pw, ok := r.pipes[p.ID] r.muPipes.Unlock() if !ok { return errors.Errorf("invalid file request %d", p.ID) } if len(p.Data) == 0 { if err := pw.Close(); err != nil { return err } } else { if _, err := pw.Write(p.Data); err != nil { return err } } case types.PACKET_FIN: for { var p types.Packet if err := r.conn.RecvMsg(&p); err != nil { if err == io.EOF { return nil } return err } } } } }) return g.Wait() } func (r *receiver) asyncDataFunc(ctx context.Context, p string, wc io.WriteCloser) error { r.mu.Lock() id, ok := r.files[p] if !ok { r.mu.Unlock() return errors.Errorf("invalid file request %s", p) } delete(r.files, p) r.mu.Unlock() wwc := newWrappedWriteCloser(wc) r.muPipes.Lock() r.pipes[id] = wwc r.muPipes.Unlock() if err := r.conn.SendMsg(&types.Packet{Type: types.PACKET_REQ, ID: id}); err != nil { return err } err := wwc.Wait(ctx) if err != nil { return err } r.muPipes.Lock() delete(r.pipes, id) r.muPipes.Unlock() return nil } type wrappedWriteCloser struct { io.WriteCloser err error once sync.Once done chan struct{} } func newWrappedWriteCloser(wc io.WriteCloser) *wrappedWriteCloser { return &wrappedWriteCloser{WriteCloser: wc, done: make(chan struct{})} } func (w *wrappedWriteCloser) Close() error { w.err = w.WriteCloser.Close() w.once.Do(func() { close(w.done) }) return w.err } func (w *wrappedWriteCloser) Wait(ctx context.Context) error { select { case <-ctx.Done(): return ctx.Err() case <-w.done: return w.err } } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/receive_test.go000066400000000000000000000257301456645347400254310ustar00rootroot00000000000000package fsutil import ( "bytes" "context" "crypto/sha256" "hash" "io" "os" "path/filepath" "sync" "testing" "time" "github.com/opencontainers/go-digest" "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tonistiigi/fsutil/types" "golang.org/x/sync/errgroup" ) func TestInvalidExcludePatterns(t *testing.T) { d, err := tmpDir(changeStream([]string{ "ADD foo file data1", })) assert.NoError(t, err) defer os.RemoveAll(d) dest := t.TempDir() ts := newNotificationBuffer() chs := &changes{fn: ts.HandleChange} eg, ctx := errgroup.WithContext(context.Background()) s1, s2 := sockPairProto(ctx) eg.Go(func() error { defer s1.(*fakeConnProto).closeSend() err := Send(ctx, s1, NewFS(d, &WalkOpt{ExcludePatterns: []string{"!"}}), nil) assert.Contains(t, err.Error(), "invalid excludepatterns") return err }) eg.Go(func() error { err := Receive(ctx, s2, dest, ReceiveOpt{ NotifyHashed: chs.HandleChange, ContentHasher: simpleSHA256Hasher, }) assert.Contains(t, err.Error(), "error from sender:") assert.Contains(t, err.Error(), "invalid excludepatterns") return err }) errCh := make(chan error) go func() { errCh <- eg.Wait() }() select { case <-time.After(15 * time.Second): t.Fatal("timeout") case err = <-errCh: assert.Contains(t, err.Error(), "invalid excludepatterns") } } func TestCopyWithSubDir(t *testing.T) { requiresRoot(t) d, err := tmpDir(changeStream([]string{ "ADD foo dir", "ADD foo/bar file data1", })) assert.NoError(t, err) defer os.RemoveAll(d) dest := t.TempDir() eg, ctx := errgroup.WithContext(context.Background()) s1, s2 := sockPairProto(ctx) eg.Go(func() error { defer s1.(*fakeConnProto).closeSend() subdir, err := SubDirFS([]Dir{{FS: NewFS(d, &WalkOpt{}), Stat: types.Stat{Path: "sub", Mode: uint32(os.ModeDir | 0755)}}}) if err != nil { return err } return Send(ctx, s1, subdir, nil) }) eg.Go(func() error { return Receive(ctx, s2, dest, ReceiveOpt{}) }) err = eg.Wait() assert.NoError(t, err) dt, err := os.ReadFile(filepath.Join(dest, "sub/foo/bar")) assert.NoError(t, err) assert.Equal(t, "data1", string(dt)) } func TestCopyDirectoryTimestamps(t *testing.T) { d, err := tmpDir(changeStream([]string{ "ADD foo dir", "ADD foo/bar file data1", })) assert.NoError(t, err) defer os.RemoveAll(d) timestamp := time.Unix(0, 0) require.NoError(t, os.Chtimes(filepath.Join(d, "foo"), timestamp, timestamp)) 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, NewFS(d, nil), nil) }) eg.Go(func() error { return Receive(ctx, s2, dest, ReceiveOpt{}) }) err = eg.Wait() assert.NoError(t, err) dt, err := os.ReadFile(filepath.Join(dest, "foo/bar")) assert.NoError(t, err) assert.Equal(t, "data1", string(dt)) stat, err := os.Stat(filepath.Join(dest, "foo")) require.NoError(t, err) assert.Equal(t, timestamp, stat.ModTime()) } func TestCopySwitchDirToFile(t *testing.T) { d, err := tmpDir(changeStream([]string{ "ADD foo file data1", })) assert.NoError(t, err) defer os.RemoveAll(d) dest, err := tmpDir(changeStream([]string{ "ADD foo dir", "ADD foo/bar dile data2", })) assert.NoError(t, err) defer os.RemoveAll(d) copy := func(src, dest string) (*changes, error) { ts := newNotificationBuffer() chs := &changes{fn: ts.HandleChange} eg, ctx := errgroup.WithContext(context.Background()) s1, s2 := sockPairProto(ctx) eg.Go(func() error { defer s1.(*fakeConnProto).closeSend() return Send(ctx, s1, NewFS(src, &WalkOpt{ Map: func(_ string, s *types.Stat) MapResult { s.Uid = 0 s.Gid = 0 return MapResultKeep }, }), nil) }) eg.Go(func() error { return Receive(ctx, s2, dest, ReceiveOpt{ NotifyHashed: chs.HandleChange, ContentHasher: simpleSHA256Hasher, Filter: func(_ string, s *types.Stat) bool { s.Uid = uint32(os.Getuid()) s.Gid = uint32(os.Getgid()) return true }, }) }) if err := eg.Wait(); err != nil { return nil, err } return chs, nil } chs, err := copy(d, dest) require.NoError(t, err) k, ok := chs.c["foo"] require.True(t, ok) require.Equal(t, k, ChangeKindAdd) require.Equal(t, len(chs.c), 1) b := &bytes.Buffer{} err = Walk(context.Background(), dest, nil, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, string(b.Bytes()), `file foo `) } func TestCopySimple(t *testing.T) { d, err := tmpDir(changeStream([]string{ "ADD foo file data1", "ADD foo2 file dat2", "ADD zzz dir", "ADD zzz/aa file data3", "ADD zzz/bb dir", "ADD zzz/bb/cc dir", "ADD zzz/bb/cc/dd symlink ../../", "ADD zzz.aa zzdata", })) assert.NoError(t, err) defer os.RemoveAll(d) dest := t.TempDir() ts := newNotificationBuffer() chs := &changes{fn: ts.HandleChange} eg, ctx := errgroup.WithContext(context.Background()) s1, s2 := sockPairProto(ctx) tm := time.Now().Truncate(time.Hour) eg.Go(func() error { defer s1.(*fakeConnProto).closeSend() return Send(ctx, s1, NewFS(d, &WalkOpt{ Map: func(_ string, s *types.Stat) MapResult { s.Uid = 0 s.Gid = 0 return MapResultKeep }, }), nil) }) eg.Go(func() error { return Receive(ctx, s2, dest, ReceiveOpt{ NotifyHashed: chs.HandleChange, ContentHasher: simpleSHA256Hasher, Filter: func(p string, s *types.Stat) bool { s.Uid = uint32(os.Getuid()) s.Gid = uint32(os.Getgid()) s.ModTime = tm.UnixNano() return true }, }) }) assert.NoError(t, eg.Wait()) b := &bytes.Buffer{} err = Walk(context.Background(), dest, nil, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, string(b.Bytes()), `file foo file foo2 dir zzz file zzz/aa dir zzz/bb dir zzz/bb/cc symlink:../../ zzz/bb/cc/dd file zzz.aa `) dt, err := os.ReadFile(filepath.Join(dest, "zzz/aa")) assert.NoError(t, err) assert.Equal(t, "data3", string(dt)) dt, err = os.ReadFile(filepath.Join(dest, "foo2")) assert.NoError(t, err) assert.Equal(t, "dat2", string(dt)) fi, err := os.Stat(filepath.Join(dest, "foo2")) assert.NoError(t, err) assert.Equal(t, tm, fi.ModTime()) h, ok := ts.Hash("zzz/aa") assert.True(t, ok) 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) h, ok = ts.Hash("zzz/bb/cc/dd") assert.True(t, ok) assert.Equal(t, digest.Digest("sha256:eca07e8f2d09bd574ea2496312e6de1685ef15b8e6a49a534ed9e722bcac8adc"), h) k, ok := chs.c["zzz/aa"] assert.Equal(t, ok, true) assert.Equal(t, k, ChangeKindAdd) err = os.WriteFile(filepath.Join(d, "zzz/bb/cc/foo"), []byte("data5"), 0600) assert.NoError(t, err) err = os.RemoveAll(filepath.Join(d, "foo2")) assert.NoError(t, err) chs = &changes{fn: ts.HandleChange} eg, ctx = errgroup.WithContext(context.Background()) s1, s2 = sockPairProto(ctx) eg.Go(func() error { defer s1.(*fakeConnProto).closeSend() return Send(ctx, s1, NewFS(d, &WalkOpt{ Map: func(_ string, s *types.Stat) MapResult { s.Uid = 0 s.Gid = 0 return MapResultKeep }, }), nil) }) eg.Go(func() error { return Receive(ctx, s2, dest, ReceiveOpt{ NotifyHashed: chs.HandleChange, ContentHasher: simpleSHA256Hasher, Filter: func(_ string, s *types.Stat) bool { s.Uid = uint32(os.Getuid()) s.Gid = uint32(os.Getgid()) s.ModTime = tm.UnixNano() return true }, }) }) assert.NoError(t, eg.Wait()) b = &bytes.Buffer{} err = Walk(context.Background(), dest, nil, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, string(b.Bytes()), `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 `) 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") assert.True(t, ok) assert.Equal(t, digest.Digest("sha256:eca07e8f2d09bd574ea2496312e6de1685ef15b8e6a49a534ed9e722bcac8adc"), h) h, ok = ts.Hash("zzz/bb/cc/foo") assert.True(t, ok) 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) k, ok = chs.c["zzz/bb/cc/foo"] assert.Equal(t, ok, true) assert.Equal(t, k, ChangeKindAdd) _, ok = chs.c["zzz/aa"] assert.Equal(t, ok, false) _, ok = chs.c["zzz.aa"] assert.Equal(t, ok, false) } func sockPairProto(ctx context.Context) (Stream, Stream) { c1 := make(chan []byte, 32) c2 := make(chan []byte, 32) return &fakeConnProto{ctx, c1, c2}, &fakeConnProto{ctx, c2, c1} } //nolint:unused type fakeConn struct { ctx context.Context recvChan chan *types.Packet sendChan chan *types.Packet } //nolint:unused func (fc *fakeConn) Context() context.Context { return fc.ctx } //nolint:unused func (fc *fakeConn) RecvMsg(m interface{}) error { p, 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 return nil } } //nolint:unused func (fc *fakeConn) SendMsg(m interface{}) error { p, ok := m.(*types.Packet) if !ok { return errors.Errorf("invalid msg: %#v", m) } p2 := *p p2.Data = append([]byte{}, p2.Data...) select { case <-fc.ctx.Done(): return fc.ctx.Err() case fc.sendChan <- &p2: return nil } } type fakeConnProto struct { ctx context.Context recvChan chan []byte sendChan chan []byte } func (fc *fakeConnProto) Context() context.Context { return fc.ctx } func (fc *fakeConnProto) RecvMsg(m interface{}) error { p, ok := m.(*types.Packet) if !ok { return errors.Errorf("invalid msg: %#v", m) } select { case <-fc.ctx.Done(): return fc.ctx.Err() case dt, ok := <-fc.recvChan: if !ok { return io.EOF } return p.Unmarshal(dt) } } func (fc *fakeConnProto) SendMsg(m interface{}) error { p, ok := m.(*types.Packet) if !ok { return errors.Errorf("invalid msg: %#v", m) } dt, err := p.Marshal() if err != nil { return err } select { case <-fc.ctx.Done(): return fc.ctx.Err() case fc.sendChan <- dt: return nil } } func (fc *fakeConnProto) closeSend() { close(fc.sendChan) } type changes struct { c map[string]ChangeKind fn ChangeFunc mu sync.Mutex } func (c *changes) HandleChange(kind ChangeKind, p string, fi os.FileInfo, err error) error { c.mu.Lock() if c.c == nil { c.c = make(map[string]ChangeKind) } c.c[p] = kind c.mu.Unlock() return c.fn(kind, p, fi, err) } func simpleSHA256Hasher(s *types.Stat) (hash.Hash, error) { h := sha256.New() ss := *s ss.ModTime = 0 // Unlike Linux, on FreeBSD's stat() call returns -1 in st_rdev for regular files ss.Devminor = 0 ss.Devmajor = 0 if os.FileMode(ss.Mode)&os.ModeSymlink != 0 { ss.Mode = ss.Mode | 0777 } dt, err := ss.Marshal() if err != nil { return nil, err } h.Write(dt) return h, nil } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/send.go000066400000000000000000000103231456645347400236710ustar00rootroot00000000000000package fsutil import ( "context" "io" "os" "sync" "syscall" "github.com/pkg/errors" "github.com/tonistiigi/fsutil/types" "golang.org/x/sync/errgroup" ) var bufPool = sync.Pool{ New: func() interface{} { buf := make([]byte, 32*1<<10) return &buf }, } type Stream interface { RecvMsg(interface{}) error SendMsg(m interface{}) error Context() context.Context } func Send(ctx context.Context, conn Stream, fs FS, progressCb func(int, bool)) error { s := &sender{ conn: &syncStream{Stream: conn}, fs: fs, files: make(map[uint32]string), progressCb: progressCb, sendpipeline: make(chan *sendHandle, 128), } return s.run(ctx) } type sendHandle struct { id uint32 path string } type sender struct { conn Stream fs FS files map[uint32]string mu sync.RWMutex progressCb func(int, bool) progressCurrent int sendpipeline chan *sendHandle } func (s *sender) run(ctx context.Context) error { g, ctx := errgroup.WithContext(ctx) defer s.updateProgress(0, true) g.Go(func() error { err := s.walk(ctx) if err != nil { s.conn.SendMsg(&types.Packet{Type: types.PACKET_ERR, Data: []byte(err.Error())}) } return err }) for i := 0; i < 4; i++ { g.Go(func() error { for h := range s.sendpipeline { select { case <-ctx.Done(): return ctx.Err() default: } if err := s.sendFile(h); err != nil { return err } } return nil }) } g.Go(func() error { defer close(s.sendpipeline) for { select { case <-ctx.Done(): return ctx.Err() default: } var p types.Packet if err := s.conn.RecvMsg(&p); err != nil { return err } switch p.Type { case types.PACKET_ERR: return errors.Errorf("error from receiver: %s", p.Data) case types.PACKET_REQ: if err := s.queue(p.ID); err != nil { return err } case types.PACKET_FIN: return s.conn.SendMsg(&types.Packet{Type: types.PACKET_FIN}) } } }) return g.Wait() } func (s *sender) updateProgress(size int, last bool) { if s.progressCb != nil { s.progressCurrent += size s.progressCb(s.progressCurrent, last) } } func (s *sender) queue(id uint32) error { s.mu.Lock() p, ok := s.files[id] if !ok { s.mu.Unlock() return errors.Errorf("invalid file id %d", id) } delete(s.files, id) s.mu.Unlock() s.sendpipeline <- &sendHandle{id, p} return nil } func (s *sender) sendFile(h *sendHandle) error { f, err := s.fs.Open(h.path) if err == nil { defer f.Close() buf := bufPool.Get().(*[]byte) defer bufPool.Put(buf) if _, err := io.CopyBuffer(&fileSender{sender: s, id: h.id}, struct{ io.Reader }{f}, *buf); err != nil { return err } } return s.conn.SendMsg(&types.Packet{ID: h.id, Type: types.PACKET_DATA}) } func (s *sender) walk(ctx context.Context) error { var i uint32 = 0 err := s.fs.Walk(ctx, func(path string, fi os.FileInfo, err error) error { if err != nil { return err } stat, ok := fi.Sys().(*types.Stat) if !ok { return errors.WithStack(&os.PathError{Path: path, Err: syscall.EBADMSG, Op: "fileinfo without stat info"}) } p := &types.Packet{ Type: types.PACKET_STAT, Stat: stat, } if fileCanRequestData(os.FileMode(stat.Mode)) { s.mu.Lock() s.files[i] = stat.Path s.mu.Unlock() } i++ s.updateProgress(p.Size(), false) return errors.Wrapf(s.conn.SendMsg(p), "failed to send stat %s", path) }) if err != nil { return err } return errors.Wrapf(s.conn.SendMsg(&types.Packet{Type: types.PACKET_STAT}), "failed to send last stat") } func fileCanRequestData(m os.FileMode) bool { // avoid updating this function as it needs to match between sender/receiver. // version if needed return m&os.ModeType == 0 } type fileSender struct { sender *sender id uint32 } func (fs *fileSender) Write(dt []byte) (int, error) { if len(dt) == 0 { return 0, nil } p := &types.Packet{Type: types.PACKET_DATA, ID: fs.id, Data: dt} if err := fs.sender.conn.SendMsg(p); err != nil { return 0, err } fs.sender.updateProgress(p.Size(), false) return len(dt), nil } type syncStream struct { Stream mu sync.Mutex } func (ss *syncStream) SendMsg(m interface{}) error { ss.mu.Lock() err := ss.Stream.SendMsg(m) ss.mu.Unlock() return err } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/stat.go000066400000000000000000000031571456645347400237220ustar00rootroot00000000000000package fsutil import ( "os" "path/filepath" "runtime" "github.com/pkg/errors" "github.com/tonistiigi/fsutil/types" ) // constructs a Stat object. path is where the path can be found right // now, relpath is the desired path to be recorded in the stat (so // relative to whatever base dir is relevant). fi is the os.Stat // info. inodemap is used to calculate hardlinks over a series of // mkstat calls and maps inode to the canonical (aka "first") path for // a set of hardlinks to that inode. func mkstat(path, relpath string, fi os.FileInfo, inodemap map[uint64]string) (*types.Stat, error) { relpath = filepath.ToSlash(relpath) stat := &types.Stat{ Path: relpath, Mode: uint32(fi.Mode()), ModTime: fi.ModTime().UnixNano(), } setUnixOpt(fi, stat, relpath, inodemap) if !fi.IsDir() { stat.Size_ = fi.Size() if fi.Mode()&os.ModeSymlink != 0 { link, err := os.Readlink(path) if err != nil { return nil, errors.WithStack(err) } stat.Linkname = link } } if err := loadXattr(path, stat); err != nil { return nil, err } if runtime.GOOS == "windows" { permPart := stat.Mode & uint32(os.ModePerm) noPermPart := stat.Mode &^ uint32(os.ModePerm) // Add the x bit: make everything +x from windows permPart |= 0111 permPart &= 0755 stat.Mode = noPermPart | permPart } // Clear the socket bit since archive/tar.FileInfoHeader does not handle it stat.Mode &^= uint32(os.ModeSocket) return stat, nil } func Stat(path string) (*types.Stat, error) { fi, err := os.Lstat(path) if err != nil { return nil, errors.WithStack(err) } return mkstat(path, filepath.Base(path), fi, nil) } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/stat_test.go000066400000000000000000000026451456645347400247620ustar00rootroot00000000000000package fsutil import ( "os" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/tonistiigi/fsutil/types" ) func TestStat(t *testing.T) { requiresRoot(t) d, err := tmpDir(changeStream([]string{ "ADD foo file data1", "ADD zzz dir", "ADD zzz/aa file data3", "ADD zzz/bb dir", "ADD zzz/bb/cc dir", "ADD zzz/bb/cc/dd symlink ../../", "ADD sock socket", })) assert.NoError(t, err) defer os.RemoveAll(d) st, err := Stat(filepath.Join(d, "foo")) assert.NoError(t, err) assert.NotZero(t, st.ModTime) st.ModTime = 0 assert.Equal(t, &types.Stat{Path: "foo", Mode: 0644, Size_: 5}, st) st, err = Stat(filepath.Join(d, "zzz")) assert.NoError(t, err) assert.NotZero(t, st.ModTime) st.ModTime = 0 assert.Equal(t, &types.Stat{Path: "zzz", Mode: uint32(os.ModeDir | 0700)}, st) st, err = Stat(filepath.Join(d, "zzz/aa")) assert.NoError(t, err) assert.NotZero(t, st.ModTime) st.ModTime = 0 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) st, err = Stat(filepath.Join(d, "sock")) assert.NoError(t, err) assert.NotZero(t, st.ModTime) st.ModTime = 0 assert.Equal(t, &types.Stat{Path: "sock", Mode: 0755 /* ModeSocket not set */}, st) } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/stat_unix.go000066400000000000000000000025671456645347400247710ustar00rootroot00000000000000//go:build !windows // +build !windows package fsutil import ( "os" "syscall" "github.com/containerd/continuity/sysx" "github.com/pkg/errors" "github.com/tonistiigi/fsutil/types" ) func loadXattr(origpath string, stat *types.Stat) error { xattrs, err := sysx.LListxattr(origpath) if err != nil { if errors.Is(err, syscall.ENOTSUP) { return nil } return errors.Wrapf(err, "failed to xattr %s", origpath) } if len(xattrs) > 0 { m := make(map[string][]byte) for _, key := range xattrs { v, err := sysx.LGetxattr(origpath, key) if err == nil { m[key] = v } } stat.Xattrs = m } return nil } func setUnixOpt(fi os.FileInfo, stat *types.Stat, path string, seenFiles map[uint64]string) { s := fi.Sys().(*syscall.Stat_t) stat.Uid = s.Uid stat.Gid = s.Gid if !fi.IsDir() { if s.Mode&syscall.S_IFBLK != 0 || s.Mode&syscall.S_IFCHR != 0 { stat.Devmajor = int64(major(uint64(s.Rdev))) stat.Devminor = int64(minor(uint64(s.Rdev))) } ino := s.Ino linked := false if seenFiles != nil { if s.Nlink > 1 { if oldpath, ok := seenFiles[ino]; ok { stat.Linkname = oldpath stat.Size_ = 0 linked = true } } if !linked { seenFiles[ino] = path } } } } func major(device uint64) uint64 { return (device >> 8) & 0xfff } func minor(device uint64) uint64 { return (device & 0xff) | ((device >> 12) & 0xfff00) } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/stat_windows.go000066400000000000000000000003541456645347400254700ustar00rootroot00000000000000// +build windows package fsutil import ( "os" "github.com/tonistiigi/fsutil/types" ) func loadXattr(_ string, _ *types.Stat) error { return nil } func setUnixOpt(_ os.FileInfo, _ *types.Stat, _ string, _ map[uint64]string) { } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/tarwriter.go000066400000000000000000000033161456645347400247670ustar00rootroot00000000000000package fsutil import ( "archive/tar" "context" "io" "os" "path/filepath" "strings" "syscall" "github.com/pkg/errors" "github.com/tonistiigi/fsutil/types" ) func WriteTar(ctx context.Context, fs FS, w io.Writer) error { tw := tar.NewWriter(w) err := fs.Walk(ctx, func(path string, fi os.FileInfo, err error) error { if err != nil && !errors.Is(err, os.ErrNotExist) { return err } stat, ok := fi.Sys().(*types.Stat) if !ok { return errors.WithStack(&os.PathError{Path: path, Err: syscall.EBADMSG, Op: "fileinfo without stat info"}) } hdr, err := tar.FileInfoHeader(fi, stat.Linkname) if err != nil { return err } name := filepath.ToSlash(path) if fi.IsDir() && !strings.HasSuffix(name, "/") { name += "/" } hdr.Name = name hdr.Uid = int(stat.Uid) hdr.Gid = int(stat.Gid) hdr.Devmajor = stat.Devmajor hdr.Devminor = stat.Devminor hdr.Linkname = stat.Linkname if hdr.Linkname != "" { hdr.Size = 0 if fi.Mode()&os.ModeSymlink != 0 { hdr.Typeflag = tar.TypeSymlink } else { hdr.Typeflag = tar.TypeLink } } if len(stat.Xattrs) > 0 { hdr.PAXRecords = map[string]string{} } for k, v := range stat.Xattrs { hdr.PAXRecords["SCHILY.xattr."+k] = string(v) } if err := tw.WriteHeader(hdr); err != nil { return errors.Wrapf(err, "failed to write file header %s", name) } if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 && hdr.Linkname == "" { rc, err := fs.Open(path) if err != nil { return err } if _, err := io.Copy(tw, rc); err != nil { return errors.WithStack(err) } if err := rc.Close(); err != nil { return errors.WithStack(err) } } return nil }) if err != nil { return err } return tw.Close() } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/types/000077500000000000000000000000001456645347400235565ustar00rootroot00000000000000golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/types/generate.go000066400000000000000000000001141456645347400256730ustar00rootroot00000000000000package types //go:generate protoc --gogoslick_out=. stat.proto wire.proto golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/types/stat.go000066400000000000000000000001371456645347400250610ustar00rootroot00000000000000package types import "os" func (s Stat) IsDir() bool { return os.FileMode(s.Mode).IsDir() } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/types/stat.pb.go000066400000000000000000000535621456645347400254730ustar00rootroot00000000000000// Code generated by protoc-gen-gogo. DO NOT EDIT. // 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" reflect "reflect" strings "strings" ) // 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 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"` 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"` Devmajor int64 `protobuf:"varint,8,opt,name=devmajor,proto3" json:"devmajor,omitempty"` Devminor int64 `protobuf:"varint,9,opt,name=devminor,proto3" json:"devminor,omitempty"` 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 (m *Stat) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } 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 b[:n], nil } } 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 (m *Stat) GetPath() string { if m != nil { return m.Path } return "" } func (m *Stat) GetMode() uint32 { if m != nil { return m.Mode } return 0 } func (m *Stat) GetUid() uint32 { if m != nil { return m.Uid } return 0 } func (m *Stat) GetGid() uint32 { if m != nil { return m.Gid } return 0 } func (m *Stat) GetSize_() int64 { if m != nil { return m.Size_ } return 0 } func (m *Stat) GetModTime() int64 { if m != nil { return m.ModTime } return 0 } func (m *Stat) GetLinkname() string { if m != nil { return m.Linkname } return "" } func (m *Stat) GetDevmajor() int64 { if m != nil { return m.Devmajor } return 0 } func (m *Stat) GetDevminor() int64 { if m != nil { return m.Devminor } return 0 } func (m *Stat) GetXattrs() map[string][]byte { if m != nil { return m.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]) } 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 } } 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") ) golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/types/stat.proto000066400000000000000000000005101456645347400256120ustar00rootroot00000000000000syntax = "proto3"; package fsutil.types; option go_package = "types"; message Stat { string path = 1; uint32 mode = 2; uint32 uid = 3; uint32 gid = 4; int64 size = 5; int64 modTime = 6; // int32 typeflag = 7; string linkname = 7; int64 devmajor = 8; int64 devminor = 9; map xattrs = 10; }golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/types/wire.pb.go000066400000000000000000000324501456645347400254570ustar00rootroot00000000000000// Code generated by protoc-gen-gogo. DO NOT EDIT. // source: wire.proto package types import ( bytes "bytes" fmt "fmt" proto "github.com/gogo/protobuf/proto" io "io" math "math" math_bits "math/bits" reflect "reflect" strconv "strconv" strings "strings" ) // 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_STAT Packet_PacketType = 0 PACKET_REQ Packet_PacketType = 1 PACKET_DATA Packet_PacketType = 2 PACKET_FIN Packet_PacketType = 3 PACKET_ERR Packet_PacketType = 4 ) var Packet_PacketType_name = map[int32]string{ 0: "PACKET_STAT", 1: "PACKET_REQ", 2: "PACKET_DATA", 3: "PACKET_FIN", 4: "PACKET_ERR", } 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) EnumDescriptor() ([]byte, []int) { return fileDescriptor_f2dcdddcdf68d8e0, []int{0, 0} } type Packet struct { 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 (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) } var xxx_messageInfo_Packet proto.InternalMessageInfo func (m *Packet) GetType() Packet_PacketType { if m != nil { return m.Type } return PACKET_STAT } func (m *Packet) GetStat() *Stat { if m != nil { return m.Stat } return nil } func (m *Packet) GetID() uint32 { if m != nil { return m.ID } return 0 } func (m *Packet) GetData() []byte { if m != nil { return m.Data } 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 } 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]) } 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 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 } } 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") ) golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/types/wire.proto000066400000000000000000000005071456645347400256130ustar00rootroot00000000000000syntax = "proto3"; package fsutil.types; option go_package = "types"; import "stat.proto"; message Packet { enum PacketType { PACKET_STAT = 0; PACKET_REQ = 1; PACKET_DATA = 2; PACKET_FIN = 3; PACKET_ERR = 4; } PacketType type = 1; Stat stat = 2; uint32 ID = 3; bytes data = 4; } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/util/000077500000000000000000000000001456645347400233675ustar00rootroot00000000000000golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/util/protostream.go000066400000000000000000000026271456645347400263040ustar00rootroot00000000000000package util import ( "context" "encoding/binary" "io" "sync" "github.com/tonistiigi/fsutil" ) var bufPool = sync.Pool{ New: func() interface{} { buf := make([]byte, 32*1<<10) return &buf }, } func NewProtoStream(ctx context.Context, r io.Reader, w io.Writer) fsutil.Stream { return &protoStream{ctx, r, w} } type protoStream struct { ctx context.Context io.Reader io.Writer } func (c *protoStream) RecvMsg(m interface{}) error { type unmarshaler interface { Unmarshal([]byte) error } var h [4]byte if _, err := io.ReadFull(c.Reader, h[:]); err != nil { return err } msg := m.(unmarshaler) length := binary.BigEndian.Uint32(h[:]) if length == 0 { return nil } buf := *bufPool.Get().(*[]byte) if cap(buf) < int(length) { buf = make([]byte, length) } else { buf = buf[:length] } defer bufPool.Put(&buf) if _, err := io.ReadFull(c.Reader, buf); err != nil { return err } err := msg.Unmarshal(buf) if err != nil { return err } return nil } func (c *protoStream) SendMsg(m interface{}) error { type marshalerSizer interface { MarshalTo([]byte) (int, error) Size() int } msg := m.(marshalerSizer) size := msg.Size() b := make([]byte, msg.Size()+4) binary.BigEndian.PutUint32(b[:4], uint32(size)) if _, err := msg.MarshalTo(b[4:]); err != nil { return err } _, err := c.Writer.Write(b) return err } func (c *protoStream) Context() context.Context { return c.ctx } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/validator.go000066400000000000000000000041251456645347400247300ustar00rootroot00000000000000package fsutil import ( "os" "path" "runtime" "sort" "strings" "syscall" "github.com/pkg/errors" ) type parent struct { dir string last string } type Validator struct { parentDirs []parent } func (v *Validator) HandleChange(kind ChangeKind, p string, fi os.FileInfo, err error) (retErr error) { if err != nil { return err } // test that all paths are in order and all parent dirs were present if v.parentDirs == nil { v.parentDirs = make([]parent, 1, 10) } if runtime.GOOS == "windows" { p = strings.Replace(p, "\\", "", -1) } if p != path.Clean(p) { return errors.WithStack(&os.PathError{Path: p, Err: syscall.EINVAL, Op: "unclean path"}) } if path.IsAbs(p) { return errors.WithStack(&os.PathError{Path: p, Err: syscall.EINVAL, Op: "absolute path"}) } dir := path.Dir(p) base := path.Base(p) if dir == "." { dir = "" } if dir == ".." || strings.HasPrefix(p, "../") { return errors.WithStack(&os.PathError{Path: p, Err: syscall.EINVAL, Op: "escape check"}) } // find a parent dir from saved records i := sort.Search(len(v.parentDirs), func(i int) bool { return ComparePath(v.parentDirs[len(v.parentDirs)-1-i].dir, dir) <= 0 }) i = len(v.parentDirs) - 1 - i if i != len(v.parentDirs)-1 { // skipping back to grandparent v.parentDirs = v.parentDirs[:i+1] } 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)) } v.parentDirs[i].last = base if kind != ChangeKindDelete && fi.IsDir() { v.parentDirs = append(v.parentDirs, parent{ dir: path.Join(dir, base), last: "", }) } // todo: validate invalid mode combinations return err } func ComparePath(p1, p2 string) int { // byte-by-byte comparison to be compatible with str<>str min := min(len(p1), len(p2)) for i := 0; i < min; i++ { switch { case p1[i] == p2[i]: continue case p2[i] != '/' && p1[i] < p2[i] || p1[i] == '/': return -1 default: return 1 } } return len(p1) - len(p2) } func min(x, y int) int { if x < y { return x } return y } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/validator_test.go000066400000000000000000000106621456645347400257720ustar00rootroot00000000000000package fsutil import ( "fmt" "os" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/tonistiigi/fsutil/types" ) func TestValidatorSimpleFiles(t *testing.T) { err := checkValid(changeStream([]string{ "ADD foo file", "ADD foo2 file", })) assert.NoError(t, err) } func TestValidatorFilesNotInOrder(t *testing.T) { err := checkValid(changeStream([]string{ "ADD foo file", "ADD foo2 file", "ADD bar file", })) assert.Error(t, err) } func TestValidatorFilesNotInOrder2(t *testing.T) { err := checkValid(changeStream([]string{ "ADD foo file", "ADD foo2 file", "ADD foo2 file", })) assert.Error(t, err) } func TestValidatorDirIsFile(t *testing.T) { err := checkValid(changeStream([]string{ "ADD foo file", "ADD foo2 file", "ADD foo2 dir", })) assert.Error(t, err) } func TestValidatorDirIsFile2(t *testing.T) { err := checkValid(changeStream([]string{ "ADD foo file", "ADD foo2 dir", "ADD foo2 file", })) assert.Error(t, err) } func TestValidatorNoParentDir(t *testing.T) { err := checkValid(changeStream([]string{ "ADD bar file", "ADD foo/baz file", })) assert.Error(t, err) } func TestValidatorParentFile(t *testing.T) { err := checkValid(changeStream([]string{ "ADD bar file", "ADD bar/baz file", })) assert.Error(t, err) } func TestValidatorParentFile2(t *testing.T) { err := checkValid(changeStream([]string{ "ADD foo/bar file", })) assert.Error(t, err) } func TestValidatorSimpleDir(t *testing.T) { err := checkValid(changeStream([]string{ "ADD foo dir", "ADD foo/bar file", })) assert.NoError(t, err) } func TestValidatorSimpleDir2(t *testing.T) { err := checkValid(changeStream([]string{ "ADD foo dir", "ADD foo/bar file", "ADD foo/bay dir", "ADD foo/bay/aa file", "ADD foo/bay/ab dir", "ADD foo/bay/abb dir", "ADD foo/bay/abb/a dir", "ADD foo/bay/ba file", "ADD foo/baz file", })) assert.NoError(t, err) } func TestValidatorBackToParent(t *testing.T) { err := checkValid(changeStream([]string{ "ADD foo dir", "ADD foo/bar file", "ADD foo/bay dir", "ADD foo/bay/aa file", "ADD foo/bay/ab dir", "ADD foo/bay/ba file", "ADD foo/bay dir", "ADD foo/baz file", })) assert.Error(t, err) } func TestValidatorParentOrder(t *testing.T) { err := checkValid(changeStream([]string{ "ADD foo dir", "ADD foo/bar file", "ADD foo/bay dir", "ADD foo/bay/aa file", "ADD foo/bay/ab dir", "ADD foo/bar file", })) assert.Error(t, err) } func TestValidatorBigJump(t *testing.T) { err := checkValid(changeStream([]string{ "ADD foo dir", "ADD foo/a dir", "ADD foo/a/foo dir", "ADD foo/a/b/foo dir", "ADD foo/a/b/c/foo dir", "ADD foo/a/b/c/d/foo dir", "ADD zzz dir", })) assert.Error(t, err) } func TestValidatorDot(t *testing.T) { // dot is before / in naive sort err := checkValid(changeStream([]string{ "ADD foo dir", "ADD foo/a dir", "ADD foo.2 dir", })) assert.NoError(t, err) } func TestValidatorDot2(t *testing.T) { err := checkValid(changeStream([]string{ "ADD foo.a dir", "ADD foo/a/a dir", })) assert.Error(t, err) err = checkValid(changeStream([]string{ "ADD foo dir", "ADD foo. dir", "ADD foo dir", })) assert.Error(t, err) } func TestValidatorSkipDir(t *testing.T) { err := checkValid(changeStream([]string{ "ADD bar dir", "ADD bar/foo/a dir", })) assert.Error(t, err) } func checkValid(inp []*change) error { v := &Validator{} for _, c := range inp { if err := v.HandleChange(c.kind, c.path, c.fi, nil); err != nil { return err } } return nil } type change struct { kind ChangeKind path string fi os.FileInfo data string } func changeStream(dt []string) (changes []*change) { for _, s := range dt { changes = append(changes, parseChange(s)) } return } func parseChange(str string) *change { f := strings.Fields(str) errStr := fmt.Sprintf("invalid change %q", str) if len(f) < 3 { panic(errStr) } c := &change{} switch f[0] { case "ADD": c.kind = ChangeKindAdd case "CHG": c.kind = ChangeKindModify case "DEL": c.kind = ChangeKindDelete default: panic(errStr) } c.path = f[1] st := &types.Stat{} switch f[2] { case "file": if len(f) > 3 { if f[3][0] == '>' { st.Linkname = f[3][1:] } else { c.data = f[3] } } case "dir": st.Mode |= uint32(os.ModeDir) case "socket": st.Mode |= uint32(os.ModeSocket) case "symlink": if len(f) < 4 { panic(errStr) } st.Mode |= uint32(os.ModeSymlink) st.Linkname = f[3] } c.fi = &StatInfo{st} return c } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/walker.go000066400000000000000000000215141456645347400242310ustar00rootroot00000000000000package fsutil import ( "context" gofs "io/fs" "os" "path/filepath" "strings" "syscall" "time" "github.com/moby/patternmatcher" "github.com/pkg/errors" "github.com/tonistiigi/fsutil/types" ) type WalkOpt struct { IncludePatterns []string ExcludePatterns []string // FollowPaths contains symlinks that are resolved into include patterns // before performing the fs walk FollowPaths []string Map MapFunc } type MapFunc func(string, *types.Stat) MapResult // The result of the walk function controls // both how WalkDir continues and whether the path is kept. type MapResult int const ( // Keep the current path and continue. MapResultKeep MapResult = iota // Exclude the current path and continue. MapResultExclude // Exclude the current path, and skip the rest of the dir. // If path is a dir, skip the current directory. // If path is a file, skip the rest of the parent directory. // (This matches the semantics of fs.SkipDir.) MapResultSkipDir ) func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) error { root, err := filepath.EvalSymlinks(p) if err != nil { return errors.WithStack(&os.PathError{Op: "resolve", Path: root, Err: err}) } rootFI, err := os.Stat(root) if err != nil { return errors.WithStack(err) } if !rootFI.IsDir() { return errors.WithStack(&os.PathError{Op: "walk", Path: root, Err: syscall.ENOTDIR}) } var ( includePatterns []string includeMatcher *patternmatcher.PatternMatcher excludeMatcher *patternmatcher.PatternMatcher ) if opt != nil && opt.IncludePatterns != nil { includePatterns = make([]string, len(opt.IncludePatterns)) copy(includePatterns, opt.IncludePatterns) } if opt != nil && opt.FollowPaths != nil { targets, err := FollowLinks(p, opt.FollowPaths) if err != nil { return err } if targets != nil { includePatterns = append(includePatterns, targets...) includePatterns = dedupePaths(includePatterns) } } patternChars := "*[]?^" if os.PathSeparator != '\\' { patternChars += `\` } onlyPrefixIncludes := true if len(includePatterns) != 0 { includeMatcher, err = patternmatcher.New(includePatterns) if err != nil { return errors.Wrapf(err, "invalid includepatterns: %s", opt.IncludePatterns) } for _, p := range includeMatcher.Patterns() { if !p.Exclusion() && strings.ContainsAny(patternWithoutTrailingGlob(p), patternChars) { onlyPrefixIncludes = false break } } } onlyPrefixExcludeExceptions := true if opt != nil && opt.ExcludePatterns != nil { excludeMatcher, err = patternmatcher.New(opt.ExcludePatterns) if err != nil { return errors.Wrapf(err, "invalid excludepatterns: %s", opt.ExcludePatterns) } for _, p := range excludeMatcher.Patterns() { if p.Exclusion() && strings.ContainsAny(patternWithoutTrailingGlob(p), patternChars) { onlyPrefixExcludeExceptions = false break } } } type visitedDir struct { fi os.FileInfo path string origpath string pathWithSep string includeMatchInfo patternmatcher.MatchInfo excludeMatchInfo patternmatcher.MatchInfo calledFn bool } // used only for include/exclude handling var parentDirs []visitedDir seenFiles := make(map[uint64]string) return filepath.WalkDir(root, func(path string, dirEntry gofs.DirEntry, walkErr error) (retErr error) { defer func() { if retErr != nil && isNotExist(retErr) { retErr = filepath.SkipDir } }() origpath := path path, err = filepath.Rel(root, path) if err != nil { return err } // Skip root if path == "." { return nil } var ( dir visitedDir isDir bool fi gofs.FileInfo ) if dirEntry != nil { isDir = dirEntry.IsDir() } if includeMatcher != nil || excludeMatcher != nil { for len(parentDirs) != 0 { lastParentDir := parentDirs[len(parentDirs)-1].pathWithSep if strings.HasPrefix(path, lastParentDir) { break } parentDirs = parentDirs[:len(parentDirs)-1] } if isDir { fi, err = dirEntry.Info() if err != nil { return err } dir = visitedDir{ fi: fi, path: path, origpath: origpath, pathWithSep: path + string(filepath.Separator), } } } skip := false if includeMatcher != nil { var parentIncludeMatchInfo patternmatcher.MatchInfo if len(parentDirs) != 0 { parentIncludeMatchInfo = parentDirs[len(parentDirs)-1].includeMatchInfo } m, matchInfo, err := includeMatcher.MatchesUsingParentResults(path, parentIncludeMatchInfo) if err != nil { return errors.Wrap(err, "failed to match includepatterns") } if isDir { dir.includeMatchInfo = matchInfo } if !m { if isDir && onlyPrefixIncludes { // Optimization: we can skip walking this dir if no include // patterns could match anything inside it. dirSlash := path + string(filepath.Separator) for _, pat := range includeMatcher.Patterns() { if pat.Exclusion() { continue } patStr := patternWithoutTrailingGlob(pat) + string(filepath.Separator) if strings.HasPrefix(patStr, dirSlash) { goto passedIncludeFilter } } return filepath.SkipDir } passedIncludeFilter: skip = true } } if excludeMatcher != nil { var parentExcludeMatchInfo patternmatcher.MatchInfo if len(parentDirs) != 0 { parentExcludeMatchInfo = parentDirs[len(parentDirs)-1].excludeMatchInfo } m, matchInfo, err := excludeMatcher.MatchesUsingParentResults(path, parentExcludeMatchInfo) if err != nil { return errors.Wrap(err, "failed to match excludepatterns") } if isDir { dir.excludeMatchInfo = matchInfo } if m { if isDir && onlyPrefixExcludeExceptions { // Optimization: we can skip walking this dir if no // exceptions to exclude patterns could match anything // inside it. if !excludeMatcher.Exclusions() { return filepath.SkipDir } dirSlash := path + string(filepath.Separator) for _, pat := range excludeMatcher.Patterns() { if !pat.Exclusion() { continue } patStr := patternWithoutTrailingGlob(pat) + string(filepath.Separator) if strings.HasPrefix(patStr, dirSlash) { goto passedExcludeFilter } } return filepath.SkipDir } passedExcludeFilter: skip = true } } if walkErr != nil { if skip && errors.Is(walkErr, os.ErrPermission) { return nil } return walkErr } if includeMatcher != nil || excludeMatcher != nil { defer func() { if isDir { parentDirs = append(parentDirs, dir) } }() } if skip { return nil } dir.calledFn = true // The FileInfo might have already been read further up. if fi == nil { fi, err = dirEntry.Info() if err != nil { return err } } stat, err := mkstat(origpath, path, fi, seenFiles) if err != nil { return err } select { case <-ctx.Done(): return ctx.Err() default: if opt != nil && opt.Map != nil { result := opt.Map(stat.Path, stat) if result == MapResultSkipDir { return filepath.SkipDir } else if result == MapResultExclude { return nil } } for i, parentDir := range parentDirs { if parentDir.calledFn { continue } parentStat, err := mkstat(parentDir.origpath, parentDir.path, parentDir.fi, seenFiles) if err != nil { return err } select { case <-ctx.Done(): return ctx.Err() default: } if opt != nil && opt.Map != nil { result := opt.Map(parentStat.Path, parentStat) if result == MapResultSkipDir || result == MapResultExclude { continue } } if err := fn(parentStat.Path, &StatInfo{parentStat}, nil); err != nil { return err } parentDirs[i].calledFn = true } if err := fn(stat.Path, &StatInfo{stat}, nil); err != nil { return err } } return nil }) } func patternWithoutTrailingGlob(p *patternmatcher.Pattern) string { patStr := p.String() // We use filepath.Separator here because patternmatcher.Pattern patterns // get transformed to use the native path separator: // https://github.com/moby/patternmatcher/blob/130b41bafc16209dc1b52a103fdac1decad04f1a/patternmatcher.go#L52 patStr = strings.TrimSuffix(patStr, string(filepath.Separator)+"**") patStr = strings.TrimSuffix(patStr, string(filepath.Separator)+"*") return patStr } type StatInfo struct { *types.Stat } func (s *StatInfo) Name() string { return filepath.Base(s.Stat.Path) } func (s *StatInfo) Size() int64 { return s.Stat.Size_ } func (s *StatInfo) Mode() os.FileMode { return os.FileMode(s.Stat.Mode) } func (s *StatInfo) ModTime() time.Time { return time.Unix(s.Stat.ModTime/1e9, s.Stat.ModTime%1e9) } func (s *StatInfo) IsDir() bool { return s.Mode().IsDir() } func (s *StatInfo) Sys() interface{} { return s.Stat } func isNotExist(err error) bool { return errors.Is(err, os.ErrNotExist) || errors.Is(err, syscall.ENOTDIR) } golang-github-tonistiigi-fsutil-0.0~git20230630.36ef4d8/walker_test.go000066400000000000000000000373761456645347400253050ustar00rootroot00000000000000package fsutil import ( "bytes" "context" "fmt" "net" "os" "path/filepath" "runtime" "strings" "testing" "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tonistiigi/fsutil/types" ) func TestWalkerSimple(t *testing.T) { d, err := tmpDir(changeStream([]string{ "ADD foo file", "ADD foo2 file", })) assert.NoError(t, err) defer os.RemoveAll(d) b := &bytes.Buffer{} err = Walk(context.Background(), d, nil, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, string(b.Bytes()), `file foo file foo2 `) } func TestWalkerInclude(t *testing.T) { d, err := tmpDir(changeStream([]string{ "ADD bar dir", "ADD bar/foo file", "ADD foo2 file", })) assert.NoError(t, err) defer os.RemoveAll(d) b := &bytes.Buffer{} err = Walk(context.Background(), d, &WalkOpt{ IncludePatterns: []string{"bar"}, }, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, `dir bar file bar/foo `, string(b.Bytes())) b.Reset() err = Walk(context.Background(), d, &WalkOpt{ IncludePatterns: []string{"bar/foo"}, }, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, `dir bar file bar/foo `, string(b.Bytes())) b.Reset() err = Walk(context.Background(), d, &WalkOpt{ IncludePatterns: []string{"b*"}, }, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, `dir bar file bar/foo `, string(b.Bytes())) b.Reset() err = Walk(context.Background(), d, &WalkOpt{ IncludePatterns: []string{"bar/f*"}, }, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, `dir bar file bar/foo `, string(b.Bytes())) b.Reset() err = Walk(context.Background(), d, &WalkOpt{ IncludePatterns: []string{"bar/g*"}, }, bufWalk(b)) assert.NoError(t, err) assert.Empty(t, b.Bytes()) b.Reset() err = Walk(context.Background(), d, &WalkOpt{ IncludePatterns: []string{"f*"}, }, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, `file foo2 `, string(b.Bytes())) b.Reset() err = Walk(context.Background(), d, &WalkOpt{ IncludePatterns: []string{"b*/f*"}, }, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, `dir bar file bar/foo `, string(b.Bytes())) b.Reset() err = Walk(context.Background(), d, &WalkOpt{ IncludePatterns: []string{"b*/foo"}, }, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, `dir bar file bar/foo `, string(b.Bytes())) b.Reset() err = Walk(context.Background(), d, &WalkOpt{ IncludePatterns: []string{"b*/"}, }, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, `dir bar file bar/foo `, string(b.Bytes())) } func TestWalkerExclude(t *testing.T) { d, err := tmpDir(changeStream([]string{ "ADD bar file", "ADD foo dir", "ADD foo2 file", "ADD foo/bar2 file", })) assert.NoError(t, err) defer os.RemoveAll(d) b := &bytes.Buffer{} err = Walk(context.Background(), d, &WalkOpt{ ExcludePatterns: []string{"foo*", "!foo/bar2"}, }, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, `file bar dir foo file foo/bar2 `, string(b.Bytes())) } 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 })) assert.NoError(t, err) defer os.RemoveAll(d) b := &bytes.Buffer{} err = Walk(context.Background(), d, &WalkOpt{ FollowPaths: []string{"foo/l*", "bar"}, }, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, `file bar file bax dir baz file baz/one symlink:../bax baz/two dir foo symlink:/baz/one foo/l1 symlink:/baz/two foo/l2 `, string(b.Bytes())) } func TestWalkerFollowLinksToRoot(t *testing.T) { d, err := tmpDir(changeStream([]string{ "ADD foo symlink .", "ADD bar file", "ADD bax file", "ADD bay dir", "ADD bay/baz file", })) assert.NoError(t, err) defer os.RemoveAll(d) b := &bytes.Buffer{} err = Walk(context.Background(), d, &WalkOpt{ FollowPaths: []string{"foo"}, }, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, `file bar file bax dir bay file bay/baz symlink:. foo `, string(b.Bytes())) } func TestWalkerMap(t *testing.T) { d, err := tmpDir(changeStream([]string{ "ADD bar file", "ADD foo dir", "ADD foo2 file", "ADD foo/bar2 file", })) assert.NoError(t, err) defer os.RemoveAll(d) b := &bytes.Buffer{} err = Walk(context.Background(), d, &WalkOpt{ Map: func(_ string, s *types.Stat) MapResult { if strings.HasPrefix(s.Path, "foo") { s.Path = "_" + s.Path return MapResultKeep } return MapResultExclude }, }, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, `dir _foo file _foo/bar2 file _foo2 `, string(b.Bytes())) } func TestWalkerMapSkipDir(t *testing.T) { d, err := tmpDir(changeStream([]string{ "ADD excludeDir dir", "ADD excludeDir/a.txt file", "ADD includeDir dir", "ADD includeDir/a.txt file", })) assert.NoError(t, err) defer os.RemoveAll(d) // SkipDir is a performance optimization - don't even // bother walking directories we don't care about. walked := []string{} b := &bytes.Buffer{} err = Walk(context.Background(), d, &WalkOpt{ Map: func(_ string, s *types.Stat) MapResult { walked = append(walked, s.Path) if strings.HasPrefix(s.Path, "excludeDir") { return MapResultSkipDir } if strings.HasPrefix(s.Path, "includeDir") { return MapResultKeep } return MapResultExclude }, }, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, `dir includeDir file includeDir/a.txt `, string(b.Bytes())) assert.Equal(t, []string{"excludeDir", "includeDir", "includeDir/a.txt"}, walked) } func TestWalkerPermissionDenied(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("os.Chmod not fully supported on Windows") } if os.Getuid() == 0 { t.Skip("test cannot run as root") } d, err := tmpDir(changeStream([]string{ "ADD foo dir", "ADD foo/bar dir", })) assert.NoError(t, err) err = os.Chmod(filepath.Join(d, "foo", "bar"), 0000) require.NoError(t, err) defer func() { os.Chmod(filepath.Join(d, "bar"), 0700) os.RemoveAll(d) }() b := &bytes.Buffer{} err = Walk(context.Background(), d, &WalkOpt{}, bufWalk(b)) if assert.Error(t, err) { assert.Contains(t, err.Error(), "permission denied") } b.Reset() err = Walk(context.Background(), d, &WalkOpt{ ExcludePatterns: []string{"**/bar"}, }, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, `dir foo `, string(b.Bytes())) b.Reset() err = Walk(context.Background(), d, &WalkOpt{ ExcludePatterns: []string{"**/bar", "!foo/bar/baz"}, }, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, `dir foo `, string(b.Bytes())) b.Reset() err = Walk(context.Background(), d, &WalkOpt{ ExcludePatterns: []string{"**/bar", "!foo/bar"}, }, bufWalk(b)) if assert.Error(t, err) { assert.Contains(t, err.Error(), "permission denied") } b.Reset() err = Walk(context.Background(), d, &WalkOpt{ IncludePatterns: []string{"foo", "!**/bar"}, }, bufWalk(b)) assert.NoError(t, err) assert.Equal(t, `dir foo `, string(b.Bytes())) } func bufWalk(buf *bytes.Buffer) filepath.WalkFunc { return func(path string, fi os.FileInfo, err error) error { stat, ok := fi.Sys().(*types.Stat) if !ok { return errors.Errorf("invalid symlink %s", path) } t := "file" if fi.IsDir() { t = "dir" } if fi.Mode()&os.ModeSymlink != 0 { t = "symlink:" + stat.Linkname } fmt.Fprintf(buf, "%s %s", t, path) if fi.Mode()&os.ModeSymlink == 0 && stat.Linkname != "" { fmt.Fprintf(buf, " >%s", stat.Linkname) } fmt.Fprintln(buf) return nil } } func tmpDir(inp []*change) (dir string, retErr error) { tmpdir, err := os.MkdirTemp("", "diff") if err != nil { return "", err } defer func() { if retErr != nil { os.RemoveAll(tmpdir) } }() for _, c := range inp { if c.kind == ChangeKindAdd { p := filepath.Join(tmpdir, c.path) stat, ok := c.fi.Sys().(*types.Stat) if !ok { return "", errors.Errorf("invalid symlink change %s", p) } if c.fi.IsDir() { if err := os.Mkdir(p, 0700); err != nil { return "", err } } else if c.fi.Mode()&os.ModeSymlink != 0 { if err := os.Symlink(stat.Linkname, p); err != nil { return "", err } } else if len(stat.Linkname) > 0 { if err := os.Link(filepath.Join(tmpdir, stat.Linkname), p); err != nil { return "", err } } else if c.fi.Mode()&os.ModeSocket != 0 { // not closing listener because it would remove the socket file if _, err := net.Listen("unix", p); err != nil { return "", err } } else { f, err := os.Create(p) if err != nil { return "", err } // Make sure all files start with the same default permissions, // regardless of OS settings. err = os.Chmod(p, 0644) if err != nil { return "", err } if len(c.data) > 0 { if _, err := f.Write([]byte(c.data)); err != nil { return "", err } } f.Close() } } } return tmpdir, nil } func BenchmarkWalker(b *testing.B) { for _, scenario := range []struct { maxDepth int pattern string exclude string expected int }{{ maxDepth: 1, pattern: "target", expected: 1, }, { maxDepth: 1, pattern: "**/target", expected: 1, }, { maxDepth: 2, pattern: "*/target", expected: 52, }, { maxDepth: 2, pattern: "**/target", expected: 52, }, { maxDepth: 3, pattern: "*/*/target", expected: 1378, }, { maxDepth: 3, pattern: "**/target", expected: 1378, }, { maxDepth: 4, pattern: "*/*/*/target", expected: 2794, }, { maxDepth: 4, pattern: "**/target", expected: 2794, }, { maxDepth: 5, pattern: "*/*/*/*/target", expected: 1405, }, { maxDepth: 5, pattern: "**/target", expected: 1405, }, { maxDepth: 6, pattern: "*/*/*/*/*/target", expected: 2388, }, { maxDepth: 6, pattern: "**/target", expected: 2388, }, { maxDepth: 6, pattern: "**", exclude: "*/*/**", expected: 20, }} { scenario := scenario // copy loop var suffix := "" if scenario.exclude != "" { suffix = fmt.Sprintf("-!%s", scenario.exclude) } b.Run(fmt.Sprintf("[%d]-%s%s", scenario.maxDepth, scenario.pattern, suffix), func(b *testing.B) { tmpdir, err := os.MkdirTemp("", "walk") if err != nil { b.Error(err) } defer func() { b.StopTimer() os.RemoveAll(tmpdir) }() mkBenchTree(tmpdir, scenario.maxDepth, 1) // don't include time to setup dirs in benchmark b.ResetTimer() for i := 0; i < b.N; i++ { count := 0 walkOpt := &WalkOpt{ IncludePatterns: []string{scenario.pattern}, } if scenario.exclude != "" { walkOpt.ExcludePatterns = []string{scenario.exclude} } err = Walk(context.Background(), tmpdir, walkOpt, func(path string, fi os.FileInfo, err error) error { count++ return nil }) if err != nil { b.Error(err) } if count != scenario.expected { b.Errorf("Got count %d, expected %d", count, scenario.expected) } } }) } } func TestWalkerDoublestarInclude(t *testing.T) { d, err := tmpDir(changeStream([]string{ "ADD a dir", "ADD a/b dir", "ADD a/b/baz dir", "ADD a/b/bar dir ", "ADD a/b/bar/foo file", "ADD a/b/bar/fop file", "ADD bar dir", "ADD bar/foo file", "ADD baz dir", "ADD foo2 file", "ADD foo dir", "ADD foo/bar dir", "ADD foo/bar/bee file", })) assert.NoError(t, err) defer os.RemoveAll(d) b := &bytes.Buffer{} err = Walk(context.Background(), d, &WalkOpt{ IncludePatterns: []string{"**"}, }, bufWalk(b)) assert.NoError(t, err) trimEqual(t, ` dir a dir a/b dir a/b/bar file a/b/bar/foo file a/b/bar/fop dir a/b/baz dir bar file bar/foo dir baz dir foo dir foo/bar file foo/bar/bee file foo2 `, string(b.Bytes())) b.Reset() err = Walk(context.Background(), d, &WalkOpt{ IncludePatterns: []string{"**/bar"}, }, bufWalk(b)) assert.NoError(t, err) trimEqual(t, ` dir a dir a/b dir a/b/bar file a/b/bar/foo file a/b/bar/fop dir bar file bar/foo dir foo dir foo/bar file foo/bar/bee `, string(b.Bytes())) b.Reset() err = Walk(context.Background(), d, &WalkOpt{ IncludePatterns: []string{"**/bar/foo"}, }, bufWalk(b)) assert.NoError(t, err) trimEqual(t, ` dir a dir a/b dir a/b/bar file a/b/bar/foo dir bar file bar/foo `, string(b.Bytes())) b.Reset() err = Walk(context.Background(), d, &WalkOpt{ IncludePatterns: []string{"**/b*"}, }, bufWalk(b)) assert.NoError(t, err) trimEqual(t, ` dir a dir a/b dir a/b/bar file a/b/bar/foo file a/b/bar/fop dir a/b/baz dir bar file bar/foo dir baz dir foo dir foo/bar file foo/bar/bee `, string(b.Bytes())) b.Reset() err = Walk(context.Background(), d, &WalkOpt{ IncludePatterns: []string{"**/bar/f*"}, }, bufWalk(b)) assert.NoError(t, err) trimEqual(t, ` dir a dir a/b dir a/b/bar file a/b/bar/foo file a/b/bar/fop dir bar file bar/foo `, string(b.Bytes())) b.Reset() err = Walk(context.Background(), d, &WalkOpt{ IncludePatterns: []string{"**/bar/g*"}, }, bufWalk(b)) assert.NoError(t, err) trimEqual(t, ``, string(b.Bytes())) b.Reset() err = Walk(context.Background(), d, &WalkOpt{ IncludePatterns: []string{"**/f*"}, }, bufWalk(b)) assert.NoError(t, err) trimEqual(t, ` dir a dir a/b dir a/b/bar file a/b/bar/foo file a/b/bar/fop dir bar file bar/foo dir foo dir foo/bar file foo/bar/bee file foo2 `, string(b.Bytes())) b.Reset() err = Walk(context.Background(), d, &WalkOpt{ IncludePatterns: []string{"**/b*/f*"}, }, bufWalk(b)) assert.NoError(t, err) trimEqual(t, ` dir a dir a/b dir a/b/bar file a/b/bar/foo file a/b/bar/fop dir bar file bar/foo `, string(b.Bytes())) b.Reset() err = Walk(context.Background(), d, &WalkOpt{ IncludePatterns: []string{"**/b*/foo"}, }, bufWalk(b)) assert.NoError(t, err) trimEqual(t, ` dir a dir a/b dir a/b/bar file a/b/bar/foo dir bar file bar/foo `, string(b.Bytes())) b.Reset() err = Walk(context.Background(), d, &WalkOpt{ IncludePatterns: []string{"**/foo/**"}, }, bufWalk(b)) assert.NoError(t, err) trimEqual(t, ` dir foo dir foo/bar file foo/bar/bee `, string(b.Bytes())) b.Reset() err = Walk(context.Background(), d, &WalkOpt{ IncludePatterns: []string{"**/baz"}, }, bufWalk(b)) assert.NoError(t, err) trimEqual(t, ` dir a dir a/b dir a/b/baz dir baz `, string(b.Bytes())) } func trimEqual(t assert.TestingT, expected, actual string, msgAndArgs ...interface{}) bool { lines := []string{} for _, line := range strings.Split(expected, "\n") { line = strings.TrimSpace(line) if line != "" { lines = append(lines, line) } } lines = append(lines, "") // we expect a trailing newline expected = strings.Join(lines, "\n") return assert.Equal(t, expected, actual, msgAndArgs) } // mkBenchTree will create directories named a-z recursively // up to 3 layers deep. If maxDepth is > 3 we will shorten // the last letter to prevent the generated inodes going over // 25k. The final directory in the tree will contain only files. // Additionally there is a single file named `target` // in each leaf directory. func mkBenchTree(dir string, maxDepth, depth int) error { end := 'z' switch maxDepth { case 1, 2, 3: end = 'z' // max 19682 inodes case 4: end = 'k' // max 19030 inodes case 5: end = 'e' // max 12438 inodes case 6: end = 'd' // max 8188 inodes case 7, 8: end = 'c' // max 16398 inodes case 9, 10, 11, 12: end = 'b' // max 16378 inodes default: panic("depth cannot be > 12, would create too many files") } if depth == maxDepth { fd, err := os.Create(filepath.Join(dir, "target")) if err != nil { return err } fd.Close() } for r := 'a'; r <= end; r++ { p := filepath.Join(dir, string(r)) if depth == maxDepth { fd, err := os.Create(p) if err != nil { return err } fd.Close() } else { err := os.Mkdir(p, 0755) if err != nil { return err } err = mkBenchTree(p, maxDepth, depth+1) if err != nil { return err } } } return nil }