| package util |
| |
| import ( |
| "context" |
| "io" |
| "io/ioutil" |
| "os" |
| "path/filepath" |
| |
| "github.com/containerd/continuity/fs" |
| "github.com/moby/buildkit/cache" |
| "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, ref cache.ImmutableRef, cb func(string) error) error { |
| mount, err := ref.Mount(ctx, true) |
| if err != nil { |
| return err |
| } |
| |
| 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, ref cache.ImmutableRef, req ReadRequest) ([]byte, error) { |
| var dt []byte |
| |
| err := withMount(ctx, ref, 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, ref cache.ImmutableRef, 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, ref, 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, ref cache.ImmutableRef, path string) (*fstypes.Stat, error) { |
| var st *fstypes.Stat |
| err := withMount(ctx, ref, 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 |
| } |