140 lines
2.7 KiB
Go
140 lines
2.7 KiB
Go
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 err
|
|
}
|
|
|
|
if req.Range == nil {
|
|
dt, err = ioutil.ReadFile(fp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
f, err := os.Open(fp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
dt, err = ioutil.ReadAll(io.NewSectionReader(f, int64(req.Range.Offset), int64(req.Range.Length)))
|
|
f.Close()
|
|
if err != nil {
|
|
return 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 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 err
|
|
}
|
|
if st, err = fsutil.Stat(fp); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
return st, err
|
|
}
|