123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
- package file
- import (
- "context"
- "io/ioutil"
- "log"
- "os"
- "path/filepath"
- "strings"
- "time"
- "github.com/containerd/continuity/fs"
- "github.com/docker/docker/pkg/idtools"
- "github.com/moby/buildkit/snapshot"
- "github.com/moby/buildkit/solver/llbsolver/ops/fileoptypes"
- "github.com/moby/buildkit/solver/pb"
- "github.com/pkg/errors"
- copy "github.com/tonistiigi/fsutil/copy"
- )
- func timestampToTime(ts int64) *time.Time {
- if ts == -1 {
- return nil
- }
- tm := time.Unix(ts/1e9, ts%1e9)
- return &tm
- }
- func mapUser(user *copy.ChownOpt, idmap *idtools.IdentityMapping) (*copy.ChownOpt, error) {
- if idmap == nil || user == nil {
- return user, nil
- }
- identity, err := idmap.ToHost(idtools.Identity{
- UID: user.Uid,
- GID: user.Gid,
- })
- if err != nil {
- return nil, err
- }
- return ©.ChownOpt{Uid: identity.UID, Gid: identity.GID}, nil
- }
- func mkdir(ctx context.Context, d string, action pb.FileActionMkDir, user *copy.ChownOpt, idmap *idtools.IdentityMapping) error {
- p, err := fs.RootPath(d, filepath.Join(filepath.Join("/", action.Path)))
- if err != nil {
- return err
- }
- user, err = mapUser(user, idmap)
- if err != nil {
- return err
- }
- if action.MakeParents {
- if err := copy.MkdirAll(p, os.FileMode(action.Mode)&0777, user, timestampToTime(action.Timestamp)); err != nil {
- return err
- }
- } else {
- if err := os.Mkdir(p, os.FileMode(action.Mode)&0777); err != nil {
- if os.IsExist(err) {
- return nil
- }
- return err
- }
- if err := copy.Chown(p, user); err != nil {
- return err
- }
- if err := copy.Utimes(p, timestampToTime(action.Timestamp)); err != nil {
- return err
- }
- }
- return nil
- }
- func mkfile(ctx context.Context, d string, action pb.FileActionMkFile, user *copy.ChownOpt, idmap *idtools.IdentityMapping) error {
- p, err := fs.RootPath(d, filepath.Join(filepath.Join("/", action.Path)))
- if err != nil {
- return err
- }
- user, err = mapUser(user, idmap)
- if err != nil {
- return err
- }
- if err := ioutil.WriteFile(p, action.Data, os.FileMode(action.Mode)&0777); err != nil {
- return err
- }
- if err := copy.Chown(p, user); err != nil {
- return err
- }
- if err := copy.Utimes(p, timestampToTime(action.Timestamp)); err != nil {
- return err
- }
- return nil
- }
- func rm(ctx context.Context, d string, action pb.FileActionRm) error {
- p, err := fs.RootPath(d, filepath.Join(filepath.Join("/", action.Path)))
- if err != nil {
- return err
- }
- if err := os.RemoveAll(p); err != nil {
- if os.IsNotExist(errors.Cause(err)) && action.AllowNotFound {
- return nil
- }
- return err
- }
- return nil
- }
- func docopy(ctx context.Context, src, dest string, action pb.FileActionCopy, u *copy.ChownOpt, idmap *idtools.IdentityMapping) error {
- srcPath := cleanPath(action.Src)
- destPath := cleanPath(action.Dest)
- if !action.CreateDestPath {
- p, err := fs.RootPath(dest, filepath.Join(filepath.Join("/", action.Dest)))
- if err != nil {
- return err
- }
- if _, err := os.Lstat(filepath.Dir(p)); err != nil {
- return errors.Wrapf(err, "failed to stat %s", action.Dest)
- }
- }
- xattrErrorHandler := func(dst, src, key string, err error) error {
- log.Println(err)
- return nil
- }
- u, err := mapUser(u, idmap)
- if err != nil {
- return err
- }
- opt := []copy.Opt{
- func(ci *copy.CopyInfo) {
- ci.Chown = u
- ci.Utime = timestampToTime(action.Timestamp)
- if m := int(action.Mode); m != -1 {
- ci.Mode = &m
- }
- ci.CopyDirContents = action.DirCopyContents
- ci.FollowLinks = action.FollowSymlink
- },
- copy.WithXAttrErrorHandler(xattrErrorHandler),
- }
- if !action.AllowWildcard {
- if action.AttemptUnpackDockerCompatibility {
- if ok, err := unpack(ctx, src, srcPath, dest, destPath, u, timestampToTime(action.Timestamp)); err != nil {
- return err
- } else if ok {
- return nil
- }
- }
- return copy.Copy(ctx, src, srcPath, dest, destPath, opt...)
- }
- m, err := copy.ResolveWildcards(src, srcPath, action.FollowSymlink)
- if err != nil {
- return err
- }
- if len(m) == 0 {
- if action.AllowEmptyWildcard {
- return nil
- }
- return errors.Errorf("%s not found", srcPath)
- }
- for _, s := range m {
- if action.AttemptUnpackDockerCompatibility {
- if ok, err := unpack(ctx, src, s, dest, destPath, u, timestampToTime(action.Timestamp)); err != nil {
- return err
- } else if ok {
- continue
- }
- }
- if err := copy.Copy(ctx, src, s, dest, destPath, opt...); err != nil {
- return err
- }
- }
- return nil
- }
- func cleanPath(s string) string {
- s2 := filepath.Join("/", s)
- if strings.HasSuffix(s, "/.") {
- if s2 != "/" {
- s2 += "/"
- }
- s2 += "."
- } else if strings.HasSuffix(s, "/") && s2 != "/" {
- s2 += "/"
- }
- return s2
- }
- type Backend struct {
- }
- func (fb *Backend) Mkdir(ctx context.Context, m, user, group fileoptypes.Mount, action pb.FileActionMkDir) error {
- mnt, ok := m.(*Mount)
- if !ok {
- return errors.Errorf("invalid mount type %T", m)
- }
- lm := snapshot.LocalMounter(mnt.m)
- dir, err := lm.Mount()
- if err != nil {
- return err
- }
- defer lm.Unmount()
- u, err := readUser(action.Owner, user, group)
- if err != nil {
- return err
- }
- return mkdir(ctx, dir, action, u, mnt.m.IdentityMapping())
- }
- func (fb *Backend) Mkfile(ctx context.Context, m, user, group fileoptypes.Mount, action pb.FileActionMkFile) error {
- mnt, ok := m.(*Mount)
- if !ok {
- return errors.Errorf("invalid mount type %T", m)
- }
- lm := snapshot.LocalMounter(mnt.m)
- dir, err := lm.Mount()
- if err != nil {
- return err
- }
- defer lm.Unmount()
- u, err := readUser(action.Owner, user, group)
- if err != nil {
- return err
- }
- return mkfile(ctx, dir, action, u, mnt.m.IdentityMapping())
- }
- func (fb *Backend) Rm(ctx context.Context, m fileoptypes.Mount, action pb.FileActionRm) error {
- mnt, ok := m.(*Mount)
- if !ok {
- return errors.Errorf("invalid mount type %T", m)
- }
- lm := snapshot.LocalMounter(mnt.m)
- dir, err := lm.Mount()
- if err != nil {
- return err
- }
- defer lm.Unmount()
- return rm(ctx, dir, action)
- }
- func (fb *Backend) Copy(ctx context.Context, m1, m2, user, group fileoptypes.Mount, action pb.FileActionCopy) error {
- mnt1, ok := m1.(*Mount)
- if !ok {
- return errors.Errorf("invalid mount type %T", m1)
- }
- mnt2, ok := m2.(*Mount)
- if !ok {
- return errors.Errorf("invalid mount type %T", m2)
- }
- lm := snapshot.LocalMounter(mnt1.m)
- src, err := lm.Mount()
- if err != nil {
- return err
- }
- defer lm.Unmount()
- lm2 := snapshot.LocalMounter(mnt2.m)
- dest, err := lm2.Mount()
- if err != nil {
- return err
- }
- defer lm2.Unmount()
- u, err := readUser(action.Owner, user, group)
- if err != nil {
- return err
- }
- return docopy(ctx, src, dest, action, u, mnt2.m.IdentityMapping())
- }
|