fileop: add chown support

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
docker-19.03
Tonis Tiigi 2019-03-15 16:29:14 -07:00
parent 171feaafeb
commit 7210bf6806
12 changed files with 561 additions and 26 deletions

View File

@ -3,7 +3,6 @@ package main
import (
"fmt"
"os"
"syscall"
bccommon "github.com/moby/buildkit/cmd/buildctl/common"
"github.com/moby/buildkit/util/apicaps"
@ -16,7 +15,6 @@ import (
func init() {
apicaps.ExportedProduct = "buildkit"
syscall.Umask(0)
}
func main() {

11
cmd/buildctl/main_unix.go Normal file
View File

@ -0,0 +1,11 @@
// +build !windows
package main
import (
"syscall"
)
func init() {
syscall.Umask(0)
}

View File

@ -13,7 +13,6 @@ import (
"sort"
"strconv"
"strings"
"syscall"
"time"
"github.com/BurntSushi/toml"
@ -56,7 +55,6 @@ import (
func init() {
apicaps.ExportedProduct = "buildkit"
seed.WithTimeAndRand()
syscall.Umask(0)
}
type workerInitializerOpt struct {

View File

@ -0,0 +1,11 @@
// +build !windows
package main
import (
"syscall"
)
func init() {
syscall.Umask(0)
}

View File

@ -3,6 +3,7 @@ package file
import (
"context"
"io/ioutil"
"log"
"os"
"path/filepath"
@ -11,24 +12,34 @@ import (
"github.com/moby/buildkit/solver/llbsolver/ops/fileoptypes"
"github.com/moby/buildkit/solver/pb"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
copy "github.com/tonistiigi/fsutil/copy"
"golang.org/x/sys/unix"
)
func mkdir(ctx context.Context, d string, action pb.FileActionMkDir) error {
func mkdir(ctx context.Context, d string, action pb.FileActionMkDir, user *uidgid) error {
p, err := fs.RootPath(d, filepath.Join(filepath.Join("/", action.Path)))
if err != nil {
return err
}
if action.MakeParents {
if err := os.MkdirAll(p, os.FileMode(action.Mode)&0777); err != nil {
if err := mkdirAll(p, os.FileMode(action.Mode)&0777, user); 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 user != nil {
if err := os.Chown(p, user.uid, user.gid); err != nil {
return err
}
}
}
if action.Timestamp != -1 {
@ -42,7 +53,7 @@ func mkdir(ctx context.Context, d string, action pb.FileActionMkDir) error {
return nil
}
func mkfile(ctx context.Context, d string, action pb.FileActionMkFile) error {
func mkfile(ctx context.Context, d string, action pb.FileActionMkFile, user *uidgid) error {
p, err := fs.RootPath(d, filepath.Join(filepath.Join("/", action.Path)))
if err != nil {
return err
@ -52,6 +63,12 @@ func mkfile(ctx context.Context, d string, action pb.FileActionMkFile) error {
return err
}
if user != nil {
if err := os.Chown(p, user.uid, user.gid); err != nil {
return err
}
}
if action.Timestamp != -1 {
st := unix.Timespec{Sec: action.Timestamp / 1e9, Nsec: action.Timestamp % 1e9}
timespec := []unix.Timespec{st, st}
@ -59,6 +76,7 @@ func mkfile(ctx context.Context, d string, action pb.FileActionMkFile) error {
return errors.Wrapf(err, "failed to utime %s", p)
}
}
return nil
}
@ -78,7 +96,7 @@ func rm(ctx context.Context, d string, action pb.FileActionRm) error {
return nil
}
func docopy(ctx context.Context, src, dest string, action pb.FileActionCopy) error {
func docopy(ctx context.Context, src, dest string, action pb.FileActionCopy, u *uidgid) error {
// // src is the source path
// Src string `protobuf:"bytes,1,opt,name=src,proto3" json:"src,omitempty"`
// // dest path
@ -118,6 +136,19 @@ func docopy(ctx context.Context, src, dest string, action pb.FileActionCopy) err
opt = append(opt, copy.AllowWildcards)
}
if u != nil {
opt = append(opt, func(ci *copy.CopyInfo) {
ci.Chown = &copy.ChownOpt{Uid: u.uid, Gid: u.gid}
})
}
xattrErrorHandler := func(dst, src, key string, err error) error {
log.Println(err)
return nil
}
opt = append(opt, copy.WithXAttrErrorHandler(xattrErrorHandler))
if err := copy.Copy(ctx, srcp, destp, opt...); err != nil {
return err
}
@ -128,7 +159,7 @@ func docopy(ctx context.Context, src, dest string, action pb.FileActionCopy) err
type Backend struct {
}
func (fb *Backend) Mkdir(ctx context.Context, m fileoptypes.Mount, action pb.FileActionMkDir) error {
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)
@ -141,10 +172,15 @@ func (fb *Backend) Mkdir(ctx context.Context, m fileoptypes.Mount, action pb.Fil
}
defer lm.Unmount()
return mkdir(ctx, dir, action)
u, err := readUser(action.Owner, user, group)
if err != nil {
return err
}
return mkdir(ctx, dir, action, u)
}
func (fb *Backend) Mkfile(ctx context.Context, m fileoptypes.Mount, action pb.FileActionMkFile) error {
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)
@ -157,7 +193,12 @@ func (fb *Backend) Mkfile(ctx context.Context, m fileoptypes.Mount, action pb.Fi
}
defer lm.Unmount()
return mkfile(ctx, dir, action)
u, err := readUser(action.Owner, user, group)
if err != nil {
return err
}
return mkfile(ctx, dir, action, u)
}
func (fb *Backend) Rm(ctx context.Context, m fileoptypes.Mount, action pb.FileActionRm) error {
mnt, ok := m.(*Mount)
@ -174,7 +215,7 @@ func (fb *Backend) Rm(ctx context.Context, m fileoptypes.Mount, action pb.FileAc
return rm(ctx, dir, action)
}
func (fb *Backend) Copy(ctx context.Context, m1 fileoptypes.Mount, m2 fileoptypes.Mount, action pb.FileActionCopy) error {
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)
@ -198,5 +239,12 @@ func (fb *Backend) Copy(ctx context.Context, m1 fileoptypes.Mount, m2 fileoptype
}
defer lm2.Unmount()
return docopy(ctx, src, dest, action)
u, err := readUser(action.Owner, user, group)
if err != nil {
return err
}
logrus.Debugf("copy %+v %+v %+v", action.Owner, user, group)
return docopy(ctx, src, dest, action, u)
}

View File

@ -0,0 +1,62 @@
package file
import (
"os"
"syscall"
)
// mkdirAll is forked os.MkdirAll
func mkdirAll(path string, perm os.FileMode, user *uidgid) error {
// Fast path: if we can tell whether path is a directory or file, stop with success or error.
dir, err := os.Stat(path)
if err == nil {
if dir.IsDir() {
return nil
}
return &os.PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR}
}
// Slow path: make sure parent exists and then call Mkdir for path.
i := len(path)
for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator.
i--
}
j := i
for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element.
j--
}
if j > 1 {
// Create parent.
err = mkdirAll(fixRootDirectory(path[:j-1]), perm, user)
if err != nil {
return err
}
}
dir, err1 := os.Lstat(path)
if err1 == nil && dir.IsDir() {
return nil
}
// Parent now exists; invoke Mkdir and use its result.
err = os.Mkdir(path, perm)
if err != nil {
// Handle arguments like "foo/." by
// double-checking that directory doesn't exist.
dir, err1 := os.Lstat(path)
if err1 == nil && dir.IsDir() {
return nil
}
return err
}
if user != nil {
if err := os.Chown(path, user.uid, user.gid); err != nil {
return err
}
}
return nil
}

View File

@ -0,0 +1,7 @@
// +build !windows
package file
func fixRootDirectory(p string) string {
return p
}

View File

@ -0,0 +1,14 @@
// +build windows
package file
import "os"
func fixRootDirectory(p string) string {
if len(p) == len(`\\?\c:`) {
if os.IsPathSeparator(p[0]) && os.IsPathSeparator(p[1]) && p[2] == '?' && os.IsPathSeparator(p[3]) && p[5] == ':' {
return p + `\`
}
}
return p
}

View File

@ -0,0 +1,122 @@
package file
import (
"os"
"github.com/containerd/continuity/fs"
"github.com/moby/buildkit/snapshot"
"github.com/moby/buildkit/solver/llbsolver/ops/fileoptypes"
"github.com/moby/buildkit/solver/pb"
"github.com/opencontainers/runc/libcontainer/user"
"github.com/pkg/errors"
)
type uidgid struct {
uid, gid int
}
func readUser(chopt *pb.ChownOpt, mu, mg fileoptypes.Mount) (*uidgid, error) {
if chopt == nil {
return nil, nil
}
var us uidgid
if chopt.User != nil {
switch u := chopt.User.User.(type) {
case *pb.UserOpt_ByName:
if mu == nil {
return nil, errors.Errorf("invalid missing user mount")
}
mmu, ok := mu.(*Mount)
if !ok {
return nil, errors.Errorf("invalid mount type %T", mu)
}
lm := snapshot.LocalMounter(mmu.m)
dir, err := lm.Mount()
if err != nil {
return nil, err
}
defer lm.Unmount()
passwdPath, err := user.GetPasswdPath()
if err != nil {
return nil, err
}
passwdPath, err = fs.RootPath(dir, passwdPath)
if err != nil {
return nil, err
}
ufile, err := os.Open(passwdPath)
if err != nil {
return nil, err
}
defer ufile.Close()
users, err := user.ParsePasswdFilter(ufile, func(uu user.User) bool {
return uu.Name == u.ByName.Name
})
if err != nil {
return nil, err
}
if len(users) > 0 {
us.uid = users[0].Uid
us.gid = users[0].Gid
}
case *pb.UserOpt_ByID:
us.uid = int(u.ByID)
us.gid = int(u.ByID)
}
}
if chopt.Group != nil {
switch u := chopt.Group.User.(type) {
case *pb.UserOpt_ByName:
if mg == nil {
return nil, errors.Errorf("invalid missing group mount")
}
mmg, ok := mg.(*Mount)
if !ok {
return nil, errors.Errorf("invalid mount type %T", mg)
}
lm := snapshot.LocalMounter(mmg.m)
dir, err := lm.Mount()
if err != nil {
return nil, err
}
defer lm.Unmount()
groupPath, err := user.GetGroupPath()
if err != nil {
return nil, err
}
groupPath, err = fs.RootPath(dir, groupPath)
if err != nil {
return nil, err
}
gfile, err := os.Open(groupPath)
if err != nil {
return nil, err
}
defer gfile.Close()
groups, err := user.ParseGroupFilter(gfile, func(g user.Group) bool {
return g.Name == u.ByName.Name
})
if err != nil {
return nil, err
}
if len(groups) > 0 {
us.gid = groups[0].Gid
}
case *pb.UserOpt_ByID:
us.gid = int(u.ByID)
}
}
return &us, nil
}

View File

@ -55,7 +55,7 @@ func (f *fileOp) CacheMap(ctx context.Context, index int) (*solver.CacheMap, boo
switch a := action.Action.(type) {
case *pb.FileAction_Mkdir:
p := *a.Mkdir
p.Owner = nil
processOwner(p.Owner, selectors)
dt, err = json.Marshal(p)
if err != nil {
return nil, false, err
@ -63,6 +63,7 @@ func (f *fileOp) CacheMap(ctx context.Context, index int) (*solver.CacheMap, boo
case *pb.FileAction_Mkfile:
p := *a.Mkfile
p.Owner = nil
processOwner(p.Owner, selectors)
dt, err = json.Marshal(p)
if err != nil {
return nil, false, err
@ -75,6 +76,7 @@ func (f *fileOp) CacheMap(ctx context.Context, index int) (*solver.CacheMap, boo
}
case *pb.FileAction_Copy:
p := *a.Copy
processOwner(p.Owner, selectors)
p.Owner = nil
if action.SecondaryInput != -1 && int(action.SecondaryInput) < f.numInputs {
p.Src = path.Base(p.Src)
@ -213,6 +215,29 @@ func dedupeSelectors(m map[llbsolver.Selector]struct{}) []llbsolver.Selector {
return selectors
}
func processOwner(chopt *pb.ChownOpt, selectors map[int]map[llbsolver.Selector]struct{}) error {
if chopt == nil {
return nil
}
if chopt.User != nil {
if u, ok := chopt.User.User.(*pb.UserOpt_ByName); ok {
if u.ByName.Input < 0 {
return errors.Errorf("invalid user index %d", u.ByName.Input)
}
addSelector(selectors, int(u.ByName.Input), "/etc/passwd", false, true)
}
}
if chopt.Group != nil {
if u, ok := chopt.Group.User.(*pb.UserOpt_ByName); ok {
if u.ByName.Input < 0 {
return errors.Errorf("invalid user index %d", u.ByName.Input)
}
addSelector(selectors, int(u.ByName.Input), "/etc/group", false, true)
}
}
return nil
}
func NewFileOpSolver(b fileoptypes.Backend, r fileoptypes.RefManager) *FileOpSolver {
return &FileOpSolver{
b: b,
@ -354,6 +379,13 @@ func (s *FileOpSolver) getInput(ctx context.Context, idx int, inputs []fileoptyp
return inp, nil
}
var toRelease []fileoptypes.Mount
defer func() {
for _, m := range toRelease {
m.Release(context.TODO())
}
}()
var inpMount, inpMountSecondary fileoptypes.Mount
action := actions[idx-len(inputs)]
@ -388,6 +420,7 @@ func (s *FileOpSolver) getInput(ctx context.Context, idx int, inputs []fileoptyp
return err
}
inpMountSecondary = m
toRelease = append(toRelease, m)
return nil
}
inpMountSecondary = inp.mount
@ -395,6 +428,51 @@ func (s *FileOpSolver) getInput(ctx context.Context, idx int, inputs []fileoptyp
}
}
loadUser := func(ctx context.Context, uopt *pb.UserOpt) (fileoptypes.Mount, error) {
if uopt == nil {
return nil, nil
}
switch u := uopt.User.(type) {
case *pb.UserOpt_ByName:
var m fileoptypes.Mount
if u.ByName.Input < 0 {
return nil, errors.Errorf("invalid user index: %d", u.ByName.Input)
}
inp, err := s.getInput(ctx, int(u.ByName.Input), inputs, actions)
if err != nil {
return nil, err
}
if inp.ref != nil {
mm, err := s.r.Prepare(ctx, inp.ref, true)
if err != nil {
return nil, err
}
toRelease = append(toRelease, mm)
m = mm
} else {
m = inp.mount
}
return m, nil
default:
return nil, nil
}
}
loadOwner := func(ctx context.Context, chopt *pb.ChownOpt) (fileoptypes.Mount, fileoptypes.Mount, error) {
if chopt == nil {
return nil, nil, nil
}
um, err := loadUser(ctx, chopt.User)
if err != nil {
return nil, nil, err
}
gm, err := loadUser(ctx, chopt.Group)
if err != nil {
return nil, nil, err
}
return um, gm, nil
}
if action.Input != -1 && action.SecondaryInput != -1 {
eg, ctx := errgroup.WithContext(ctx)
eg.Go(loadInput(ctx))
@ -425,11 +503,19 @@ func (s *FileOpSolver) getInput(ctx context.Context, idx int, inputs []fileoptyp
switch a := action.Action.(type) {
case *pb.FileAction_Mkdir:
if err := s.b.Mkdir(ctx, inpMount, *a.Mkdir); err != nil {
user, group, err := loadOwner(ctx, a.Mkdir.Owner)
if err != nil {
return nil, err
}
if err := s.b.Mkdir(ctx, inpMount, user, group, *a.Mkdir); err != nil {
return nil, err
}
case *pb.FileAction_Mkfile:
if err := s.b.Mkfile(ctx, inpMount, *a.Mkfile); err != nil {
user, group, err := loadOwner(ctx, a.Mkfile.Owner)
if err != nil {
return nil, err
}
if err := s.b.Mkfile(ctx, inpMount, user, group, *a.Mkfile); err != nil {
return nil, err
}
case *pb.FileAction_Rm:
@ -444,7 +530,11 @@ func (s *FileOpSolver) getInput(ctx context.Context, idx int, inputs []fileoptyp
}
inpMountSecondary = m
}
if err := s.b.Copy(ctx, inpMountSecondary, inpMount, *a.Copy); err != nil {
user, group, err := loadOwner(ctx, a.Copy.Owner)
if err != nil {
return nil, err
}
if err := s.b.Copy(ctx, inpMountSecondary, inpMount, user, group, *a.Copy); err != nil {
return nil, err
}
default:

View File

@ -54,6 +54,135 @@ func TestMkdirMkfile(t *testing.T) {
require.Equal(t, fo.Actions[1].Action.(*pb.FileAction_Mkfile).Mkfile, o.mount.chain[1].mkfile)
}
func TestChownOpt(t *testing.T) {
fo := &pb.FileOp{
Actions: []*pb.FileAction{
{
Input: 0,
SecondaryInput: -1,
Output: -1,
Action: &pb.FileAction_Mkdir{
Mkdir: &pb.FileActionMkDir{
Path: "/foo/bar",
MakeParents: true,
Mode: 0700,
Owner: &pb.ChownOpt{
User: &pb.UserOpt{
User: &pb.UserOpt_ByName{
ByName: &pb.NamedUserOpt{
Input: 1,
Name: "myuser",
},
},
},
Group: &pb.UserOpt{
User: &pb.UserOpt_ByName{
ByName: &pb.NamedUserOpt{
Input: 1,
Name: "myuser",
},
},
},
},
},
},
},
{
Input: 2,
SecondaryInput: -1,
Output: 0,
Action: &pb.FileAction_Mkfile{
Mkfile: &pb.FileActionMkFile{
Path: "/foo/bar/baz",
Mode: 0700,
Owner: &pb.ChownOpt{
User: &pb.UserOpt{
User: &pb.UserOpt_ByID{
ByID: 100,
},
},
},
},
},
},
},
}
s, rb := newTestFileSolver()
inp := rb.NewRef("ref1")
inp2 := rb.NewRef("usermount")
outs, err := s.Solve(context.TODO(), []fileoptypes.Ref{inp, inp2}, fo.Actions)
require.NoError(t, err)
require.Equal(t, len(outs), 1)
rb.checkReleased(t, append(outs, inp, inp2))
o := outs[0].(*testFileRef)
require.Equal(t, "mount-ref1-mkdir#u(mount-usermount)#g(mount-usermount)-mkfile-commit", o.id)
require.Equal(t, 2, len(o.mount.chain))
require.Equal(t, fo.Actions[0].Action.(*pb.FileAction_Mkdir).Mkdir, o.mount.chain[0].mkdir)
require.Equal(t, fo.Actions[1].Action.(*pb.FileAction_Mkfile).Mkfile, o.mount.chain[1].mkfile)
}
func TestChownCopy(t *testing.T) {
fo := &pb.FileOp{
Actions: []*pb.FileAction{
{
Input: -1,
SecondaryInput: -1,
Output: -1,
Action: &pb.FileAction_Mkfile{
Mkfile: &pb.FileActionMkFile{
Path: "/foo/bar/baz",
Mode: 0700,
},
},
},
{
Input: 1,
SecondaryInput: 0,
Output: 0,
Action: &pb.FileAction_Copy{
Copy: &pb.FileActionCopy{
Src: "/src",
Dest: "/dest",
Owner: &pb.ChownOpt{
User: &pb.UserOpt{
User: &pb.UserOpt_ByName{
ByName: &pb.NamedUserOpt{
Input: 1,
Name: "myuser",
},
},
},
Group: &pb.UserOpt{
User: &pb.UserOpt_ByName{
ByName: &pb.NamedUserOpt{
Input: 2,
Name: "mygroup",
},
},
},
},
},
},
},
},
}
s, rb := newTestFileSolver()
inpSrc := rb.NewRef("src")
inpDest := rb.NewRef("dest")
outs, err := s.Solve(context.TODO(), []fileoptypes.Ref{inpSrc, inpDest}, fo.Actions)
require.NoError(t, err)
require.Equal(t, len(outs), 1)
rb.checkReleased(t, append(outs, inpSrc, inpDest))
o := outs[0].(*testFileRef)
require.Equal(t, "mount-dest-copy(mount-src)#u(mount-dest)#g(mount-scratch-mkfile)-commit", o.id)
require.Equal(t, 1, len(o.mount.chain))
require.Equal(t, fo.Actions[1].Action.(*pb.FileAction_Copy).Copy, o.mount.chain[0].copy)
}
func TestInvalidNoOutput(t *testing.T) {
fo := &pb.FileOp{
Actions: []*pb.FileAction{
@ -267,6 +396,37 @@ func TestFileFromScratch(t *testing.T) {
require.Equal(t, fo.Actions[1].Action.(*pb.FileAction_Mkfile).Mkfile, o.mount.chain[1].mkfile)
}
func TestFileCopyInputSrc(t *testing.T) {
fo := &pb.FileOp{
Actions: []*pb.FileAction{
{
Input: 1,
SecondaryInput: 0,
Output: 0,
Action: &pb.FileAction_Copy{
Copy: &pb.FileActionCopy{
Src: "/src",
Dest: "/dest",
},
},
},
},
}
s, rb := newTestFileSolver()
inp0 := rb.NewRef("srcref")
inp1 := rb.NewRef("destref")
outs, err := s.Solve(context.TODO(), []fileoptypes.Ref{inp0, inp1}, fo.Actions)
require.NoError(t, err)
require.Equal(t, len(outs), 1)
rb.checkReleased(t, append(outs, inp0, inp1))
o := outs[0].(*testFileRef)
require.Equal(t, "mount-destref-copy(mount-srcref)-commit", o.id)
require.Equal(t, 1, len(o.mount.chain))
require.Equal(t, fo.Actions[0].Action.(*pb.FileAction_Copy).Copy, o.mount.chain[0].copy)
}
func TestFileCopyInputRm(t *testing.T) {
fo := &pb.FileOp{
Actions: []*pb.FileAction{
@ -409,6 +569,17 @@ type testMount struct {
active *testFileRef
}
func (tm *testMount) addUser(user, group fileoptypes.Mount) {
if user != nil {
um := user.(*testMount)
tm.id += "#u(" + um.id + ")"
}
if group != nil {
gm := group.(*testMount)
tm.id += "#g(" + gm.id + ")"
}
}
type mod struct {
mkdir *pb.FileActionMkDir
rm *pb.FileActionRm
@ -419,7 +590,7 @@ type mod struct {
func (m *testMount) IsFileOpMount() {}
func (m *testMount) Release(ctx context.Context) error {
if m.initID != m.id {
if m.b.mounts[m.initID] != m {
return m.b.mounts[m.initID].Release(ctx)
}
if m.unmounted {
@ -435,19 +606,21 @@ func (m *testMount) Release(ctx context.Context) error {
type testFileBackend struct {
}
func (b *testFileBackend) Mkdir(_ context.Context, m fileoptypes.Mount, a pb.FileActionMkDir) error {
func (b *testFileBackend) Mkdir(_ context.Context, m, user, group fileoptypes.Mount, a pb.FileActionMkDir) error {
mm := m.(*testMount)
if mm.callback != nil {
mm.callback()
}
mm.id += "-mkdir"
mm.addUser(user, group)
mm.chain = append(mm.chain, mod{mkdir: &a})
return nil
}
func (b *testFileBackend) Mkfile(_ context.Context, m fileoptypes.Mount, a pb.FileActionMkFile) error {
func (b *testFileBackend) Mkfile(_ context.Context, m, user, group fileoptypes.Mount, a pb.FileActionMkFile) error {
mm := m.(*testMount)
mm.id += "-mkfile"
mm.addUser(user, group)
mm.chain = append(mm.chain, mod{mkfile: &a})
return nil
}
@ -457,10 +630,11 @@ func (b *testFileBackend) Rm(_ context.Context, m fileoptypes.Mount, a pb.FileAc
mm.chain = append(mm.chain, mod{rm: &a})
return nil
}
func (b *testFileBackend) Copy(_ context.Context, m1 fileoptypes.Mount, m fileoptypes.Mount, a pb.FileActionCopy) error {
func (b *testFileBackend) Copy(_ context.Context, m1, m, user, group fileoptypes.Mount, a pb.FileActionCopy) error {
mm := m.(*testMount)
mm1 := m1.(*testMount)
mm.id += "-copy(" + mm1.id + ")"
mm.addUser(user, group)
mm.chain = append(mm.chain, mod{copy: &a, copySrc: mm1.chain})
return nil
}
@ -524,6 +698,6 @@ loop0:
}
for _, m := range b.mounts {
require.True(t, m.unmounted, "%s still mounted", m.id)
require.True(t, m.unmounted, "%s %p still mounted", m.id, m)
}
}

View File

@ -16,10 +16,10 @@ type Mount interface {
}
type Backend interface {
Mkdir(context.Context, Mount, pb.FileActionMkDir) error
Mkfile(context.Context, Mount, pb.FileActionMkFile) error
Mkdir(context.Context, Mount, Mount, Mount, pb.FileActionMkDir) error
Mkfile(context.Context, Mount, Mount, Mount, pb.FileActionMkFile) error
Rm(context.Context, Mount, pb.FileActionRm) error
Copy(context.Context, Mount, Mount, pb.FileActionCopy) error
Copy(context.Context, Mount, Mount, Mount, Mount, pb.FileActionCopy) error
}
type RefManager interface {