package util import ( "context" "io" "io/ioutil" "os" "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" ) type ReadRequest struct { Filename string Range *FileRange } type FileRange struct { Offset int Length int } func withMount(ctx context.Context, mount snapshot.Mountable, cb func(string) error) error { lm := snapshot.LocalMounter(mount) root, err := lm.Mount() if err != nil { return err } defer func() { if lm != nil { lm.Unmount() } }() if err := cb(root); err != nil { return err } if err := lm.Unmount(); err != nil { return err } lm = nil return nil } func ReadFile(ctx context.Context, mount snapshot.Mountable, req ReadRequest) ([]byte, error) { var dt []byte err := withMount(ctx, mount, func(root string) error { fp, err := fs.RootPath(root, req.Filename) if err != nil { return errors.WithStack(err) } if req.Range == nil { dt, err = ioutil.ReadFile(fp) if err != nil { return errors.WithStack(err) } } else { f, err := os.Open(fp) if err != nil { return errors.WithStack(err) } dt, err = ioutil.ReadAll(io.NewSectionReader(f, int64(req.Range.Offset), int64(req.Range.Length))) f.Close() if err != nil { return errors.WithStack(err) } } return nil }) return dt, err } type ReadDirRequest struct { Path string IncludePattern string } func ReadDir(ctx context.Context, mount snapshot.Mountable, req ReadDirRequest) ([]*fstypes.Stat, error) { var ( rd []*fstypes.Stat wo fsutil.WalkOpt ) if req.IncludePattern != "" { wo.IncludePatterns = append(wo.IncludePatterns, req.IncludePattern) } err := withMount(ctx, mount, func(root string) error { fp, err := fs.RootPath(root, req.Path) if err != nil { return errors.WithStack(err) } return fsutil.Walk(ctx, fp, &wo, 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(ctx, mount, func(root string) error { fp, err := fs.RootPath(root, path) if err != nil { return errors.WithStack(err) } if st, err = fsutil.Stat(fp); err != nil { return errors.WithStack(err) } return nil }) return st, err }