1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
package util
import (
"context"
"io"
"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(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(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.
if pe, ok := err.(*os.PathError); ok {
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
}
type ReadDirRequest struct {
Path string
IncludePattern string
}
func ReadDir(ctx context.Context, mount snapshot.Mountable, 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)
if err != nil {
return errors.WithStack(err)
}
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)
}
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.
err1 := err
if err := errors.Cause(err); err != nil {
err1 = err
}
if pe, ok := err1.(*os.PathError); ok {
pe.Path = path
}
return errors.WithStack(err)
}
return nil
})
return st, err
}
|