diff --git a/client/client_test.go b/client/client_test.go index cf41ac20..920b2153 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -17,6 +17,7 @@ import ( "path/filepath" "runtime" "strings" + "syscall" "testing" "time" @@ -85,6 +86,7 @@ func TestClientIntegration(t *testing.T) { testStdinClosed, testHostnameLookup, testBasicInlineCacheImportExport, + testExportBusyboxLocal, }, integration.WithMirroredImages(integration.OfficialImages("busybox:latest", "alpine:latest")), ) @@ -94,6 +96,41 @@ func newContainerd(cdAddress string) (*containerd.Client, error) { return containerd.New(cdAddress, containerd.WithTimeout(60*time.Second)) } +// #877 +func testExportBusyboxLocal(t *testing.T, sb integration.Sandbox) { + c, err := New(context.TODO(), sb.Address()) + require.NoError(t, err) + defer c.Close() + + def, err := llb.Image("busybox").Marshal() + require.NoError(t, err) + + destDir, err := ioutil.TempDir("", "buildkit") + require.NoError(t, err) + defer os.RemoveAll(destDir) + + _, err = c.Solve(context.TODO(), def, SolveOpt{ + Exports: []ExportEntry{ + { + Type: ExporterLocal, + OutputDir: destDir, + }, + }, + }, nil) + require.NoError(t, err) + + fi, err := os.Stat(filepath.Join(destDir, "bin/busybox")) + require.NoError(t, err) + + fi2, err := os.Stat(filepath.Join(destDir, "bin/vi")) + require.NoError(t, err) + + st1 := fi.Sys().(*syscall.Stat_t) + st2 := fi2.Sys().(*syscall.Stat_t) + + require.Equal(t, st1.Ino, st2.Ino) +} + func testHostnameLookup(t *testing.T, sb integration.Sandbox) { if sb.Rootless() { t.SkipNow() diff --git a/client/solve.go b/client/solve.go index cba5a1a0..ce321f92 100644 --- a/client/solve.go +++ b/client/solve.go @@ -298,7 +298,7 @@ func prepareSyncedDirs(def *llb.Definition, localDirs map[string]string) ([]file return nil, errors.Errorf("%s not a directory", d) } } - resetUIDAndGID := func(st *fstypes.Stat) bool { + resetUIDAndGID := func(p string, st *fstypes.Stat) bool { st.Uid = 0 st.Gid = 0 return true diff --git a/go.mod b/go.mod index e67959dc..806e3994 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ require ( github.com/sirupsen/logrus v1.0.3 github.com/stretchr/testify v1.3.0 github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8 // indirect - github.com/tonistiigi/fsutil v0.0.0-20190316003333-2a10686c7e92 + github.com/tonistiigi/fsutil v0.0.0-20190319020005-1bdbf124ad49 github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea github.com/uber/jaeger-client-go v0.0.0-20180103221425-e02c85f9069e github.com/uber/jaeger-lib v1.2.1 // indirect diff --git a/go.sum b/go.sum index c6fce101..8a8355b3 100644 --- a/go.sum +++ b/go.sum @@ -118,8 +118,8 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8 h1:zLV6q4e8Jv9EHjNg/iHfzwDkCve6Ua5jCygptrtXHvI= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/tonistiigi/fsutil v0.0.0-20190316003333-2a10686c7e92 h1:+Njk7pGJkAqK0k007oRFmr9xSmZUA+VjV0SdW0ctqXs= -github.com/tonistiigi/fsutil v0.0.0-20190316003333-2a10686c7e92/go.mod h1:pzh7kdwkDRh+Bx8J30uqaKJ1M4QrSH/um8fcIXeM8rc= +github.com/tonistiigi/fsutil v0.0.0-20190319020005-1bdbf124ad49 h1:UFQ7uDVXIH4fFfOb+fISgTl8Ukk0CkGQudHQh980l+0= +github.com/tonistiigi/fsutil v0.0.0-20190319020005-1bdbf124ad49/go.mod h1:pzh7kdwkDRh+Bx8J30uqaKJ1M4QrSH/um8fcIXeM8rc= github.com/tonistiigi/go-immutable-radix v0.0.0-20170803185627-826af9ccf0fe h1:pd7hrFSqUPxYS9IB+UMG1AB/8EXGXo17ssx0bSQ5L6Y= github.com/tonistiigi/go-immutable-radix v0.0.0-20170803185627-826af9ccf0fe/go.mod h1:/+MCh11CJf2oz0BXmlmqyopK/ad1rKkcOXPoYuPCJYU= github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0= diff --git a/session/filesync/diffcopy.go b/session/filesync/diffcopy.go index 8334ab60..5148ade7 100644 --- a/session/filesync/diffcopy.go +++ b/session/filesync/diffcopy.go @@ -82,10 +82,10 @@ func syncTargetDiffCopy(ds grpc.Stream, dest string) error { } return fsutil.Receive(ds.Context(), ds, dest, fsutil.ReceiveOpt{ Merge: true, - Filter: func() func(*fstypes.Stat) bool { + Filter: func() func(string, *fstypes.Stat) bool { uid := os.Getuid() gid := os.Getgid() - return func(st *fstypes.Stat) bool { + return func(p string, st *fstypes.Stat) bool { st.Uid = uint32(uid) st.Gid = uint32(gid) return true diff --git a/session/filesync/filesync.go b/session/filesync/filesync.go index ae6775f7..d6adee82 100644 --- a/session/filesync/filesync.go +++ b/session/filesync/filesync.go @@ -35,7 +35,7 @@ type SyncedDir struct { Name string Dir string Excludes []string - Map func(*fstypes.Stat) bool + Map func(string, *fstypes.Stat) bool } // NewFSSyncProvider creates a new provider for sending files from client diff --git a/vendor/github.com/tonistiigi/fsutil/diskwriter.go b/vendor/github.com/tonistiigi/fsutil/diskwriter.go index 3213113e..79a1673b 100644 --- a/vendor/github.com/tonistiigi/fsutil/diskwriter.go +++ b/vendor/github.com/tonistiigi/fsutil/diskwriter.go @@ -26,7 +26,7 @@ type DiskWriterOpt struct { Filter FilterFunc } -type FilterFunc func(*types.Stat) bool +type FilterFunc func(string, *types.Stat) bool type DiskWriter struct { opt DiskWriterOpt @@ -84,6 +84,12 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er 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) @@ -104,7 +110,7 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er statCopy := *stat if dw.filter != nil { - if ok := dw.filter(&statCopy); !ok { + if ok := dw.filter(p, &statCopy); !ok { return nil } } diff --git a/vendor/github.com/tonistiigi/fsutil/stat_unix.go b/vendor/github.com/tonistiigi/fsutil/stat_unix.go index b2e8fea2..af08522c 100644 --- a/vendor/github.com/tonistiigi/fsutil/stat_unix.go +++ b/vendor/github.com/tonistiigi/fsutil/stat_unix.go @@ -46,14 +46,18 @@ func setUnixOpt(fi os.FileInfo, stat *types.Stat, path string, seenFiles map[uin } 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 } } - seenFiles[ino] = path + if !linked { + seenFiles[ino] = path + } } } } diff --git a/vendor/github.com/tonistiigi/fsutil/walker.go b/vendor/github.com/tonistiigi/fsutil/walker.go index d78340df..e0518e23 100644 --- a/vendor/github.com/tonistiigi/fsutil/walker.go +++ b/vendor/github.com/tonistiigi/fsutil/walker.go @@ -19,7 +19,7 @@ type WalkOpt struct { // FollowPaths contains symlinks that are resolved into include patterns // before performing the fs walk FollowPaths []string - Map func(*types.Stat) bool + Map FilterFunc } func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) error { @@ -157,7 +157,7 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err return ctx.Err() default: if opt != nil && opt.Map != nil { - if allowed := opt.Map(stat); !allowed { + if allowed := opt.Map(stat.Path, stat); !allowed { return nil } } diff --git a/vendor/modules.txt b/vendor/modules.txt index 9659bbd8..474122ab 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -214,7 +214,7 @@ github.com/stretchr/testify/require github.com/stretchr/testify/assert # github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8 github.com/syndtr/gocapability/capability -# github.com/tonistiigi/fsutil v0.0.0-20190316003333-2a10686c7e92 +# github.com/tonistiigi/fsutil v0.0.0-20190319020005-1bdbf124ad49 github.com/tonistiigi/fsutil github.com/tonistiigi/fsutil/types github.com/tonistiigi/fsutil/copy