Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 56 additions & 89 deletions cache/util/fsutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"path/filepath"

"github.com/containerd/continuity/fs"
"github.com/moby/buildkit/snapshot"
"github.com/pkg/errors"
"github.com/tonistiigi/fsutil"
fstypes "github.com/tonistiigi/fsutil/types"
Expand All @@ -23,121 +22,89 @@ type FileRange struct {
Length int
}

func withMount(mount snapshot.Mountable, cb func(string) error) error {
lm := snapshot.LocalMounter(mount)

root, err := lm.Mount()
func ReadFile(ctx context.Context, root string, req ReadRequest) ([]byte, error) {
fp, err := fs.RootPath(root, req.Filename)
if err != nil {
return err
return nil, errors.WithStack(err)
}

defer func() {
if lm != nil {
lm.Unmount()
f, err := os.Open(fp)
if err != nil {
// The filename here is internal to the mount, so we can restore
// the request base path for error reporting.
// See os.DirFS.Open for details.
pe := &os.PathError{}
if errors.As(err, &pe) {
pe.Path = req.Filename
}
}()

if err := cb(root); err != nil {
return err
return nil, errors.WithStack(err)
}
defer f.Close()

if err := lm.Unmount(); err != nil {
return err
var rdr io.Reader = f
if req.Range != nil {
rdr = io.NewSectionReader(f, int64(req.Range.Offset), int64(req.Range.Length))
}
lm = nil
return nil
}

func ReadFile(ctx context.Context, mount snapshot.Mountable, req ReadRequest) ([]byte, error) {
var dt []byte

err := withMount(mount, func(root string) error {
fp, err := fs.RootPath(root, req.Filename)
if err != nil {
return errors.WithStack(err)
}

f, err := os.Open(fp)
if err != nil {
// The filename here is internal to the mount, so we can restore
// the request base path for error reporting.
// See os.DirFS.Open for details.
pe := &os.PathError{}
if errors.As(err, &pe) {
pe.Path = req.Filename
}
return errors.WithStack(err)
}
defer f.Close()

var rdr io.Reader = f
if req.Range != nil {
rdr = io.NewSectionReader(f, int64(req.Range.Offset), int64(req.Range.Length))
}
dt, err = io.ReadAll(rdr)
if err != nil {
return errors.WithStack(err)
}
return nil
})
return dt, err
dt, err := io.ReadAll(rdr)
if err != nil {
return nil, errors.WithStack(err)
}
return dt, nil
}

type ReadDirRequest struct {
Path string
IncludePattern string
}

func ReadDir(ctx context.Context, mount snapshot.Mountable, req ReadDirRequest) ([]*fstypes.Stat, error) {
func ReadDir(ctx context.Context, root string, req ReadDirRequest) ([]*fstypes.Stat, error) {
var (
rd []*fstypes.Stat
fo fsutil.FilterOpt
)
if req.IncludePattern != "" {
fo.IncludePatterns = append(fo.IncludePatterns, req.IncludePattern)
}
err := withMount(mount, func(root string) error {
fp, err := fs.RootPath(root, req.Path)
fp, err := fs.RootPath(root, req.Path)
if err != nil {
return nil, errors.WithStack(err)
}
err = fsutil.Walk(ctx, fp, &fo, func(path string, info os.FileInfo, err error) error {
if err != nil {
return errors.WithStack(err)
return errors.Wrapf(err, "walking %q", root)
}
return fsutil.Walk(ctx, fp, &fo, func(path string, info os.FileInfo, err error) error {
if err != nil {
return errors.Wrapf(err, "walking %q", root)
}
stat, ok := info.Sys().(*fstypes.Stat)
if !ok {
// This "can't happen(tm)".
return errors.Errorf("expected a *fsutil.Stat but got %T", info.Sys())
}
rd = append(rd, stat)

if info.IsDir() {
return filepath.SkipDir
}
return nil
})
})
return rd, err
}

func StatFile(ctx context.Context, mount snapshot.Mountable, path string) (*fstypes.Stat, error) {
var st *fstypes.Stat
err := withMount(mount, func(root string) error {
fp, err := fs.RootPath(root, path)
if err != nil {
return errors.WithStack(err)
stat, ok := info.Sys().(*fstypes.Stat)
if !ok {
// This "can't happen(tm)".
return errors.Errorf("expected a *fsutil.Stat but got %T", info.Sys())
}
if st, err = fsutil.Stat(fp); err != nil {
// The filename here is internal to the mount, so we can restore
// the request base path for error reporting.
// See os.DirFS.Open for details.
replaceErrorPath(err, path)
return errors.WithStack(err)
rd = append(rd, stat)

if info.IsDir() {
return filepath.SkipDir
}
return nil
})
return st, err
if err != nil {
return nil, err
}
return rd, nil
}

func StatFile(ctx context.Context, root string, path string) (*fstypes.Stat, error) {
fp, err := fs.RootPath(root, path)
if err != nil {
return nil, errors.WithStack(err)
}
st, err := fsutil.Stat(fp)
if err != nil {
// The filename here is internal to the mount, so we can restore
// the request base path for error reporting.
// See os.DirFS.Open for details.
replaceErrorPath(err, path)
return nil, errors.WithStack(err)
}
return st, nil
}

// replaceErrorPath will override the path in an os.PathError in the error chain.
Expand Down
44 changes: 36 additions & 8 deletions frontend/gateway/forwarder/forward.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func LLBBridgeToGatewayClient(ctx context.Context, llbBridge frontend.FrontendLL
workers: w,
workerRefByID: make(map[string]*worker.WorkerRef),
executor: exec,
mounts: make(map[string]snapshot.Mounter),
}
bc.buildOpts = bc.loadBuildOpts()
return bc, nil
Expand All @@ -56,6 +57,7 @@ type BridgeClient struct {
buildOpts client.BuildOpts
ctrs []client.Container
executor executor.Executor
mounts map[string]snapshot.Mounter
}

func (c *BridgeClient) Solve(ctx context.Context, req client.SolveRequest) (*client.Result, error) {
Expand Down Expand Up @@ -213,6 +215,10 @@ func (c *BridgeClient) discard(err error) {
ctr.Release(context.TODO())
}

for _, mount := range c.mounts {
mount.Unmount()
}

for id, workerRef := range c.workerRefByID {
workerRef.ImmutableRef.Release(context.TODO())
delete(c.workerRefByID, id)
Expand Down Expand Up @@ -345,7 +351,7 @@ func (r *ref) Evaluate(ctx context.Context) error {
}

func (r *ref) ReadFile(ctx context.Context, req client.ReadRequest) ([]byte, error) {
m, err := r.getMountable(ctx)
root, err := r.getMount(ctx)
if err != nil {
return nil, err
}
Expand All @@ -358,30 +364,35 @@ func (r *ref) ReadFile(ctx context.Context, req client.ReadRequest) ([]byte, err
Length: r.Length,
}
}
return cacheutil.ReadFile(ctx, m, newReq)
return cacheutil.ReadFile(ctx, root, newReq)
}

func (r *ref) ReadDir(ctx context.Context, req client.ReadDirRequest) ([]*fstypes.Stat, error) {
m, err := r.getMountable(ctx)
root, err := r.getMount(ctx)
if err != nil {
return nil, err
}
newReq := cacheutil.ReadDirRequest{
Path: req.Path,
IncludePattern: req.IncludePattern,
}
return cacheutil.ReadDir(ctx, m, newReq)
return cacheutil.ReadDir(ctx, root, newReq)
}

func (r *ref) StatFile(ctx context.Context, req client.StatRequest) (*fstypes.Stat, error) {
m, err := r.getMountable(ctx)
root, err := r.getMount(ctx)
if err != nil {
return nil, err
}
return cacheutil.StatFile(ctx, m, req.Path)
return cacheutil.StatFile(ctx, root, req.Path)
}

func (r *ref) getMountable(ctx context.Context) (snapshot.Mountable, error) {
func (r *ref) getMounter(ctx context.Context) (snapshot.Mounter, error) {
id := r.resultProxy.ID()
if mounter, ok := r.c.mounts[id]; ok {
return mounter, nil
}

rr, err := r.resultProxy.Result(ctx)
if err != nil {
return nil, r.c.wrapSolveError(err)
Expand All @@ -390,5 +401,22 @@ func (r *ref) getMountable(ctx context.Context) (snapshot.Mountable, error) {
if !ok {
return nil, errors.Errorf("invalid ref: %T", rr.Sys())
}
return ref.ImmutableRef.Mount(ctx, true, r.session)

mountable, err := ref.ImmutableRef.Mount(ctx, true, r.session)
if err != nil {
return nil, err
}
mounter := snapshot.LocalMounter(mountable)

r.c.mounts[id] = mounter
return mounter, nil
}

func (r *ref) getMount(ctx context.Context) (string, error) {
mounter, err := r.getMounter(ctx)
if err != nil {
return "", err
}
// corresponding Unmount call is made in discard()
return mounter.Mount()
}
Loading