Browse code

vendor: update buildkit to e9aca5be

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>

Tonis Tiigi authored on 2019/03/21 10:45:16
Showing 40 changed files
... ...
@@ -165,6 +165,8 @@ func (w *Worker) ResolveOp(v solver.Vertex, s frontend.FrontendLLBBridge, sm *se
165 165
 			return ops.NewSourceOp(v, op, baseOp.Platform, w.SourceManager, sm, w)
166 166
 		case *pb.Op_Exec:
167 167
 			return ops.NewExecOp(v, op, baseOp.Platform, w.CacheManager, sm, w.MetadataStore, w.Executor, w)
168
+		case *pb.Op_File:
169
+			return ops.NewFileOp(v, op, w.CacheManager, w.MetadataStore, w)
168 170
 		case *pb.Op_Build:
169 171
 			return ops.NewBuildOp(v, op, s, w)
170 172
 		}
... ...
@@ -27,8 +27,8 @@ github.com/imdario/mergo v0.3.6
27 27
 golang.org/x/sync 1d60e4601c6fd243af51cc01ddf169918a5407ca
28 28
 
29 29
 # buildkit
30
-github.com/moby/buildkit c35410878ab9070498c66f6c67d3e8bc3b92241f
31
-github.com/tonistiigi/fsutil 1ec1983587cde7e8ac2978e354ff5360af622464
30
+github.com/moby/buildkit e9aca5bef87e19173b99d8668db0338dcaaa5f33
31
+github.com/tonistiigi/fsutil 1bdbf124ad494a771e99e0cdcd16326375f8b2c9
32 32
 github.com/grpc-ecosystem/grpc-opentracing 8e809c8a86450a29b90dcc9efbf062d0fe6d9746
33 33
 github.com/opentracing/opentracing-go 1361b9cd60be79c4c3a7fa9841b3c132e40066a7
34 34
 github.com/google/shlex 6f45313302b9c56850fc17f99e40caebce98c716
... ...
@@ -47,6 +47,10 @@ func Checksum(ctx context.Context, ref cache.ImmutableRef, path string, followLi
47 47
 	return getDefaultManager().Checksum(ctx, ref, path, followLinks)
48 48
 }
49 49
 
50
+func ChecksumWildcard(ctx context.Context, ref cache.ImmutableRef, path string, followLinks bool) (digest.Digest, error) {
51
+	return getDefaultManager().ChecksumWildcard(ctx, ref, path, followLinks)
52
+}
53
+
50 54
 func GetCacheContext(ctx context.Context, md *metadata.StorageItem) (CacheContext, error) {
51 55
 	return getDefaultManager().GetCacheContext(ctx, md)
52 56
 }
... ...
@@ -84,6 +88,14 @@ func (cm *cacheManager) Checksum(ctx context.Context, ref cache.ImmutableRef, p
84 84
 	return cc.Checksum(ctx, ref, p, followLinks)
85 85
 }
86 86
 
87
+func (cm *cacheManager) ChecksumWildcard(ctx context.Context, ref cache.ImmutableRef, p string, followLinks bool) (digest.Digest, error) {
88
+	cc, err := cm.GetCacheContext(ctx, ensureOriginMetadata(ref.Metadata()))
89
+	if err != nil {
90
+		return "", nil
91
+	}
92
+	return cc.ChecksumWildcard(ctx, ref, p, followLinks)
93
+}
94
+
87 95
 func (cm *cacheManager) GetCacheContext(ctx context.Context, md *metadata.StorageItem) (CacheContext, error) {
88 96
 	cm.locker.Lock(md.ID())
89 97
 	cm.lruMu.Lock()
... ...
@@ -91,6 +103,7 @@ func (cm *cacheManager) GetCacheContext(ctx context.Context, md *metadata.Storag
91 91
 	cm.lruMu.Unlock()
92 92
 	if ok {
93 93
 		cm.locker.Unlock(md.ID())
94
+		v.(*cacheContext).linkMap = map[string][][]byte{}
94 95
 		return v.(*cacheContext), nil
95 96
 	}
96 97
 	cc, err := newCacheContext(md)
... ...
@@ -115,6 +128,7 @@ func (cm *cacheManager) SetCacheContext(ctx context.Context, md *metadata.Storag
115 115
 			md:       md,
116 116
 			tree:     cci.(*cacheContext).tree,
117 117
 			dirtyMap: map[string]struct{}{},
118
+			linkMap:  map[string][][]byte{},
118 119
 		}
119 120
 	} else {
120 121
 		if err := cc.save(); err != nil {
... ...
@@ -137,6 +151,7 @@ type cacheContext struct {
137 137
 	txn      *iradix.Txn
138 138
 	node     *iradix.Node
139 139
 	dirtyMap map[string]struct{}
140
+	linkMap  map[string][][]byte
140 141
 }
141 142
 
142 143
 type mount struct {
... ...
@@ -181,6 +196,7 @@ func newCacheContext(md *metadata.StorageItem) (*cacheContext, error) {
181 181
 		md:       md,
182 182
 		tree:     iradix.New(),
183 183
 		dirtyMap: map[string]struct{}{},
184
+		linkMap:  map[string][][]byte{},
184 185
 	}
185 186
 	if err := cc.load(); err != nil {
186 187
 		return nil, err
... ...
@@ -313,7 +329,35 @@ func (cc *cacheContext) HandleChange(kind fsutil.ChangeKind, p string, fi os.Fil
313 313
 		p += "/"
314 314
 	}
315 315
 	cr.Digest = h.Digest()
316
+
317
+	// if we receive a hardlink just use the digest of the source
318
+	// note that the source may be called later because data writing is async
319
+	if fi.Mode()&os.ModeSymlink == 0 && stat.Linkname != "" {
320
+		ln := path.Join("/", filepath.ToSlash(stat.Linkname))
321
+		v, ok := cc.txn.Get(convertPathToKey([]byte(ln)))
322
+		if ok {
323
+			cp := *v.(*CacheRecord)
324
+			cr = &cp
325
+		}
326
+		cc.linkMap[ln] = append(cc.linkMap[ln], k)
327
+	}
328
+
316 329
 	cc.txn.Insert(k, cr)
330
+	if !fi.IsDir() {
331
+		if links, ok := cc.linkMap[p]; ok {
332
+			for _, l := range links {
333
+				pp := convertKeyToPath(l)
334
+				cc.txn.Insert(l, cr)
335
+				d := path.Dir(string(pp))
336
+				if d == "/" {
337
+					d = ""
338
+				}
339
+				cc.dirtyMap[d] = struct{}{}
340
+			}
341
+			delete(cc.linkMap, p)
342
+		}
343
+	}
344
+
317 345
 	d := path.Dir(p)
318 346
 	if d == "/" {
319 347
 		d = ""
... ...
@@ -343,6 +387,9 @@ func (cc *cacheContext) ChecksumWildcard(ctx context.Context, mountable cache.Mo
343 343
 			}
344 344
 		}
345 345
 	}
346
+	if len(wildcards) == 0 {
347
+		return digest.FromBytes([]byte{}), nil
348
+	}
346 349
 
347 350
 	if len(wildcards) > 1 {
348 351
 		digester := digest.Canonical.Digester()
... ...
@@ -543,12 +590,13 @@ func (cc *cacheContext) lazyChecksum(ctx context.Context, m *mount, p string) (*
543 543
 }
544 544
 
545 545
 func (cc *cacheContext) checksum(ctx context.Context, root *iradix.Node, txn *iradix.Txn, m *mount, k []byte, follow bool) (*CacheRecord, bool, error) {
546
+	origk := k
546 547
 	k, cr, err := getFollowLinks(root, k, follow)
547 548
 	if err != nil {
548 549
 		return nil, false, err
549 550
 	}
550 551
 	if cr == nil {
551
-		return nil, false, errors.Wrapf(errNotFound, "%s not found", convertKeyToPath(k))
552
+		return nil, false, errors.Wrapf(errNotFound, "%q not found", convertKeyToPath(origk))
552 553
 	}
553 554
 	if cr.Digest != "" {
554 555
 		return cr, false, nil
... ...
@@ -311,7 +311,7 @@ func (sr *mutableRef) updateLastUsed() bool {
311 311
 
312 312
 func (sr *mutableRef) commit(ctx context.Context) (ImmutableRef, error) {
313 313
 	if !sr.mutable || len(sr.refs) == 0 {
314
-		return nil, errors.Wrapf(errInvalid, "invalid mutable ref")
314
+		return nil, errors.Wrapf(errInvalid, "invalid mutable ref %p", sr)
315 315
 	}
316 316
 
317 317
 	id := identity.NewID()
318 318
new file mode 100644
... ...
@@ -0,0 +1,727 @@
0
+package llb
1
+
2
+import (
3
+	_ "crypto/sha256"
4
+	"os"
5
+	"path"
6
+	"strconv"
7
+	"strings"
8
+	"time"
9
+
10
+	"github.com/moby/buildkit/solver/pb"
11
+	digest "github.com/opencontainers/go-digest"
12
+	"github.com/pkg/errors"
13
+)
14
+
15
+// Examples:
16
+// local := llb.Local(...)
17
+// llb.Image().Dir("/abc").File(Mkdir("./foo").Mkfile("/abc/foo/bar", []byte("data")))
18
+// llb.Image().File(Mkdir("/foo").Mkfile("/foo/bar", []byte("data")))
19
+// llb.Image().File(Copy(local, "/foo", "/bar")).File(Copy(local, "/foo2", "/bar2"))
20
+//
21
+// a := Mkdir("./foo")  // *FileAction /ced/foo
22
+// b := Mkdir("./bar") // /abc/bar
23
+// c := b.Copy(a.WithState(llb.Scratch().Dir("/ced")), "./foo", "./baz") // /abc/baz
24
+// llb.Image().Dir("/abc").File(c)
25
+//
26
+// In future this can be extended to multiple outputs with:
27
+// a := Mkdir("./foo")
28
+// b, id := a.GetSelector()
29
+// c := b.Mkdir("./bar")
30
+// filestate = state.File(c)
31
+// filestate.GetOutput(id).Exec()
32
+
33
+func NewFileOp(s State, action *FileAction, c Constraints) *FileOp {
34
+	action = action.bind(s)
35
+
36
+	f := &FileOp{
37
+		action:      action,
38
+		constraints: c,
39
+	}
40
+
41
+	f.output = &output{vertex: f, getIndex: func() (pb.OutputIndex, error) {
42
+		return pb.OutputIndex(0), nil
43
+	}}
44
+
45
+	return f
46
+}
47
+
48
+// CopyInput is either llb.State or *FileActionWithState
49
+type CopyInput interface {
50
+	isFileOpCopyInput()
51
+}
52
+
53
+type subAction interface {
54
+	toProtoAction(string, pb.InputIndex) pb.IsFileAction
55
+}
56
+
57
+type FileAction struct {
58
+	state  *State
59
+	prev   *FileAction
60
+	action subAction
61
+	err    error
62
+}
63
+
64
+func (fa *FileAction) Mkdir(p string, m os.FileMode, opt ...MkdirOption) *FileAction {
65
+	a := Mkdir(p, m, opt...)
66
+	a.prev = fa
67
+	return a
68
+}
69
+
70
+func (fa *FileAction) Mkfile(p string, m os.FileMode, dt []byte, opt ...MkfileOption) *FileAction {
71
+	a := Mkfile(p, m, dt, opt...)
72
+	a.prev = fa
73
+	return a
74
+}
75
+
76
+func (fa *FileAction) Rm(p string, opt ...RmOption) *FileAction {
77
+	a := Rm(p, opt...)
78
+	a.prev = fa
79
+	return a
80
+}
81
+
82
+func (fa *FileAction) Copy(input CopyInput, src, dest string, opt ...CopyOption) *FileAction {
83
+	a := Copy(input, src, dest, opt...)
84
+	a.prev = fa
85
+	return a
86
+}
87
+
88
+func (fa *FileAction) allOutputs(m map[Output]struct{}) {
89
+	if fa == nil {
90
+		return
91
+	}
92
+	if fa.state != nil && fa.state.Output() != nil {
93
+		m[fa.state.Output()] = struct{}{}
94
+	}
95
+
96
+	if a, ok := fa.action.(*fileActionCopy); ok {
97
+		if a.state != nil {
98
+			if out := a.state.Output(); out != nil {
99
+				m[out] = struct{}{}
100
+			}
101
+		} else if a.fas != nil {
102
+			a.fas.allOutputs(m)
103
+		}
104
+	}
105
+	fa.prev.allOutputs(m)
106
+}
107
+
108
+func (fa *FileAction) bind(s State) *FileAction {
109
+	if fa == nil {
110
+		return nil
111
+	}
112
+	fa2 := *fa
113
+	fa2.prev = fa.prev.bind(s)
114
+	fa2.state = &s
115
+	return &fa2
116
+}
117
+
118
+func (fa *FileAction) WithState(s State) CopyInput {
119
+	return &fileActionWithState{FileAction: fa.bind(s)}
120
+}
121
+
122
+type fileActionWithState struct {
123
+	*FileAction
124
+}
125
+
126
+func (fas *fileActionWithState) isFileOpCopyInput() {}
127
+
128
+func Mkdir(p string, m os.FileMode, opt ...MkdirOption) *FileAction {
129
+	var mi MkdirInfo
130
+	for _, o := range opt {
131
+		o.SetMkdirOption(&mi)
132
+	}
133
+	return &FileAction{
134
+		action: &fileActionMkdir{
135
+			file: p,
136
+			mode: m,
137
+			info: mi,
138
+		},
139
+	}
140
+}
141
+
142
+type fileActionMkdir struct {
143
+	file string
144
+	mode os.FileMode
145
+	info MkdirInfo
146
+}
147
+
148
+func (a *fileActionMkdir) toProtoAction(parent string, base pb.InputIndex) pb.IsFileAction {
149
+	return &pb.FileAction_Mkdir{
150
+		Mkdir: &pb.FileActionMkDir{
151
+			Path:        normalizePath(parent, a.file, false),
152
+			Mode:        int32(a.mode & 0777),
153
+			MakeParents: a.info.MakeParents,
154
+			Owner:       a.info.ChownOpt.marshal(base),
155
+			Timestamp:   marshalTime(a.info.CreatedTime),
156
+		},
157
+	}
158
+}
159
+
160
+type MkdirOption interface {
161
+	SetMkdirOption(*MkdirInfo)
162
+}
163
+
164
+type ChownOption interface {
165
+	MkdirOption
166
+	MkfileOption
167
+	CopyOption
168
+}
169
+
170
+type mkdirOptionFunc func(*MkdirInfo)
171
+
172
+func (fn mkdirOptionFunc) SetMkdirOption(mi *MkdirInfo) {
173
+	fn(mi)
174
+}
175
+
176
+var _ MkdirOption = &MkdirInfo{}
177
+
178
+func WithParents(b bool) MkdirOption {
179
+	return mkdirOptionFunc(func(mi *MkdirInfo) {
180
+		mi.MakeParents = b
181
+	})
182
+}
183
+
184
+type MkdirInfo struct {
185
+	MakeParents bool
186
+	ChownOpt    *ChownOpt
187
+	CreatedTime *time.Time
188
+}
189
+
190
+func (mi *MkdirInfo) SetMkdirOption(mi2 *MkdirInfo) {
191
+	*mi2 = *mi
192
+}
193
+
194
+func WithUser(name string) ChownOption {
195
+	opt := ChownOpt{}
196
+
197
+	parts := strings.SplitN(name, ":", 2)
198
+	for i, v := range parts {
199
+		switch i {
200
+		case 0:
201
+			uid, err := parseUID(v)
202
+			if err != nil {
203
+				opt.User = &UserOpt{Name: v}
204
+			} else {
205
+				opt.User = &UserOpt{UID: uid}
206
+			}
207
+		case 1:
208
+			gid, err := parseUID(v)
209
+			if err != nil {
210
+				opt.Group = &UserOpt{Name: v}
211
+			} else {
212
+				opt.Group = &UserOpt{UID: gid}
213
+			}
214
+		}
215
+	}
216
+
217
+	return opt
218
+}
219
+
220
+func parseUID(str string) (int, error) {
221
+	if str == "root" {
222
+		return 0, nil
223
+	}
224
+	uid, err := strconv.ParseInt(str, 10, 32)
225
+	if err != nil {
226
+		return 0, err
227
+	}
228
+	return int(uid), nil
229
+}
230
+
231
+func WithUIDGID(uid, gid int) ChownOption {
232
+	return ChownOpt{
233
+		User:  &UserOpt{UID: uid},
234
+		Group: &UserOpt{UID: gid},
235
+	}
236
+}
237
+
238
+type ChownOpt struct {
239
+	User  *UserOpt
240
+	Group *UserOpt
241
+}
242
+
243
+func (co ChownOpt) SetMkdirOption(mi *MkdirInfo) {
244
+	mi.ChownOpt = &co
245
+}
246
+func (co ChownOpt) SetMkfileOption(mi *MkfileInfo) {
247
+	mi.ChownOpt = &co
248
+}
249
+func (co ChownOpt) SetCopyOption(mi *CopyInfo) {
250
+	mi.ChownOpt = &co
251
+}
252
+
253
+func (cp *ChownOpt) marshal(base pb.InputIndex) *pb.ChownOpt {
254
+	if cp == nil {
255
+		return nil
256
+	}
257
+	return &pb.ChownOpt{
258
+		User:  cp.User.marshal(base),
259
+		Group: cp.Group.marshal(base),
260
+	}
261
+}
262
+
263
+type UserOpt struct {
264
+	UID  int
265
+	Name string
266
+}
267
+
268
+func (up *UserOpt) marshal(base pb.InputIndex) *pb.UserOpt {
269
+	if up == nil {
270
+		return nil
271
+	}
272
+	if up.Name != "" {
273
+		return &pb.UserOpt{User: &pb.UserOpt_ByName{ByName: &pb.NamedUserOpt{
274
+			Name: up.Name, Input: base}}}
275
+	}
276
+	return &pb.UserOpt{User: &pb.UserOpt_ByID{ByID: uint32(up.UID)}}
277
+}
278
+
279
+func Mkfile(p string, m os.FileMode, dt []byte, opts ...MkfileOption) *FileAction {
280
+	var mi MkfileInfo
281
+	for _, o := range opts {
282
+		o.SetMkfileOption(&mi)
283
+	}
284
+
285
+	return &FileAction{
286
+		action: &fileActionMkfile{
287
+			file: p,
288
+			mode: m,
289
+			dt:   dt,
290
+			info: mi,
291
+		},
292
+	}
293
+}
294
+
295
+type MkfileOption interface {
296
+	SetMkfileOption(*MkfileInfo)
297
+}
298
+
299
+type MkfileInfo struct {
300
+	ChownOpt    *ChownOpt
301
+	CreatedTime *time.Time
302
+}
303
+
304
+func (mi *MkfileInfo) SetMkfileOption(mi2 *MkfileInfo) {
305
+	*mi2 = *mi
306
+}
307
+
308
+var _ MkfileOption = &MkfileInfo{}
309
+
310
+type fileActionMkfile struct {
311
+	file string
312
+	mode os.FileMode
313
+	dt   []byte
314
+	info MkfileInfo
315
+}
316
+
317
+func (a *fileActionMkfile) toProtoAction(parent string, base pb.InputIndex) pb.IsFileAction {
318
+	return &pb.FileAction_Mkfile{
319
+		Mkfile: &pb.FileActionMkFile{
320
+			Path:      normalizePath(parent, a.file, false),
321
+			Mode:      int32(a.mode & 0777),
322
+			Data:      a.dt,
323
+			Owner:     a.info.ChownOpt.marshal(base),
324
+			Timestamp: marshalTime(a.info.CreatedTime),
325
+		},
326
+	}
327
+}
328
+
329
+func Rm(p string, opts ...RmOption) *FileAction {
330
+	var mi RmInfo
331
+	for _, o := range opts {
332
+		o.SetRmOption(&mi)
333
+	}
334
+
335
+	return &FileAction{
336
+		action: &fileActionRm{
337
+			file: p,
338
+			info: mi,
339
+		},
340
+	}
341
+}
342
+
343
+type RmOption interface {
344
+	SetRmOption(*RmInfo)
345
+}
346
+
347
+type rmOptionFunc func(*RmInfo)
348
+
349
+func (fn rmOptionFunc) SetRmOption(mi *RmInfo) {
350
+	fn(mi)
351
+}
352
+
353
+type RmInfo struct {
354
+	AllowNotFound bool
355
+	AllowWildcard bool
356
+}
357
+
358
+func (mi *RmInfo) SetRmOption(mi2 *RmInfo) {
359
+	*mi2 = *mi
360
+}
361
+
362
+var _ RmOption = &RmInfo{}
363
+
364
+func WithAllowNotFound(b bool) RmOption {
365
+	return rmOptionFunc(func(mi *RmInfo) {
366
+		mi.AllowNotFound = b
367
+	})
368
+}
369
+
370
+func WithAllowWildcard(b bool) RmOption {
371
+	return rmOptionFunc(func(mi *RmInfo) {
372
+		mi.AllowWildcard = b
373
+	})
374
+}
375
+
376
+type fileActionRm struct {
377
+	file string
378
+	info RmInfo
379
+}
380
+
381
+func (a *fileActionRm) toProtoAction(parent string, base pb.InputIndex) pb.IsFileAction {
382
+	return &pb.FileAction_Rm{
383
+		Rm: &pb.FileActionRm{
384
+			Path:          normalizePath(parent, a.file, false),
385
+			AllowNotFound: a.info.AllowNotFound,
386
+			AllowWildcard: a.info.AllowWildcard,
387
+		},
388
+	}
389
+}
390
+
391
+func Copy(input CopyInput, src, dest string, opts ...CopyOption) *FileAction {
392
+	var state *State
393
+	var fas *fileActionWithState
394
+	var err error
395
+	if st, ok := input.(State); ok {
396
+		state = &st
397
+	} else if v, ok := input.(*fileActionWithState); ok {
398
+		fas = v
399
+	} else {
400
+		err = errors.Errorf("invalid input type %T for copy", input)
401
+	}
402
+
403
+	var mi CopyInfo
404
+	for _, o := range opts {
405
+		o.SetCopyOption(&mi)
406
+	}
407
+
408
+	return &FileAction{
409
+		action: &fileActionCopy{
410
+			state: state,
411
+			fas:   fas,
412
+			src:   src,
413
+			dest:  dest,
414
+			info:  mi,
415
+		},
416
+		err: err,
417
+	}
418
+}
419
+
420
+type CopyOption interface {
421
+	SetCopyOption(*CopyInfo)
422
+}
423
+
424
+type CopyInfo struct {
425
+	Mode                *os.FileMode
426
+	FollowSymlinks      bool
427
+	CopyDirContentsOnly bool
428
+	AttemptUnpack       bool
429
+	CreateDestPath      bool
430
+	AllowWildcard       bool
431
+	AllowEmptyWildcard  bool
432
+	ChownOpt            *ChownOpt
433
+	CreatedTime         *time.Time
434
+}
435
+
436
+func (mi *CopyInfo) SetCopyOption(mi2 *CopyInfo) {
437
+	*mi2 = *mi
438
+}
439
+
440
+var _ CopyOption = &CopyInfo{}
441
+
442
+type fileActionCopy struct {
443
+	state *State
444
+	fas   *fileActionWithState
445
+	src   string
446
+	dest  string
447
+	info  CopyInfo
448
+}
449
+
450
+func (a *fileActionCopy) toProtoAction(parent string, base pb.InputIndex) pb.IsFileAction {
451
+	c := &pb.FileActionCopy{
452
+		Src:                              a.sourcePath(),
453
+		Dest:                             normalizePath(parent, a.dest, true),
454
+		Owner:                            a.info.ChownOpt.marshal(base),
455
+		AllowWildcard:                    a.info.AllowWildcard,
456
+		AllowEmptyWildcard:               a.info.AllowEmptyWildcard,
457
+		FollowSymlink:                    a.info.FollowSymlinks,
458
+		DirCopyContents:                  a.info.CopyDirContentsOnly,
459
+		AttemptUnpackDockerCompatibility: a.info.AttemptUnpack,
460
+		CreateDestPath:                   a.info.CreateDestPath,
461
+		Timestamp:                        marshalTime(a.info.CreatedTime),
462
+	}
463
+	if a.info.Mode != nil {
464
+		c.Mode = int32(*a.info.Mode)
465
+	} else {
466
+		c.Mode = -1
467
+	}
468
+	return &pb.FileAction_Copy{
469
+		Copy: c,
470
+	}
471
+}
472
+
473
+func (c *fileActionCopy) sourcePath() string {
474
+	p := path.Clean(c.src)
475
+	if !path.IsAbs(p) {
476
+		if c.state != nil {
477
+			p = path.Join("/", c.state.GetDir(), p)
478
+		} else if c.fas != nil {
479
+			p = path.Join("/", c.fas.state.GetDir(), p)
480
+		}
481
+	}
482
+	return p
483
+}
484
+
485
+type CreatedTime time.Time
486
+
487
+func WithCreatedTime(t time.Time) CreatedTime {
488
+	return CreatedTime(t)
489
+}
490
+
491
+func (c CreatedTime) SetMkdirOption(mi *MkdirInfo) {
492
+	mi.CreatedTime = (*time.Time)(&c)
493
+}
494
+
495
+func (c CreatedTime) SetMkfileOption(mi *MkfileInfo) {
496
+	mi.CreatedTime = (*time.Time)(&c)
497
+}
498
+
499
+func (c CreatedTime) SetCopyOption(mi *CopyInfo) {
500
+	mi.CreatedTime = (*time.Time)(&c)
501
+}
502
+
503
+func marshalTime(t *time.Time) int64 {
504
+	if t == nil {
505
+		return -1
506
+	}
507
+	return t.UnixNano()
508
+}
509
+
510
+type FileOp struct {
511
+	MarshalCache
512
+	action *FileAction
513
+	output Output
514
+
515
+	constraints Constraints
516
+	isValidated bool
517
+}
518
+
519
+func (f *FileOp) Validate() error {
520
+	if f.isValidated {
521
+		return nil
522
+	}
523
+	if f.action == nil {
524
+		return errors.Errorf("action is required")
525
+	}
526
+	f.isValidated = true
527
+	return nil
528
+}
529
+
530
+type marshalState struct {
531
+	visited map[*FileAction]*fileActionState
532
+	inputs  []*pb.Input
533
+	actions []*fileActionState
534
+}
535
+
536
+func newMarshalState() *marshalState {
537
+	return &marshalState{
538
+		visited: map[*FileAction]*fileActionState{},
539
+	}
540
+}
541
+
542
+type fileActionState struct {
543
+	base           pb.InputIndex
544
+	input          pb.InputIndex
545
+	inputRelative  *int
546
+	input2         pb.InputIndex
547
+	input2Relative *int
548
+	target         int
549
+	action         subAction
550
+	fa             *FileAction
551
+}
552
+
553
+func (ms *marshalState) addInput(st *fileActionState, c *Constraints, o Output) (pb.InputIndex, error) {
554
+	inp, err := o.ToInput(c)
555
+	if err != nil {
556
+		return 0, err
557
+	}
558
+	for i, inp2 := range ms.inputs {
559
+		if *inp == *inp2 {
560
+			return pb.InputIndex(i), nil
561
+		}
562
+	}
563
+	i := pb.InputIndex(len(ms.inputs))
564
+	ms.inputs = append(ms.inputs, inp)
565
+	return i, nil
566
+}
567
+
568
+func (ms *marshalState) add(fa *FileAction, c *Constraints) (*fileActionState, error) {
569
+	if st, ok := ms.visited[fa]; ok {
570
+		return st, nil
571
+	}
572
+
573
+	if fa.err != nil {
574
+		return nil, fa.err
575
+	}
576
+
577
+	var prevState *fileActionState
578
+	if parent := fa.prev; parent != nil {
579
+		var err error
580
+		prevState, err = ms.add(parent, c)
581
+		if err != nil {
582
+			return nil, err
583
+		}
584
+	}
585
+
586
+	st := &fileActionState{
587
+		action: fa.action,
588
+		input:  -1,
589
+		input2: -1,
590
+		base:   -1,
591
+		fa:     fa,
592
+	}
593
+
594
+	if source := fa.state.Output(); source != nil {
595
+		inp, err := ms.addInput(st, c, source)
596
+		if err != nil {
597
+			return nil, err
598
+		}
599
+		st.base = inp
600
+	}
601
+
602
+	if fa.prev == nil {
603
+		st.input = st.base
604
+	} else {
605
+		st.inputRelative = &prevState.target
606
+	}
607
+
608
+	if a, ok := fa.action.(*fileActionCopy); ok {
609
+		if a.state != nil {
610
+			if out := a.state.Output(); out != nil {
611
+				inp, err := ms.addInput(st, c, out)
612
+				if err != nil {
613
+					return nil, err
614
+				}
615
+				st.input2 = inp
616
+			}
617
+		} else if a.fas != nil {
618
+			src, err := ms.add(a.fas.FileAction, c)
619
+			if err != nil {
620
+				return nil, err
621
+			}
622
+			st.input2Relative = &src.target
623
+		} else {
624
+			return nil, errors.Errorf("invalid empty source for copy")
625
+		}
626
+	}
627
+
628
+	st.target = len(ms.actions)
629
+
630
+	ms.visited[fa] = st
631
+	ms.actions = append(ms.actions, st)
632
+
633
+	return st, nil
634
+}
635
+
636
+func (f *FileOp) Marshal(c *Constraints) (digest.Digest, []byte, *pb.OpMetadata, error) {
637
+	if f.Cached(c) {
638
+		return f.Load()
639
+	}
640
+	if err := f.Validate(); err != nil {
641
+		return "", nil, nil, err
642
+	}
643
+
644
+	addCap(&f.constraints, pb.CapFileBase)
645
+
646
+	pfo := &pb.FileOp{}
647
+
648
+	pop, md := MarshalConstraints(c, &f.constraints)
649
+	pop.Op = &pb.Op_File{
650
+		File: pfo,
651
+	}
652
+
653
+	state := newMarshalState()
654
+	_, err := state.add(f.action, c)
655
+	if err != nil {
656
+		return "", nil, nil, err
657
+	}
658
+	pop.Inputs = state.inputs
659
+
660
+	for i, st := range state.actions {
661
+		output := pb.OutputIndex(-1)
662
+		if i+1 == len(state.actions) {
663
+			output = 0
664
+		}
665
+
666
+		var parent string
667
+		if st.fa.state != nil {
668
+			parent = st.fa.state.GetDir()
669
+		}
670
+
671
+		pfo.Actions = append(pfo.Actions, &pb.FileAction{
672
+			Input:          getIndex(st.input, len(state.inputs), st.inputRelative),
673
+			SecondaryInput: getIndex(st.input2, len(state.inputs), st.input2Relative),
674
+			Output:         output,
675
+			Action:         st.action.toProtoAction(parent, st.base),
676
+		})
677
+	}
678
+
679
+	dt, err := pop.Marshal()
680
+	if err != nil {
681
+		return "", nil, nil, err
682
+	}
683
+	f.Store(dt, md, c)
684
+	return f.Load()
685
+}
686
+
687
+func normalizePath(parent, p string, keepSlash bool) string {
688
+	origPath := p
689
+	p = path.Clean(p)
690
+	if !path.IsAbs(p) {
691
+		p = path.Join("/", parent, p)
692
+	}
693
+	if keepSlash {
694
+		if strings.HasSuffix(origPath, "/") && !strings.HasSuffix(p, "/") {
695
+			p += "/"
696
+		} else if strings.HasSuffix(origPath, "/.") {
697
+			if p != "/" {
698
+				p += "/"
699
+			}
700
+			p += "."
701
+		}
702
+	}
703
+	return p
704
+}
705
+
706
+func (f *FileOp) Output() Output {
707
+	return f.output
708
+}
709
+
710
+func (f *FileOp) Inputs() (inputs []Output) {
711
+	mm := map[Output]struct{}{}
712
+
713
+	f.action.allOutputs(mm)
714
+
715
+	for o := range mm {
716
+		inputs = append(inputs, o)
717
+	}
718
+	return inputs
719
+}
720
+
721
+func getIndex(input pb.InputIndex, len int, relative *int) pb.InputIndex {
722
+	if relative != nil {
723
+		return pb.InputIndex(len + *relative)
724
+	}
725
+	return input
726
+}
... ...
@@ -229,6 +229,15 @@ func (s State) Run(ro ...RunOption) ExecState {
229 229
 	}
230 230
 }
231 231
 
232
+func (s State) File(a *FileAction, opts ...ConstraintsOpt) State {
233
+	var c Constraints
234
+	for _, o := range opts {
235
+		o.SetConstraintsOption(&c)
236
+	}
237
+
238
+	return s.WithOutput(NewFileOp(s, a, c).Output())
239
+}
240
+
232 241
 func (s State) AddEnv(key, value string) State {
233 242
 	return s.AddEnvf(key, value)
234 243
 }
... ...
@@ -295,6 +304,8 @@ func (s State) AddExtraHost(host string, ip net.IP) State {
295 295
 	return extraHost(host, ip)(s)
296 296
 }
297 297
 
298
+func (s State) isFileOpCopyInput() {}
299
+
298 300
 type output struct {
299 301
 	vertex   Vertex
300 302
 	getIndex func() (pb.OutputIndex, error)
... ...
@@ -298,7 +298,7 @@ func prepareSyncedDirs(def *llb.Definition, localDirs map[string]string) ([]file
298 298
 			return nil, errors.Errorf("%s not a directory", d)
299 299
 		}
300 300
 	}
301
-	resetUIDAndGID := func(st *fstypes.Stat) bool {
301
+	resetUIDAndGID := func(p string, st *fstypes.Stat) bool {
302 302
 		st.Uid = 0
303 303
 		st.Gid = 0
304 304
 		return true
... ...
@@ -151,6 +151,10 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
151 151
 			switch cmd.(type) {
152 152
 			case *instructions.AddCommand, *instructions.CopyCommand, *instructions.RunCommand:
153 153
 				total++
154
+			case *instructions.WorkdirCommand:
155
+				if useFileOp(opt.BuildArgs, opt.LLBCaps) {
156
+					total++
157
+				}
154 158
 			}
155 159
 		}
156 160
 		ds.cmdTotal = total
... ...
@@ -307,7 +311,7 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
307 307
 			d.state = d.state.AddEnv(k, v)
308 308
 		}
309 309
 		if d.image.Config.WorkingDir != "" {
310
-			if err = dispatchWorkdir(d, &instructions.WorkdirCommand{Path: d.image.Config.WorkingDir}, false); err != nil {
310
+			if err = dispatchWorkdir(d, &instructions.WorkdirCommand{Path: d.image.Config.WorkingDir}, false, nil); err != nil {
311 311
 				return nil, nil, err
312 312
 			}
313 313
 		}
... ...
@@ -468,9 +472,9 @@ func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error {
468 468
 	case *instructions.RunCommand:
469 469
 		err = dispatchRun(d, c, opt.proxyEnv, cmd.sources, opt)
470 470
 	case *instructions.WorkdirCommand:
471
-		err = dispatchWorkdir(d, c, true)
471
+		err = dispatchWorkdir(d, c, true, &opt)
472 472
 	case *instructions.AddCommand:
473
-		err = dispatchCopy(d, c.SourcesAndDest, opt.buildContext, true, c, "", opt)
473
+		err = dispatchCopy(d, c.SourcesAndDest, opt.buildContext, true, c, c.Chown, opt)
474 474
 		if err == nil {
475 475
 			for _, src := range c.Sources() {
476 476
 				if !strings.HasPrefix(src, "http://") && !strings.HasPrefix(src, "https://") {
... ...
@@ -648,7 +652,7 @@ func dispatchRun(d *dispatchState, c *instructions.RunCommand, proxy *llb.ProxyE
648 648
 	return commitToHistory(&d.image, "RUN "+runCommandString(args, d.buildArgs), true, &d.state)
649 649
 }
650 650
 
651
-func dispatchWorkdir(d *dispatchState, c *instructions.WorkdirCommand, commit bool) error {
651
+func dispatchWorkdir(d *dispatchState, c *instructions.WorkdirCommand, commit bool, opt *dispatchOpt) error {
652 652
 	d.state = d.state.Dir(c.Path)
653 653
 	wd := c.Path
654 654
 	if !path.IsAbs(c.Path) {
... ...
@@ -656,13 +660,115 @@ func dispatchWorkdir(d *dispatchState, c *instructions.WorkdirCommand, commit bo
656 656
 	}
657 657
 	d.image.Config.WorkingDir = wd
658 658
 	if commit {
659
-		return commitToHistory(&d.image, "WORKDIR "+wd, false, nil)
659
+		withLayer := false
660
+		if wd != "/" && opt != nil && useFileOp(opt.buildArgValues, opt.llbCaps) {
661
+			mkdirOpt := []llb.MkdirOption{llb.WithParents(true)}
662
+			if user := d.image.Config.User; user != "" {
663
+				mkdirOpt = append(mkdirOpt, llb.WithUser(user))
664
+			}
665
+			platform := opt.targetPlatform
666
+			if d.platform != nil {
667
+				platform = *d.platform
668
+			}
669
+			d.state = d.state.File(llb.Mkdir(wd, 0755, mkdirOpt...), llb.WithCustomName(prefixCommand(d, uppercaseCmd(processCmdEnv(opt.shlex, c.String(), d.state.Env())), d.prefixPlatform, &platform)))
670
+			withLayer = true
671
+		}
672
+		return commitToHistory(&d.image, "WORKDIR "+wd, withLayer, nil)
660 673
 	}
661 674
 	return nil
662 675
 }
663 676
 
677
+func dispatchCopyFileOp(d *dispatchState, c instructions.SourcesAndDest, sourceState llb.State, isAddCommand bool, cmdToPrint fmt.Stringer, chown string, opt dispatchOpt) error {
678
+	dest := path.Join("/", pathRelativeToWorkingDir(d.state, c.Dest()))
679
+	if c.Dest() == "." || c.Dest() == "" || c.Dest()[len(c.Dest())-1] == filepath.Separator {
680
+		dest += string(filepath.Separator)
681
+	}
682
+
683
+	var copyOpt []llb.CopyOption
684
+
685
+	if chown != "" {
686
+		copyOpt = append(copyOpt, llb.WithUser(chown))
687
+	}
688
+
689
+	commitMessage := bytes.NewBufferString("")
690
+	if isAddCommand {
691
+		commitMessage.WriteString("ADD")
692
+	} else {
693
+		commitMessage.WriteString("COPY")
694
+	}
695
+
696
+	var a *llb.FileAction
697
+
698
+	for _, src := range c.Sources() {
699
+		commitMessage.WriteString(" " + src)
700
+		if strings.HasPrefix(src, "http://") || strings.HasPrefix(src, "https://") {
701
+			if !isAddCommand {
702
+				return errors.New("source can't be a URL for COPY")
703
+			}
704
+
705
+			// Resources from remote URLs are not decompressed.
706
+			// https://docs.docker.com/engine/reference/builder/#add
707
+			//
708
+			// Note: mixing up remote archives and local archives in a single ADD instruction
709
+			// would result in undefined behavior: https://github.com/moby/buildkit/pull/387#discussion_r189494717
710
+			u, err := url.Parse(src)
711
+			f := "__unnamed__"
712
+			if err == nil {
713
+				if base := path.Base(u.Path); base != "." && base != "/" {
714
+					f = base
715
+				}
716
+			}
717
+
718
+			st := llb.HTTP(src, llb.Filename(f), dfCmd(c))
719
+
720
+			opts := append([]llb.CopyOption{&llb.CopyInfo{
721
+				CreateDestPath: true,
722
+			}}, copyOpt...)
723
+
724
+			if a == nil {
725
+				a = llb.Copy(st, f, dest, opts...)
726
+			} else {
727
+				a = a.Copy(st, f, dest, opts...)
728
+			}
729
+		} else {
730
+			opts := append([]llb.CopyOption{&llb.CopyInfo{
731
+				FollowSymlinks:      true,
732
+				CopyDirContentsOnly: true,
733
+				AttemptUnpack:       isAddCommand,
734
+				CreateDestPath:      true,
735
+				AllowWildcard:       true,
736
+				AllowEmptyWildcard:  true,
737
+			}}, copyOpt...)
738
+
739
+			if a == nil {
740
+				a = llb.Copy(sourceState, src, dest, opts...)
741
+			} else {
742
+				a = a.Copy(sourceState, src, dest, opts...)
743
+			}
744
+		}
745
+	}
746
+
747
+	commitMessage.WriteString(" " + c.Dest())
748
+
749
+	platform := opt.targetPlatform
750
+	if d.platform != nil {
751
+		platform = *d.platform
752
+	}
753
+
754
+	fileOpt := []llb.ConstraintsOpt{llb.WithCustomName(prefixCommand(d, uppercaseCmd(processCmdEnv(opt.shlex, cmdToPrint.String(), d.state.Env())), d.prefixPlatform, &platform))}
755
+	if d.ignoreCache {
756
+		fileOpt = append(fileOpt, llb.IgnoreCache)
757
+	}
758
+
759
+	d.state = d.state.File(a, fileOpt...)
760
+	return commitToHistory(&d.image, commitMessage.String(), true, &d.state)
761
+}
762
+
664 763
 func dispatchCopy(d *dispatchState, c instructions.SourcesAndDest, sourceState llb.State, isAddCommand bool, cmdToPrint fmt.Stringer, chown string, opt dispatchOpt) error {
665
-	// TODO: this should use CopyOp instead. Current implementation is inefficient
764
+	if useFileOp(opt.buildArgValues, opt.llbCaps) {
765
+		return dispatchCopyFileOp(d, c, sourceState, isAddCommand, cmdToPrint, chown, opt)
766
+	}
767
+
666 768
 	img := llb.Image(opt.copyImage, llb.MarkImageInternal, llb.Platform(opt.buildPlatforms[0]), WithInternalName("helper image for file operations"))
667 769
 
668 770
 	dest := path.Join(".", pathRelativeToWorkingDir(d.state, c.Dest()))
... ...
@@ -1176,3 +1282,13 @@ func prefixCommand(ds *dispatchState, str string, prefixPlatform bool, platform
1176 1176
 	out += fmt.Sprintf("%d/%d] ", ds.cmdIndex, ds.cmdTotal)
1177 1177
 	return out + str
1178 1178
 }
1179
+
1180
+func useFileOp(args map[string]string, caps *apicaps.CapSet) bool {
1181
+	enabled := true
1182
+	if v, ok := args["BUILDKIT_DISABLE_FILEOP"]; ok {
1183
+		if b, err := strconv.ParseBool(v); err == nil {
1184
+			enabled = !b
1185
+		}
1186
+	}
1187
+	return enabled && caps != nil && caps.Supports(pb.CapFileBase) == nil
1188
+}
... ...
@@ -82,10 +82,10 @@ func syncTargetDiffCopy(ds grpc.Stream, dest string) error {
82 82
 	}
83 83
 	return fsutil.Receive(ds.Context(), ds, dest, fsutil.ReceiveOpt{
84 84
 		Merge: true,
85
-		Filter: func() func(*fstypes.Stat) bool {
85
+		Filter: func() func(string, *fstypes.Stat) bool {
86 86
 			uid := os.Getuid()
87 87
 			gid := os.Getgid()
88
-			return func(st *fstypes.Stat) bool {
88
+			return func(p string, st *fstypes.Stat) bool {
89 89
 				st.Uid = uint32(uid)
90 90
 				st.Gid = uint32(gid)
91 91
 				return true
... ...
@@ -35,7 +35,7 @@ type SyncedDir struct {
35 35
 	Name     string
36 36
 	Dir      string
37 37
 	Excludes []string
38
-	Map      func(*fstypes.Stat) bool
38
+	Map      func(string, *fstypes.Stat) bool
39 39
 }
40 40
 
41 41
 // NewFSSyncProvider creates a new provider for sending files from client
42 42
new file mode 100644
... ...
@@ -0,0 +1,266 @@
0
+package file
1
+
2
+import (
3
+	"context"
4
+	"io/ioutil"
5
+	"log"
6
+	"os"
7
+	"path/filepath"
8
+	"strings"
9
+	"time"
10
+
11
+	"github.com/containerd/continuity/fs"
12
+	"github.com/moby/buildkit/snapshot"
13
+	"github.com/moby/buildkit/solver/llbsolver/ops/fileoptypes"
14
+	"github.com/moby/buildkit/solver/pb"
15
+	"github.com/pkg/errors"
16
+	copy "github.com/tonistiigi/fsutil/copy"
17
+)
18
+
19
+func timestampToTime(ts int64) *time.Time {
20
+	if ts == -1 {
21
+		return nil
22
+	}
23
+	tm := time.Unix(ts/1e9, ts%1e9)
24
+	return &tm
25
+}
26
+
27
+func mkdir(ctx context.Context, d string, action pb.FileActionMkDir, user *copy.ChownOpt) error {
28
+	p, err := fs.RootPath(d, filepath.Join(filepath.Join("/", action.Path)))
29
+	if err != nil {
30
+		return err
31
+	}
32
+
33
+	if action.MakeParents {
34
+		if err := copy.MkdirAll(p, os.FileMode(action.Mode)&0777, user, timestampToTime(action.Timestamp)); err != nil {
35
+			return err
36
+		}
37
+	} else {
38
+		if err := os.Mkdir(p, os.FileMode(action.Mode)&0777); err != nil {
39
+			if os.IsExist(err) {
40
+				return nil
41
+			}
42
+			return err
43
+		}
44
+		if err := copy.Chown(p, user); err != nil {
45
+			return err
46
+		}
47
+		if err := copy.Utimes(p, timestampToTime(action.Timestamp)); err != nil {
48
+			return err
49
+		}
50
+	}
51
+
52
+	return nil
53
+}
54
+
55
+func mkfile(ctx context.Context, d string, action pb.FileActionMkFile, user *copy.ChownOpt) error {
56
+	p, err := fs.RootPath(d, filepath.Join(filepath.Join("/", action.Path)))
57
+	if err != nil {
58
+		return err
59
+	}
60
+
61
+	if err := ioutil.WriteFile(p, action.Data, os.FileMode(action.Mode)&0777); err != nil {
62
+		return err
63
+	}
64
+
65
+	if err := copy.Chown(p, user); err != nil {
66
+		return err
67
+	}
68
+
69
+	if err := copy.Utimes(p, timestampToTime(action.Timestamp)); err != nil {
70
+		return err
71
+	}
72
+
73
+	return nil
74
+}
75
+
76
+func rm(ctx context.Context, d string, action pb.FileActionRm) error {
77
+	p, err := fs.RootPath(d, filepath.Join(filepath.Join("/", action.Path)))
78
+	if err != nil {
79
+		return err
80
+	}
81
+
82
+	if err := os.RemoveAll(p); err != nil {
83
+		if os.IsNotExist(errors.Cause(err)) && action.AllowNotFound {
84
+			return nil
85
+		}
86
+		return err
87
+	}
88
+
89
+	return nil
90
+}
91
+
92
+func docopy(ctx context.Context, src, dest string, action pb.FileActionCopy, u *copy.ChownOpt) error {
93
+	srcPath := cleanPath(action.Src)
94
+	destPath := cleanPath(action.Dest)
95
+
96
+	if !action.CreateDestPath {
97
+		p, err := fs.RootPath(dest, filepath.Join(filepath.Join("/", action.Dest)))
98
+		if err != nil {
99
+			return err
100
+		}
101
+		if _, err := os.Lstat(filepath.Dir(p)); err != nil {
102
+			return errors.Wrapf(err, "failed to stat %s", action.Dest)
103
+		}
104
+	}
105
+
106
+	xattrErrorHandler := func(dst, src, key string, err error) error {
107
+		log.Println(err)
108
+		return nil
109
+	}
110
+
111
+	opt := []copy.Opt{
112
+		func(ci *copy.CopyInfo) {
113
+			ci.Chown = u
114
+			ci.Utime = timestampToTime(action.Timestamp)
115
+			if m := int(action.Mode); m != -1 {
116
+				ci.Mode = &m
117
+			}
118
+			ci.CopyDirContents = action.DirCopyContents
119
+			ci.FollowLinks = action.FollowSymlink
120
+		},
121
+		copy.WithXAttrErrorHandler(xattrErrorHandler),
122
+	}
123
+
124
+	if !action.AllowWildcard {
125
+		if action.AttemptUnpackDockerCompatibility {
126
+			if ok, err := unpack(ctx, src, srcPath, dest, destPath, u, timestampToTime(action.Timestamp)); err != nil {
127
+				return err
128
+			} else if ok {
129
+				return nil
130
+			}
131
+		}
132
+		return copy.Copy(ctx, src, srcPath, dest, destPath, opt...)
133
+	}
134
+
135
+	m, err := copy.ResolveWildcards(src, srcPath, action.FollowSymlink)
136
+	if err != nil {
137
+		return err
138
+	}
139
+
140
+	if len(m) == 0 {
141
+		if action.AllowEmptyWildcard {
142
+			return nil
143
+		}
144
+		return errors.Errorf("%s not found", srcPath)
145
+	}
146
+
147
+	for _, s := range m {
148
+		if action.AttemptUnpackDockerCompatibility {
149
+			if ok, err := unpack(ctx, src, s, dest, destPath, u, timestampToTime(action.Timestamp)); err != nil {
150
+				return err
151
+			} else if ok {
152
+				continue
153
+			}
154
+		}
155
+		if err := copy.Copy(ctx, src, s, dest, destPath, opt...); err != nil {
156
+			return err
157
+		}
158
+	}
159
+
160
+	return nil
161
+}
162
+
163
+func cleanPath(s string) string {
164
+	s2 := filepath.Join("/", s)
165
+	if strings.HasSuffix(s, "/.") {
166
+		if s2 != "/" {
167
+			s2 += "/"
168
+		}
169
+		s2 += "."
170
+	} else if strings.HasSuffix(s, "/") && s2 != "/" {
171
+		s2 += "/"
172
+	}
173
+	return s2
174
+}
175
+
176
+type Backend struct {
177
+}
178
+
179
+func (fb *Backend) Mkdir(ctx context.Context, m, user, group fileoptypes.Mount, action pb.FileActionMkDir) error {
180
+	mnt, ok := m.(*Mount)
181
+	if !ok {
182
+		return errors.Errorf("invalid mount type %T", m)
183
+	}
184
+
185
+	lm := snapshot.LocalMounter(mnt.m)
186
+	dir, err := lm.Mount()
187
+	if err != nil {
188
+		return err
189
+	}
190
+	defer lm.Unmount()
191
+
192
+	u, err := readUser(action.Owner, user, group)
193
+	if err != nil {
194
+		return err
195
+	}
196
+
197
+	return mkdir(ctx, dir, action, u)
198
+}
199
+
200
+func (fb *Backend) Mkfile(ctx context.Context, m, user, group fileoptypes.Mount, action pb.FileActionMkFile) error {
201
+	mnt, ok := m.(*Mount)
202
+	if !ok {
203
+		return errors.Errorf("invalid mount type %T", m)
204
+	}
205
+
206
+	lm := snapshot.LocalMounter(mnt.m)
207
+	dir, err := lm.Mount()
208
+	if err != nil {
209
+		return err
210
+	}
211
+	defer lm.Unmount()
212
+
213
+	u, err := readUser(action.Owner, user, group)
214
+	if err != nil {
215
+		return err
216
+	}
217
+
218
+	return mkfile(ctx, dir, action, u)
219
+}
220
+func (fb *Backend) Rm(ctx context.Context, m fileoptypes.Mount, action pb.FileActionRm) error {
221
+	mnt, ok := m.(*Mount)
222
+	if !ok {
223
+		return errors.Errorf("invalid mount type %T", m)
224
+	}
225
+
226
+	lm := snapshot.LocalMounter(mnt.m)
227
+	dir, err := lm.Mount()
228
+	if err != nil {
229
+		return err
230
+	}
231
+	defer lm.Unmount()
232
+
233
+	return rm(ctx, dir, action)
234
+}
235
+func (fb *Backend) Copy(ctx context.Context, m1, m2, user, group fileoptypes.Mount, action pb.FileActionCopy) error {
236
+	mnt1, ok := m1.(*Mount)
237
+	if !ok {
238
+		return errors.Errorf("invalid mount type %T", m1)
239
+	}
240
+	mnt2, ok := m2.(*Mount)
241
+	if !ok {
242
+		return errors.Errorf("invalid mount type %T", m2)
243
+	}
244
+
245
+	lm := snapshot.LocalMounter(mnt1.m)
246
+	src, err := lm.Mount()
247
+	if err != nil {
248
+		return err
249
+	}
250
+	defer lm.Unmount()
251
+
252
+	lm2 := snapshot.LocalMounter(mnt2.m)
253
+	dest, err := lm2.Mount()
254
+	if err != nil {
255
+		return err
256
+	}
257
+	defer lm2.Unmount()
258
+
259
+	u, err := readUser(action.Owner, user, group)
260
+	if err != nil {
261
+		return err
262
+	}
263
+
264
+	return docopy(ctx, src, dest, action, u)
265
+}
0 266
new file mode 100644
... ...
@@ -0,0 +1,71 @@
0
+package file
1
+
2
+import (
3
+	"context"
4
+
5
+	"github.com/moby/buildkit/cache"
6
+	"github.com/moby/buildkit/snapshot"
7
+	"github.com/moby/buildkit/solver/llbsolver/ops/fileoptypes"
8
+	"github.com/pkg/errors"
9
+)
10
+
11
+func NewRefManager(cm cache.Manager) *RefManager {
12
+	return &RefManager{cm: cm}
13
+}
14
+
15
+type RefManager struct {
16
+	cm cache.Manager
17
+}
18
+
19
+func (rm *RefManager) Prepare(ctx context.Context, ref fileoptypes.Ref, readonly bool) (fileoptypes.Mount, error) {
20
+	ir, ok := ref.(cache.ImmutableRef)
21
+	if !ok && ref != nil {
22
+		return nil, errors.Errorf("invalid ref type: %T", ref)
23
+	}
24
+
25
+	if ir != nil && readonly {
26
+		m, err := ir.Mount(ctx, readonly)
27
+		if err != nil {
28
+			return nil, err
29
+		}
30
+		return &Mount{m: m}, nil
31
+	}
32
+
33
+	mr, err := rm.cm.New(ctx, ir, cache.WithDescription("fileop target"), cache.CachePolicyRetain)
34
+	if err != nil {
35
+		return nil, err
36
+	}
37
+	m, err := mr.Mount(ctx, readonly)
38
+	if err != nil {
39
+		return nil, err
40
+	}
41
+	return &Mount{m: m, mr: mr}, nil
42
+}
43
+
44
+func (rm *RefManager) Commit(ctx context.Context, mount fileoptypes.Mount) (fileoptypes.Ref, error) {
45
+	m, ok := mount.(*Mount)
46
+	if !ok {
47
+		return nil, errors.Errorf("invalid mount type %T", mount)
48
+	}
49
+	if err := m.m.Release(); err != nil {
50
+		return nil, err
51
+	}
52
+	if m.mr == nil {
53
+		return nil, errors.Errorf("invalid mount without active ref for commit")
54
+	}
55
+	return m.mr.Commit(ctx)
56
+}
57
+
58
+type Mount struct {
59
+	m  snapshot.Mountable
60
+	mr cache.MutableRef
61
+}
62
+
63
+func (m *Mount) Release(ctx context.Context) error {
64
+	m.m.Release()
65
+	if m.mr != nil {
66
+		return m.mr.Release(ctx)
67
+	}
68
+	return nil
69
+}
70
+func (m *Mount) IsFileOpMount() {}
0 71
new file mode 100644
... ...
@@ -0,0 +1,61 @@
0
+package file
1
+
2
+import (
3
+	"archive/tar"
4
+	"context"
5
+	"os"
6
+	"time"
7
+
8
+	"github.com/containerd/continuity/fs"
9
+	"github.com/docker/docker/pkg/archive"
10
+	"github.com/docker/docker/pkg/chrootarchive"
11
+	copy "github.com/tonistiigi/fsutil/copy"
12
+)
13
+
14
+func unpack(ctx context.Context, srcRoot string, src string, destRoot string, dest string, user *copy.ChownOpt, tm *time.Time) (bool, error) {
15
+	src, err := fs.RootPath(srcRoot, src)
16
+	if err != nil {
17
+		return false, err
18
+	}
19
+	if !isArchivePath(src) {
20
+		return false, nil
21
+	}
22
+
23
+	dest, err = fs.RootPath(destRoot, dest)
24
+	if err != nil {
25
+		return false, err
26
+	}
27
+	if err := copy.MkdirAll(dest, 0755, user, tm); err != nil {
28
+		return false, err
29
+	}
30
+
31
+	file, err := os.Open(src)
32
+	if err != nil {
33
+		return false, err
34
+	}
35
+	defer file.Close()
36
+
37
+	return true, chrootarchive.Untar(file, dest, nil)
38
+}
39
+
40
+func isArchivePath(path string) bool {
41
+	fi, err := os.Lstat(path)
42
+	if err != nil {
43
+		return false
44
+	}
45
+	if fi.Mode()&os.ModeType != 0 {
46
+		return false
47
+	}
48
+	file, err := os.Open(path)
49
+	if err != nil {
50
+		return false
51
+	}
52
+	defer file.Close()
53
+	rdr, err := archive.DecompressStream(file)
54
+	if err != nil {
55
+		return false
56
+	}
57
+	r := tar.NewReader(rdr)
58
+	_, err = r.Next()
59
+	return err == nil
60
+}
0 61
new file mode 100644
... ...
@@ -0,0 +1,119 @@
0
+package file
1
+
2
+import (
3
+	"os"
4
+
5
+	"github.com/containerd/continuity/fs"
6
+	"github.com/moby/buildkit/snapshot"
7
+	"github.com/moby/buildkit/solver/llbsolver/ops/fileoptypes"
8
+	"github.com/moby/buildkit/solver/pb"
9
+	"github.com/opencontainers/runc/libcontainer/user"
10
+	"github.com/pkg/errors"
11
+	copy "github.com/tonistiigi/fsutil/copy"
12
+)
13
+
14
+func readUser(chopt *pb.ChownOpt, mu, mg fileoptypes.Mount) (*copy.ChownOpt, error) {
15
+	if chopt == nil {
16
+		return nil, nil
17
+	}
18
+	var us copy.ChownOpt
19
+	if chopt.User != nil {
20
+		switch u := chopt.User.User.(type) {
21
+		case *pb.UserOpt_ByName:
22
+			if mu == nil {
23
+				return nil, errors.Errorf("invalid missing user mount")
24
+			}
25
+			mmu, ok := mu.(*Mount)
26
+			if !ok {
27
+				return nil, errors.Errorf("invalid mount type %T", mu)
28
+			}
29
+			lm := snapshot.LocalMounter(mmu.m)
30
+			dir, err := lm.Mount()
31
+			if err != nil {
32
+				return nil, err
33
+			}
34
+			defer lm.Unmount()
35
+
36
+			passwdPath, err := user.GetPasswdPath()
37
+			if err != nil {
38
+				return nil, err
39
+			}
40
+
41
+			passwdPath, err = fs.RootPath(dir, passwdPath)
42
+			if err != nil {
43
+				return nil, err
44
+			}
45
+
46
+			ufile, err := os.Open(passwdPath)
47
+			if err != nil {
48
+				return nil, err
49
+			}
50
+			defer ufile.Close()
51
+
52
+			users, err := user.ParsePasswdFilter(ufile, func(uu user.User) bool {
53
+				return uu.Name == u.ByName.Name
54
+			})
55
+			if err != nil {
56
+				return nil, err
57
+			}
58
+
59
+			if len(users) > 0 {
60
+				us.Uid = users[0].Uid
61
+				us.Gid = users[0].Gid
62
+			}
63
+		case *pb.UserOpt_ByID:
64
+			us.Uid = int(u.ByID)
65
+			us.Gid = int(u.ByID)
66
+		}
67
+	}
68
+
69
+	if chopt.Group != nil {
70
+		switch u := chopt.Group.User.(type) {
71
+		case *pb.UserOpt_ByName:
72
+			if mg == nil {
73
+				return nil, errors.Errorf("invalid missing group mount")
74
+			}
75
+			mmg, ok := mg.(*Mount)
76
+			if !ok {
77
+				return nil, errors.Errorf("invalid mount type %T", mg)
78
+			}
79
+			lm := snapshot.LocalMounter(mmg.m)
80
+			dir, err := lm.Mount()
81
+			if err != nil {
82
+				return nil, err
83
+			}
84
+			defer lm.Unmount()
85
+
86
+			groupPath, err := user.GetGroupPath()
87
+			if err != nil {
88
+				return nil, err
89
+			}
90
+
91
+			groupPath, err = fs.RootPath(dir, groupPath)
92
+			if err != nil {
93
+				return nil, err
94
+			}
95
+
96
+			gfile, err := os.Open(groupPath)
97
+			if err != nil {
98
+				return nil, err
99
+			}
100
+			defer gfile.Close()
101
+
102
+			groups, err := user.ParseGroupFilter(gfile, func(g user.Group) bool {
103
+				return g.Name == u.ByName.Name
104
+			})
105
+			if err != nil {
106
+				return nil, err
107
+			}
108
+
109
+			if len(groups) > 0 {
110
+				us.Gid = groups[0].Gid
111
+			}
112
+		case *pb.UserOpt_ByID:
113
+			us.Gid = int(u.ByID)
114
+		}
115
+	}
116
+
117
+	return &us, nil
118
+}
0 119
new file mode 100644
... ...
@@ -0,0 +1,14 @@
0
+// +build !linux
1
+
2
+package file
3
+
4
+import (
5
+	"github.com/moby/buildkit/solver/llbsolver/ops/fileoptypes"
6
+	"github.com/moby/buildkit/solver/pb"
7
+	"github.com/pkg/errors"
8
+	copy "github.com/tonistiigi/fsutil/copy"
9
+)
10
+
11
+func readUser(chopt *pb.ChownOpt, mu, mg fileoptypes.Mount) (*copy.ChownOpt, error) {
12
+	return nil, errors.New("only implemented in linux")
13
+}
... ...
@@ -149,7 +149,7 @@ func (e *execOp) CacheMap(ctx context.Context, index int) (*solver.CacheMap, boo
149 149
 			cm.Deps[i].Selector = digest.FromBytes(bytes.Join(dgsts, []byte{0}))
150 150
 		}
151 151
 		if !dep.NoContentBasedHash {
152
-			cm.Deps[i].ComputeDigestFunc = llbsolver.NewContentHashFunc(dedupePaths(dep.Selectors))
152
+			cm.Deps[i].ComputeDigestFunc = llbsolver.NewContentHashFunc(toSelectors(dedupePaths(dep.Selectors)))
153 153
 		}
154 154
 	}
155 155
 
... ...
@@ -180,6 +180,14 @@ func dedupePaths(inp []string) []string {
180 180
 	return paths
181 181
 }
182 182
 
183
+func toSelectors(p []string) []llbsolver.Selector {
184
+	sel := make([]llbsolver.Selector, 0, len(p))
185
+	for _, p := range p {
186
+		sel = append(sel, llbsolver.Selector{Path: p, FollowLinks: true})
187
+	}
188
+	return sel
189
+}
190
+
183 191
 type dep struct {
184 192
 	Selectors          []string
185 193
 	NoContentBasedHash bool
186 194
new file mode 100644
... ...
@@ -0,0 +1,580 @@
0
+package ops
1
+
2
+import (
3
+	"bytes"
4
+	"context"
5
+	"encoding/json"
6
+	"fmt"
7
+	"path"
8
+	"runtime"
9
+	"sort"
10
+	"sync"
11
+
12
+	"github.com/moby/buildkit/cache"
13
+	"github.com/moby/buildkit/cache/metadata"
14
+	"github.com/moby/buildkit/solver"
15
+	"github.com/moby/buildkit/solver/llbsolver"
16
+	"github.com/moby/buildkit/solver/llbsolver/file"
17
+	"github.com/moby/buildkit/solver/llbsolver/ops/fileoptypes"
18
+	"github.com/moby/buildkit/solver/pb"
19
+	"github.com/moby/buildkit/util/flightcontrol"
20
+	"github.com/moby/buildkit/worker"
21
+	digest "github.com/opencontainers/go-digest"
22
+	"github.com/pkg/errors"
23
+	"golang.org/x/sync/errgroup"
24
+)
25
+
26
+const fileCacheType = "buildkit.file.v0"
27
+
28
+type fileOp struct {
29
+	op        *pb.FileOp
30
+	md        *metadata.Store
31
+	w         worker.Worker
32
+	solver    *FileOpSolver
33
+	numInputs int
34
+}
35
+
36
+func NewFileOp(v solver.Vertex, op *pb.Op_File, cm cache.Manager, md *metadata.Store, w worker.Worker) (solver.Op, error) {
37
+	return &fileOp{
38
+		op:        op.File,
39
+		md:        md,
40
+		numInputs: len(v.Inputs()),
41
+		w:         w,
42
+		solver:    NewFileOpSolver(&file.Backend{}, file.NewRefManager(cm)),
43
+	}, nil
44
+}
45
+
46
+func (f *fileOp) CacheMap(ctx context.Context, index int) (*solver.CacheMap, bool, error) {
47
+	selectors := map[int]map[llbsolver.Selector]struct{}{}
48
+	invalidSelectors := map[int]struct{}{}
49
+
50
+	actions := make([][]byte, 0, len(f.op.Actions))
51
+
52
+	markInvalid := func(idx pb.InputIndex) {
53
+		if idx != -1 {
54
+			invalidSelectors[int(idx)] = struct{}{}
55
+		}
56
+	}
57
+
58
+	for _, action := range f.op.Actions {
59
+		var dt []byte
60
+		var err error
61
+		switch a := action.Action.(type) {
62
+		case *pb.FileAction_Mkdir:
63
+			p := *a.Mkdir
64
+			markInvalid(action.Input)
65
+			processOwner(p.Owner, selectors)
66
+			dt, err = json.Marshal(p)
67
+			if err != nil {
68
+				return nil, false, err
69
+			}
70
+		case *pb.FileAction_Mkfile:
71
+			p := *a.Mkfile
72
+			markInvalid(action.Input)
73
+			processOwner(p.Owner, selectors)
74
+			dt, err = json.Marshal(p)
75
+			if err != nil {
76
+				return nil, false, err
77
+			}
78
+		case *pb.FileAction_Rm:
79
+			p := *a.Rm
80
+			markInvalid(action.Input)
81
+			dt, err = json.Marshal(p)
82
+			if err != nil {
83
+				return nil, false, err
84
+			}
85
+		case *pb.FileAction_Copy:
86
+			p := *a.Copy
87
+			markInvalid(action.Input)
88
+			processOwner(p.Owner, selectors)
89
+			if action.SecondaryInput != -1 && int(action.SecondaryInput) < f.numInputs {
90
+				addSelector(selectors, int(action.SecondaryInput), p.Src, p.AllowWildcard, p.FollowSymlink)
91
+				p.Src = path.Base(p.Src)
92
+			}
93
+			dt, err = json.Marshal(p)
94
+			if err != nil {
95
+				return nil, false, err
96
+			}
97
+		}
98
+
99
+		actions = append(actions, dt)
100
+	}
101
+
102
+	dt, err := json.Marshal(struct {
103
+		Type    string
104
+		Actions [][]byte
105
+	}{
106
+		Type:    fileCacheType,
107
+		Actions: actions,
108
+	})
109
+	if err != nil {
110
+		return nil, false, err
111
+	}
112
+
113
+	cm := &solver.CacheMap{
114
+		Digest: digest.FromBytes(dt),
115
+		Deps: make([]struct {
116
+			Selector          digest.Digest
117
+			ComputeDigestFunc solver.ResultBasedCacheFunc
118
+		}, f.numInputs),
119
+	}
120
+
121
+	for idx, m := range selectors {
122
+		if _, ok := invalidSelectors[idx]; ok {
123
+			continue
124
+		}
125
+		dgsts := make([][]byte, 0, len(m))
126
+		for k := range m {
127
+			dgsts = append(dgsts, []byte(k.Path))
128
+		}
129
+		sort.Slice(dgsts, func(i, j int) bool {
130
+			return bytes.Compare(dgsts[i], dgsts[j]) > 0
131
+		})
132
+		cm.Deps[idx].Selector = digest.FromBytes(bytes.Join(dgsts, []byte{0}))
133
+
134
+		cm.Deps[idx].ComputeDigestFunc = llbsolver.NewContentHashFunc(dedupeSelectors(m))
135
+	}
136
+
137
+	return cm, true, nil
138
+}
139
+
140
+func (f *fileOp) Exec(ctx context.Context, inputs []solver.Result) ([]solver.Result, error) {
141
+	inpRefs := make([]fileoptypes.Ref, 0, len(inputs))
142
+	for _, inp := range inputs {
143
+		workerRef, ok := inp.Sys().(*worker.WorkerRef)
144
+		if !ok {
145
+			return nil, errors.Errorf("invalid reference for exec %T", inp.Sys())
146
+		}
147
+		inpRefs = append(inpRefs, workerRef.ImmutableRef)
148
+	}
149
+
150
+	outs, err := f.solver.Solve(ctx, inpRefs, f.op.Actions)
151
+	if err != nil {
152
+		return nil, err
153
+	}
154
+
155
+	outResults := make([]solver.Result, 0, len(outs))
156
+	for _, out := range outs {
157
+		outResults = append(outResults, worker.NewWorkerRefResult(out.(cache.ImmutableRef), f.w))
158
+	}
159
+
160
+	return outResults, nil
161
+}
162
+
163
+func addSelector(m map[int]map[llbsolver.Selector]struct{}, idx int, sel string, wildcard, followLinks bool) {
164
+	mm, ok := m[idx]
165
+	if !ok {
166
+		mm = map[llbsolver.Selector]struct{}{}
167
+		m[idx] = mm
168
+	}
169
+	s := llbsolver.Selector{Path: sel}
170
+
171
+	if wildcard && containsWildcards(sel) {
172
+		s.Wildcard = true
173
+	}
174
+	if followLinks {
175
+		s.FollowLinks = true
176
+	}
177
+	mm[s] = struct{}{}
178
+}
179
+
180
+func containsWildcards(name string) bool {
181
+	isWindows := runtime.GOOS == "windows"
182
+	for i := 0; i < len(name); i++ {
183
+		ch := name[i]
184
+		if ch == '\\' && !isWindows {
185
+			i++
186
+		} else if ch == '*' || ch == '?' || ch == '[' {
187
+			return true
188
+		}
189
+	}
190
+	return false
191
+}
192
+
193
+func dedupeSelectors(m map[llbsolver.Selector]struct{}) []llbsolver.Selector {
194
+	paths := make([]string, 0, len(m))
195
+	pathsFollow := make([]string, 0, len(m))
196
+	for sel := range m {
197
+		if !sel.Wildcard {
198
+			if sel.FollowLinks {
199
+				pathsFollow = append(pathsFollow, sel.Path)
200
+			} else {
201
+				paths = append(paths, sel.Path)
202
+			}
203
+		}
204
+	}
205
+	paths = dedupePaths(paths)
206
+	pathsFollow = dedupePaths(pathsFollow)
207
+	selectors := make([]llbsolver.Selector, 0, len(m))
208
+
209
+	for _, p := range paths {
210
+		selectors = append(selectors, llbsolver.Selector{Path: p})
211
+	}
212
+	for _, p := range pathsFollow {
213
+		selectors = append(selectors, llbsolver.Selector{Path: p, FollowLinks: true})
214
+	}
215
+
216
+	for sel := range m {
217
+		if sel.Wildcard {
218
+			selectors = append(selectors, sel)
219
+		}
220
+	}
221
+
222
+	sort.Slice(selectors, func(i, j int) bool {
223
+		return selectors[i].Path < selectors[j].Path
224
+	})
225
+
226
+	return selectors
227
+}
228
+
229
+func processOwner(chopt *pb.ChownOpt, selectors map[int]map[llbsolver.Selector]struct{}) error {
230
+	if chopt == nil {
231
+		return nil
232
+	}
233
+	if chopt.User != nil {
234
+		if u, ok := chopt.User.User.(*pb.UserOpt_ByName); ok {
235
+			if u.ByName.Input < 0 {
236
+				return errors.Errorf("invalid user index %d", u.ByName.Input)
237
+			}
238
+			addSelector(selectors, int(u.ByName.Input), "/etc/passwd", false, true)
239
+		}
240
+	}
241
+	if chopt.Group != nil {
242
+		if u, ok := chopt.Group.User.(*pb.UserOpt_ByName); ok {
243
+			if u.ByName.Input < 0 {
244
+				return errors.Errorf("invalid user index %d", u.ByName.Input)
245
+			}
246
+			addSelector(selectors, int(u.ByName.Input), "/etc/group", false, true)
247
+		}
248
+	}
249
+	return nil
250
+}
251
+
252
+func NewFileOpSolver(b fileoptypes.Backend, r fileoptypes.RefManager) *FileOpSolver {
253
+	return &FileOpSolver{
254
+		b:    b,
255
+		r:    r,
256
+		outs: map[int]int{},
257
+		ins:  map[int]input{},
258
+	}
259
+}
260
+
261
+type FileOpSolver struct {
262
+	b fileoptypes.Backend
263
+	r fileoptypes.RefManager
264
+
265
+	mu   sync.Mutex
266
+	outs map[int]int
267
+	ins  map[int]input
268
+	g    flightcontrol.Group
269
+}
270
+
271
+type input struct {
272
+	requiresCommit bool
273
+	mount          fileoptypes.Mount
274
+	ref            fileoptypes.Ref
275
+}
276
+
277
+func (s *FileOpSolver) Solve(ctx context.Context, inputs []fileoptypes.Ref, actions []*pb.FileAction) ([]fileoptypes.Ref, error) {
278
+	for i, a := range actions {
279
+		if int(a.Input) < -1 || int(a.Input) >= len(inputs)+len(actions) {
280
+			return nil, errors.Errorf("invalid input index %d, %d provided", a.Input, len(inputs)+len(actions))
281
+		}
282
+		if int(a.SecondaryInput) < -1 || int(a.SecondaryInput) >= len(inputs)+len(actions) {
283
+			return nil, errors.Errorf("invalid secondary input index %d, %d provided", a.Input, len(inputs))
284
+		}
285
+
286
+		inp, ok := s.ins[int(a.Input)]
287
+		if ok {
288
+			inp.requiresCommit = true
289
+		}
290
+		s.ins[int(a.Input)] = inp
291
+
292
+		inp, ok = s.ins[int(a.SecondaryInput)]
293
+		if ok {
294
+			inp.requiresCommit = true
295
+		}
296
+		s.ins[int(a.SecondaryInput)] = inp
297
+
298
+		if a.Output != -1 {
299
+			if _, ok := s.outs[int(a.Output)]; ok {
300
+				return nil, errors.Errorf("duplicate output %d", a.Output)
301
+			}
302
+			idx := len(inputs) + i
303
+			s.outs[int(a.Output)] = idx
304
+			s.ins[idx] = input{requiresCommit: true}
305
+		}
306
+	}
307
+
308
+	if len(s.outs) == 0 {
309
+		return nil, errors.Errorf("no outputs specified")
310
+	}
311
+
312
+	for i := 0; i < len(s.outs); i++ {
313
+		if _, ok := s.outs[i]; !ok {
314
+			return nil, errors.Errorf("missing output index %d", i)
315
+		}
316
+	}
317
+
318
+	defer func() {
319
+		for _, in := range s.ins {
320
+			if in.ref == nil && in.mount != nil {
321
+				in.mount.Release(context.TODO())
322
+			}
323
+		}
324
+	}()
325
+
326
+	outs := make([]fileoptypes.Ref, len(s.outs))
327
+
328
+	eg, ctx := errgroup.WithContext(ctx)
329
+	for i, idx := range s.outs {
330
+		func(i, idx int) {
331
+			eg.Go(func() error {
332
+				if err := s.validate(idx, inputs, actions, nil); err != nil {
333
+					return err
334
+				}
335
+				inp, err := s.getInput(ctx, idx, inputs, actions)
336
+				if err != nil {
337
+					return err
338
+				}
339
+				outs[i] = inp.ref
340
+				return nil
341
+			})
342
+		}(i, idx)
343
+	}
344
+
345
+	if err := eg.Wait(); err != nil {
346
+		for _, r := range outs {
347
+			if r != nil {
348
+				r.Release(context.TODO())
349
+			}
350
+		}
351
+		return nil, err
352
+	}
353
+
354
+	return outs, nil
355
+}
356
+
357
+func (s *FileOpSolver) validate(idx int, inputs []fileoptypes.Ref, actions []*pb.FileAction, loaded []int) error {
358
+	for _, check := range loaded {
359
+		if idx == check {
360
+			return errors.Errorf("loop from index %d", idx)
361
+		}
362
+	}
363
+	if idx < len(inputs) {
364
+		return nil
365
+	}
366
+	loaded = append(loaded, idx)
367
+	action := actions[idx-len(inputs)]
368
+	for _, inp := range []int{int(action.Input), int(action.SecondaryInput)} {
369
+		if err := s.validate(inp, inputs, actions, loaded); err != nil {
370
+			return err
371
+		}
372
+	}
373
+	return nil
374
+}
375
+
376
+func (s *FileOpSolver) getInput(ctx context.Context, idx int, inputs []fileoptypes.Ref, actions []*pb.FileAction) (input, error) {
377
+	inp, err := s.g.Do(ctx, fmt.Sprintf("inp-%d", idx), func(ctx context.Context) (_ interface{}, err error) {
378
+		s.mu.Lock()
379
+		inp := s.ins[idx]
380
+		s.mu.Unlock()
381
+		if inp.mount != nil || inp.ref != nil {
382
+			return inp, nil
383
+		}
384
+
385
+		if idx < len(inputs) {
386
+			inp.ref = inputs[idx]
387
+			s.mu.Lock()
388
+			s.ins[idx] = inp
389
+			s.mu.Unlock()
390
+			return inp, nil
391
+		}
392
+
393
+		var inpMount, inpMountSecondary fileoptypes.Mount
394
+		var toRelease []fileoptypes.Mount
395
+		var inpMountPrepared bool
396
+		defer func() {
397
+			for _, m := range toRelease {
398
+				m.Release(context.TODO())
399
+			}
400
+			if err != nil && inpMount != nil && inpMountPrepared {
401
+				inpMount.Release(context.TODO())
402
+			}
403
+		}()
404
+
405
+		action := actions[idx-len(inputs)]
406
+
407
+		loadInput := func(ctx context.Context) func() error {
408
+			return func() error {
409
+				inp, err := s.getInput(ctx, int(action.Input), inputs, actions)
410
+				if err != nil {
411
+					return err
412
+				}
413
+				if inp.ref != nil {
414
+					m, err := s.r.Prepare(ctx, inp.ref, false)
415
+					if err != nil {
416
+						return err
417
+					}
418
+					inpMount = m
419
+					inpMountPrepared = true
420
+					return nil
421
+				}
422
+				inpMount = inp.mount
423
+				return nil
424
+			}
425
+		}
426
+
427
+		loadSecondaryInput := func(ctx context.Context) func() error {
428
+			return func() error {
429
+				inp, err := s.getInput(ctx, int(action.SecondaryInput), inputs, actions)
430
+				if err != nil {
431
+					return err
432
+				}
433
+				if inp.ref != nil {
434
+					m, err := s.r.Prepare(ctx, inp.ref, true)
435
+					if err != nil {
436
+						return err
437
+					}
438
+					inpMountSecondary = m
439
+					toRelease = append(toRelease, m)
440
+					return nil
441
+				}
442
+				inpMountSecondary = inp.mount
443
+				return nil
444
+			}
445
+		}
446
+
447
+		loadUser := func(ctx context.Context, uopt *pb.UserOpt) (fileoptypes.Mount, error) {
448
+			if uopt == nil {
449
+				return nil, nil
450
+			}
451
+			switch u := uopt.User.(type) {
452
+			case *pb.UserOpt_ByName:
453
+				var m fileoptypes.Mount
454
+				if u.ByName.Input < 0 {
455
+					return nil, errors.Errorf("invalid user index: %d", u.ByName.Input)
456
+				}
457
+				inp, err := s.getInput(ctx, int(u.ByName.Input), inputs, actions)
458
+				if err != nil {
459
+					return nil, err
460
+				}
461
+				if inp.ref != nil {
462
+					mm, err := s.r.Prepare(ctx, inp.ref, true)
463
+					if err != nil {
464
+						return nil, err
465
+					}
466
+					toRelease = append(toRelease, mm)
467
+					m = mm
468
+				} else {
469
+					m = inp.mount
470
+				}
471
+				return m, nil
472
+			default:
473
+				return nil, nil
474
+			}
475
+		}
476
+
477
+		loadOwner := func(ctx context.Context, chopt *pb.ChownOpt) (fileoptypes.Mount, fileoptypes.Mount, error) {
478
+			if chopt == nil {
479
+				return nil, nil, nil
480
+			}
481
+			um, err := loadUser(ctx, chopt.User)
482
+			if err != nil {
483
+				return nil, nil, err
484
+			}
485
+			gm, err := loadUser(ctx, chopt.Group)
486
+			if err != nil {
487
+				return nil, nil, err
488
+			}
489
+			return um, gm, nil
490
+		}
491
+
492
+		if action.Input != -1 && action.SecondaryInput != -1 {
493
+			eg, ctx := errgroup.WithContext(ctx)
494
+			eg.Go(loadInput(ctx))
495
+			eg.Go(loadSecondaryInput(ctx))
496
+			if err := eg.Wait(); err != nil {
497
+				return nil, err
498
+			}
499
+		} else {
500
+			if action.Input != -1 {
501
+				if err := loadInput(ctx)(); err != nil {
502
+					return nil, err
503
+				}
504
+			}
505
+			if action.SecondaryInput != -1 {
506
+				if err := loadSecondaryInput(ctx)(); err != nil {
507
+					return nil, err
508
+				}
509
+			}
510
+		}
511
+
512
+		if inpMount == nil {
513
+			m, err := s.r.Prepare(ctx, nil, false)
514
+			if err != nil {
515
+				return nil, err
516
+			}
517
+			inpMount = m
518
+			inpMountPrepared = true
519
+		}
520
+
521
+		switch a := action.Action.(type) {
522
+		case *pb.FileAction_Mkdir:
523
+			user, group, err := loadOwner(ctx, a.Mkdir.Owner)
524
+			if err != nil {
525
+				return nil, err
526
+			}
527
+			if err := s.b.Mkdir(ctx, inpMount, user, group, *a.Mkdir); err != nil {
528
+				return nil, err
529
+			}
530
+		case *pb.FileAction_Mkfile:
531
+			user, group, err := loadOwner(ctx, a.Mkfile.Owner)
532
+			if err != nil {
533
+				return nil, err
534
+			}
535
+			if err := s.b.Mkfile(ctx, inpMount, user, group, *a.Mkfile); err != nil {
536
+				return nil, err
537
+			}
538
+		case *pb.FileAction_Rm:
539
+			if err := s.b.Rm(ctx, inpMount, *a.Rm); err != nil {
540
+				return nil, err
541
+			}
542
+		case *pb.FileAction_Copy:
543
+			if inpMountSecondary == nil {
544
+				m, err := s.r.Prepare(ctx, nil, true)
545
+				if err != nil {
546
+					return nil, err
547
+				}
548
+				inpMountSecondary = m
549
+			}
550
+			user, group, err := loadOwner(ctx, a.Copy.Owner)
551
+			if err != nil {
552
+				return nil, err
553
+			}
554
+			if err := s.b.Copy(ctx, inpMountSecondary, inpMount, user, group, *a.Copy); err != nil {
555
+				return nil, err
556
+			}
557
+		default:
558
+			return nil, errors.Errorf("invalid action type %T", action.Action)
559
+		}
560
+
561
+		if inp.requiresCommit {
562
+			ref, err := s.r.Commit(ctx, inpMount)
563
+			if err != nil {
564
+				return nil, err
565
+			}
566
+			inp.ref = ref
567
+		} else {
568
+			inp.mount = inpMount
569
+		}
570
+		s.mu.Lock()
571
+		s.ins[idx] = inp
572
+		s.mu.Unlock()
573
+		return inp, nil
574
+	})
575
+	if err != nil {
576
+		return input{}, err
577
+	}
578
+	return inp.(input), err
579
+}
0 580
new file mode 100644
... ...
@@ -0,0 +1,28 @@
0
+package fileoptypes
1
+
2
+import (
3
+	"context"
4
+
5
+	"github.com/moby/buildkit/solver/pb"
6
+)
7
+
8
+type Ref interface {
9
+	Release(context.Context) error
10
+}
11
+
12
+type Mount interface {
13
+	IsFileOpMount()
14
+	Release(context.Context) error
15
+}
16
+
17
+type Backend interface {
18
+	Mkdir(context.Context, Mount, Mount, Mount, pb.FileActionMkDir) error
19
+	Mkfile(context.Context, Mount, Mount, Mount, pb.FileActionMkFile) error
20
+	Rm(context.Context, Mount, pb.FileActionRm) error
21
+	Copy(context.Context, Mount, Mount, Mount, Mount, pb.FileActionCopy) error
22
+}
23
+
24
+type RefManager interface {
25
+	Prepare(ctx context.Context, ref Ref, readonly bool) (Mount, error)
26
+	Commit(ctx context.Context, mount Mount) (Ref, error)
27
+}
... ...
@@ -13,7 +13,13 @@ import (
13 13
 	"golang.org/x/sync/errgroup"
14 14
 )
15 15
 
16
-func NewContentHashFunc(selectors []string) solver.ResultBasedCacheFunc {
16
+type Selector struct {
17
+	Path        string
18
+	Wildcard    bool
19
+	FollowLinks bool
20
+}
21
+
22
+func NewContentHashFunc(selectors []Selector) solver.ResultBasedCacheFunc {
17 23
 	return func(ctx context.Context, res solver.Result) (digest.Digest, error) {
18 24
 		ref, ok := res.Sys().(*worker.WorkerRef)
19 25
 		if !ok {
... ...
@@ -21,7 +27,7 @@ func NewContentHashFunc(selectors []string) solver.ResultBasedCacheFunc {
21 21
 		}
22 22
 
23 23
 		if len(selectors) == 0 {
24
-			selectors = []string{""}
24
+			selectors = []Selector{{}}
25 25
 		}
26 26
 
27 27
 		dgsts := make([][]byte, len(selectors))
... ...
@@ -32,11 +38,19 @@ func NewContentHashFunc(selectors []string) solver.ResultBasedCacheFunc {
32 32
 			// FIXME(tonistiigi): enabling this parallelization seems to create wrong results for some big inputs(like gobuild)
33 33
 			// func(i int) {
34 34
 			// 	eg.Go(func() error {
35
-			dgst, err := contenthash.Checksum(ctx, ref.ImmutableRef, path.Join("/", sel), true)
36
-			if err != nil {
37
-				return "", err
35
+			if !sel.Wildcard {
36
+				dgst, err := contenthash.Checksum(ctx, ref.ImmutableRef, path.Join("/", sel.Path), sel.FollowLinks)
37
+				if err != nil {
38
+					return "", err
39
+				}
40
+				dgsts[i] = []byte(dgst)
41
+			} else {
42
+				dgst, err := contenthash.ChecksumWildcard(ctx, ref.ImmutableRef, path.Join("/", sel.Path), sel.FollowLinks)
43
+				if err != nil {
44
+					return "", err
45
+				}
46
+				dgsts[i] = []byte(dgst)
38 47
 			}
39
-			dgsts[i] = []byte(dgst)
40 48
 			// return nil
41 49
 			// })
42 50
 			// }(i)
... ...
@@ -1,6 +1,7 @@
1 1
 package llbsolver
2 2
 
3 3
 import (
4
+	"fmt"
4 5
 	"strings"
5 6
 
6 7
 	"github.com/containerd/containerd/platforms"
... ...
@@ -228,9 +229,29 @@ func llbOpName(op *pb.Op) string {
228 228
 		return op.Source.Identifier
229 229
 	case *pb.Op_Exec:
230 230
 		return strings.Join(op.Exec.Meta.Args, " ")
231
+	case *pb.Op_File:
232
+		return fileOpName(op.File.Actions)
231 233
 	case *pb.Op_Build:
232 234
 		return "build"
233 235
 	default:
234 236
 		return "unknown"
235 237
 	}
236 238
 }
239
+
240
+func fileOpName(actions []*pb.FileAction) string {
241
+	names := make([]string, 0, len(actions))
242
+	for _, action := range actions {
243
+		switch a := action.Action.(type) {
244
+		case *pb.FileAction_Mkdir:
245
+			names = append(names, fmt.Sprintf("mkdir %s", a.Mkdir.Path))
246
+		case *pb.FileAction_Mkfile:
247
+			names = append(names, fmt.Sprintf("mkfile %s", a.Mkfile.Path))
248
+		case *pb.FileAction_Rm:
249
+			names = append(names, fmt.Sprintf("rm %s", a.Rm.Path))
250
+		case *pb.FileAction_Copy:
251
+			names = append(names, fmt.Sprintf("copy %s %s", a.Copy.Src, a.Copy.Dest))
252
+		}
253
+	}
254
+
255
+	return strings.Join(names, ", ")
256
+}
... ...
@@ -21,3 +21,5 @@ const AttrImageResolveModeDefault = "default"
21 21
 const AttrImageResolveModeForcePull = "pull"
22 22
 const AttrImageResolveModePreferLocal = "local"
23 23
 const AttrImageRecordType = "image.recordtype"
24
+
25
+type IsFileAction = isFileAction_Action
... ...
@@ -43,6 +43,8 @@ const (
43 43
 	CapExecMountSSH            apicaps.CapID = "exec.mount.ssh"
44 44
 	CapExecCgroupsMounted      apicaps.CapID = "exec.cgroup"
45 45
 
46
+	CapFileBase apicaps.CapID = "file.base"
47
+
46 48
 	CapConstraints apicaps.CapID = "constraints"
47 49
 	CapPlatform    apicaps.CapID = "platform"
48 50
 
... ...
@@ -227,6 +229,16 @@ func init() {
227 227
 	})
228 228
 
229 229
 	Caps.Init(apicaps.Cap{
230
+		ID:      CapFileBase,
231
+		Enabled: true,
232
+		Status:  apicaps.CapStatusPrerelease,
233
+		SupportedHint: map[string]string{
234
+			"docker":   "Docker v19.03",
235
+			"buildkit": "BuildKit v0.5.0",
236
+		},
237
+	})
238
+
239
+	Caps.Init(apicaps.Cap{
230 240
 		ID:      CapConstraints,
231 241
 		Enabled: true,
232 242
 		Status:  apicaps.CapStatusExperimental,
... ...
@@ -54,7 +54,7 @@ func (x NetMode) String() string {
54 54
 	return proto.EnumName(NetMode_name, int32(x))
55 55
 }
56 56
 func (NetMode) EnumDescriptor() ([]byte, []int) {
57
-	return fileDescriptor_ops_821a7942fdf920e6, []int{0}
57
+	return fileDescriptor_ops_8d64813b9835ab08, []int{0}
58 58
 }
59 59
 
60 60
 // MountType defines a type of a mount from a supported set
... ...
@@ -87,7 +87,7 @@ func (x MountType) String() string {
87 87
 	return proto.EnumName(MountType_name, int32(x))
88 88
 }
89 89
 func (MountType) EnumDescriptor() ([]byte, []int) {
90
-	return fileDescriptor_ops_821a7942fdf920e6, []int{1}
90
+	return fileDescriptor_ops_8d64813b9835ab08, []int{1}
91 91
 }
92 92
 
93 93
 // CacheSharingOpt defines different sharing modes for cache mount
... ...
@@ -117,7 +117,7 @@ func (x CacheSharingOpt) String() string {
117 117
 	return proto.EnumName(CacheSharingOpt_name, int32(x))
118 118
 }
119 119
 func (CacheSharingOpt) EnumDescriptor() ([]byte, []int) {
120
-	return fileDescriptor_ops_821a7942fdf920e6, []int{2}
120
+	return fileDescriptor_ops_8d64813b9835ab08, []int{2}
121 121
 }
122 122
 
123 123
 // Op represents a vertex of the LLB DAG.
... ...
@@ -127,7 +127,7 @@ type Op struct {
127 127
 	// Types that are valid to be assigned to Op:
128 128
 	//	*Op_Exec
129 129
 	//	*Op_Source
130
-	//	*Op_Copy
130
+	//	*Op_File
131 131
 	//	*Op_Build
132 132
 	Op          isOp_Op            `protobuf_oneof:"op"`
133 133
 	Platform    *Platform          `protobuf:"bytes,10,opt,name=platform,proto3" json:"platform,omitempty"`
... ...
@@ -138,7 +138,7 @@ func (m *Op) Reset()         { *m = Op{} }
138 138
 func (m *Op) String() string { return proto.CompactTextString(m) }
139 139
 func (*Op) ProtoMessage()    {}
140 140
 func (*Op) Descriptor() ([]byte, []int) {
141
-	return fileDescriptor_ops_821a7942fdf920e6, []int{0}
141
+	return fileDescriptor_ops_8d64813b9835ab08, []int{0}
142 142
 }
143 143
 func (m *Op) XXX_Unmarshal(b []byte) error {
144 144
 	return m.Unmarshal(b)
... ...
@@ -175,8 +175,8 @@ type Op_Exec struct {
175 175
 type Op_Source struct {
176 176
 	Source *SourceOp `protobuf:"bytes,3,opt,name=source,proto3,oneof"`
177 177
 }
178
-type Op_Copy struct {
179
-	Copy *CopyOp `protobuf:"bytes,4,opt,name=copy,proto3,oneof"`
178
+type Op_File struct {
179
+	File *FileOp `protobuf:"bytes,4,opt,name=file,proto3,oneof"`
180 180
 }
181 181
 type Op_Build struct {
182 182
 	Build *BuildOp `protobuf:"bytes,5,opt,name=build,proto3,oneof"`
... ...
@@ -184,7 +184,7 @@ type Op_Build struct {
184 184
 
185 185
 func (*Op_Exec) isOp_Op()   {}
186 186
 func (*Op_Source) isOp_Op() {}
187
-func (*Op_Copy) isOp_Op()   {}
187
+func (*Op_File) isOp_Op()   {}
188 188
 func (*Op_Build) isOp_Op()  {}
189 189
 
190 190
 func (m *Op) GetOp() isOp_Op {
... ...
@@ -215,9 +215,9 @@ func (m *Op) GetSource() *SourceOp {
215 215
 	return nil
216 216
 }
217 217
 
218
-func (m *Op) GetCopy() *CopyOp {
219
-	if x, ok := m.GetOp().(*Op_Copy); ok {
220
-		return x.Copy
218
+func (m *Op) GetFile() *FileOp {
219
+	if x, ok := m.GetOp().(*Op_File); ok {
220
+		return x.File
221 221
 	}
222 222
 	return nil
223 223
 }
... ...
@@ -248,7 +248,7 @@ func (*Op) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, fun
248 248
 	return _Op_OneofMarshaler, _Op_OneofUnmarshaler, _Op_OneofSizer, []interface{}{
249 249
 		(*Op_Exec)(nil),
250 250
 		(*Op_Source)(nil),
251
-		(*Op_Copy)(nil),
251
+		(*Op_File)(nil),
252 252
 		(*Op_Build)(nil),
253 253
 	}
254 254
 }
... ...
@@ -267,9 +267,9 @@ func _Op_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
267 267
 		if err := b.EncodeMessage(x.Source); err != nil {
268 268
 			return err
269 269
 		}
270
-	case *Op_Copy:
270
+	case *Op_File:
271 271
 		_ = b.EncodeVarint(4<<3 | proto.WireBytes)
272
-		if err := b.EncodeMessage(x.Copy); err != nil {
272
+		if err := b.EncodeMessage(x.File); err != nil {
273 273
 			return err
274 274
 		}
275 275
 	case *Op_Build:
... ...
@@ -303,13 +303,13 @@ func _Op_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bo
303 303
 		err := b.DecodeMessage(msg)
304 304
 		m.Op = &Op_Source{msg}
305 305
 		return true, err
306
-	case 4: // op.copy
306
+	case 4: // op.file
307 307
 		if wire != proto.WireBytes {
308 308
 			return true, proto.ErrInternalBadWireType
309 309
 		}
310
-		msg := new(CopyOp)
310
+		msg := new(FileOp)
311 311
 		err := b.DecodeMessage(msg)
312
-		m.Op = &Op_Copy{msg}
312
+		m.Op = &Op_File{msg}
313 313
 		return true, err
314 314
 	case 5: // op.build
315 315
 		if wire != proto.WireBytes {
... ...
@@ -338,8 +338,8 @@ func _Op_OneofSizer(msg proto.Message) (n int) {
338 338
 		n += 1 // tag and wire
339 339
 		n += proto.SizeVarint(uint64(s))
340 340
 		n += s
341
-	case *Op_Copy:
342
-		s := proto.Size(x.Copy)
341
+	case *Op_File:
342
+		s := proto.Size(x.File)
343 343
 		n += 1 // tag and wire
344 344
 		n += proto.SizeVarint(uint64(s))
345 345
 		n += s
... ...
@@ -368,7 +368,7 @@ func (m *Platform) Reset()         { *m = Platform{} }
368 368
 func (m *Platform) String() string { return proto.CompactTextString(m) }
369 369
 func (*Platform) ProtoMessage()    {}
370 370
 func (*Platform) Descriptor() ([]byte, []int) {
371
-	return fileDescriptor_ops_821a7942fdf920e6, []int{1}
371
+	return fileDescriptor_ops_8d64813b9835ab08, []int{1}
372 372
 }
373 373
 func (m *Platform) XXX_Unmarshal(b []byte) error {
374 374
 	return m.Unmarshal(b)
... ...
@@ -440,7 +440,7 @@ func (m *Input) Reset()         { *m = Input{} }
440 440
 func (m *Input) String() string { return proto.CompactTextString(m) }
441 441
 func (*Input) ProtoMessage()    {}
442 442
 func (*Input) Descriptor() ([]byte, []int) {
443
-	return fileDescriptor_ops_821a7942fdf920e6, []int{2}
443
+	return fileDescriptor_ops_8d64813b9835ab08, []int{2}
444 444
 }
445 445
 func (m *Input) XXX_Unmarshal(b []byte) error {
446 446
 	return m.Unmarshal(b)
... ...
@@ -476,7 +476,7 @@ func (m *ExecOp) Reset()         { *m = ExecOp{} }
476 476
 func (m *ExecOp) String() string { return proto.CompactTextString(m) }
477 477
 func (*ExecOp) ProtoMessage()    {}
478 478
 func (*ExecOp) Descriptor() ([]byte, []int) {
479
-	return fileDescriptor_ops_821a7942fdf920e6, []int{3}
479
+	return fileDescriptor_ops_8d64813b9835ab08, []int{3}
480 480
 }
481 481
 func (m *ExecOp) XXX_Unmarshal(b []byte) error {
482 482
 	return m.Unmarshal(b)
... ...
@@ -538,7 +538,7 @@ func (m *Meta) Reset()         { *m = Meta{} }
538 538
 func (m *Meta) String() string { return proto.CompactTextString(m) }
539 539
 func (*Meta) ProtoMessage()    {}
540 540
 func (*Meta) Descriptor() ([]byte, []int) {
541
-	return fileDescriptor_ops_821a7942fdf920e6, []int{4}
541
+	return fileDescriptor_ops_8d64813b9835ab08, []int{4}
542 542
 }
543 543
 func (m *Meta) XXX_Unmarshal(b []byte) error {
544 544
 	return m.Unmarshal(b)
... ...
@@ -622,7 +622,7 @@ func (m *Mount) Reset()         { *m = Mount{} }
622 622
 func (m *Mount) String() string { return proto.CompactTextString(m) }
623 623
 func (*Mount) ProtoMessage()    {}
624 624
 func (*Mount) Descriptor() ([]byte, []int) {
625
-	return fileDescriptor_ops_821a7942fdf920e6, []int{5}
625
+	return fileDescriptor_ops_8d64813b9835ab08, []int{5}
626 626
 }
627 627
 func (m *Mount) XXX_Unmarshal(b []byte) error {
628 628
 	return m.Unmarshal(b)
... ...
@@ -708,7 +708,7 @@ func (m *CacheOpt) Reset()         { *m = CacheOpt{} }
708 708
 func (m *CacheOpt) String() string { return proto.CompactTextString(m) }
709 709
 func (*CacheOpt) ProtoMessage()    {}
710 710
 func (*CacheOpt) Descriptor() ([]byte, []int) {
711
-	return fileDescriptor_ops_821a7942fdf920e6, []int{6}
711
+	return fileDescriptor_ops_8d64813b9835ab08, []int{6}
712 712
 }
713 713
 func (m *CacheOpt) XXX_Unmarshal(b []byte) error {
714 714
 	return m.Unmarshal(b)
... ...
@@ -766,7 +766,7 @@ func (m *SecretOpt) Reset()         { *m = SecretOpt{} }
766 766
 func (m *SecretOpt) String() string { return proto.CompactTextString(m) }
767 767
 func (*SecretOpt) ProtoMessage()    {}
768 768
 func (*SecretOpt) Descriptor() ([]byte, []int) {
769
-	return fileDescriptor_ops_821a7942fdf920e6, []int{7}
769
+	return fileDescriptor_ops_8d64813b9835ab08, []int{7}
770 770
 }
771 771
 func (m *SecretOpt) XXX_Unmarshal(b []byte) error {
772 772
 	return m.Unmarshal(b)
... ...
@@ -845,7 +845,7 @@ func (m *SSHOpt) Reset()         { *m = SSHOpt{} }
845 845
 func (m *SSHOpt) String() string { return proto.CompactTextString(m) }
846 846
 func (*SSHOpt) ProtoMessage()    {}
847 847
 func (*SSHOpt) Descriptor() ([]byte, []int) {
848
-	return fileDescriptor_ops_821a7942fdf920e6, []int{8}
848
+	return fileDescriptor_ops_8d64813b9835ab08, []int{8}
849 849
 }
850 850
 func (m *SSHOpt) XXX_Unmarshal(b []byte) error {
851 851
 	return m.Unmarshal(b)
... ...
@@ -905,97 +905,6 @@ func (m *SSHOpt) GetOptional() bool {
905 905
 	return false
906 906
 }
907 907
 
908
-// CopyOp copies files across Ops.
909
-type CopyOp struct {
910
-	Src  []*CopySource `protobuf:"bytes,1,rep,name=src,proto3" json:"src,omitempty"`
911
-	Dest string        `protobuf:"bytes,2,opt,name=dest,proto3" json:"dest,omitempty"`
912
-}
913
-
914
-func (m *CopyOp) Reset()         { *m = CopyOp{} }
915
-func (m *CopyOp) String() string { return proto.CompactTextString(m) }
916
-func (*CopyOp) ProtoMessage()    {}
917
-func (*CopyOp) Descriptor() ([]byte, []int) {
918
-	return fileDescriptor_ops_821a7942fdf920e6, []int{9}
919
-}
920
-func (m *CopyOp) XXX_Unmarshal(b []byte) error {
921
-	return m.Unmarshal(b)
922
-}
923
-func (m *CopyOp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
924
-	b = b[:cap(b)]
925
-	n, err := m.MarshalTo(b)
926
-	if err != nil {
927
-		return nil, err
928
-	}
929
-	return b[:n], nil
930
-}
931
-func (dst *CopyOp) XXX_Merge(src proto.Message) {
932
-	xxx_messageInfo_CopyOp.Merge(dst, src)
933
-}
934
-func (m *CopyOp) XXX_Size() int {
935
-	return m.Size()
936
-}
937
-func (m *CopyOp) XXX_DiscardUnknown() {
938
-	xxx_messageInfo_CopyOp.DiscardUnknown(m)
939
-}
940
-
941
-var xxx_messageInfo_CopyOp proto.InternalMessageInfo
942
-
943
-func (m *CopyOp) GetSrc() []*CopySource {
944
-	if m != nil {
945
-		return m.Src
946
-	}
947
-	return nil
948
-}
949
-
950
-func (m *CopyOp) GetDest() string {
951
-	if m != nil {
952
-		return m.Dest
953
-	}
954
-	return ""
955
-}
956
-
957
-// CopySource specifies a source for CopyOp.
958
-type CopySource struct {
959
-	Input    InputIndex `protobuf:"varint,1,opt,name=input,proto3,customtype=InputIndex" json:"input"`
960
-	Selector string     `protobuf:"bytes,2,opt,name=selector,proto3" json:"selector,omitempty"`
961
-}
962
-
963
-func (m *CopySource) Reset()         { *m = CopySource{} }
964
-func (m *CopySource) String() string { return proto.CompactTextString(m) }
965
-func (*CopySource) ProtoMessage()    {}
966
-func (*CopySource) Descriptor() ([]byte, []int) {
967
-	return fileDescriptor_ops_821a7942fdf920e6, []int{10}
968
-}
969
-func (m *CopySource) XXX_Unmarshal(b []byte) error {
970
-	return m.Unmarshal(b)
971
-}
972
-func (m *CopySource) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
973
-	b = b[:cap(b)]
974
-	n, err := m.MarshalTo(b)
975
-	if err != nil {
976
-		return nil, err
977
-	}
978
-	return b[:n], nil
979
-}
980
-func (dst *CopySource) XXX_Merge(src proto.Message) {
981
-	xxx_messageInfo_CopySource.Merge(dst, src)
982
-}
983
-func (m *CopySource) XXX_Size() int {
984
-	return m.Size()
985
-}
986
-func (m *CopySource) XXX_DiscardUnknown() {
987
-	xxx_messageInfo_CopySource.DiscardUnknown(m)
988
-}
989
-
990
-var xxx_messageInfo_CopySource proto.InternalMessageInfo
991
-
992
-func (m *CopySource) GetSelector() string {
993
-	if m != nil {
994
-		return m.Selector
995
-	}
996
-	return ""
997
-}
998
-
999 908
 // SourceOp specifies a source such as build contexts and images.
1000 909
 type SourceOp struct {
1001 910
 	// TODO: use source type or any type instead of URL protocol.
... ...
@@ -1009,7 +918,7 @@ func (m *SourceOp) Reset()         { *m = SourceOp{} }
1009 1009
 func (m *SourceOp) String() string { return proto.CompactTextString(m) }
1010 1010
 func (*SourceOp) ProtoMessage()    {}
1011 1011
 func (*SourceOp) Descriptor() ([]byte, []int) {
1012
-	return fileDescriptor_ops_821a7942fdf920e6, []int{11}
1012
+	return fileDescriptor_ops_8d64813b9835ab08, []int{9}
1013 1013
 }
1014 1014
 func (m *SourceOp) XXX_Unmarshal(b []byte) error {
1015 1015
 	return m.Unmarshal(b)
... ...
@@ -1061,7 +970,7 @@ func (m *BuildOp) Reset()         { *m = BuildOp{} }
1061 1061
 func (m *BuildOp) String() string { return proto.CompactTextString(m) }
1062 1062
 func (*BuildOp) ProtoMessage()    {}
1063 1063
 func (*BuildOp) Descriptor() ([]byte, []int) {
1064
-	return fileDescriptor_ops_821a7942fdf920e6, []int{12}
1064
+	return fileDescriptor_ops_8d64813b9835ab08, []int{10}
1065 1065
 }
1066 1066
 func (m *BuildOp) XXX_Unmarshal(b []byte) error {
1067 1067
 	return m.Unmarshal(b)
... ...
@@ -1116,7 +1025,7 @@ func (m *BuildInput) Reset()         { *m = BuildInput{} }
1116 1116
 func (m *BuildInput) String() string { return proto.CompactTextString(m) }
1117 1117
 func (*BuildInput) ProtoMessage()    {}
1118 1118
 func (*BuildInput) Descriptor() ([]byte, []int) {
1119
-	return fileDescriptor_ops_821a7942fdf920e6, []int{13}
1119
+	return fileDescriptor_ops_8d64813b9835ab08, []int{11}
1120 1120
 }
1121 1121
 func (m *BuildInput) XXX_Unmarshal(b []byte) error {
1122 1122
 	return m.Unmarshal(b)
... ...
@@ -1157,7 +1066,7 @@ func (m *OpMetadata) Reset()         { *m = OpMetadata{} }
1157 1157
 func (m *OpMetadata) String() string { return proto.CompactTextString(m) }
1158 1158
 func (*OpMetadata) ProtoMessage()    {}
1159 1159
 func (*OpMetadata) Descriptor() ([]byte, []int) {
1160
-	return fileDescriptor_ops_821a7942fdf920e6, []int{14}
1160
+	return fileDescriptor_ops_8d64813b9835ab08, []int{12}
1161 1161
 }
1162 1162
 func (m *OpMetadata) XXX_Unmarshal(b []byte) error {
1163 1163
 	return m.Unmarshal(b)
... ...
@@ -1218,7 +1127,7 @@ func (m *ExportCache) Reset()         { *m = ExportCache{} }
1218 1218
 func (m *ExportCache) String() string { return proto.CompactTextString(m) }
1219 1219
 func (*ExportCache) ProtoMessage()    {}
1220 1220
 func (*ExportCache) Descriptor() ([]byte, []int) {
1221
-	return fileDescriptor_ops_821a7942fdf920e6, []int{15}
1221
+	return fileDescriptor_ops_8d64813b9835ab08, []int{13}
1222 1222
 }
1223 1223
 func (m *ExportCache) XXX_Unmarshal(b []byte) error {
1224 1224
 	return m.Unmarshal(b)
... ...
@@ -1261,7 +1170,7 @@ func (m *ProxyEnv) Reset()         { *m = ProxyEnv{} }
1261 1261
 func (m *ProxyEnv) String() string { return proto.CompactTextString(m) }
1262 1262
 func (*ProxyEnv) ProtoMessage()    {}
1263 1263
 func (*ProxyEnv) Descriptor() ([]byte, []int) {
1264
-	return fileDescriptor_ops_821a7942fdf920e6, []int{16}
1264
+	return fileDescriptor_ops_8d64813b9835ab08, []int{14}
1265 1265
 }
1266 1266
 func (m *ProxyEnv) XXX_Unmarshal(b []byte) error {
1267 1267
 	return m.Unmarshal(b)
... ...
@@ -1323,7 +1232,7 @@ func (m *WorkerConstraints) Reset()         { *m = WorkerConstraints{} }
1323 1323
 func (m *WorkerConstraints) String() string { return proto.CompactTextString(m) }
1324 1324
 func (*WorkerConstraints) ProtoMessage()    {}
1325 1325
 func (*WorkerConstraints) Descriptor() ([]byte, []int) {
1326
-	return fileDescriptor_ops_821a7942fdf920e6, []int{17}
1326
+	return fileDescriptor_ops_8d64813b9835ab08, []int{15}
1327 1327
 }
1328 1328
 func (m *WorkerConstraints) XXX_Unmarshal(b []byte) error {
1329 1329
 	return m.Unmarshal(b)
... ...
@@ -1368,7 +1277,7 @@ func (m *Definition) Reset()         { *m = Definition{} }
1368 1368
 func (m *Definition) String() string { return proto.CompactTextString(m) }
1369 1369
 func (*Definition) ProtoMessage()    {}
1370 1370
 func (*Definition) Descriptor() ([]byte, []int) {
1371
-	return fileDescriptor_ops_821a7942fdf920e6, []int{18}
1371
+	return fileDescriptor_ops_8d64813b9835ab08, []int{16}
1372 1372
 }
1373 1373
 func (m *Definition) XXX_Unmarshal(b []byte) error {
1374 1374
 	return m.Unmarshal(b)
... ...
@@ -1416,7 +1325,7 @@ func (m *HostIP) Reset()         { *m = HostIP{} }
1416 1416
 func (m *HostIP) String() string { return proto.CompactTextString(m) }
1417 1417
 func (*HostIP) ProtoMessage()    {}
1418 1418
 func (*HostIP) Descriptor() ([]byte, []int) {
1419
-	return fileDescriptor_ops_821a7942fdf920e6, []int{19}
1419
+	return fileDescriptor_ops_8d64813b9835ab08, []int{17}
1420 1420
 }
1421 1421
 func (m *HostIP) XXX_Unmarshal(b []byte) error {
1422 1422
 	return m.Unmarshal(b)
... ...
@@ -1455,6 +1364,833 @@ func (m *HostIP) GetIP() string {
1455 1455
 	return ""
1456 1456
 }
1457 1457
 
1458
+type FileOp struct {
1459
+	Actions []*FileAction `protobuf:"bytes,2,rep,name=actions,proto3" json:"actions,omitempty"`
1460
+}
1461
+
1462
+func (m *FileOp) Reset()         { *m = FileOp{} }
1463
+func (m *FileOp) String() string { return proto.CompactTextString(m) }
1464
+func (*FileOp) ProtoMessage()    {}
1465
+func (*FileOp) Descriptor() ([]byte, []int) {
1466
+	return fileDescriptor_ops_8d64813b9835ab08, []int{18}
1467
+}
1468
+func (m *FileOp) XXX_Unmarshal(b []byte) error {
1469
+	return m.Unmarshal(b)
1470
+}
1471
+func (m *FileOp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
1472
+	b = b[:cap(b)]
1473
+	n, err := m.MarshalTo(b)
1474
+	if err != nil {
1475
+		return nil, err
1476
+	}
1477
+	return b[:n], nil
1478
+}
1479
+func (dst *FileOp) XXX_Merge(src proto.Message) {
1480
+	xxx_messageInfo_FileOp.Merge(dst, src)
1481
+}
1482
+func (m *FileOp) XXX_Size() int {
1483
+	return m.Size()
1484
+}
1485
+func (m *FileOp) XXX_DiscardUnknown() {
1486
+	xxx_messageInfo_FileOp.DiscardUnknown(m)
1487
+}
1488
+
1489
+var xxx_messageInfo_FileOp proto.InternalMessageInfo
1490
+
1491
+func (m *FileOp) GetActions() []*FileAction {
1492
+	if m != nil {
1493
+		return m.Actions
1494
+	}
1495
+	return nil
1496
+}
1497
+
1498
+type FileAction struct {
1499
+	Input          InputIndex  `protobuf:"varint,1,opt,name=input,proto3,customtype=InputIndex" json:"input"`
1500
+	SecondaryInput InputIndex  `protobuf:"varint,2,opt,name=secondaryInput,proto3,customtype=InputIndex" json:"secondaryInput"`
1501
+	Output         OutputIndex `protobuf:"varint,3,opt,name=output,proto3,customtype=OutputIndex" json:"output"`
1502
+	// Types that are valid to be assigned to Action:
1503
+	//	*FileAction_Copy
1504
+	//	*FileAction_Mkfile
1505
+	//	*FileAction_Mkdir
1506
+	//	*FileAction_Rm
1507
+	Action isFileAction_Action `protobuf_oneof:"action"`
1508
+}
1509
+
1510
+func (m *FileAction) Reset()         { *m = FileAction{} }
1511
+func (m *FileAction) String() string { return proto.CompactTextString(m) }
1512
+func (*FileAction) ProtoMessage()    {}
1513
+func (*FileAction) Descriptor() ([]byte, []int) {
1514
+	return fileDescriptor_ops_8d64813b9835ab08, []int{19}
1515
+}
1516
+func (m *FileAction) XXX_Unmarshal(b []byte) error {
1517
+	return m.Unmarshal(b)
1518
+}
1519
+func (m *FileAction) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
1520
+	b = b[:cap(b)]
1521
+	n, err := m.MarshalTo(b)
1522
+	if err != nil {
1523
+		return nil, err
1524
+	}
1525
+	return b[:n], nil
1526
+}
1527
+func (dst *FileAction) XXX_Merge(src proto.Message) {
1528
+	xxx_messageInfo_FileAction.Merge(dst, src)
1529
+}
1530
+func (m *FileAction) XXX_Size() int {
1531
+	return m.Size()
1532
+}
1533
+func (m *FileAction) XXX_DiscardUnknown() {
1534
+	xxx_messageInfo_FileAction.DiscardUnknown(m)
1535
+}
1536
+
1537
+var xxx_messageInfo_FileAction proto.InternalMessageInfo
1538
+
1539
+type isFileAction_Action interface {
1540
+	isFileAction_Action()
1541
+	MarshalTo([]byte) (int, error)
1542
+	Size() int
1543
+}
1544
+
1545
+type FileAction_Copy struct {
1546
+	Copy *FileActionCopy `protobuf:"bytes,4,opt,name=copy,proto3,oneof"`
1547
+}
1548
+type FileAction_Mkfile struct {
1549
+	Mkfile *FileActionMkFile `protobuf:"bytes,5,opt,name=mkfile,proto3,oneof"`
1550
+}
1551
+type FileAction_Mkdir struct {
1552
+	Mkdir *FileActionMkDir `protobuf:"bytes,6,opt,name=mkdir,proto3,oneof"`
1553
+}
1554
+type FileAction_Rm struct {
1555
+	Rm *FileActionRm `protobuf:"bytes,7,opt,name=rm,proto3,oneof"`
1556
+}
1557
+
1558
+func (*FileAction_Copy) isFileAction_Action()   {}
1559
+func (*FileAction_Mkfile) isFileAction_Action() {}
1560
+func (*FileAction_Mkdir) isFileAction_Action()  {}
1561
+func (*FileAction_Rm) isFileAction_Action()     {}
1562
+
1563
+func (m *FileAction) GetAction() isFileAction_Action {
1564
+	if m != nil {
1565
+		return m.Action
1566
+	}
1567
+	return nil
1568
+}
1569
+
1570
+func (m *FileAction) GetCopy() *FileActionCopy {
1571
+	if x, ok := m.GetAction().(*FileAction_Copy); ok {
1572
+		return x.Copy
1573
+	}
1574
+	return nil
1575
+}
1576
+
1577
+func (m *FileAction) GetMkfile() *FileActionMkFile {
1578
+	if x, ok := m.GetAction().(*FileAction_Mkfile); ok {
1579
+		return x.Mkfile
1580
+	}
1581
+	return nil
1582
+}
1583
+
1584
+func (m *FileAction) GetMkdir() *FileActionMkDir {
1585
+	if x, ok := m.GetAction().(*FileAction_Mkdir); ok {
1586
+		return x.Mkdir
1587
+	}
1588
+	return nil
1589
+}
1590
+
1591
+func (m *FileAction) GetRm() *FileActionRm {
1592
+	if x, ok := m.GetAction().(*FileAction_Rm); ok {
1593
+		return x.Rm
1594
+	}
1595
+	return nil
1596
+}
1597
+
1598
+// XXX_OneofFuncs is for the internal use of the proto package.
1599
+func (*FileAction) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
1600
+	return _FileAction_OneofMarshaler, _FileAction_OneofUnmarshaler, _FileAction_OneofSizer, []interface{}{
1601
+		(*FileAction_Copy)(nil),
1602
+		(*FileAction_Mkfile)(nil),
1603
+		(*FileAction_Mkdir)(nil),
1604
+		(*FileAction_Rm)(nil),
1605
+	}
1606
+}
1607
+
1608
+func _FileAction_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
1609
+	m := msg.(*FileAction)
1610
+	// action
1611
+	switch x := m.Action.(type) {
1612
+	case *FileAction_Copy:
1613
+		_ = b.EncodeVarint(4<<3 | proto.WireBytes)
1614
+		if err := b.EncodeMessage(x.Copy); err != nil {
1615
+			return err
1616
+		}
1617
+	case *FileAction_Mkfile:
1618
+		_ = b.EncodeVarint(5<<3 | proto.WireBytes)
1619
+		if err := b.EncodeMessage(x.Mkfile); err != nil {
1620
+			return err
1621
+		}
1622
+	case *FileAction_Mkdir:
1623
+		_ = b.EncodeVarint(6<<3 | proto.WireBytes)
1624
+		if err := b.EncodeMessage(x.Mkdir); err != nil {
1625
+			return err
1626
+		}
1627
+	case *FileAction_Rm:
1628
+		_ = b.EncodeVarint(7<<3 | proto.WireBytes)
1629
+		if err := b.EncodeMessage(x.Rm); err != nil {
1630
+			return err
1631
+		}
1632
+	case nil:
1633
+	default:
1634
+		return fmt.Errorf("FileAction.Action has unexpected type %T", x)
1635
+	}
1636
+	return nil
1637
+}
1638
+
1639
+func _FileAction_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
1640
+	m := msg.(*FileAction)
1641
+	switch tag {
1642
+	case 4: // action.copy
1643
+		if wire != proto.WireBytes {
1644
+			return true, proto.ErrInternalBadWireType
1645
+		}
1646
+		msg := new(FileActionCopy)
1647
+		err := b.DecodeMessage(msg)
1648
+		m.Action = &FileAction_Copy{msg}
1649
+		return true, err
1650
+	case 5: // action.mkfile
1651
+		if wire != proto.WireBytes {
1652
+			return true, proto.ErrInternalBadWireType
1653
+		}
1654
+		msg := new(FileActionMkFile)
1655
+		err := b.DecodeMessage(msg)
1656
+		m.Action = &FileAction_Mkfile{msg}
1657
+		return true, err
1658
+	case 6: // action.mkdir
1659
+		if wire != proto.WireBytes {
1660
+			return true, proto.ErrInternalBadWireType
1661
+		}
1662
+		msg := new(FileActionMkDir)
1663
+		err := b.DecodeMessage(msg)
1664
+		m.Action = &FileAction_Mkdir{msg}
1665
+		return true, err
1666
+	case 7: // action.rm
1667
+		if wire != proto.WireBytes {
1668
+			return true, proto.ErrInternalBadWireType
1669
+		}
1670
+		msg := new(FileActionRm)
1671
+		err := b.DecodeMessage(msg)
1672
+		m.Action = &FileAction_Rm{msg}
1673
+		return true, err
1674
+	default:
1675
+		return false, nil
1676
+	}
1677
+}
1678
+
1679
+func _FileAction_OneofSizer(msg proto.Message) (n int) {
1680
+	m := msg.(*FileAction)
1681
+	// action
1682
+	switch x := m.Action.(type) {
1683
+	case *FileAction_Copy:
1684
+		s := proto.Size(x.Copy)
1685
+		n += 1 // tag and wire
1686
+		n += proto.SizeVarint(uint64(s))
1687
+		n += s
1688
+	case *FileAction_Mkfile:
1689
+		s := proto.Size(x.Mkfile)
1690
+		n += 1 // tag and wire
1691
+		n += proto.SizeVarint(uint64(s))
1692
+		n += s
1693
+	case *FileAction_Mkdir:
1694
+		s := proto.Size(x.Mkdir)
1695
+		n += 1 // tag and wire
1696
+		n += proto.SizeVarint(uint64(s))
1697
+		n += s
1698
+	case *FileAction_Rm:
1699
+		s := proto.Size(x.Rm)
1700
+		n += 1 // tag and wire
1701
+		n += proto.SizeVarint(uint64(s))
1702
+		n += s
1703
+	case nil:
1704
+	default:
1705
+		panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
1706
+	}
1707
+	return n
1708
+}
1709
+
1710
+type FileActionCopy struct {
1711
+	// src is the source path
1712
+	Src string `protobuf:"bytes,1,opt,name=src,proto3" json:"src,omitempty"`
1713
+	// dest path
1714
+	Dest string `protobuf:"bytes,2,opt,name=dest,proto3" json:"dest,omitempty"`
1715
+	// optional owner override
1716
+	Owner *ChownOpt `protobuf:"bytes,3,opt,name=owner,proto3" json:"owner,omitempty"`
1717
+	// optional permission bits override
1718
+	Mode int32 `protobuf:"varint,4,opt,name=mode,proto3" json:"mode,omitempty"`
1719
+	// followSymlink resolves symlinks in src
1720
+	FollowSymlink bool `protobuf:"varint,5,opt,name=followSymlink,proto3" json:"followSymlink,omitempty"`
1721
+	// dirCopyContents only copies contents if src is a directory
1722
+	DirCopyContents bool `protobuf:"varint,6,opt,name=dirCopyContents,proto3" json:"dirCopyContents,omitempty"`
1723
+	// attemptUnpackDockerCompatibility detects if src is an archive to unpack it instead
1724
+	AttemptUnpackDockerCompatibility bool `protobuf:"varint,7,opt,name=attemptUnpackDockerCompatibility,proto3" json:"attemptUnpackDockerCompatibility,omitempty"`
1725
+	// createDestPath creates dest path directories if needed
1726
+	CreateDestPath bool `protobuf:"varint,8,opt,name=createDestPath,proto3" json:"createDestPath,omitempty"`
1727
+	// allowWildcard allows filepath.Match wildcards in src path
1728
+	AllowWildcard bool `protobuf:"varint,9,opt,name=allowWildcard,proto3" json:"allowWildcard,omitempty"`
1729
+	// allowEmptyWildcard doesn't fail the whole copy if wildcard doesn't resolve to files
1730
+	AllowEmptyWildcard bool `protobuf:"varint,10,opt,name=allowEmptyWildcard,proto3" json:"allowEmptyWildcard,omitempty"`
1731
+	// optional created time override
1732
+	Timestamp int64 `protobuf:"varint,11,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
1733
+}
1734
+
1735
+func (m *FileActionCopy) Reset()         { *m = FileActionCopy{} }
1736
+func (m *FileActionCopy) String() string { return proto.CompactTextString(m) }
1737
+func (*FileActionCopy) ProtoMessage()    {}
1738
+func (*FileActionCopy) Descriptor() ([]byte, []int) {
1739
+	return fileDescriptor_ops_8d64813b9835ab08, []int{20}
1740
+}
1741
+func (m *FileActionCopy) XXX_Unmarshal(b []byte) error {
1742
+	return m.Unmarshal(b)
1743
+}
1744
+func (m *FileActionCopy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
1745
+	b = b[:cap(b)]
1746
+	n, err := m.MarshalTo(b)
1747
+	if err != nil {
1748
+		return nil, err
1749
+	}
1750
+	return b[:n], nil
1751
+}
1752
+func (dst *FileActionCopy) XXX_Merge(src proto.Message) {
1753
+	xxx_messageInfo_FileActionCopy.Merge(dst, src)
1754
+}
1755
+func (m *FileActionCopy) XXX_Size() int {
1756
+	return m.Size()
1757
+}
1758
+func (m *FileActionCopy) XXX_DiscardUnknown() {
1759
+	xxx_messageInfo_FileActionCopy.DiscardUnknown(m)
1760
+}
1761
+
1762
+var xxx_messageInfo_FileActionCopy proto.InternalMessageInfo
1763
+
1764
+func (m *FileActionCopy) GetSrc() string {
1765
+	if m != nil {
1766
+		return m.Src
1767
+	}
1768
+	return ""
1769
+}
1770
+
1771
+func (m *FileActionCopy) GetDest() string {
1772
+	if m != nil {
1773
+		return m.Dest
1774
+	}
1775
+	return ""
1776
+}
1777
+
1778
+func (m *FileActionCopy) GetOwner() *ChownOpt {
1779
+	if m != nil {
1780
+		return m.Owner
1781
+	}
1782
+	return nil
1783
+}
1784
+
1785
+func (m *FileActionCopy) GetMode() int32 {
1786
+	if m != nil {
1787
+		return m.Mode
1788
+	}
1789
+	return 0
1790
+}
1791
+
1792
+func (m *FileActionCopy) GetFollowSymlink() bool {
1793
+	if m != nil {
1794
+		return m.FollowSymlink
1795
+	}
1796
+	return false
1797
+}
1798
+
1799
+func (m *FileActionCopy) GetDirCopyContents() bool {
1800
+	if m != nil {
1801
+		return m.DirCopyContents
1802
+	}
1803
+	return false
1804
+}
1805
+
1806
+func (m *FileActionCopy) GetAttemptUnpackDockerCompatibility() bool {
1807
+	if m != nil {
1808
+		return m.AttemptUnpackDockerCompatibility
1809
+	}
1810
+	return false
1811
+}
1812
+
1813
+func (m *FileActionCopy) GetCreateDestPath() bool {
1814
+	if m != nil {
1815
+		return m.CreateDestPath
1816
+	}
1817
+	return false
1818
+}
1819
+
1820
+func (m *FileActionCopy) GetAllowWildcard() bool {
1821
+	if m != nil {
1822
+		return m.AllowWildcard
1823
+	}
1824
+	return false
1825
+}
1826
+
1827
+func (m *FileActionCopy) GetAllowEmptyWildcard() bool {
1828
+	if m != nil {
1829
+		return m.AllowEmptyWildcard
1830
+	}
1831
+	return false
1832
+}
1833
+
1834
+func (m *FileActionCopy) GetTimestamp() int64 {
1835
+	if m != nil {
1836
+		return m.Timestamp
1837
+	}
1838
+	return 0
1839
+}
1840
+
1841
+type FileActionMkFile struct {
1842
+	// path for the new file
1843
+	Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
1844
+	// permission bits
1845
+	Mode int32 `protobuf:"varint,2,opt,name=mode,proto3" json:"mode,omitempty"`
1846
+	// data is the new file contents
1847
+	Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"`
1848
+	// optional owner for the new file
1849
+	Owner *ChownOpt `protobuf:"bytes,4,opt,name=owner,proto3" json:"owner,omitempty"`
1850
+	// optional created time override
1851
+	Timestamp int64 `protobuf:"varint,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
1852
+}
1853
+
1854
+func (m *FileActionMkFile) Reset()         { *m = FileActionMkFile{} }
1855
+func (m *FileActionMkFile) String() string { return proto.CompactTextString(m) }
1856
+func (*FileActionMkFile) ProtoMessage()    {}
1857
+func (*FileActionMkFile) Descriptor() ([]byte, []int) {
1858
+	return fileDescriptor_ops_8d64813b9835ab08, []int{21}
1859
+}
1860
+func (m *FileActionMkFile) XXX_Unmarshal(b []byte) error {
1861
+	return m.Unmarshal(b)
1862
+}
1863
+func (m *FileActionMkFile) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
1864
+	b = b[:cap(b)]
1865
+	n, err := m.MarshalTo(b)
1866
+	if err != nil {
1867
+		return nil, err
1868
+	}
1869
+	return b[:n], nil
1870
+}
1871
+func (dst *FileActionMkFile) XXX_Merge(src proto.Message) {
1872
+	xxx_messageInfo_FileActionMkFile.Merge(dst, src)
1873
+}
1874
+func (m *FileActionMkFile) XXX_Size() int {
1875
+	return m.Size()
1876
+}
1877
+func (m *FileActionMkFile) XXX_DiscardUnknown() {
1878
+	xxx_messageInfo_FileActionMkFile.DiscardUnknown(m)
1879
+}
1880
+
1881
+var xxx_messageInfo_FileActionMkFile proto.InternalMessageInfo
1882
+
1883
+func (m *FileActionMkFile) GetPath() string {
1884
+	if m != nil {
1885
+		return m.Path
1886
+	}
1887
+	return ""
1888
+}
1889
+
1890
+func (m *FileActionMkFile) GetMode() int32 {
1891
+	if m != nil {
1892
+		return m.Mode
1893
+	}
1894
+	return 0
1895
+}
1896
+
1897
+func (m *FileActionMkFile) GetData() []byte {
1898
+	if m != nil {
1899
+		return m.Data
1900
+	}
1901
+	return nil
1902
+}
1903
+
1904
+func (m *FileActionMkFile) GetOwner() *ChownOpt {
1905
+	if m != nil {
1906
+		return m.Owner
1907
+	}
1908
+	return nil
1909
+}
1910
+
1911
+func (m *FileActionMkFile) GetTimestamp() int64 {
1912
+	if m != nil {
1913
+		return m.Timestamp
1914
+	}
1915
+	return 0
1916
+}
1917
+
1918
+type FileActionMkDir struct {
1919
+	// path for the new directory
1920
+	Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
1921
+	// permission bits
1922
+	Mode int32 `protobuf:"varint,2,opt,name=mode,proto3" json:"mode,omitempty"`
1923
+	// makeParents creates parent directories as well if needed
1924
+	MakeParents bool `protobuf:"varint,3,opt,name=makeParents,proto3" json:"makeParents,omitempty"`
1925
+	// optional owner for the new directory
1926
+	Owner *ChownOpt `protobuf:"bytes,4,opt,name=owner,proto3" json:"owner,omitempty"`
1927
+	// optional created time override
1928
+	Timestamp int64 `protobuf:"varint,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
1929
+}
1930
+
1931
+func (m *FileActionMkDir) Reset()         { *m = FileActionMkDir{} }
1932
+func (m *FileActionMkDir) String() string { return proto.CompactTextString(m) }
1933
+func (*FileActionMkDir) ProtoMessage()    {}
1934
+func (*FileActionMkDir) Descriptor() ([]byte, []int) {
1935
+	return fileDescriptor_ops_8d64813b9835ab08, []int{22}
1936
+}
1937
+func (m *FileActionMkDir) XXX_Unmarshal(b []byte) error {
1938
+	return m.Unmarshal(b)
1939
+}
1940
+func (m *FileActionMkDir) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
1941
+	b = b[:cap(b)]
1942
+	n, err := m.MarshalTo(b)
1943
+	if err != nil {
1944
+		return nil, err
1945
+	}
1946
+	return b[:n], nil
1947
+}
1948
+func (dst *FileActionMkDir) XXX_Merge(src proto.Message) {
1949
+	xxx_messageInfo_FileActionMkDir.Merge(dst, src)
1950
+}
1951
+func (m *FileActionMkDir) XXX_Size() int {
1952
+	return m.Size()
1953
+}
1954
+func (m *FileActionMkDir) XXX_DiscardUnknown() {
1955
+	xxx_messageInfo_FileActionMkDir.DiscardUnknown(m)
1956
+}
1957
+
1958
+var xxx_messageInfo_FileActionMkDir proto.InternalMessageInfo
1959
+
1960
+func (m *FileActionMkDir) GetPath() string {
1961
+	if m != nil {
1962
+		return m.Path
1963
+	}
1964
+	return ""
1965
+}
1966
+
1967
+func (m *FileActionMkDir) GetMode() int32 {
1968
+	if m != nil {
1969
+		return m.Mode
1970
+	}
1971
+	return 0
1972
+}
1973
+
1974
+func (m *FileActionMkDir) GetMakeParents() bool {
1975
+	if m != nil {
1976
+		return m.MakeParents
1977
+	}
1978
+	return false
1979
+}
1980
+
1981
+func (m *FileActionMkDir) GetOwner() *ChownOpt {
1982
+	if m != nil {
1983
+		return m.Owner
1984
+	}
1985
+	return nil
1986
+}
1987
+
1988
+func (m *FileActionMkDir) GetTimestamp() int64 {
1989
+	if m != nil {
1990
+		return m.Timestamp
1991
+	}
1992
+	return 0
1993
+}
1994
+
1995
+type FileActionRm struct {
1996
+	// path to remove
1997
+	Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
1998
+	// allowNotFound doesn't fail the rm if file is not found
1999
+	AllowNotFound bool `protobuf:"varint,2,opt,name=allowNotFound,proto3" json:"allowNotFound,omitempty"`
2000
+	// allowWildcard allows filepath.Match wildcards in path
2001
+	AllowWildcard bool `protobuf:"varint,3,opt,name=allowWildcard,proto3" json:"allowWildcard,omitempty"`
2002
+}
2003
+
2004
+func (m *FileActionRm) Reset()         { *m = FileActionRm{} }
2005
+func (m *FileActionRm) String() string { return proto.CompactTextString(m) }
2006
+func (*FileActionRm) ProtoMessage()    {}
2007
+func (*FileActionRm) Descriptor() ([]byte, []int) {
2008
+	return fileDescriptor_ops_8d64813b9835ab08, []int{23}
2009
+}
2010
+func (m *FileActionRm) XXX_Unmarshal(b []byte) error {
2011
+	return m.Unmarshal(b)
2012
+}
2013
+func (m *FileActionRm) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
2014
+	b = b[:cap(b)]
2015
+	n, err := m.MarshalTo(b)
2016
+	if err != nil {
2017
+		return nil, err
2018
+	}
2019
+	return b[:n], nil
2020
+}
2021
+func (dst *FileActionRm) XXX_Merge(src proto.Message) {
2022
+	xxx_messageInfo_FileActionRm.Merge(dst, src)
2023
+}
2024
+func (m *FileActionRm) XXX_Size() int {
2025
+	return m.Size()
2026
+}
2027
+func (m *FileActionRm) XXX_DiscardUnknown() {
2028
+	xxx_messageInfo_FileActionRm.DiscardUnknown(m)
2029
+}
2030
+
2031
+var xxx_messageInfo_FileActionRm proto.InternalMessageInfo
2032
+
2033
+func (m *FileActionRm) GetPath() string {
2034
+	if m != nil {
2035
+		return m.Path
2036
+	}
2037
+	return ""
2038
+}
2039
+
2040
+func (m *FileActionRm) GetAllowNotFound() bool {
2041
+	if m != nil {
2042
+		return m.AllowNotFound
2043
+	}
2044
+	return false
2045
+}
2046
+
2047
+func (m *FileActionRm) GetAllowWildcard() bool {
2048
+	if m != nil {
2049
+		return m.AllowWildcard
2050
+	}
2051
+	return false
2052
+}
2053
+
2054
+type ChownOpt struct {
2055
+	User  *UserOpt `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
2056
+	Group *UserOpt `protobuf:"bytes,2,opt,name=group,proto3" json:"group,omitempty"`
2057
+}
2058
+
2059
+func (m *ChownOpt) Reset()         { *m = ChownOpt{} }
2060
+func (m *ChownOpt) String() string { return proto.CompactTextString(m) }
2061
+func (*ChownOpt) ProtoMessage()    {}
2062
+func (*ChownOpt) Descriptor() ([]byte, []int) {
2063
+	return fileDescriptor_ops_8d64813b9835ab08, []int{24}
2064
+}
2065
+func (m *ChownOpt) XXX_Unmarshal(b []byte) error {
2066
+	return m.Unmarshal(b)
2067
+}
2068
+func (m *ChownOpt) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
2069
+	b = b[:cap(b)]
2070
+	n, err := m.MarshalTo(b)
2071
+	if err != nil {
2072
+		return nil, err
2073
+	}
2074
+	return b[:n], nil
2075
+}
2076
+func (dst *ChownOpt) XXX_Merge(src proto.Message) {
2077
+	xxx_messageInfo_ChownOpt.Merge(dst, src)
2078
+}
2079
+func (m *ChownOpt) XXX_Size() int {
2080
+	return m.Size()
2081
+}
2082
+func (m *ChownOpt) XXX_DiscardUnknown() {
2083
+	xxx_messageInfo_ChownOpt.DiscardUnknown(m)
2084
+}
2085
+
2086
+var xxx_messageInfo_ChownOpt proto.InternalMessageInfo
2087
+
2088
+func (m *ChownOpt) GetUser() *UserOpt {
2089
+	if m != nil {
2090
+		return m.User
2091
+	}
2092
+	return nil
2093
+}
2094
+
2095
+func (m *ChownOpt) GetGroup() *UserOpt {
2096
+	if m != nil {
2097
+		return m.Group
2098
+	}
2099
+	return nil
2100
+}
2101
+
2102
+type UserOpt struct {
2103
+	// Types that are valid to be assigned to User:
2104
+	//	*UserOpt_ByName
2105
+	//	*UserOpt_ByID
2106
+	User isUserOpt_User `protobuf_oneof:"user"`
2107
+}
2108
+
2109
+func (m *UserOpt) Reset()         { *m = UserOpt{} }
2110
+func (m *UserOpt) String() string { return proto.CompactTextString(m) }
2111
+func (*UserOpt) ProtoMessage()    {}
2112
+func (*UserOpt) Descriptor() ([]byte, []int) {
2113
+	return fileDescriptor_ops_8d64813b9835ab08, []int{25}
2114
+}
2115
+func (m *UserOpt) XXX_Unmarshal(b []byte) error {
2116
+	return m.Unmarshal(b)
2117
+}
2118
+func (m *UserOpt) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
2119
+	b = b[:cap(b)]
2120
+	n, err := m.MarshalTo(b)
2121
+	if err != nil {
2122
+		return nil, err
2123
+	}
2124
+	return b[:n], nil
2125
+}
2126
+func (dst *UserOpt) XXX_Merge(src proto.Message) {
2127
+	xxx_messageInfo_UserOpt.Merge(dst, src)
2128
+}
2129
+func (m *UserOpt) XXX_Size() int {
2130
+	return m.Size()
2131
+}
2132
+func (m *UserOpt) XXX_DiscardUnknown() {
2133
+	xxx_messageInfo_UserOpt.DiscardUnknown(m)
2134
+}
2135
+
2136
+var xxx_messageInfo_UserOpt proto.InternalMessageInfo
2137
+
2138
+type isUserOpt_User interface {
2139
+	isUserOpt_User()
2140
+	MarshalTo([]byte) (int, error)
2141
+	Size() int
2142
+}
2143
+
2144
+type UserOpt_ByName struct {
2145
+	ByName *NamedUserOpt `protobuf:"bytes,1,opt,name=byName,proto3,oneof"`
2146
+}
2147
+type UserOpt_ByID struct {
2148
+	ByID uint32 `protobuf:"varint,2,opt,name=byID,proto3,oneof"`
2149
+}
2150
+
2151
+func (*UserOpt_ByName) isUserOpt_User() {}
2152
+func (*UserOpt_ByID) isUserOpt_User()   {}
2153
+
2154
+func (m *UserOpt) GetUser() isUserOpt_User {
2155
+	if m != nil {
2156
+		return m.User
2157
+	}
2158
+	return nil
2159
+}
2160
+
2161
+func (m *UserOpt) GetByName() *NamedUserOpt {
2162
+	if x, ok := m.GetUser().(*UserOpt_ByName); ok {
2163
+		return x.ByName
2164
+	}
2165
+	return nil
2166
+}
2167
+
2168
+func (m *UserOpt) GetByID() uint32 {
2169
+	if x, ok := m.GetUser().(*UserOpt_ByID); ok {
2170
+		return x.ByID
2171
+	}
2172
+	return 0
2173
+}
2174
+
2175
+// XXX_OneofFuncs is for the internal use of the proto package.
2176
+func (*UserOpt) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
2177
+	return _UserOpt_OneofMarshaler, _UserOpt_OneofUnmarshaler, _UserOpt_OneofSizer, []interface{}{
2178
+		(*UserOpt_ByName)(nil),
2179
+		(*UserOpt_ByID)(nil),
2180
+	}
2181
+}
2182
+
2183
+func _UserOpt_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
2184
+	m := msg.(*UserOpt)
2185
+	// user
2186
+	switch x := m.User.(type) {
2187
+	case *UserOpt_ByName:
2188
+		_ = b.EncodeVarint(1<<3 | proto.WireBytes)
2189
+		if err := b.EncodeMessage(x.ByName); err != nil {
2190
+			return err
2191
+		}
2192
+	case *UserOpt_ByID:
2193
+		_ = b.EncodeVarint(2<<3 | proto.WireVarint)
2194
+		_ = b.EncodeVarint(uint64(x.ByID))
2195
+	case nil:
2196
+	default:
2197
+		return fmt.Errorf("UserOpt.User has unexpected type %T", x)
2198
+	}
2199
+	return nil
2200
+}
2201
+
2202
+func _UserOpt_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
2203
+	m := msg.(*UserOpt)
2204
+	switch tag {
2205
+	case 1: // user.byName
2206
+		if wire != proto.WireBytes {
2207
+			return true, proto.ErrInternalBadWireType
2208
+		}
2209
+		msg := new(NamedUserOpt)
2210
+		err := b.DecodeMessage(msg)
2211
+		m.User = &UserOpt_ByName{msg}
2212
+		return true, err
2213
+	case 2: // user.byID
2214
+		if wire != proto.WireVarint {
2215
+			return true, proto.ErrInternalBadWireType
2216
+		}
2217
+		x, err := b.DecodeVarint()
2218
+		m.User = &UserOpt_ByID{uint32(x)}
2219
+		return true, err
2220
+	default:
2221
+		return false, nil
2222
+	}
2223
+}
2224
+
2225
+func _UserOpt_OneofSizer(msg proto.Message) (n int) {
2226
+	m := msg.(*UserOpt)
2227
+	// user
2228
+	switch x := m.User.(type) {
2229
+	case *UserOpt_ByName:
2230
+		s := proto.Size(x.ByName)
2231
+		n += 1 // tag and wire
2232
+		n += proto.SizeVarint(uint64(s))
2233
+		n += s
2234
+	case *UserOpt_ByID:
2235
+		n += 1 // tag and wire
2236
+		n += proto.SizeVarint(uint64(x.ByID))
2237
+	case nil:
2238
+	default:
2239
+		panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
2240
+	}
2241
+	return n
2242
+}
2243
+
2244
+type NamedUserOpt struct {
2245
+	Name  string     `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
2246
+	Input InputIndex `protobuf:"varint,2,opt,name=input,proto3,customtype=InputIndex" json:"input"`
2247
+}
2248
+
2249
+func (m *NamedUserOpt) Reset()         { *m = NamedUserOpt{} }
2250
+func (m *NamedUserOpt) String() string { return proto.CompactTextString(m) }
2251
+func (*NamedUserOpt) ProtoMessage()    {}
2252
+func (*NamedUserOpt) Descriptor() ([]byte, []int) {
2253
+	return fileDescriptor_ops_8d64813b9835ab08, []int{26}
2254
+}
2255
+func (m *NamedUserOpt) XXX_Unmarshal(b []byte) error {
2256
+	return m.Unmarshal(b)
2257
+}
2258
+func (m *NamedUserOpt) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
2259
+	b = b[:cap(b)]
2260
+	n, err := m.MarshalTo(b)
2261
+	if err != nil {
2262
+		return nil, err
2263
+	}
2264
+	return b[:n], nil
2265
+}
2266
+func (dst *NamedUserOpt) XXX_Merge(src proto.Message) {
2267
+	xxx_messageInfo_NamedUserOpt.Merge(dst, src)
2268
+}
2269
+func (m *NamedUserOpt) XXX_Size() int {
2270
+	return m.Size()
2271
+}
2272
+func (m *NamedUserOpt) XXX_DiscardUnknown() {
2273
+	xxx_messageInfo_NamedUserOpt.DiscardUnknown(m)
2274
+}
2275
+
2276
+var xxx_messageInfo_NamedUserOpt proto.InternalMessageInfo
2277
+
2278
+func (m *NamedUserOpt) GetName() string {
2279
+	if m != nil {
2280
+		return m.Name
2281
+	}
2282
+	return ""
2283
+}
2284
+
1458 2285
 func init() {
1459 2286
 	proto.RegisterType((*Op)(nil), "pb.Op")
1460 2287
 	proto.RegisterType((*Platform)(nil), "pb.Platform")
... ...
@@ -1465,8 +2201,6 @@ func init() {
1465 1465
 	proto.RegisterType((*CacheOpt)(nil), "pb.CacheOpt")
1466 1466
 	proto.RegisterType((*SecretOpt)(nil), "pb.SecretOpt")
1467 1467
 	proto.RegisterType((*SSHOpt)(nil), "pb.SSHOpt")
1468
-	proto.RegisterType((*CopyOp)(nil), "pb.CopyOp")
1469
-	proto.RegisterType((*CopySource)(nil), "pb.CopySource")
1470 1468
 	proto.RegisterType((*SourceOp)(nil), "pb.SourceOp")
1471 1469
 	proto.RegisterMapType((map[string]string)(nil), "pb.SourceOp.AttrsEntry")
1472 1470
 	proto.RegisterType((*BuildOp)(nil), "pb.BuildOp")
... ...
@@ -1482,6 +2216,15 @@ func init() {
1482 1482
 	proto.RegisterType((*Definition)(nil), "pb.Definition")
1483 1483
 	proto.RegisterMapType((map[github_com_opencontainers_go_digest.Digest]OpMetadata)(nil), "pb.Definition.MetadataEntry")
1484 1484
 	proto.RegisterType((*HostIP)(nil), "pb.HostIP")
1485
+	proto.RegisterType((*FileOp)(nil), "pb.FileOp")
1486
+	proto.RegisterType((*FileAction)(nil), "pb.FileAction")
1487
+	proto.RegisterType((*FileActionCopy)(nil), "pb.FileActionCopy")
1488
+	proto.RegisterType((*FileActionMkFile)(nil), "pb.FileActionMkFile")
1489
+	proto.RegisterType((*FileActionMkDir)(nil), "pb.FileActionMkDir")
1490
+	proto.RegisterType((*FileActionRm)(nil), "pb.FileActionRm")
1491
+	proto.RegisterType((*ChownOpt)(nil), "pb.ChownOpt")
1492
+	proto.RegisterType((*UserOpt)(nil), "pb.UserOpt")
1493
+	proto.RegisterType((*NamedUserOpt)(nil), "pb.NamedUserOpt")
1485 1494
 	proto.RegisterEnum("pb.NetMode", NetMode_name, NetMode_value)
1486 1495
 	proto.RegisterEnum("pb.MountType", MountType_name, MountType_value)
1487 1496
 	proto.RegisterEnum("pb.CacheSharingOpt", CacheSharingOpt_name, CacheSharingOpt_value)
... ...
@@ -1571,13 +2314,13 @@ func (m *Op_Source) MarshalTo(dAtA []byte) (int, error) {
1571 1571
 	}
1572 1572
 	return i, nil
1573 1573
 }
1574
-func (m *Op_Copy) MarshalTo(dAtA []byte) (int, error) {
1574
+func (m *Op_File) MarshalTo(dAtA []byte) (int, error) {
1575 1575
 	i := 0
1576
-	if m.Copy != nil {
1576
+	if m.File != nil {
1577 1577
 		dAtA[i] = 0x22
1578 1578
 		i++
1579
-		i = encodeVarintOps(dAtA, i, uint64(m.Copy.Size()))
1580
-		n6, err := m.Copy.MarshalTo(dAtA[i:])
1579
+		i = encodeVarintOps(dAtA, i, uint64(m.File.Size()))
1580
+		n6, err := m.File.MarshalTo(dAtA[i:])
1581 1581
 		if err != nil {
1582 1582
 			return 0, err
1583 1583
 		}
... ...
@@ -2030,71 +2773,6 @@ func (m *SSHOpt) MarshalTo(dAtA []byte) (int, error) {
2030 2030
 	return i, nil
2031 2031
 }
2032 2032
 
2033
-func (m *CopyOp) Marshal() (dAtA []byte, err error) {
2034
-	size := m.Size()
2035
-	dAtA = make([]byte, size)
2036
-	n, err := m.MarshalTo(dAtA)
2037
-	if err != nil {
2038
-		return nil, err
2039
-	}
2040
-	return dAtA[:n], nil
2041
-}
2042
-
2043
-func (m *CopyOp) MarshalTo(dAtA []byte) (int, error) {
2044
-	var i int
2045
-	_ = i
2046
-	var l int
2047
-	_ = l
2048
-	if len(m.Src) > 0 {
2049
-		for _, msg := range m.Src {
2050
-			dAtA[i] = 0xa
2051
-			i++
2052
-			i = encodeVarintOps(dAtA, i, uint64(msg.Size()))
2053
-			n, err := msg.MarshalTo(dAtA[i:])
2054
-			if err != nil {
2055
-				return 0, err
2056
-			}
2057
-			i += n
2058
-		}
2059
-	}
2060
-	if len(m.Dest) > 0 {
2061
-		dAtA[i] = 0x12
2062
-		i++
2063
-		i = encodeVarintOps(dAtA, i, uint64(len(m.Dest)))
2064
-		i += copy(dAtA[i:], m.Dest)
2065
-	}
2066
-	return i, nil
2067
-}
2068
-
2069
-func (m *CopySource) Marshal() (dAtA []byte, err error) {
2070
-	size := m.Size()
2071
-	dAtA = make([]byte, size)
2072
-	n, err := m.MarshalTo(dAtA)
2073
-	if err != nil {
2074
-		return nil, err
2075
-	}
2076
-	return dAtA[:n], nil
2077
-}
2078
-
2079
-func (m *CopySource) MarshalTo(dAtA []byte) (int, error) {
2080
-	var i int
2081
-	_ = i
2082
-	var l int
2083
-	_ = l
2084
-	if m.Input != 0 {
2085
-		dAtA[i] = 0x8
2086
-		i++
2087
-		i = encodeVarintOps(dAtA, i, uint64(m.Input))
2088
-	}
2089
-	if len(m.Selector) > 0 {
2090
-		dAtA[i] = 0x12
2091
-		i++
2092
-		i = encodeVarintOps(dAtA, i, uint64(len(m.Selector)))
2093
-		i += copy(dAtA[i:], m.Selector)
2094
-	}
2095
-	return i, nil
2096
-}
2097
-
2098 2033
 func (m *SourceOp) Marshal() (dAtA []byte, err error) {
2099 2034
 	size := m.Size()
2100 2035
 	dAtA = make([]byte, size)
... ...
@@ -2528,6 +3206,503 @@ func (m *HostIP) MarshalTo(dAtA []byte) (int, error) {
2528 2528
 	return i, nil
2529 2529
 }
2530 2530
 
2531
+func (m *FileOp) Marshal() (dAtA []byte, err error) {
2532
+	size := m.Size()
2533
+	dAtA = make([]byte, size)
2534
+	n, err := m.MarshalTo(dAtA)
2535
+	if err != nil {
2536
+		return nil, err
2537
+	}
2538
+	return dAtA[:n], nil
2539
+}
2540
+
2541
+func (m *FileOp) MarshalTo(dAtA []byte) (int, error) {
2542
+	var i int
2543
+	_ = i
2544
+	var l int
2545
+	_ = l
2546
+	if len(m.Actions) > 0 {
2547
+		for _, msg := range m.Actions {
2548
+			dAtA[i] = 0x12
2549
+			i++
2550
+			i = encodeVarintOps(dAtA, i, uint64(msg.Size()))
2551
+			n, err := msg.MarshalTo(dAtA[i:])
2552
+			if err != nil {
2553
+				return 0, err
2554
+			}
2555
+			i += n
2556
+		}
2557
+	}
2558
+	return i, nil
2559
+}
2560
+
2561
+func (m *FileAction) Marshal() (dAtA []byte, err error) {
2562
+	size := m.Size()
2563
+	dAtA = make([]byte, size)
2564
+	n, err := m.MarshalTo(dAtA)
2565
+	if err != nil {
2566
+		return nil, err
2567
+	}
2568
+	return dAtA[:n], nil
2569
+}
2570
+
2571
+func (m *FileAction) MarshalTo(dAtA []byte) (int, error) {
2572
+	var i int
2573
+	_ = i
2574
+	var l int
2575
+	_ = l
2576
+	if m.Input != 0 {
2577
+		dAtA[i] = 0x8
2578
+		i++
2579
+		i = encodeVarintOps(dAtA, i, uint64(m.Input))
2580
+	}
2581
+	if m.SecondaryInput != 0 {
2582
+		dAtA[i] = 0x10
2583
+		i++
2584
+		i = encodeVarintOps(dAtA, i, uint64(m.SecondaryInput))
2585
+	}
2586
+	if m.Output != 0 {
2587
+		dAtA[i] = 0x18
2588
+		i++
2589
+		i = encodeVarintOps(dAtA, i, uint64(m.Output))
2590
+	}
2591
+	if m.Action != nil {
2592
+		nn17, err := m.Action.MarshalTo(dAtA[i:])
2593
+		if err != nil {
2594
+			return 0, err
2595
+		}
2596
+		i += nn17
2597
+	}
2598
+	return i, nil
2599
+}
2600
+
2601
+func (m *FileAction_Copy) MarshalTo(dAtA []byte) (int, error) {
2602
+	i := 0
2603
+	if m.Copy != nil {
2604
+		dAtA[i] = 0x22
2605
+		i++
2606
+		i = encodeVarintOps(dAtA, i, uint64(m.Copy.Size()))
2607
+		n18, err := m.Copy.MarshalTo(dAtA[i:])
2608
+		if err != nil {
2609
+			return 0, err
2610
+		}
2611
+		i += n18
2612
+	}
2613
+	return i, nil
2614
+}
2615
+func (m *FileAction_Mkfile) MarshalTo(dAtA []byte) (int, error) {
2616
+	i := 0
2617
+	if m.Mkfile != nil {
2618
+		dAtA[i] = 0x2a
2619
+		i++
2620
+		i = encodeVarintOps(dAtA, i, uint64(m.Mkfile.Size()))
2621
+		n19, err := m.Mkfile.MarshalTo(dAtA[i:])
2622
+		if err != nil {
2623
+			return 0, err
2624
+		}
2625
+		i += n19
2626
+	}
2627
+	return i, nil
2628
+}
2629
+func (m *FileAction_Mkdir) MarshalTo(dAtA []byte) (int, error) {
2630
+	i := 0
2631
+	if m.Mkdir != nil {
2632
+		dAtA[i] = 0x32
2633
+		i++
2634
+		i = encodeVarintOps(dAtA, i, uint64(m.Mkdir.Size()))
2635
+		n20, err := m.Mkdir.MarshalTo(dAtA[i:])
2636
+		if err != nil {
2637
+			return 0, err
2638
+		}
2639
+		i += n20
2640
+	}
2641
+	return i, nil
2642
+}
2643
+func (m *FileAction_Rm) MarshalTo(dAtA []byte) (int, error) {
2644
+	i := 0
2645
+	if m.Rm != nil {
2646
+		dAtA[i] = 0x3a
2647
+		i++
2648
+		i = encodeVarintOps(dAtA, i, uint64(m.Rm.Size()))
2649
+		n21, err := m.Rm.MarshalTo(dAtA[i:])
2650
+		if err != nil {
2651
+			return 0, err
2652
+		}
2653
+		i += n21
2654
+	}
2655
+	return i, nil
2656
+}
2657
+func (m *FileActionCopy) Marshal() (dAtA []byte, err error) {
2658
+	size := m.Size()
2659
+	dAtA = make([]byte, size)
2660
+	n, err := m.MarshalTo(dAtA)
2661
+	if err != nil {
2662
+		return nil, err
2663
+	}
2664
+	return dAtA[:n], nil
2665
+}
2666
+
2667
+func (m *FileActionCopy) MarshalTo(dAtA []byte) (int, error) {
2668
+	var i int
2669
+	_ = i
2670
+	var l int
2671
+	_ = l
2672
+	if len(m.Src) > 0 {
2673
+		dAtA[i] = 0xa
2674
+		i++
2675
+		i = encodeVarintOps(dAtA, i, uint64(len(m.Src)))
2676
+		i += copy(dAtA[i:], m.Src)
2677
+	}
2678
+	if len(m.Dest) > 0 {
2679
+		dAtA[i] = 0x12
2680
+		i++
2681
+		i = encodeVarintOps(dAtA, i, uint64(len(m.Dest)))
2682
+		i += copy(dAtA[i:], m.Dest)
2683
+	}
2684
+	if m.Owner != nil {
2685
+		dAtA[i] = 0x1a
2686
+		i++
2687
+		i = encodeVarintOps(dAtA, i, uint64(m.Owner.Size()))
2688
+		n22, err := m.Owner.MarshalTo(dAtA[i:])
2689
+		if err != nil {
2690
+			return 0, err
2691
+		}
2692
+		i += n22
2693
+	}
2694
+	if m.Mode != 0 {
2695
+		dAtA[i] = 0x20
2696
+		i++
2697
+		i = encodeVarintOps(dAtA, i, uint64(m.Mode))
2698
+	}
2699
+	if m.FollowSymlink {
2700
+		dAtA[i] = 0x28
2701
+		i++
2702
+		if m.FollowSymlink {
2703
+			dAtA[i] = 1
2704
+		} else {
2705
+			dAtA[i] = 0
2706
+		}
2707
+		i++
2708
+	}
2709
+	if m.DirCopyContents {
2710
+		dAtA[i] = 0x30
2711
+		i++
2712
+		if m.DirCopyContents {
2713
+			dAtA[i] = 1
2714
+		} else {
2715
+			dAtA[i] = 0
2716
+		}
2717
+		i++
2718
+	}
2719
+	if m.AttemptUnpackDockerCompatibility {
2720
+		dAtA[i] = 0x38
2721
+		i++
2722
+		if m.AttemptUnpackDockerCompatibility {
2723
+			dAtA[i] = 1
2724
+		} else {
2725
+			dAtA[i] = 0
2726
+		}
2727
+		i++
2728
+	}
2729
+	if m.CreateDestPath {
2730
+		dAtA[i] = 0x40
2731
+		i++
2732
+		if m.CreateDestPath {
2733
+			dAtA[i] = 1
2734
+		} else {
2735
+			dAtA[i] = 0
2736
+		}
2737
+		i++
2738
+	}
2739
+	if m.AllowWildcard {
2740
+		dAtA[i] = 0x48
2741
+		i++
2742
+		if m.AllowWildcard {
2743
+			dAtA[i] = 1
2744
+		} else {
2745
+			dAtA[i] = 0
2746
+		}
2747
+		i++
2748
+	}
2749
+	if m.AllowEmptyWildcard {
2750
+		dAtA[i] = 0x50
2751
+		i++
2752
+		if m.AllowEmptyWildcard {
2753
+			dAtA[i] = 1
2754
+		} else {
2755
+			dAtA[i] = 0
2756
+		}
2757
+		i++
2758
+	}
2759
+	if m.Timestamp != 0 {
2760
+		dAtA[i] = 0x58
2761
+		i++
2762
+		i = encodeVarintOps(dAtA, i, uint64(m.Timestamp))
2763
+	}
2764
+	return i, nil
2765
+}
2766
+
2767
+func (m *FileActionMkFile) Marshal() (dAtA []byte, err error) {
2768
+	size := m.Size()
2769
+	dAtA = make([]byte, size)
2770
+	n, err := m.MarshalTo(dAtA)
2771
+	if err != nil {
2772
+		return nil, err
2773
+	}
2774
+	return dAtA[:n], nil
2775
+}
2776
+
2777
+func (m *FileActionMkFile) MarshalTo(dAtA []byte) (int, error) {
2778
+	var i int
2779
+	_ = i
2780
+	var l int
2781
+	_ = l
2782
+	if len(m.Path) > 0 {
2783
+		dAtA[i] = 0xa
2784
+		i++
2785
+		i = encodeVarintOps(dAtA, i, uint64(len(m.Path)))
2786
+		i += copy(dAtA[i:], m.Path)
2787
+	}
2788
+	if m.Mode != 0 {
2789
+		dAtA[i] = 0x10
2790
+		i++
2791
+		i = encodeVarintOps(dAtA, i, uint64(m.Mode))
2792
+	}
2793
+	if len(m.Data) > 0 {
2794
+		dAtA[i] = 0x1a
2795
+		i++
2796
+		i = encodeVarintOps(dAtA, i, uint64(len(m.Data)))
2797
+		i += copy(dAtA[i:], m.Data)
2798
+	}
2799
+	if m.Owner != nil {
2800
+		dAtA[i] = 0x22
2801
+		i++
2802
+		i = encodeVarintOps(dAtA, i, uint64(m.Owner.Size()))
2803
+		n23, err := m.Owner.MarshalTo(dAtA[i:])
2804
+		if err != nil {
2805
+			return 0, err
2806
+		}
2807
+		i += n23
2808
+	}
2809
+	if m.Timestamp != 0 {
2810
+		dAtA[i] = 0x28
2811
+		i++
2812
+		i = encodeVarintOps(dAtA, i, uint64(m.Timestamp))
2813
+	}
2814
+	return i, nil
2815
+}
2816
+
2817
+func (m *FileActionMkDir) Marshal() (dAtA []byte, err error) {
2818
+	size := m.Size()
2819
+	dAtA = make([]byte, size)
2820
+	n, err := m.MarshalTo(dAtA)
2821
+	if err != nil {
2822
+		return nil, err
2823
+	}
2824
+	return dAtA[:n], nil
2825
+}
2826
+
2827
+func (m *FileActionMkDir) MarshalTo(dAtA []byte) (int, error) {
2828
+	var i int
2829
+	_ = i
2830
+	var l int
2831
+	_ = l
2832
+	if len(m.Path) > 0 {
2833
+		dAtA[i] = 0xa
2834
+		i++
2835
+		i = encodeVarintOps(dAtA, i, uint64(len(m.Path)))
2836
+		i += copy(dAtA[i:], m.Path)
2837
+	}
2838
+	if m.Mode != 0 {
2839
+		dAtA[i] = 0x10
2840
+		i++
2841
+		i = encodeVarintOps(dAtA, i, uint64(m.Mode))
2842
+	}
2843
+	if m.MakeParents {
2844
+		dAtA[i] = 0x18
2845
+		i++
2846
+		if m.MakeParents {
2847
+			dAtA[i] = 1
2848
+		} else {
2849
+			dAtA[i] = 0
2850
+		}
2851
+		i++
2852
+	}
2853
+	if m.Owner != nil {
2854
+		dAtA[i] = 0x22
2855
+		i++
2856
+		i = encodeVarintOps(dAtA, i, uint64(m.Owner.Size()))
2857
+		n24, err := m.Owner.MarshalTo(dAtA[i:])
2858
+		if err != nil {
2859
+			return 0, err
2860
+		}
2861
+		i += n24
2862
+	}
2863
+	if m.Timestamp != 0 {
2864
+		dAtA[i] = 0x28
2865
+		i++
2866
+		i = encodeVarintOps(dAtA, i, uint64(m.Timestamp))
2867
+	}
2868
+	return i, nil
2869
+}
2870
+
2871
+func (m *FileActionRm) Marshal() (dAtA []byte, err error) {
2872
+	size := m.Size()
2873
+	dAtA = make([]byte, size)
2874
+	n, err := m.MarshalTo(dAtA)
2875
+	if err != nil {
2876
+		return nil, err
2877
+	}
2878
+	return dAtA[:n], nil
2879
+}
2880
+
2881
+func (m *FileActionRm) MarshalTo(dAtA []byte) (int, error) {
2882
+	var i int
2883
+	_ = i
2884
+	var l int
2885
+	_ = l
2886
+	if len(m.Path) > 0 {
2887
+		dAtA[i] = 0xa
2888
+		i++
2889
+		i = encodeVarintOps(dAtA, i, uint64(len(m.Path)))
2890
+		i += copy(dAtA[i:], m.Path)
2891
+	}
2892
+	if m.AllowNotFound {
2893
+		dAtA[i] = 0x10
2894
+		i++
2895
+		if m.AllowNotFound {
2896
+			dAtA[i] = 1
2897
+		} else {
2898
+			dAtA[i] = 0
2899
+		}
2900
+		i++
2901
+	}
2902
+	if m.AllowWildcard {
2903
+		dAtA[i] = 0x18
2904
+		i++
2905
+		if m.AllowWildcard {
2906
+			dAtA[i] = 1
2907
+		} else {
2908
+			dAtA[i] = 0
2909
+		}
2910
+		i++
2911
+	}
2912
+	return i, nil
2913
+}
2914
+
2915
+func (m *ChownOpt) Marshal() (dAtA []byte, err error) {
2916
+	size := m.Size()
2917
+	dAtA = make([]byte, size)
2918
+	n, err := m.MarshalTo(dAtA)
2919
+	if err != nil {
2920
+		return nil, err
2921
+	}
2922
+	return dAtA[:n], nil
2923
+}
2924
+
2925
+func (m *ChownOpt) MarshalTo(dAtA []byte) (int, error) {
2926
+	var i int
2927
+	_ = i
2928
+	var l int
2929
+	_ = l
2930
+	if m.User != nil {
2931
+		dAtA[i] = 0xa
2932
+		i++
2933
+		i = encodeVarintOps(dAtA, i, uint64(m.User.Size()))
2934
+		n25, err := m.User.MarshalTo(dAtA[i:])
2935
+		if err != nil {
2936
+			return 0, err
2937
+		}
2938
+		i += n25
2939
+	}
2940
+	if m.Group != nil {
2941
+		dAtA[i] = 0x12
2942
+		i++
2943
+		i = encodeVarintOps(dAtA, i, uint64(m.Group.Size()))
2944
+		n26, err := m.Group.MarshalTo(dAtA[i:])
2945
+		if err != nil {
2946
+			return 0, err
2947
+		}
2948
+		i += n26
2949
+	}
2950
+	return i, nil
2951
+}
2952
+
2953
+func (m *UserOpt) Marshal() (dAtA []byte, err error) {
2954
+	size := m.Size()
2955
+	dAtA = make([]byte, size)
2956
+	n, err := m.MarshalTo(dAtA)
2957
+	if err != nil {
2958
+		return nil, err
2959
+	}
2960
+	return dAtA[:n], nil
2961
+}
2962
+
2963
+func (m *UserOpt) MarshalTo(dAtA []byte) (int, error) {
2964
+	var i int
2965
+	_ = i
2966
+	var l int
2967
+	_ = l
2968
+	if m.User != nil {
2969
+		nn27, err := m.User.MarshalTo(dAtA[i:])
2970
+		if err != nil {
2971
+			return 0, err
2972
+		}
2973
+		i += nn27
2974
+	}
2975
+	return i, nil
2976
+}
2977
+
2978
+func (m *UserOpt_ByName) MarshalTo(dAtA []byte) (int, error) {
2979
+	i := 0
2980
+	if m.ByName != nil {
2981
+		dAtA[i] = 0xa
2982
+		i++
2983
+		i = encodeVarintOps(dAtA, i, uint64(m.ByName.Size()))
2984
+		n28, err := m.ByName.MarshalTo(dAtA[i:])
2985
+		if err != nil {
2986
+			return 0, err
2987
+		}
2988
+		i += n28
2989
+	}
2990
+	return i, nil
2991
+}
2992
+func (m *UserOpt_ByID) MarshalTo(dAtA []byte) (int, error) {
2993
+	i := 0
2994
+	dAtA[i] = 0x10
2995
+	i++
2996
+	i = encodeVarintOps(dAtA, i, uint64(m.ByID))
2997
+	return i, nil
2998
+}
2999
+func (m *NamedUserOpt) Marshal() (dAtA []byte, err error) {
3000
+	size := m.Size()
3001
+	dAtA = make([]byte, size)
3002
+	n, err := m.MarshalTo(dAtA)
3003
+	if err != nil {
3004
+		return nil, err
3005
+	}
3006
+	return dAtA[:n], nil
3007
+}
3008
+
3009
+func (m *NamedUserOpt) MarshalTo(dAtA []byte) (int, error) {
3010
+	var i int
3011
+	_ = i
3012
+	var l int
3013
+	_ = l
3014
+	if len(m.Name) > 0 {
3015
+		dAtA[i] = 0xa
3016
+		i++
3017
+		i = encodeVarintOps(dAtA, i, uint64(len(m.Name)))
3018
+		i += copy(dAtA[i:], m.Name)
3019
+	}
3020
+	if m.Input != 0 {
3021
+		dAtA[i] = 0x10
3022
+		i++
3023
+		i = encodeVarintOps(dAtA, i, uint64(m.Input))
3024
+	}
3025
+	return i, nil
3026
+}
3027
+
2531 3028
 func encodeVarintOps(dAtA []byte, offset int, v uint64) int {
2532 3029
 	for v >= 1<<7 {
2533 3030
 		dAtA[offset] = uint8(v&0x7f | 0x80)
... ...
@@ -2587,14 +3762,14 @@ func (m *Op_Source) Size() (n int) {
2587 2587
 	}
2588 2588
 	return n
2589 2589
 }
2590
-func (m *Op_Copy) Size() (n int) {
2590
+func (m *Op_File) Size() (n int) {
2591 2591
 	if m == nil {
2592 2592
 		return 0
2593 2593
 	}
2594 2594
 	var l int
2595 2595
 	_ = l
2596
-	if m.Copy != nil {
2597
-		l = m.Copy.Size()
2596
+	if m.File != nil {
2597
+		l = m.File.Size()
2598 2598
 		n += 1 + l + sovOps(uint64(l))
2599 2599
 	}
2600 2600
 	return n
... ...
@@ -2826,41 +4001,6 @@ func (m *SSHOpt) Size() (n int) {
2826 2826
 	return n
2827 2827
 }
2828 2828
 
2829
-func (m *CopyOp) Size() (n int) {
2830
-	if m == nil {
2831
-		return 0
2832
-	}
2833
-	var l int
2834
-	_ = l
2835
-	if len(m.Src) > 0 {
2836
-		for _, e := range m.Src {
2837
-			l = e.Size()
2838
-			n += 1 + l + sovOps(uint64(l))
2839
-		}
2840
-	}
2841
-	l = len(m.Dest)
2842
-	if l > 0 {
2843
-		n += 1 + l + sovOps(uint64(l))
2844
-	}
2845
-	return n
2846
-}
2847
-
2848
-func (m *CopySource) Size() (n int) {
2849
-	if m == nil {
2850
-		return 0
2851
-	}
2852
-	var l int
2853
-	_ = l
2854
-	if m.Input != 0 {
2855
-		n += 1 + sovOps(uint64(m.Input))
2856
-	}
2857
-	l = len(m.Selector)
2858
-	if l > 0 {
2859
-		n += 1 + l + sovOps(uint64(l))
2860
-	}
2861
-	return n
2862
-}
2863
-
2864 2829
 func (m *SourceOp) Size() (n int) {
2865 2830
 	if m == nil {
2866 2831
 		return 0
... ...
@@ -3056,6 +4196,273 @@ func (m *HostIP) Size() (n int) {
3056 3056
 	return n
3057 3057
 }
3058 3058
 
3059
+func (m *FileOp) Size() (n int) {
3060
+	if m == nil {
3061
+		return 0
3062
+	}
3063
+	var l int
3064
+	_ = l
3065
+	if len(m.Actions) > 0 {
3066
+		for _, e := range m.Actions {
3067
+			l = e.Size()
3068
+			n += 1 + l + sovOps(uint64(l))
3069
+		}
3070
+	}
3071
+	return n
3072
+}
3073
+
3074
+func (m *FileAction) Size() (n int) {
3075
+	if m == nil {
3076
+		return 0
3077
+	}
3078
+	var l int
3079
+	_ = l
3080
+	if m.Input != 0 {
3081
+		n += 1 + sovOps(uint64(m.Input))
3082
+	}
3083
+	if m.SecondaryInput != 0 {
3084
+		n += 1 + sovOps(uint64(m.SecondaryInput))
3085
+	}
3086
+	if m.Output != 0 {
3087
+		n += 1 + sovOps(uint64(m.Output))
3088
+	}
3089
+	if m.Action != nil {
3090
+		n += m.Action.Size()
3091
+	}
3092
+	return n
3093
+}
3094
+
3095
+func (m *FileAction_Copy) Size() (n int) {
3096
+	if m == nil {
3097
+		return 0
3098
+	}
3099
+	var l int
3100
+	_ = l
3101
+	if m.Copy != nil {
3102
+		l = m.Copy.Size()
3103
+		n += 1 + l + sovOps(uint64(l))
3104
+	}
3105
+	return n
3106
+}
3107
+func (m *FileAction_Mkfile) Size() (n int) {
3108
+	if m == nil {
3109
+		return 0
3110
+	}
3111
+	var l int
3112
+	_ = l
3113
+	if m.Mkfile != nil {
3114
+		l = m.Mkfile.Size()
3115
+		n += 1 + l + sovOps(uint64(l))
3116
+	}
3117
+	return n
3118
+}
3119
+func (m *FileAction_Mkdir) Size() (n int) {
3120
+	if m == nil {
3121
+		return 0
3122
+	}
3123
+	var l int
3124
+	_ = l
3125
+	if m.Mkdir != nil {
3126
+		l = m.Mkdir.Size()
3127
+		n += 1 + l + sovOps(uint64(l))
3128
+	}
3129
+	return n
3130
+}
3131
+func (m *FileAction_Rm) Size() (n int) {
3132
+	if m == nil {
3133
+		return 0
3134
+	}
3135
+	var l int
3136
+	_ = l
3137
+	if m.Rm != nil {
3138
+		l = m.Rm.Size()
3139
+		n += 1 + l + sovOps(uint64(l))
3140
+	}
3141
+	return n
3142
+}
3143
+func (m *FileActionCopy) Size() (n int) {
3144
+	if m == nil {
3145
+		return 0
3146
+	}
3147
+	var l int
3148
+	_ = l
3149
+	l = len(m.Src)
3150
+	if l > 0 {
3151
+		n += 1 + l + sovOps(uint64(l))
3152
+	}
3153
+	l = len(m.Dest)
3154
+	if l > 0 {
3155
+		n += 1 + l + sovOps(uint64(l))
3156
+	}
3157
+	if m.Owner != nil {
3158
+		l = m.Owner.Size()
3159
+		n += 1 + l + sovOps(uint64(l))
3160
+	}
3161
+	if m.Mode != 0 {
3162
+		n += 1 + sovOps(uint64(m.Mode))
3163
+	}
3164
+	if m.FollowSymlink {
3165
+		n += 2
3166
+	}
3167
+	if m.DirCopyContents {
3168
+		n += 2
3169
+	}
3170
+	if m.AttemptUnpackDockerCompatibility {
3171
+		n += 2
3172
+	}
3173
+	if m.CreateDestPath {
3174
+		n += 2
3175
+	}
3176
+	if m.AllowWildcard {
3177
+		n += 2
3178
+	}
3179
+	if m.AllowEmptyWildcard {
3180
+		n += 2
3181
+	}
3182
+	if m.Timestamp != 0 {
3183
+		n += 1 + sovOps(uint64(m.Timestamp))
3184
+	}
3185
+	return n
3186
+}
3187
+
3188
+func (m *FileActionMkFile) Size() (n int) {
3189
+	if m == nil {
3190
+		return 0
3191
+	}
3192
+	var l int
3193
+	_ = l
3194
+	l = len(m.Path)
3195
+	if l > 0 {
3196
+		n += 1 + l + sovOps(uint64(l))
3197
+	}
3198
+	if m.Mode != 0 {
3199
+		n += 1 + sovOps(uint64(m.Mode))
3200
+	}
3201
+	l = len(m.Data)
3202
+	if l > 0 {
3203
+		n += 1 + l + sovOps(uint64(l))
3204
+	}
3205
+	if m.Owner != nil {
3206
+		l = m.Owner.Size()
3207
+		n += 1 + l + sovOps(uint64(l))
3208
+	}
3209
+	if m.Timestamp != 0 {
3210
+		n += 1 + sovOps(uint64(m.Timestamp))
3211
+	}
3212
+	return n
3213
+}
3214
+
3215
+func (m *FileActionMkDir) Size() (n int) {
3216
+	if m == nil {
3217
+		return 0
3218
+	}
3219
+	var l int
3220
+	_ = l
3221
+	l = len(m.Path)
3222
+	if l > 0 {
3223
+		n += 1 + l + sovOps(uint64(l))
3224
+	}
3225
+	if m.Mode != 0 {
3226
+		n += 1 + sovOps(uint64(m.Mode))
3227
+	}
3228
+	if m.MakeParents {
3229
+		n += 2
3230
+	}
3231
+	if m.Owner != nil {
3232
+		l = m.Owner.Size()
3233
+		n += 1 + l + sovOps(uint64(l))
3234
+	}
3235
+	if m.Timestamp != 0 {
3236
+		n += 1 + sovOps(uint64(m.Timestamp))
3237
+	}
3238
+	return n
3239
+}
3240
+
3241
+func (m *FileActionRm) Size() (n int) {
3242
+	if m == nil {
3243
+		return 0
3244
+	}
3245
+	var l int
3246
+	_ = l
3247
+	l = len(m.Path)
3248
+	if l > 0 {
3249
+		n += 1 + l + sovOps(uint64(l))
3250
+	}
3251
+	if m.AllowNotFound {
3252
+		n += 2
3253
+	}
3254
+	if m.AllowWildcard {
3255
+		n += 2
3256
+	}
3257
+	return n
3258
+}
3259
+
3260
+func (m *ChownOpt) Size() (n int) {
3261
+	if m == nil {
3262
+		return 0
3263
+	}
3264
+	var l int
3265
+	_ = l
3266
+	if m.User != nil {
3267
+		l = m.User.Size()
3268
+		n += 1 + l + sovOps(uint64(l))
3269
+	}
3270
+	if m.Group != nil {
3271
+		l = m.Group.Size()
3272
+		n += 1 + l + sovOps(uint64(l))
3273
+	}
3274
+	return n
3275
+}
3276
+
3277
+func (m *UserOpt) Size() (n int) {
3278
+	if m == nil {
3279
+		return 0
3280
+	}
3281
+	var l int
3282
+	_ = l
3283
+	if m.User != nil {
3284
+		n += m.User.Size()
3285
+	}
3286
+	return n
3287
+}
3288
+
3289
+func (m *UserOpt_ByName) Size() (n int) {
3290
+	if m == nil {
3291
+		return 0
3292
+	}
3293
+	var l int
3294
+	_ = l
3295
+	if m.ByName != nil {
3296
+		l = m.ByName.Size()
3297
+		n += 1 + l + sovOps(uint64(l))
3298
+	}
3299
+	return n
3300
+}
3301
+func (m *UserOpt_ByID) Size() (n int) {
3302
+	if m == nil {
3303
+		return 0
3304
+	}
3305
+	var l int
3306
+	_ = l
3307
+	n += 1 + sovOps(uint64(m.ByID))
3308
+	return n
3309
+}
3310
+func (m *NamedUserOpt) Size() (n int) {
3311
+	if m == nil {
3312
+		return 0
3313
+	}
3314
+	var l int
3315
+	_ = l
3316
+	l = len(m.Name)
3317
+	if l > 0 {
3318
+		n += 1 + l + sovOps(uint64(l))
3319
+	}
3320
+	if m.Input != 0 {
3321
+		n += 1 + sovOps(uint64(m.Input))
3322
+	}
3323
+	return n
3324
+}
3325
+
3059 3326
 func sovOps(x uint64) (n int) {
3060 3327
 	for {
3061 3328
 		n++
... ...
@@ -3195,7 +4602,7 @@ func (m *Op) Unmarshal(dAtA []byte) error {
3195 3195
 			iNdEx = postIndex
3196 3196
 		case 4:
3197 3197
 			if wireType != 2 {
3198
-				return fmt.Errorf("proto: wrong wireType = %d for field Copy", wireType)
3198
+				return fmt.Errorf("proto: wrong wireType = %d for field File", wireType)
3199 3199
 			}
3200 3200
 			var msglen int
3201 3201
 			for shift := uint(0); ; shift += 7 {
... ...
@@ -3219,11 +4626,11 @@ func (m *Op) Unmarshal(dAtA []byte) error {
3219 3219
 			if postIndex > l {
3220 3220
 				return io.ErrUnexpectedEOF
3221 3221
 			}
3222
-			v := &CopyOp{}
3222
+			v := &FileOp{}
3223 3223
 			if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
3224 3224
 				return err
3225 3225
 			}
3226
-			m.Op = &Op_Copy{v}
3226
+			m.Op = &Op_File{v}
3227 3227
 			iNdEx = postIndex
3228 3228
 		case 5:
3229 3229
 			if wireType != 2 {
... ...
@@ -4694,214 +6101,6 @@ func (m *SSHOpt) Unmarshal(dAtA []byte) error {
4694 4694
 	}
4695 4695
 	return nil
4696 4696
 }
4697
-func (m *CopyOp) Unmarshal(dAtA []byte) error {
4698
-	l := len(dAtA)
4699
-	iNdEx := 0
4700
-	for iNdEx < l {
4701
-		preIndex := iNdEx
4702
-		var wire uint64
4703
-		for shift := uint(0); ; shift += 7 {
4704
-			if shift >= 64 {
4705
-				return ErrIntOverflowOps
4706
-			}
4707
-			if iNdEx >= l {
4708
-				return io.ErrUnexpectedEOF
4709
-			}
4710
-			b := dAtA[iNdEx]
4711
-			iNdEx++
4712
-			wire |= (uint64(b) & 0x7F) << shift
4713
-			if b < 0x80 {
4714
-				break
4715
-			}
4716
-		}
4717
-		fieldNum := int32(wire >> 3)
4718
-		wireType := int(wire & 0x7)
4719
-		if wireType == 4 {
4720
-			return fmt.Errorf("proto: CopyOp: wiretype end group for non-group")
4721
-		}
4722
-		if fieldNum <= 0 {
4723
-			return fmt.Errorf("proto: CopyOp: illegal tag %d (wire type %d)", fieldNum, wire)
4724
-		}
4725
-		switch fieldNum {
4726
-		case 1:
4727
-			if wireType != 2 {
4728
-				return fmt.Errorf("proto: wrong wireType = %d for field Src", wireType)
4729
-			}
4730
-			var msglen int
4731
-			for shift := uint(0); ; shift += 7 {
4732
-				if shift >= 64 {
4733
-					return ErrIntOverflowOps
4734
-				}
4735
-				if iNdEx >= l {
4736
-					return io.ErrUnexpectedEOF
4737
-				}
4738
-				b := dAtA[iNdEx]
4739
-				iNdEx++
4740
-				msglen |= (int(b) & 0x7F) << shift
4741
-				if b < 0x80 {
4742
-					break
4743
-				}
4744
-			}
4745
-			if msglen < 0 {
4746
-				return ErrInvalidLengthOps
4747
-			}
4748
-			postIndex := iNdEx + msglen
4749
-			if postIndex > l {
4750
-				return io.ErrUnexpectedEOF
4751
-			}
4752
-			m.Src = append(m.Src, &CopySource{})
4753
-			if err := m.Src[len(m.Src)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
4754
-				return err
4755
-			}
4756
-			iNdEx = postIndex
4757
-		case 2:
4758
-			if wireType != 2 {
4759
-				return fmt.Errorf("proto: wrong wireType = %d for field Dest", wireType)
4760
-			}
4761
-			var stringLen uint64
4762
-			for shift := uint(0); ; shift += 7 {
4763
-				if shift >= 64 {
4764
-					return ErrIntOverflowOps
4765
-				}
4766
-				if iNdEx >= l {
4767
-					return io.ErrUnexpectedEOF
4768
-				}
4769
-				b := dAtA[iNdEx]
4770
-				iNdEx++
4771
-				stringLen |= (uint64(b) & 0x7F) << shift
4772
-				if b < 0x80 {
4773
-					break
4774
-				}
4775
-			}
4776
-			intStringLen := int(stringLen)
4777
-			if intStringLen < 0 {
4778
-				return ErrInvalidLengthOps
4779
-			}
4780
-			postIndex := iNdEx + intStringLen
4781
-			if postIndex > l {
4782
-				return io.ErrUnexpectedEOF
4783
-			}
4784
-			m.Dest = string(dAtA[iNdEx:postIndex])
4785
-			iNdEx = postIndex
4786
-		default:
4787
-			iNdEx = preIndex
4788
-			skippy, err := skipOps(dAtA[iNdEx:])
4789
-			if err != nil {
4790
-				return err
4791
-			}
4792
-			if skippy < 0 {
4793
-				return ErrInvalidLengthOps
4794
-			}
4795
-			if (iNdEx + skippy) > l {
4796
-				return io.ErrUnexpectedEOF
4797
-			}
4798
-			iNdEx += skippy
4799
-		}
4800
-	}
4801
-
4802
-	if iNdEx > l {
4803
-		return io.ErrUnexpectedEOF
4804
-	}
4805
-	return nil
4806
-}
4807
-func (m *CopySource) Unmarshal(dAtA []byte) error {
4808
-	l := len(dAtA)
4809
-	iNdEx := 0
4810
-	for iNdEx < l {
4811
-		preIndex := iNdEx
4812
-		var wire uint64
4813
-		for shift := uint(0); ; shift += 7 {
4814
-			if shift >= 64 {
4815
-				return ErrIntOverflowOps
4816
-			}
4817
-			if iNdEx >= l {
4818
-				return io.ErrUnexpectedEOF
4819
-			}
4820
-			b := dAtA[iNdEx]
4821
-			iNdEx++
4822
-			wire |= (uint64(b) & 0x7F) << shift
4823
-			if b < 0x80 {
4824
-				break
4825
-			}
4826
-		}
4827
-		fieldNum := int32(wire >> 3)
4828
-		wireType := int(wire & 0x7)
4829
-		if wireType == 4 {
4830
-			return fmt.Errorf("proto: CopySource: wiretype end group for non-group")
4831
-		}
4832
-		if fieldNum <= 0 {
4833
-			return fmt.Errorf("proto: CopySource: illegal tag %d (wire type %d)", fieldNum, wire)
4834
-		}
4835
-		switch fieldNum {
4836
-		case 1:
4837
-			if wireType != 0 {
4838
-				return fmt.Errorf("proto: wrong wireType = %d for field Input", wireType)
4839
-			}
4840
-			m.Input = 0
4841
-			for shift := uint(0); ; shift += 7 {
4842
-				if shift >= 64 {
4843
-					return ErrIntOverflowOps
4844
-				}
4845
-				if iNdEx >= l {
4846
-					return io.ErrUnexpectedEOF
4847
-				}
4848
-				b := dAtA[iNdEx]
4849
-				iNdEx++
4850
-				m.Input |= (InputIndex(b) & 0x7F) << shift
4851
-				if b < 0x80 {
4852
-					break
4853
-				}
4854
-			}
4855
-		case 2:
4856
-			if wireType != 2 {
4857
-				return fmt.Errorf("proto: wrong wireType = %d for field Selector", wireType)
4858
-			}
4859
-			var stringLen uint64
4860
-			for shift := uint(0); ; shift += 7 {
4861
-				if shift >= 64 {
4862
-					return ErrIntOverflowOps
4863
-				}
4864
-				if iNdEx >= l {
4865
-					return io.ErrUnexpectedEOF
4866
-				}
4867
-				b := dAtA[iNdEx]
4868
-				iNdEx++
4869
-				stringLen |= (uint64(b) & 0x7F) << shift
4870
-				if b < 0x80 {
4871
-					break
4872
-				}
4873
-			}
4874
-			intStringLen := int(stringLen)
4875
-			if intStringLen < 0 {
4876
-				return ErrInvalidLengthOps
4877
-			}
4878
-			postIndex := iNdEx + intStringLen
4879
-			if postIndex > l {
4880
-				return io.ErrUnexpectedEOF
4881
-			}
4882
-			m.Selector = string(dAtA[iNdEx:postIndex])
4883
-			iNdEx = postIndex
4884
-		default:
4885
-			iNdEx = preIndex
4886
-			skippy, err := skipOps(dAtA[iNdEx:])
4887
-			if err != nil {
4888
-				return err
4889
-			}
4890
-			if skippy < 0 {
4891
-				return ErrInvalidLengthOps
4892
-			}
4893
-			if (iNdEx + skippy) > l {
4894
-				return io.ErrUnexpectedEOF
4895
-			}
4896
-			iNdEx += skippy
4897
-		}
4898
-	}
4899
-
4900
-	if iNdEx > l {
4901
-		return io.ErrUnexpectedEOF
4902
-	}
4903
-	return nil
4904
-}
4905 4697
 func (m *SourceOp) Unmarshal(dAtA []byte) error {
4906 4698
 	l := len(dAtA)
4907 4699
 	iNdEx := 0
... ...
@@ -6466,6 +7665,1407 @@ func (m *HostIP) Unmarshal(dAtA []byte) error {
6466 6466
 	}
6467 6467
 	return nil
6468 6468
 }
6469
+func (m *FileOp) Unmarshal(dAtA []byte) error {
6470
+	l := len(dAtA)
6471
+	iNdEx := 0
6472
+	for iNdEx < l {
6473
+		preIndex := iNdEx
6474
+		var wire uint64
6475
+		for shift := uint(0); ; shift += 7 {
6476
+			if shift >= 64 {
6477
+				return ErrIntOverflowOps
6478
+			}
6479
+			if iNdEx >= l {
6480
+				return io.ErrUnexpectedEOF
6481
+			}
6482
+			b := dAtA[iNdEx]
6483
+			iNdEx++
6484
+			wire |= (uint64(b) & 0x7F) << shift
6485
+			if b < 0x80 {
6486
+				break
6487
+			}
6488
+		}
6489
+		fieldNum := int32(wire >> 3)
6490
+		wireType := int(wire & 0x7)
6491
+		if wireType == 4 {
6492
+			return fmt.Errorf("proto: FileOp: wiretype end group for non-group")
6493
+		}
6494
+		if fieldNum <= 0 {
6495
+			return fmt.Errorf("proto: FileOp: illegal tag %d (wire type %d)", fieldNum, wire)
6496
+		}
6497
+		switch fieldNum {
6498
+		case 2:
6499
+			if wireType != 2 {
6500
+				return fmt.Errorf("proto: wrong wireType = %d for field Actions", wireType)
6501
+			}
6502
+			var msglen int
6503
+			for shift := uint(0); ; shift += 7 {
6504
+				if shift >= 64 {
6505
+					return ErrIntOverflowOps
6506
+				}
6507
+				if iNdEx >= l {
6508
+					return io.ErrUnexpectedEOF
6509
+				}
6510
+				b := dAtA[iNdEx]
6511
+				iNdEx++
6512
+				msglen |= (int(b) & 0x7F) << shift
6513
+				if b < 0x80 {
6514
+					break
6515
+				}
6516
+			}
6517
+			if msglen < 0 {
6518
+				return ErrInvalidLengthOps
6519
+			}
6520
+			postIndex := iNdEx + msglen
6521
+			if postIndex > l {
6522
+				return io.ErrUnexpectedEOF
6523
+			}
6524
+			m.Actions = append(m.Actions, &FileAction{})
6525
+			if err := m.Actions[len(m.Actions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
6526
+				return err
6527
+			}
6528
+			iNdEx = postIndex
6529
+		default:
6530
+			iNdEx = preIndex
6531
+			skippy, err := skipOps(dAtA[iNdEx:])
6532
+			if err != nil {
6533
+				return err
6534
+			}
6535
+			if skippy < 0 {
6536
+				return ErrInvalidLengthOps
6537
+			}
6538
+			if (iNdEx + skippy) > l {
6539
+				return io.ErrUnexpectedEOF
6540
+			}
6541
+			iNdEx += skippy
6542
+		}
6543
+	}
6544
+
6545
+	if iNdEx > l {
6546
+		return io.ErrUnexpectedEOF
6547
+	}
6548
+	return nil
6549
+}
6550
+func (m *FileAction) Unmarshal(dAtA []byte) error {
6551
+	l := len(dAtA)
6552
+	iNdEx := 0
6553
+	for iNdEx < l {
6554
+		preIndex := iNdEx
6555
+		var wire uint64
6556
+		for shift := uint(0); ; shift += 7 {
6557
+			if shift >= 64 {
6558
+				return ErrIntOverflowOps
6559
+			}
6560
+			if iNdEx >= l {
6561
+				return io.ErrUnexpectedEOF
6562
+			}
6563
+			b := dAtA[iNdEx]
6564
+			iNdEx++
6565
+			wire |= (uint64(b) & 0x7F) << shift
6566
+			if b < 0x80 {
6567
+				break
6568
+			}
6569
+		}
6570
+		fieldNum := int32(wire >> 3)
6571
+		wireType := int(wire & 0x7)
6572
+		if wireType == 4 {
6573
+			return fmt.Errorf("proto: FileAction: wiretype end group for non-group")
6574
+		}
6575
+		if fieldNum <= 0 {
6576
+			return fmt.Errorf("proto: FileAction: illegal tag %d (wire type %d)", fieldNum, wire)
6577
+		}
6578
+		switch fieldNum {
6579
+		case 1:
6580
+			if wireType != 0 {
6581
+				return fmt.Errorf("proto: wrong wireType = %d for field Input", wireType)
6582
+			}
6583
+			m.Input = 0
6584
+			for shift := uint(0); ; shift += 7 {
6585
+				if shift >= 64 {
6586
+					return ErrIntOverflowOps
6587
+				}
6588
+				if iNdEx >= l {
6589
+					return io.ErrUnexpectedEOF
6590
+				}
6591
+				b := dAtA[iNdEx]
6592
+				iNdEx++
6593
+				m.Input |= (InputIndex(b) & 0x7F) << shift
6594
+				if b < 0x80 {
6595
+					break
6596
+				}
6597
+			}
6598
+		case 2:
6599
+			if wireType != 0 {
6600
+				return fmt.Errorf("proto: wrong wireType = %d for field SecondaryInput", wireType)
6601
+			}
6602
+			m.SecondaryInput = 0
6603
+			for shift := uint(0); ; shift += 7 {
6604
+				if shift >= 64 {
6605
+					return ErrIntOverflowOps
6606
+				}
6607
+				if iNdEx >= l {
6608
+					return io.ErrUnexpectedEOF
6609
+				}
6610
+				b := dAtA[iNdEx]
6611
+				iNdEx++
6612
+				m.SecondaryInput |= (InputIndex(b) & 0x7F) << shift
6613
+				if b < 0x80 {
6614
+					break
6615
+				}
6616
+			}
6617
+		case 3:
6618
+			if wireType != 0 {
6619
+				return fmt.Errorf("proto: wrong wireType = %d for field Output", wireType)
6620
+			}
6621
+			m.Output = 0
6622
+			for shift := uint(0); ; shift += 7 {
6623
+				if shift >= 64 {
6624
+					return ErrIntOverflowOps
6625
+				}
6626
+				if iNdEx >= l {
6627
+					return io.ErrUnexpectedEOF
6628
+				}
6629
+				b := dAtA[iNdEx]
6630
+				iNdEx++
6631
+				m.Output |= (OutputIndex(b) & 0x7F) << shift
6632
+				if b < 0x80 {
6633
+					break
6634
+				}
6635
+			}
6636
+		case 4:
6637
+			if wireType != 2 {
6638
+				return fmt.Errorf("proto: wrong wireType = %d for field Copy", wireType)
6639
+			}
6640
+			var msglen int
6641
+			for shift := uint(0); ; shift += 7 {
6642
+				if shift >= 64 {
6643
+					return ErrIntOverflowOps
6644
+				}
6645
+				if iNdEx >= l {
6646
+					return io.ErrUnexpectedEOF
6647
+				}
6648
+				b := dAtA[iNdEx]
6649
+				iNdEx++
6650
+				msglen |= (int(b) & 0x7F) << shift
6651
+				if b < 0x80 {
6652
+					break
6653
+				}
6654
+			}
6655
+			if msglen < 0 {
6656
+				return ErrInvalidLengthOps
6657
+			}
6658
+			postIndex := iNdEx + msglen
6659
+			if postIndex > l {
6660
+				return io.ErrUnexpectedEOF
6661
+			}
6662
+			v := &FileActionCopy{}
6663
+			if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
6664
+				return err
6665
+			}
6666
+			m.Action = &FileAction_Copy{v}
6667
+			iNdEx = postIndex
6668
+		case 5:
6669
+			if wireType != 2 {
6670
+				return fmt.Errorf("proto: wrong wireType = %d for field Mkfile", wireType)
6671
+			}
6672
+			var msglen int
6673
+			for shift := uint(0); ; shift += 7 {
6674
+				if shift >= 64 {
6675
+					return ErrIntOverflowOps
6676
+				}
6677
+				if iNdEx >= l {
6678
+					return io.ErrUnexpectedEOF
6679
+				}
6680
+				b := dAtA[iNdEx]
6681
+				iNdEx++
6682
+				msglen |= (int(b) & 0x7F) << shift
6683
+				if b < 0x80 {
6684
+					break
6685
+				}
6686
+			}
6687
+			if msglen < 0 {
6688
+				return ErrInvalidLengthOps
6689
+			}
6690
+			postIndex := iNdEx + msglen
6691
+			if postIndex > l {
6692
+				return io.ErrUnexpectedEOF
6693
+			}
6694
+			v := &FileActionMkFile{}
6695
+			if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
6696
+				return err
6697
+			}
6698
+			m.Action = &FileAction_Mkfile{v}
6699
+			iNdEx = postIndex
6700
+		case 6:
6701
+			if wireType != 2 {
6702
+				return fmt.Errorf("proto: wrong wireType = %d for field Mkdir", wireType)
6703
+			}
6704
+			var msglen int
6705
+			for shift := uint(0); ; shift += 7 {
6706
+				if shift >= 64 {
6707
+					return ErrIntOverflowOps
6708
+				}
6709
+				if iNdEx >= l {
6710
+					return io.ErrUnexpectedEOF
6711
+				}
6712
+				b := dAtA[iNdEx]
6713
+				iNdEx++
6714
+				msglen |= (int(b) & 0x7F) << shift
6715
+				if b < 0x80 {
6716
+					break
6717
+				}
6718
+			}
6719
+			if msglen < 0 {
6720
+				return ErrInvalidLengthOps
6721
+			}
6722
+			postIndex := iNdEx + msglen
6723
+			if postIndex > l {
6724
+				return io.ErrUnexpectedEOF
6725
+			}
6726
+			v := &FileActionMkDir{}
6727
+			if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
6728
+				return err
6729
+			}
6730
+			m.Action = &FileAction_Mkdir{v}
6731
+			iNdEx = postIndex
6732
+		case 7:
6733
+			if wireType != 2 {
6734
+				return fmt.Errorf("proto: wrong wireType = %d for field Rm", wireType)
6735
+			}
6736
+			var msglen int
6737
+			for shift := uint(0); ; shift += 7 {
6738
+				if shift >= 64 {
6739
+					return ErrIntOverflowOps
6740
+				}
6741
+				if iNdEx >= l {
6742
+					return io.ErrUnexpectedEOF
6743
+				}
6744
+				b := dAtA[iNdEx]
6745
+				iNdEx++
6746
+				msglen |= (int(b) & 0x7F) << shift
6747
+				if b < 0x80 {
6748
+					break
6749
+				}
6750
+			}
6751
+			if msglen < 0 {
6752
+				return ErrInvalidLengthOps
6753
+			}
6754
+			postIndex := iNdEx + msglen
6755
+			if postIndex > l {
6756
+				return io.ErrUnexpectedEOF
6757
+			}
6758
+			v := &FileActionRm{}
6759
+			if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
6760
+				return err
6761
+			}
6762
+			m.Action = &FileAction_Rm{v}
6763
+			iNdEx = postIndex
6764
+		default:
6765
+			iNdEx = preIndex
6766
+			skippy, err := skipOps(dAtA[iNdEx:])
6767
+			if err != nil {
6768
+				return err
6769
+			}
6770
+			if skippy < 0 {
6771
+				return ErrInvalidLengthOps
6772
+			}
6773
+			if (iNdEx + skippy) > l {
6774
+				return io.ErrUnexpectedEOF
6775
+			}
6776
+			iNdEx += skippy
6777
+		}
6778
+	}
6779
+
6780
+	if iNdEx > l {
6781
+		return io.ErrUnexpectedEOF
6782
+	}
6783
+	return nil
6784
+}
6785
+func (m *FileActionCopy) Unmarshal(dAtA []byte) error {
6786
+	l := len(dAtA)
6787
+	iNdEx := 0
6788
+	for iNdEx < l {
6789
+		preIndex := iNdEx
6790
+		var wire uint64
6791
+		for shift := uint(0); ; shift += 7 {
6792
+			if shift >= 64 {
6793
+				return ErrIntOverflowOps
6794
+			}
6795
+			if iNdEx >= l {
6796
+				return io.ErrUnexpectedEOF
6797
+			}
6798
+			b := dAtA[iNdEx]
6799
+			iNdEx++
6800
+			wire |= (uint64(b) & 0x7F) << shift
6801
+			if b < 0x80 {
6802
+				break
6803
+			}
6804
+		}
6805
+		fieldNum := int32(wire >> 3)
6806
+		wireType := int(wire & 0x7)
6807
+		if wireType == 4 {
6808
+			return fmt.Errorf("proto: FileActionCopy: wiretype end group for non-group")
6809
+		}
6810
+		if fieldNum <= 0 {
6811
+			return fmt.Errorf("proto: FileActionCopy: illegal tag %d (wire type %d)", fieldNum, wire)
6812
+		}
6813
+		switch fieldNum {
6814
+		case 1:
6815
+			if wireType != 2 {
6816
+				return fmt.Errorf("proto: wrong wireType = %d for field Src", wireType)
6817
+			}
6818
+			var stringLen uint64
6819
+			for shift := uint(0); ; shift += 7 {
6820
+				if shift >= 64 {
6821
+					return ErrIntOverflowOps
6822
+				}
6823
+				if iNdEx >= l {
6824
+					return io.ErrUnexpectedEOF
6825
+				}
6826
+				b := dAtA[iNdEx]
6827
+				iNdEx++
6828
+				stringLen |= (uint64(b) & 0x7F) << shift
6829
+				if b < 0x80 {
6830
+					break
6831
+				}
6832
+			}
6833
+			intStringLen := int(stringLen)
6834
+			if intStringLen < 0 {
6835
+				return ErrInvalidLengthOps
6836
+			}
6837
+			postIndex := iNdEx + intStringLen
6838
+			if postIndex > l {
6839
+				return io.ErrUnexpectedEOF
6840
+			}
6841
+			m.Src = string(dAtA[iNdEx:postIndex])
6842
+			iNdEx = postIndex
6843
+		case 2:
6844
+			if wireType != 2 {
6845
+				return fmt.Errorf("proto: wrong wireType = %d for field Dest", wireType)
6846
+			}
6847
+			var stringLen uint64
6848
+			for shift := uint(0); ; shift += 7 {
6849
+				if shift >= 64 {
6850
+					return ErrIntOverflowOps
6851
+				}
6852
+				if iNdEx >= l {
6853
+					return io.ErrUnexpectedEOF
6854
+				}
6855
+				b := dAtA[iNdEx]
6856
+				iNdEx++
6857
+				stringLen |= (uint64(b) & 0x7F) << shift
6858
+				if b < 0x80 {
6859
+					break
6860
+				}
6861
+			}
6862
+			intStringLen := int(stringLen)
6863
+			if intStringLen < 0 {
6864
+				return ErrInvalidLengthOps
6865
+			}
6866
+			postIndex := iNdEx + intStringLen
6867
+			if postIndex > l {
6868
+				return io.ErrUnexpectedEOF
6869
+			}
6870
+			m.Dest = string(dAtA[iNdEx:postIndex])
6871
+			iNdEx = postIndex
6872
+		case 3:
6873
+			if wireType != 2 {
6874
+				return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType)
6875
+			}
6876
+			var msglen int
6877
+			for shift := uint(0); ; shift += 7 {
6878
+				if shift >= 64 {
6879
+					return ErrIntOverflowOps
6880
+				}
6881
+				if iNdEx >= l {
6882
+					return io.ErrUnexpectedEOF
6883
+				}
6884
+				b := dAtA[iNdEx]
6885
+				iNdEx++
6886
+				msglen |= (int(b) & 0x7F) << shift
6887
+				if b < 0x80 {
6888
+					break
6889
+				}
6890
+			}
6891
+			if msglen < 0 {
6892
+				return ErrInvalidLengthOps
6893
+			}
6894
+			postIndex := iNdEx + msglen
6895
+			if postIndex > l {
6896
+				return io.ErrUnexpectedEOF
6897
+			}
6898
+			if m.Owner == nil {
6899
+				m.Owner = &ChownOpt{}
6900
+			}
6901
+			if err := m.Owner.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
6902
+				return err
6903
+			}
6904
+			iNdEx = postIndex
6905
+		case 4:
6906
+			if wireType != 0 {
6907
+				return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType)
6908
+			}
6909
+			m.Mode = 0
6910
+			for shift := uint(0); ; shift += 7 {
6911
+				if shift >= 64 {
6912
+					return ErrIntOverflowOps
6913
+				}
6914
+				if iNdEx >= l {
6915
+					return io.ErrUnexpectedEOF
6916
+				}
6917
+				b := dAtA[iNdEx]
6918
+				iNdEx++
6919
+				m.Mode |= (int32(b) & 0x7F) << shift
6920
+				if b < 0x80 {
6921
+					break
6922
+				}
6923
+			}
6924
+		case 5:
6925
+			if wireType != 0 {
6926
+				return fmt.Errorf("proto: wrong wireType = %d for field FollowSymlink", wireType)
6927
+			}
6928
+			var v int
6929
+			for shift := uint(0); ; shift += 7 {
6930
+				if shift >= 64 {
6931
+					return ErrIntOverflowOps
6932
+				}
6933
+				if iNdEx >= l {
6934
+					return io.ErrUnexpectedEOF
6935
+				}
6936
+				b := dAtA[iNdEx]
6937
+				iNdEx++
6938
+				v |= (int(b) & 0x7F) << shift
6939
+				if b < 0x80 {
6940
+					break
6941
+				}
6942
+			}
6943
+			m.FollowSymlink = bool(v != 0)
6944
+		case 6:
6945
+			if wireType != 0 {
6946
+				return fmt.Errorf("proto: wrong wireType = %d for field DirCopyContents", wireType)
6947
+			}
6948
+			var v int
6949
+			for shift := uint(0); ; shift += 7 {
6950
+				if shift >= 64 {
6951
+					return ErrIntOverflowOps
6952
+				}
6953
+				if iNdEx >= l {
6954
+					return io.ErrUnexpectedEOF
6955
+				}
6956
+				b := dAtA[iNdEx]
6957
+				iNdEx++
6958
+				v |= (int(b) & 0x7F) << shift
6959
+				if b < 0x80 {
6960
+					break
6961
+				}
6962
+			}
6963
+			m.DirCopyContents = bool(v != 0)
6964
+		case 7:
6965
+			if wireType != 0 {
6966
+				return fmt.Errorf("proto: wrong wireType = %d for field AttemptUnpackDockerCompatibility", wireType)
6967
+			}
6968
+			var v int
6969
+			for shift := uint(0); ; shift += 7 {
6970
+				if shift >= 64 {
6971
+					return ErrIntOverflowOps
6972
+				}
6973
+				if iNdEx >= l {
6974
+					return io.ErrUnexpectedEOF
6975
+				}
6976
+				b := dAtA[iNdEx]
6977
+				iNdEx++
6978
+				v |= (int(b) & 0x7F) << shift
6979
+				if b < 0x80 {
6980
+					break
6981
+				}
6982
+			}
6983
+			m.AttemptUnpackDockerCompatibility = bool(v != 0)
6984
+		case 8:
6985
+			if wireType != 0 {
6986
+				return fmt.Errorf("proto: wrong wireType = %d for field CreateDestPath", wireType)
6987
+			}
6988
+			var v int
6989
+			for shift := uint(0); ; shift += 7 {
6990
+				if shift >= 64 {
6991
+					return ErrIntOverflowOps
6992
+				}
6993
+				if iNdEx >= l {
6994
+					return io.ErrUnexpectedEOF
6995
+				}
6996
+				b := dAtA[iNdEx]
6997
+				iNdEx++
6998
+				v |= (int(b) & 0x7F) << shift
6999
+				if b < 0x80 {
7000
+					break
7001
+				}
7002
+			}
7003
+			m.CreateDestPath = bool(v != 0)
7004
+		case 9:
7005
+			if wireType != 0 {
7006
+				return fmt.Errorf("proto: wrong wireType = %d for field AllowWildcard", wireType)
7007
+			}
7008
+			var v int
7009
+			for shift := uint(0); ; shift += 7 {
7010
+				if shift >= 64 {
7011
+					return ErrIntOverflowOps
7012
+				}
7013
+				if iNdEx >= l {
7014
+					return io.ErrUnexpectedEOF
7015
+				}
7016
+				b := dAtA[iNdEx]
7017
+				iNdEx++
7018
+				v |= (int(b) & 0x7F) << shift
7019
+				if b < 0x80 {
7020
+					break
7021
+				}
7022
+			}
7023
+			m.AllowWildcard = bool(v != 0)
7024
+		case 10:
7025
+			if wireType != 0 {
7026
+				return fmt.Errorf("proto: wrong wireType = %d for field AllowEmptyWildcard", wireType)
7027
+			}
7028
+			var v int
7029
+			for shift := uint(0); ; shift += 7 {
7030
+				if shift >= 64 {
7031
+					return ErrIntOverflowOps
7032
+				}
7033
+				if iNdEx >= l {
7034
+					return io.ErrUnexpectedEOF
7035
+				}
7036
+				b := dAtA[iNdEx]
7037
+				iNdEx++
7038
+				v |= (int(b) & 0x7F) << shift
7039
+				if b < 0x80 {
7040
+					break
7041
+				}
7042
+			}
7043
+			m.AllowEmptyWildcard = bool(v != 0)
7044
+		case 11:
7045
+			if wireType != 0 {
7046
+				return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType)
7047
+			}
7048
+			m.Timestamp = 0
7049
+			for shift := uint(0); ; shift += 7 {
7050
+				if shift >= 64 {
7051
+					return ErrIntOverflowOps
7052
+				}
7053
+				if iNdEx >= l {
7054
+					return io.ErrUnexpectedEOF
7055
+				}
7056
+				b := dAtA[iNdEx]
7057
+				iNdEx++
7058
+				m.Timestamp |= (int64(b) & 0x7F) << shift
7059
+				if b < 0x80 {
7060
+					break
7061
+				}
7062
+			}
7063
+		default:
7064
+			iNdEx = preIndex
7065
+			skippy, err := skipOps(dAtA[iNdEx:])
7066
+			if err != nil {
7067
+				return err
7068
+			}
7069
+			if skippy < 0 {
7070
+				return ErrInvalidLengthOps
7071
+			}
7072
+			if (iNdEx + skippy) > l {
7073
+				return io.ErrUnexpectedEOF
7074
+			}
7075
+			iNdEx += skippy
7076
+		}
7077
+	}
7078
+
7079
+	if iNdEx > l {
7080
+		return io.ErrUnexpectedEOF
7081
+	}
7082
+	return nil
7083
+}
7084
+func (m *FileActionMkFile) Unmarshal(dAtA []byte) error {
7085
+	l := len(dAtA)
7086
+	iNdEx := 0
7087
+	for iNdEx < l {
7088
+		preIndex := iNdEx
7089
+		var wire uint64
7090
+		for shift := uint(0); ; shift += 7 {
7091
+			if shift >= 64 {
7092
+				return ErrIntOverflowOps
7093
+			}
7094
+			if iNdEx >= l {
7095
+				return io.ErrUnexpectedEOF
7096
+			}
7097
+			b := dAtA[iNdEx]
7098
+			iNdEx++
7099
+			wire |= (uint64(b) & 0x7F) << shift
7100
+			if b < 0x80 {
7101
+				break
7102
+			}
7103
+		}
7104
+		fieldNum := int32(wire >> 3)
7105
+		wireType := int(wire & 0x7)
7106
+		if wireType == 4 {
7107
+			return fmt.Errorf("proto: FileActionMkFile: wiretype end group for non-group")
7108
+		}
7109
+		if fieldNum <= 0 {
7110
+			return fmt.Errorf("proto: FileActionMkFile: illegal tag %d (wire type %d)", fieldNum, wire)
7111
+		}
7112
+		switch fieldNum {
7113
+		case 1:
7114
+			if wireType != 2 {
7115
+				return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType)
7116
+			}
7117
+			var stringLen uint64
7118
+			for shift := uint(0); ; shift += 7 {
7119
+				if shift >= 64 {
7120
+					return ErrIntOverflowOps
7121
+				}
7122
+				if iNdEx >= l {
7123
+					return io.ErrUnexpectedEOF
7124
+				}
7125
+				b := dAtA[iNdEx]
7126
+				iNdEx++
7127
+				stringLen |= (uint64(b) & 0x7F) << shift
7128
+				if b < 0x80 {
7129
+					break
7130
+				}
7131
+			}
7132
+			intStringLen := int(stringLen)
7133
+			if intStringLen < 0 {
7134
+				return ErrInvalidLengthOps
7135
+			}
7136
+			postIndex := iNdEx + intStringLen
7137
+			if postIndex > l {
7138
+				return io.ErrUnexpectedEOF
7139
+			}
7140
+			m.Path = string(dAtA[iNdEx:postIndex])
7141
+			iNdEx = postIndex
7142
+		case 2:
7143
+			if wireType != 0 {
7144
+				return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType)
7145
+			}
7146
+			m.Mode = 0
7147
+			for shift := uint(0); ; shift += 7 {
7148
+				if shift >= 64 {
7149
+					return ErrIntOverflowOps
7150
+				}
7151
+				if iNdEx >= l {
7152
+					return io.ErrUnexpectedEOF
7153
+				}
7154
+				b := dAtA[iNdEx]
7155
+				iNdEx++
7156
+				m.Mode |= (int32(b) & 0x7F) << shift
7157
+				if b < 0x80 {
7158
+					break
7159
+				}
7160
+			}
7161
+		case 3:
7162
+			if wireType != 2 {
7163
+				return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType)
7164
+			}
7165
+			var byteLen int
7166
+			for shift := uint(0); ; shift += 7 {
7167
+				if shift >= 64 {
7168
+					return ErrIntOverflowOps
7169
+				}
7170
+				if iNdEx >= l {
7171
+					return io.ErrUnexpectedEOF
7172
+				}
7173
+				b := dAtA[iNdEx]
7174
+				iNdEx++
7175
+				byteLen |= (int(b) & 0x7F) << shift
7176
+				if b < 0x80 {
7177
+					break
7178
+				}
7179
+			}
7180
+			if byteLen < 0 {
7181
+				return ErrInvalidLengthOps
7182
+			}
7183
+			postIndex := iNdEx + byteLen
7184
+			if postIndex > l {
7185
+				return io.ErrUnexpectedEOF
7186
+			}
7187
+			m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...)
7188
+			if m.Data == nil {
7189
+				m.Data = []byte{}
7190
+			}
7191
+			iNdEx = postIndex
7192
+		case 4:
7193
+			if wireType != 2 {
7194
+				return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType)
7195
+			}
7196
+			var msglen int
7197
+			for shift := uint(0); ; shift += 7 {
7198
+				if shift >= 64 {
7199
+					return ErrIntOverflowOps
7200
+				}
7201
+				if iNdEx >= l {
7202
+					return io.ErrUnexpectedEOF
7203
+				}
7204
+				b := dAtA[iNdEx]
7205
+				iNdEx++
7206
+				msglen |= (int(b) & 0x7F) << shift
7207
+				if b < 0x80 {
7208
+					break
7209
+				}
7210
+			}
7211
+			if msglen < 0 {
7212
+				return ErrInvalidLengthOps
7213
+			}
7214
+			postIndex := iNdEx + msglen
7215
+			if postIndex > l {
7216
+				return io.ErrUnexpectedEOF
7217
+			}
7218
+			if m.Owner == nil {
7219
+				m.Owner = &ChownOpt{}
7220
+			}
7221
+			if err := m.Owner.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
7222
+				return err
7223
+			}
7224
+			iNdEx = postIndex
7225
+		case 5:
7226
+			if wireType != 0 {
7227
+				return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType)
7228
+			}
7229
+			m.Timestamp = 0
7230
+			for shift := uint(0); ; shift += 7 {
7231
+				if shift >= 64 {
7232
+					return ErrIntOverflowOps
7233
+				}
7234
+				if iNdEx >= l {
7235
+					return io.ErrUnexpectedEOF
7236
+				}
7237
+				b := dAtA[iNdEx]
7238
+				iNdEx++
7239
+				m.Timestamp |= (int64(b) & 0x7F) << shift
7240
+				if b < 0x80 {
7241
+					break
7242
+				}
7243
+			}
7244
+		default:
7245
+			iNdEx = preIndex
7246
+			skippy, err := skipOps(dAtA[iNdEx:])
7247
+			if err != nil {
7248
+				return err
7249
+			}
7250
+			if skippy < 0 {
7251
+				return ErrInvalidLengthOps
7252
+			}
7253
+			if (iNdEx + skippy) > l {
7254
+				return io.ErrUnexpectedEOF
7255
+			}
7256
+			iNdEx += skippy
7257
+		}
7258
+	}
7259
+
7260
+	if iNdEx > l {
7261
+		return io.ErrUnexpectedEOF
7262
+	}
7263
+	return nil
7264
+}
7265
+func (m *FileActionMkDir) Unmarshal(dAtA []byte) error {
7266
+	l := len(dAtA)
7267
+	iNdEx := 0
7268
+	for iNdEx < l {
7269
+		preIndex := iNdEx
7270
+		var wire uint64
7271
+		for shift := uint(0); ; shift += 7 {
7272
+			if shift >= 64 {
7273
+				return ErrIntOverflowOps
7274
+			}
7275
+			if iNdEx >= l {
7276
+				return io.ErrUnexpectedEOF
7277
+			}
7278
+			b := dAtA[iNdEx]
7279
+			iNdEx++
7280
+			wire |= (uint64(b) & 0x7F) << shift
7281
+			if b < 0x80 {
7282
+				break
7283
+			}
7284
+		}
7285
+		fieldNum := int32(wire >> 3)
7286
+		wireType := int(wire & 0x7)
7287
+		if wireType == 4 {
7288
+			return fmt.Errorf("proto: FileActionMkDir: wiretype end group for non-group")
7289
+		}
7290
+		if fieldNum <= 0 {
7291
+			return fmt.Errorf("proto: FileActionMkDir: illegal tag %d (wire type %d)", fieldNum, wire)
7292
+		}
7293
+		switch fieldNum {
7294
+		case 1:
7295
+			if wireType != 2 {
7296
+				return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType)
7297
+			}
7298
+			var stringLen uint64
7299
+			for shift := uint(0); ; shift += 7 {
7300
+				if shift >= 64 {
7301
+					return ErrIntOverflowOps
7302
+				}
7303
+				if iNdEx >= l {
7304
+					return io.ErrUnexpectedEOF
7305
+				}
7306
+				b := dAtA[iNdEx]
7307
+				iNdEx++
7308
+				stringLen |= (uint64(b) & 0x7F) << shift
7309
+				if b < 0x80 {
7310
+					break
7311
+				}
7312
+			}
7313
+			intStringLen := int(stringLen)
7314
+			if intStringLen < 0 {
7315
+				return ErrInvalidLengthOps
7316
+			}
7317
+			postIndex := iNdEx + intStringLen
7318
+			if postIndex > l {
7319
+				return io.ErrUnexpectedEOF
7320
+			}
7321
+			m.Path = string(dAtA[iNdEx:postIndex])
7322
+			iNdEx = postIndex
7323
+		case 2:
7324
+			if wireType != 0 {
7325
+				return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType)
7326
+			}
7327
+			m.Mode = 0
7328
+			for shift := uint(0); ; shift += 7 {
7329
+				if shift >= 64 {
7330
+					return ErrIntOverflowOps
7331
+				}
7332
+				if iNdEx >= l {
7333
+					return io.ErrUnexpectedEOF
7334
+				}
7335
+				b := dAtA[iNdEx]
7336
+				iNdEx++
7337
+				m.Mode |= (int32(b) & 0x7F) << shift
7338
+				if b < 0x80 {
7339
+					break
7340
+				}
7341
+			}
7342
+		case 3:
7343
+			if wireType != 0 {
7344
+				return fmt.Errorf("proto: wrong wireType = %d for field MakeParents", wireType)
7345
+			}
7346
+			var v int
7347
+			for shift := uint(0); ; shift += 7 {
7348
+				if shift >= 64 {
7349
+					return ErrIntOverflowOps
7350
+				}
7351
+				if iNdEx >= l {
7352
+					return io.ErrUnexpectedEOF
7353
+				}
7354
+				b := dAtA[iNdEx]
7355
+				iNdEx++
7356
+				v |= (int(b) & 0x7F) << shift
7357
+				if b < 0x80 {
7358
+					break
7359
+				}
7360
+			}
7361
+			m.MakeParents = bool(v != 0)
7362
+		case 4:
7363
+			if wireType != 2 {
7364
+				return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType)
7365
+			}
7366
+			var msglen int
7367
+			for shift := uint(0); ; shift += 7 {
7368
+				if shift >= 64 {
7369
+					return ErrIntOverflowOps
7370
+				}
7371
+				if iNdEx >= l {
7372
+					return io.ErrUnexpectedEOF
7373
+				}
7374
+				b := dAtA[iNdEx]
7375
+				iNdEx++
7376
+				msglen |= (int(b) & 0x7F) << shift
7377
+				if b < 0x80 {
7378
+					break
7379
+				}
7380
+			}
7381
+			if msglen < 0 {
7382
+				return ErrInvalidLengthOps
7383
+			}
7384
+			postIndex := iNdEx + msglen
7385
+			if postIndex > l {
7386
+				return io.ErrUnexpectedEOF
7387
+			}
7388
+			if m.Owner == nil {
7389
+				m.Owner = &ChownOpt{}
7390
+			}
7391
+			if err := m.Owner.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
7392
+				return err
7393
+			}
7394
+			iNdEx = postIndex
7395
+		case 5:
7396
+			if wireType != 0 {
7397
+				return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType)
7398
+			}
7399
+			m.Timestamp = 0
7400
+			for shift := uint(0); ; shift += 7 {
7401
+				if shift >= 64 {
7402
+					return ErrIntOverflowOps
7403
+				}
7404
+				if iNdEx >= l {
7405
+					return io.ErrUnexpectedEOF
7406
+				}
7407
+				b := dAtA[iNdEx]
7408
+				iNdEx++
7409
+				m.Timestamp |= (int64(b) & 0x7F) << shift
7410
+				if b < 0x80 {
7411
+					break
7412
+				}
7413
+			}
7414
+		default:
7415
+			iNdEx = preIndex
7416
+			skippy, err := skipOps(dAtA[iNdEx:])
7417
+			if err != nil {
7418
+				return err
7419
+			}
7420
+			if skippy < 0 {
7421
+				return ErrInvalidLengthOps
7422
+			}
7423
+			if (iNdEx + skippy) > l {
7424
+				return io.ErrUnexpectedEOF
7425
+			}
7426
+			iNdEx += skippy
7427
+		}
7428
+	}
7429
+
7430
+	if iNdEx > l {
7431
+		return io.ErrUnexpectedEOF
7432
+	}
7433
+	return nil
7434
+}
7435
+func (m *FileActionRm) Unmarshal(dAtA []byte) error {
7436
+	l := len(dAtA)
7437
+	iNdEx := 0
7438
+	for iNdEx < l {
7439
+		preIndex := iNdEx
7440
+		var wire uint64
7441
+		for shift := uint(0); ; shift += 7 {
7442
+			if shift >= 64 {
7443
+				return ErrIntOverflowOps
7444
+			}
7445
+			if iNdEx >= l {
7446
+				return io.ErrUnexpectedEOF
7447
+			}
7448
+			b := dAtA[iNdEx]
7449
+			iNdEx++
7450
+			wire |= (uint64(b) & 0x7F) << shift
7451
+			if b < 0x80 {
7452
+				break
7453
+			}
7454
+		}
7455
+		fieldNum := int32(wire >> 3)
7456
+		wireType := int(wire & 0x7)
7457
+		if wireType == 4 {
7458
+			return fmt.Errorf("proto: FileActionRm: wiretype end group for non-group")
7459
+		}
7460
+		if fieldNum <= 0 {
7461
+			return fmt.Errorf("proto: FileActionRm: illegal tag %d (wire type %d)", fieldNum, wire)
7462
+		}
7463
+		switch fieldNum {
7464
+		case 1:
7465
+			if wireType != 2 {
7466
+				return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType)
7467
+			}
7468
+			var stringLen uint64
7469
+			for shift := uint(0); ; shift += 7 {
7470
+				if shift >= 64 {
7471
+					return ErrIntOverflowOps
7472
+				}
7473
+				if iNdEx >= l {
7474
+					return io.ErrUnexpectedEOF
7475
+				}
7476
+				b := dAtA[iNdEx]
7477
+				iNdEx++
7478
+				stringLen |= (uint64(b) & 0x7F) << shift
7479
+				if b < 0x80 {
7480
+					break
7481
+				}
7482
+			}
7483
+			intStringLen := int(stringLen)
7484
+			if intStringLen < 0 {
7485
+				return ErrInvalidLengthOps
7486
+			}
7487
+			postIndex := iNdEx + intStringLen
7488
+			if postIndex > l {
7489
+				return io.ErrUnexpectedEOF
7490
+			}
7491
+			m.Path = string(dAtA[iNdEx:postIndex])
7492
+			iNdEx = postIndex
7493
+		case 2:
7494
+			if wireType != 0 {
7495
+				return fmt.Errorf("proto: wrong wireType = %d for field AllowNotFound", wireType)
7496
+			}
7497
+			var v int
7498
+			for shift := uint(0); ; shift += 7 {
7499
+				if shift >= 64 {
7500
+					return ErrIntOverflowOps
7501
+				}
7502
+				if iNdEx >= l {
7503
+					return io.ErrUnexpectedEOF
7504
+				}
7505
+				b := dAtA[iNdEx]
7506
+				iNdEx++
7507
+				v |= (int(b) & 0x7F) << shift
7508
+				if b < 0x80 {
7509
+					break
7510
+				}
7511
+			}
7512
+			m.AllowNotFound = bool(v != 0)
7513
+		case 3:
7514
+			if wireType != 0 {
7515
+				return fmt.Errorf("proto: wrong wireType = %d for field AllowWildcard", wireType)
7516
+			}
7517
+			var v int
7518
+			for shift := uint(0); ; shift += 7 {
7519
+				if shift >= 64 {
7520
+					return ErrIntOverflowOps
7521
+				}
7522
+				if iNdEx >= l {
7523
+					return io.ErrUnexpectedEOF
7524
+				}
7525
+				b := dAtA[iNdEx]
7526
+				iNdEx++
7527
+				v |= (int(b) & 0x7F) << shift
7528
+				if b < 0x80 {
7529
+					break
7530
+				}
7531
+			}
7532
+			m.AllowWildcard = bool(v != 0)
7533
+		default:
7534
+			iNdEx = preIndex
7535
+			skippy, err := skipOps(dAtA[iNdEx:])
7536
+			if err != nil {
7537
+				return err
7538
+			}
7539
+			if skippy < 0 {
7540
+				return ErrInvalidLengthOps
7541
+			}
7542
+			if (iNdEx + skippy) > l {
7543
+				return io.ErrUnexpectedEOF
7544
+			}
7545
+			iNdEx += skippy
7546
+		}
7547
+	}
7548
+
7549
+	if iNdEx > l {
7550
+		return io.ErrUnexpectedEOF
7551
+	}
7552
+	return nil
7553
+}
7554
+func (m *ChownOpt) Unmarshal(dAtA []byte) error {
7555
+	l := len(dAtA)
7556
+	iNdEx := 0
7557
+	for iNdEx < l {
7558
+		preIndex := iNdEx
7559
+		var wire uint64
7560
+		for shift := uint(0); ; shift += 7 {
7561
+			if shift >= 64 {
7562
+				return ErrIntOverflowOps
7563
+			}
7564
+			if iNdEx >= l {
7565
+				return io.ErrUnexpectedEOF
7566
+			}
7567
+			b := dAtA[iNdEx]
7568
+			iNdEx++
7569
+			wire |= (uint64(b) & 0x7F) << shift
7570
+			if b < 0x80 {
7571
+				break
7572
+			}
7573
+		}
7574
+		fieldNum := int32(wire >> 3)
7575
+		wireType := int(wire & 0x7)
7576
+		if wireType == 4 {
7577
+			return fmt.Errorf("proto: ChownOpt: wiretype end group for non-group")
7578
+		}
7579
+		if fieldNum <= 0 {
7580
+			return fmt.Errorf("proto: ChownOpt: illegal tag %d (wire type %d)", fieldNum, wire)
7581
+		}
7582
+		switch fieldNum {
7583
+		case 1:
7584
+			if wireType != 2 {
7585
+				return fmt.Errorf("proto: wrong wireType = %d for field User", wireType)
7586
+			}
7587
+			var msglen int
7588
+			for shift := uint(0); ; shift += 7 {
7589
+				if shift >= 64 {
7590
+					return ErrIntOverflowOps
7591
+				}
7592
+				if iNdEx >= l {
7593
+					return io.ErrUnexpectedEOF
7594
+				}
7595
+				b := dAtA[iNdEx]
7596
+				iNdEx++
7597
+				msglen |= (int(b) & 0x7F) << shift
7598
+				if b < 0x80 {
7599
+					break
7600
+				}
7601
+			}
7602
+			if msglen < 0 {
7603
+				return ErrInvalidLengthOps
7604
+			}
7605
+			postIndex := iNdEx + msglen
7606
+			if postIndex > l {
7607
+				return io.ErrUnexpectedEOF
7608
+			}
7609
+			if m.User == nil {
7610
+				m.User = &UserOpt{}
7611
+			}
7612
+			if err := m.User.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
7613
+				return err
7614
+			}
7615
+			iNdEx = postIndex
7616
+		case 2:
7617
+			if wireType != 2 {
7618
+				return fmt.Errorf("proto: wrong wireType = %d for field Group", wireType)
7619
+			}
7620
+			var msglen int
7621
+			for shift := uint(0); ; shift += 7 {
7622
+				if shift >= 64 {
7623
+					return ErrIntOverflowOps
7624
+				}
7625
+				if iNdEx >= l {
7626
+					return io.ErrUnexpectedEOF
7627
+				}
7628
+				b := dAtA[iNdEx]
7629
+				iNdEx++
7630
+				msglen |= (int(b) & 0x7F) << shift
7631
+				if b < 0x80 {
7632
+					break
7633
+				}
7634
+			}
7635
+			if msglen < 0 {
7636
+				return ErrInvalidLengthOps
7637
+			}
7638
+			postIndex := iNdEx + msglen
7639
+			if postIndex > l {
7640
+				return io.ErrUnexpectedEOF
7641
+			}
7642
+			if m.Group == nil {
7643
+				m.Group = &UserOpt{}
7644
+			}
7645
+			if err := m.Group.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
7646
+				return err
7647
+			}
7648
+			iNdEx = postIndex
7649
+		default:
7650
+			iNdEx = preIndex
7651
+			skippy, err := skipOps(dAtA[iNdEx:])
7652
+			if err != nil {
7653
+				return err
7654
+			}
7655
+			if skippy < 0 {
7656
+				return ErrInvalidLengthOps
7657
+			}
7658
+			if (iNdEx + skippy) > l {
7659
+				return io.ErrUnexpectedEOF
7660
+			}
7661
+			iNdEx += skippy
7662
+		}
7663
+	}
7664
+
7665
+	if iNdEx > l {
7666
+		return io.ErrUnexpectedEOF
7667
+	}
7668
+	return nil
7669
+}
7670
+func (m *UserOpt) Unmarshal(dAtA []byte) error {
7671
+	l := len(dAtA)
7672
+	iNdEx := 0
7673
+	for iNdEx < l {
7674
+		preIndex := iNdEx
7675
+		var wire uint64
7676
+		for shift := uint(0); ; shift += 7 {
7677
+			if shift >= 64 {
7678
+				return ErrIntOverflowOps
7679
+			}
7680
+			if iNdEx >= l {
7681
+				return io.ErrUnexpectedEOF
7682
+			}
7683
+			b := dAtA[iNdEx]
7684
+			iNdEx++
7685
+			wire |= (uint64(b) & 0x7F) << shift
7686
+			if b < 0x80 {
7687
+				break
7688
+			}
7689
+		}
7690
+		fieldNum := int32(wire >> 3)
7691
+		wireType := int(wire & 0x7)
7692
+		if wireType == 4 {
7693
+			return fmt.Errorf("proto: UserOpt: wiretype end group for non-group")
7694
+		}
7695
+		if fieldNum <= 0 {
7696
+			return fmt.Errorf("proto: UserOpt: illegal tag %d (wire type %d)", fieldNum, wire)
7697
+		}
7698
+		switch fieldNum {
7699
+		case 1:
7700
+			if wireType != 2 {
7701
+				return fmt.Errorf("proto: wrong wireType = %d for field ByName", wireType)
7702
+			}
7703
+			var msglen int
7704
+			for shift := uint(0); ; shift += 7 {
7705
+				if shift >= 64 {
7706
+					return ErrIntOverflowOps
7707
+				}
7708
+				if iNdEx >= l {
7709
+					return io.ErrUnexpectedEOF
7710
+				}
7711
+				b := dAtA[iNdEx]
7712
+				iNdEx++
7713
+				msglen |= (int(b) & 0x7F) << shift
7714
+				if b < 0x80 {
7715
+					break
7716
+				}
7717
+			}
7718
+			if msglen < 0 {
7719
+				return ErrInvalidLengthOps
7720
+			}
7721
+			postIndex := iNdEx + msglen
7722
+			if postIndex > l {
7723
+				return io.ErrUnexpectedEOF
7724
+			}
7725
+			v := &NamedUserOpt{}
7726
+			if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
7727
+				return err
7728
+			}
7729
+			m.User = &UserOpt_ByName{v}
7730
+			iNdEx = postIndex
7731
+		case 2:
7732
+			if wireType != 0 {
7733
+				return fmt.Errorf("proto: wrong wireType = %d for field ByID", wireType)
7734
+			}
7735
+			var v uint32
7736
+			for shift := uint(0); ; shift += 7 {
7737
+				if shift >= 64 {
7738
+					return ErrIntOverflowOps
7739
+				}
7740
+				if iNdEx >= l {
7741
+					return io.ErrUnexpectedEOF
7742
+				}
7743
+				b := dAtA[iNdEx]
7744
+				iNdEx++
7745
+				v |= (uint32(b) & 0x7F) << shift
7746
+				if b < 0x80 {
7747
+					break
7748
+				}
7749
+			}
7750
+			m.User = &UserOpt_ByID{v}
7751
+		default:
7752
+			iNdEx = preIndex
7753
+			skippy, err := skipOps(dAtA[iNdEx:])
7754
+			if err != nil {
7755
+				return err
7756
+			}
7757
+			if skippy < 0 {
7758
+				return ErrInvalidLengthOps
7759
+			}
7760
+			if (iNdEx + skippy) > l {
7761
+				return io.ErrUnexpectedEOF
7762
+			}
7763
+			iNdEx += skippy
7764
+		}
7765
+	}
7766
+
7767
+	if iNdEx > l {
7768
+		return io.ErrUnexpectedEOF
7769
+	}
7770
+	return nil
7771
+}
7772
+func (m *NamedUserOpt) Unmarshal(dAtA []byte) error {
7773
+	l := len(dAtA)
7774
+	iNdEx := 0
7775
+	for iNdEx < l {
7776
+		preIndex := iNdEx
7777
+		var wire uint64
7778
+		for shift := uint(0); ; shift += 7 {
7779
+			if shift >= 64 {
7780
+				return ErrIntOverflowOps
7781
+			}
7782
+			if iNdEx >= l {
7783
+				return io.ErrUnexpectedEOF
7784
+			}
7785
+			b := dAtA[iNdEx]
7786
+			iNdEx++
7787
+			wire |= (uint64(b) & 0x7F) << shift
7788
+			if b < 0x80 {
7789
+				break
7790
+			}
7791
+		}
7792
+		fieldNum := int32(wire >> 3)
7793
+		wireType := int(wire & 0x7)
7794
+		if wireType == 4 {
7795
+			return fmt.Errorf("proto: NamedUserOpt: wiretype end group for non-group")
7796
+		}
7797
+		if fieldNum <= 0 {
7798
+			return fmt.Errorf("proto: NamedUserOpt: illegal tag %d (wire type %d)", fieldNum, wire)
7799
+		}
7800
+		switch fieldNum {
7801
+		case 1:
7802
+			if wireType != 2 {
7803
+				return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
7804
+			}
7805
+			var stringLen uint64
7806
+			for shift := uint(0); ; shift += 7 {
7807
+				if shift >= 64 {
7808
+					return ErrIntOverflowOps
7809
+				}
7810
+				if iNdEx >= l {
7811
+					return io.ErrUnexpectedEOF
7812
+				}
7813
+				b := dAtA[iNdEx]
7814
+				iNdEx++
7815
+				stringLen |= (uint64(b) & 0x7F) << shift
7816
+				if b < 0x80 {
7817
+					break
7818
+				}
7819
+			}
7820
+			intStringLen := int(stringLen)
7821
+			if intStringLen < 0 {
7822
+				return ErrInvalidLengthOps
7823
+			}
7824
+			postIndex := iNdEx + intStringLen
7825
+			if postIndex > l {
7826
+				return io.ErrUnexpectedEOF
7827
+			}
7828
+			m.Name = string(dAtA[iNdEx:postIndex])
7829
+			iNdEx = postIndex
7830
+		case 2:
7831
+			if wireType != 0 {
7832
+				return fmt.Errorf("proto: wrong wireType = %d for field Input", wireType)
7833
+			}
7834
+			m.Input = 0
7835
+			for shift := uint(0); ; shift += 7 {
7836
+				if shift >= 64 {
7837
+					return ErrIntOverflowOps
7838
+				}
7839
+				if iNdEx >= l {
7840
+					return io.ErrUnexpectedEOF
7841
+				}
7842
+				b := dAtA[iNdEx]
7843
+				iNdEx++
7844
+				m.Input |= (InputIndex(b) & 0x7F) << shift
7845
+				if b < 0x80 {
7846
+					break
7847
+				}
7848
+			}
7849
+		default:
7850
+			iNdEx = preIndex
7851
+			skippy, err := skipOps(dAtA[iNdEx:])
7852
+			if err != nil {
7853
+				return err
7854
+			}
7855
+			if skippy < 0 {
7856
+				return ErrInvalidLengthOps
7857
+			}
7858
+			if (iNdEx + skippy) > l {
7859
+				return io.ErrUnexpectedEOF
7860
+			}
7861
+			iNdEx += skippy
7862
+		}
7863
+	}
7864
+
7865
+	if iNdEx > l {
7866
+		return io.ErrUnexpectedEOF
7867
+	}
7868
+	return nil
7869
+}
6469 7870
 func skipOps(dAtA []byte) (n int, err error) {
6470 7871
 	l := len(dAtA)
6471 7872
 	iNdEx := 0
... ...
@@ -6571,99 +9171,129 @@ var (
6571 6571
 	ErrIntOverflowOps   = fmt.Errorf("proto: integer overflow")
6572 6572
 )
6573 6573
 
6574
-func init() { proto.RegisterFile("ops.proto", fileDescriptor_ops_821a7942fdf920e6) }
6575
-
6576
-var fileDescriptor_ops_821a7942fdf920e6 = []byte{
6577
-	// 1452 bytes of a gzipped FileDescriptorProto
6578
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0x4b, 0x6f, 0x1b, 0xc9,
6579
-	0x11, 0x16, 0x87, 0xcf, 0x29, 0x4a, 0x32, 0xd3, 0x7e, 0x84, 0x51, 0x14, 0x4a, 0x19, 0x27, 0x81,
6580
-	0x2c, 0x59, 0x14, 0x40, 0x03, 0xb6, 0x91, 0x83, 0x11, 0xf1, 0x61, 0x88, 0x71, 0x24, 0x0a, 0x4d,
6581
-	0x45, 0x39, 0x1a, 0xa3, 0x61, 0x93, 0x1a, 0x88, 0x9a, 0x1e, 0xcc, 0x34, 0x6d, 0xf1, 0x92, 0x83,
6582
-	0x7f, 0x41, 0x80, 0x00, 0xb9, 0xe7, 0x98, 0x1f, 0x91, 0xbb, 0x8f, 0x46, 0x4e, 0x4e, 0x0e, 0xce,
6583
-	0x42, 0xfe, 0x23, 0x8b, 0xaa, 0xee, 0xe1, 0x8c, 0x1f, 0x8b, 0xb5, 0xb1, 0x8b, 0x3d, 0xb1, 0xba,
6584
-	0xea, 0xeb, 0xaf, 0xeb, 0xd5, 0x5d, 0x43, 0xb0, 0x65, 0x18, 0x37, 0xc3, 0x48, 0x2a, 0xc9, 0xac,
6585
-	0xf0, 0x6c, 0x6d, 0x77, 0xe2, 0xab, 0xf3, 0xd9, 0x59, 0xd3, 0x93, 0x97, 0x7b, 0x13, 0x39, 0x91,
6586
-	0x7b, 0x64, 0x3a, 0x9b, 0x8d, 0x69, 0x45, 0x0b, 0x92, 0xf4, 0x16, 0xe7, 0x9f, 0x16, 0x58, 0x83,
6587
-	0x90, 0xfd, 0x1a, 0x4a, 0x7e, 0x10, 0xce, 0x54, 0x5c, 0xcf, 0x6d, 0xe6, 0xb7, 0xaa, 0x2d, 0xbb,
6588
-	0x19, 0x9e, 0x35, 0xfb, 0xa8, 0xe1, 0xc6, 0xc0, 0x36, 0xa1, 0x20, 0xae, 0x84, 0x57, 0xb7, 0x36,
6589
-	0x73, 0x5b, 0xd5, 0x16, 0x20, 0xa0, 0x77, 0x25, 0xbc, 0x41, 0x78, 0xb0, 0xc4, 0xc9, 0xc2, 0x7e,
6590
-	0x07, 0xa5, 0x58, 0xce, 0x22, 0x4f, 0xd4, 0xf3, 0x84, 0x59, 0x46, 0xcc, 0x90, 0x34, 0x84, 0x32,
6591
-	0x56, 0x64, 0xf2, 0x64, 0x38, 0xaf, 0x17, 0x52, 0xa6, 0x8e, 0x0c, 0xe7, 0x9a, 0x09, 0x2d, 0xec,
6592
-	0x2e, 0x14, 0xcf, 0x66, 0xfe, 0x74, 0x54, 0x2f, 0x12, 0xa4, 0x8a, 0x90, 0x36, 0x2a, 0x08, 0xa3,
6593
-	0x6d, 0x6c, 0x0b, 0x2a, 0xe1, 0xd4, 0x55, 0x63, 0x19, 0x5d, 0xd6, 0x21, 0x3d, 0xf0, 0xd8, 0xe8,
6594
-	0xf8, 0xc2, 0xca, 0x1e, 0x41, 0xd5, 0x93, 0x41, 0xac, 0x22, 0xd7, 0x0f, 0x54, 0x5c, 0xaf, 0x12,
6595
-	0xf8, 0x36, 0x82, 0xff, 0x22, 0xa3, 0x0b, 0x11, 0x75, 0x52, 0x23, 0xcf, 0x22, 0xdb, 0x05, 0xb0,
6596
-	0x64, 0xe8, 0xfc, 0x23, 0x07, 0x95, 0x84, 0x95, 0x39, 0xb0, 0xbc, 0x1f, 0x79, 0xe7, 0xbe, 0x12,
6597
-	0x9e, 0x9a, 0x45, 0xa2, 0x9e, 0xdb, 0xcc, 0x6d, 0xd9, 0xfc, 0x03, 0x1d, 0x5b, 0x05, 0x6b, 0x30,
6598
-	0xa4, 0x44, 0xd9, 0xdc, 0x1a, 0x0c, 0x59, 0x1d, 0xca, 0xa7, 0x6e, 0xe4, 0xbb, 0x81, 0xa2, 0xcc,
6599
-	0xd8, 0x3c, 0x59, 0xb2, 0x75, 0xb0, 0x07, 0xc3, 0x53, 0x11, 0xc5, 0xbe, 0x0c, 0x28, 0x1f, 0x36,
6600
-	0x4f, 0x15, 0xac, 0x01, 0x30, 0x18, 0x3e, 0x15, 0x2e, 0x92, 0xc6, 0xf5, 0xe2, 0x66, 0x7e, 0xcb,
6601
-	0xe6, 0x19, 0x8d, 0xf3, 0x57, 0x28, 0x52, 0x8d, 0xd8, 0x1f, 0xa1, 0x34, 0xf2, 0x27, 0x22, 0x56,
6602
-	0xda, 0x9d, 0x76, 0xeb, 0xf5, 0xbb, 0x8d, 0xa5, 0xff, 0xbd, 0xdb, 0xd8, 0xce, 0x34, 0x83, 0x0c,
6603
-	0x45, 0xe0, 0xc9, 0x40, 0xb9, 0x7e, 0x20, 0xa2, 0x78, 0x6f, 0x22, 0x77, 0xf5, 0x96, 0x66, 0x97,
6604
-	0x7e, 0xb8, 0x61, 0x60, 0xf7, 0xa0, 0xe8, 0x07, 0x23, 0x71, 0x45, 0xfe, 0xe7, 0xdb, 0x37, 0x0d,
6605
-	0x55, 0x75, 0x30, 0x53, 0xe1, 0x4c, 0xf5, 0xd1, 0xc4, 0x35, 0xc2, 0x09, 0xa1, 0xa4, 0x5b, 0x80,
6606
-	0xad, 0x43, 0xe1, 0x52, 0x28, 0x97, 0x8e, 0xaf, 0xb6, 0x2a, 0x98, 0xda, 0x43, 0xa1, 0x5c, 0x4e,
6607
-	0x5a, 0xec, 0xae, 0x4b, 0x39, 0xc3, 0xd4, 0x5b, 0x69, 0x77, 0x1d, 0xa2, 0x86, 0x1b, 0x03, 0xfb,
6608
-	0x2d, 0x94, 0x03, 0xa1, 0x5e, 0xca, 0xe8, 0x82, 0x52, 0xb4, 0xaa, 0x6b, 0x7e, 0x24, 0xd4, 0xa1,
6609
-	0x1c, 0x09, 0x9e, 0xd8, 0x9c, 0x7f, 0xe5, 0xa0, 0x80, 0xc4, 0x8c, 0x41, 0xc1, 0x8d, 0x26, 0xba,
6610
-	0x5d, 0x6d, 0x4e, 0x32, 0xab, 0x41, 0x5e, 0x04, 0x2f, 0xe8, 0x0c, 0x9b, 0xa3, 0x88, 0x1a, 0xef,
6611
-	0xe5, 0xc8, 0x24, 0x1d, 0x45, 0xdc, 0x37, 0x8b, 0x45, 0x64, 0x72, 0x4d, 0x32, 0xbb, 0x07, 0x76,
6612
-	0x18, 0xc9, 0xab, 0xf9, 0x73, 0xdc, 0x5d, 0xcc, 0x74, 0x12, 0x2a, 0x7b, 0xc1, 0x0b, 0x5e, 0x09,
6613
-	0x8d, 0xc4, 0xb6, 0x01, 0xc4, 0x95, 0x8a, 0xdc, 0x03, 0x19, 0xab, 0xb8, 0x5e, 0xa2, 0x68, 0xa8,
6614
-	0x81, 0x51, 0xd1, 0x3f, 0xe6, 0x19, 0xab, 0xf3, 0x1f, 0x0b, 0x8a, 0x14, 0x24, 0xdb, 0xc2, 0x94,
6615
-	0x86, 0x33, 0x5d, 0x9d, 0x7c, 0x9b, 0x99, 0x94, 0x02, 0x15, 0x6f, 0x91, 0x51, 0x2c, 0xe4, 0x1a,
6616
-	0x54, 0x62, 0x31, 0x15, 0x9e, 0x92, 0x91, 0xe9, 0x9f, 0xc5, 0x1a, 0x5d, 0x1f, 0x61, 0x89, 0x75,
6617
-	0x34, 0x24, 0xb3, 0x1d, 0x28, 0x49, 0xaa, 0x0b, 0x05, 0xf4, 0x1d, 0xd5, 0x32, 0x10, 0x24, 0x8f,
6618
-	0x84, 0x3b, 0x92, 0xc1, 0x74, 0x4e, 0x61, 0x56, 0xf8, 0x62, 0xcd, 0x76, 0xc0, 0xa6, 0x4a, 0x9c,
6619
-	0xcc, 0x43, 0x51, 0x2f, 0x51, 0x05, 0x56, 0x16, 0x55, 0x42, 0x25, 0x4f, 0xed, 0x78, 0xf3, 0x3c,
6620
-	0xd7, 0x3b, 0x17, 0x83, 0x50, 0xd5, 0x6f, 0xa5, 0xf9, 0xea, 0x18, 0x1d, 0x5f, 0x58, 0x91, 0x36,
6621
-	0x16, 0x5e, 0x24, 0x14, 0x42, 0x6f, 0x13, 0x94, 0x68, 0x87, 0x89, 0x92, 0xa7, 0x76, 0xe6, 0x40,
6622
-	0x69, 0x38, 0x3c, 0x40, 0xe4, 0x9d, 0xf4, 0x65, 0xd0, 0x1a, 0x6e, 0x2c, 0x4e, 0x1f, 0x2a, 0xc9,
6623
-	0x31, 0x78, 0xcd, 0xfa, 0x5d, 0x73, 0x01, 0xad, 0x7e, 0x97, 0xed, 0x42, 0x39, 0x3e, 0x77, 0x23,
6624
-	0x3f, 0x98, 0x50, 0xee, 0x56, 0x5b, 0x37, 0x17, 0x5e, 0x0d, 0xb5, 0x1e, 0x99, 0x12, 0x8c, 0x23,
6625
-	0xc1, 0x5e, 0xb8, 0xf1, 0x09, 0x57, 0x0d, 0xf2, 0x33, 0x7f, 0x44, 0x3c, 0x2b, 0x1c, 0x45, 0xd4,
6626
-	0x4c, 0x7c, 0xdd, 0x4b, 0x2b, 0x1c, 0x45, 0x2c, 0xc8, 0xa5, 0x1c, 0x09, 0x4a, 0xfd, 0x0a, 0x27,
6627
-	0x19, 0x73, 0x2c, 0x43, 0xe5, 0xcb, 0xc0, 0x9d, 0x26, 0x39, 0x4e, 0xd6, 0xce, 0x34, 0x89, 0xef,
6628
-	0x27, 0x39, 0xed, 0x09, 0x94, 0xf4, 0xab, 0xca, 0x36, 0x21, 0x1f, 0x47, 0x9e, 0x79, 0xd9, 0x57,
6629
-	0x93, 0xe7, 0x56, 0x3f, 0xcc, 0x1c, 0x4d, 0x8b, 0xd6, 0xb2, 0xd2, 0xd6, 0x72, 0x38, 0x40, 0x0a,
6630
-	0xfb, 0x71, 0x5a, 0xd8, 0xf9, 0x7b, 0x0e, 0x2a, 0xc9, 0x40, 0xc0, 0xd7, 0xcd, 0x1f, 0x89, 0x40,
6631
-	0xf9, 0x63, 0x5f, 0x44, 0x26, 0x19, 0x19, 0x0d, 0xdb, 0x85, 0xa2, 0xab, 0x54, 0x94, 0x3c, 0x1a,
6632
-	0x3f, 0xcf, 0x4e, 0x93, 0xe6, 0x3e, 0x5a, 0x7a, 0x81, 0x8a, 0xe6, 0x5c, 0xa3, 0xd6, 0x1e, 0x03,
6633
-	0xa4, 0x4a, 0xcc, 0xdf, 0x85, 0x98, 0x1b, 0x56, 0x14, 0xd9, 0x2d, 0x28, 0xbe, 0x70, 0xa7, 0x33,
6634
-	0x61, 0x9c, 0xd2, 0x8b, 0xdf, 0x5b, 0x8f, 0x73, 0xce, 0xbf, 0x2d, 0x28, 0x9b, 0xe9, 0xc2, 0xee,
6635
-	0x43, 0x99, 0xa6, 0x8b, 0xf1, 0xe8, 0xf3, 0x91, 0x26, 0x10, 0xb6, 0xb7, 0x18, 0x9b, 0x19, 0x1f,
6636
-	0x0d, 0x95, 0x1e, 0x9f, 0xc6, 0xc7, 0x74, 0x88, 0xe6, 0x47, 0x62, 0x6c, 0xe6, 0x23, 0x95, 0xa2,
6637
-	0x2b, 0xc6, 0x7e, 0xe0, 0x63, 0xcd, 0x38, 0x9a, 0xd8, 0xfd, 0x24, 0xea, 0x02, 0x31, 0xde, 0xc9,
6638
-	0x32, 0x7e, 0x1a, 0x74, 0x1f, 0xaa, 0x99, 0x63, 0x3e, 0x13, 0xf5, 0x6f, 0xb2, 0x51, 0x9b, 0x23,
6639
-	0x89, 0x4e, 0x0f, 0xf7, 0x34, 0x0b, 0x3f, 0x20, 0x7f, 0x0f, 0x01, 0x52, 0xca, 0x2f, 0xef, 0x14,
6640
-	0xe7, 0x55, 0x1e, 0x60, 0x10, 0xe2, 0x73, 0x3e, 0x72, 0x69, 0x4a, 0x2c, 0xfb, 0x93, 0x40, 0x46,
6641
-	0xe2, 0x39, 0x3d, 0x1f, 0xb4, 0xbf, 0xc2, 0xab, 0x5a, 0x47, 0xb7, 0x98, 0xed, 0x43, 0x75, 0x24,
6642
-	0x62, 0x2f, 0xf2, 0xa9, 0xc9, 0x4d, 0xd2, 0x37, 0x30, 0xa6, 0x94, 0xa7, 0xd9, 0x4d, 0x11, 0x3a,
6643
-	0x57, 0xd9, 0x3d, 0xac, 0x05, 0xcb, 0xe2, 0x2a, 0x94, 0x91, 0x32, 0xa7, 0xe8, 0x8f, 0x90, 0x1b,
6644
-	0xfa, 0x73, 0x06, 0xf5, 0x74, 0x12, 0xaf, 0x8a, 0x74, 0xc1, 0x5c, 0x28, 0x78, 0x6e, 0xa8, 0x27,
6645
-	0x70, 0xb5, 0x55, 0xff, 0xe8, 0xbc, 0x8e, 0x1b, 0xea, 0xa4, 0xb5, 0x1f, 0x60, 0xac, 0xaf, 0xfe,
6646
-	0xbf, 0xb1, 0x93, 0x19, 0xbb, 0x97, 0xf2, 0x6c, 0xbe, 0x47, 0xfd, 0x72, 0xe1, 0xab, 0xbd, 0x99,
6647
-	0xf2, 0xa7, 0x7b, 0x6e, 0xe8, 0x23, 0x1d, 0x6e, 0xec, 0x77, 0x39, 0x51, 0xaf, 0x3d, 0x81, 0xda,
6648
-	0xc7, 0x7e, 0x7f, 0x4d, 0x0d, 0xd6, 0x1e, 0x81, 0xbd, 0xf0, 0xe3, 0xfb, 0x36, 0x56, 0xb2, 0xc5,
6649
-	0xbb, 0x0b, 0xd5, 0x4c, 0xdc, 0x08, 0x3c, 0x25, 0xa0, 0xce, 0xbe, 0x5e, 0x38, 0xaf, 0xf0, 0x0b,
6650
-	0x28, 0x99, 0x81, 0xbf, 0x02, 0x38, 0x57, 0x2a, 0x7c, 0x4e, 0x43, 0xd1, 0x1c, 0x62, 0xa3, 0x86,
6651
-	0x10, 0x6c, 0x03, 0xaa, 0xb8, 0x88, 0x8d, 0x5d, 0x7b, 0x4a, 0x3b, 0x62, 0x0d, 0xf8, 0x25, 0xd8,
6652
-	0xe3, 0xc5, 0x76, 0x3d, 0xcc, 0x2a, 0xe3, 0x64, 0xf7, 0x2f, 0xa0, 0x12, 0x48, 0x63, 0xd3, 0x33,
6653
-	0xba, 0x1c, 0x48, 0x32, 0x39, 0x3b, 0xf0, 0xb3, 0x4f, 0x3e, 0xd7, 0xd8, 0x1d, 0x28, 0x8d, 0xfd,
6654
-	0xa9, 0xa2, 0xeb, 0x8a, 0x63, 0xdf, 0xac, 0x9c, 0xff, 0xe6, 0x00, 0xd2, 0xab, 0x85, 0x19, 0xc1,
6655
-	0x7b, 0x87, 0x98, 0x65, 0x7d, 0xcf, 0xa6, 0x50, 0xb9, 0x34, 0x15, 0x34, 0x7d, 0xb4, 0xfe, 0xe1,
6656
-	0x75, 0x6c, 0x26, 0x05, 0xd6, 0xb5, 0x6d, 0x99, 0xda, 0x7e, 0xcd, 0x27, 0xd5, 0xe2, 0x84, 0xb5,
6657
-	0x67, 0xb0, 0xf2, 0x01, 0xdd, 0x17, 0xde, 0xd4, 0xb4, 0xcb, 0xb2, 0x25, 0xbb, 0x0f, 0x25, 0xfd,
6658
-	0xb9, 0x81, 0xef, 0x36, 0x4a, 0x86, 0x86, 0x64, 0x9a, 0x2d, 0xc7, 0xc9, 0xc7, 0x67, 0xff, 0x78,
6659
-	0x7b, 0x0b, 0xca, 0xe6, 0x33, 0x8a, 0xd9, 0x50, 0xfc, 0xf3, 0xd1, 0xb0, 0x77, 0x52, 0x5b, 0x62,
6660
-	0x15, 0x28, 0x1c, 0x0c, 0x86, 0x27, 0xb5, 0x1c, 0x4a, 0x47, 0x83, 0xa3, 0x5e, 0xcd, 0xda, 0xfe,
6661
-	0x03, 0xd8, 0x8b, 0x71, 0x8f, 0xea, 0x76, 0xff, 0xa8, 0x5b, 0x5b, 0x62, 0x00, 0xa5, 0x61, 0xaf,
6662
-	0xc3, 0x7b, 0x08, 0x2e, 0x43, 0x7e, 0x38, 0x3c, 0xa8, 0x59, 0x48, 0xd5, 0xd9, 0xef, 0x1c, 0xf4,
6663
-	0x6a, 0x79, 0x14, 0x4f, 0x0e, 0x8f, 0x9f, 0x0e, 0x6b, 0x85, 0xed, 0x87, 0x70, 0xe3, 0xa3, 0x71,
6664
-	0x4b, 0xbb, 0x0f, 0xf6, 0x79, 0x0f, 0x99, 0xaa, 0x50, 0x3e, 0xe6, 0xfd, 0xd3, 0xfd, 0x93, 0x5e,
6665
-	0x2d, 0x87, 0x86, 0x3f, 0x0d, 0x3a, 0xcf, 0x7a, 0xdd, 0x9a, 0xd5, 0x5e, 0x7f, 0x7d, 0xdd, 0xc8,
6666
-	0xbd, 0xb9, 0x6e, 0xe4, 0xde, 0x5e, 0x37, 0x72, 0xdf, 0x5c, 0x37, 0x72, 0x7f, 0x7b, 0xdf, 0x58,
6667
-	0x7a, 0xf3, 0xbe, 0xb1, 0xf4, 0xf6, 0x7d, 0x63, 0xe9, 0xac, 0x44, 0x7f, 0x55, 0x1e, 0x7c, 0x1b,
6668
-	0x00, 0x00, 0xff, 0xff, 0x26, 0x19, 0xc9, 0x11, 0xea, 0x0c, 0x00, 0x00,
6574
+func init() { proto.RegisterFile("ops.proto", fileDescriptor_ops_8d64813b9835ab08) }
6575
+
6576
+var fileDescriptor_ops_8d64813b9835ab08 = []byte{
6577
+	// 1924 bytes of a gzipped FileDescriptorProto
6578
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0x5f, 0x6f, 0x1b, 0xc7,
6579
+	0x11, 0x17, 0x8f, 0x7f, 0x6f, 0x28, 0xc9, 0xec, 0xc6, 0x49, 0x59, 0xd5, 0x95, 0x94, 0x4b, 0x1a,
6580
+	0x30, 0xb2, 0x4d, 0x01, 0x0a, 0x90, 0x04, 0x79, 0x28, 0x2a, 0xfe, 0x31, 0xc4, 0x24, 0x16, 0x85,
6581
+	0xa5, 0xed, 0x3c, 0x1a, 0xc7, 0xbb, 0x25, 0x75, 0x20, 0xef, 0xf6, 0xb0, 0xb7, 0xb4, 0xc4, 0x97,
6582
+	0x3e, 0xf8, 0x13, 0x04, 0x28, 0xd0, 0xb7, 0x3e, 0xf4, 0xa5, 0x40, 0x3f, 0x44, 0xdf, 0xf3, 0x18,
6583
+	0x14, 0x7d, 0x48, 0xfb, 0x90, 0x16, 0xf6, 0x17, 0x29, 0x66, 0x77, 0x8f, 0x77, 0xa4, 0x15, 0xd8,
6584
+	0x46, 0x8b, 0x3e, 0x71, 0x76, 0xe6, 0xb7, 0xb3, 0xb3, 0x33, 0xb3, 0x33, 0x73, 0x04, 0x9b, 0xc7,
6585
+	0x49, 0x3b, 0x16, 0x5c, 0x72, 0x62, 0xc5, 0xe3, 0xbd, 0xfb, 0xd3, 0x40, 0x5e, 0x2e, 0xc6, 0x6d,
6586
+	0x8f, 0x87, 0xc7, 0x53, 0x3e, 0xe5, 0xc7, 0x4a, 0x34, 0x5e, 0x4c, 0xd4, 0x4a, 0x2d, 0x14, 0xa5,
6587
+	0xb7, 0x38, 0x7f, 0xb2, 0xc0, 0x1a, 0xc6, 0xe4, 0x7d, 0xa8, 0x04, 0x51, 0xbc, 0x90, 0x49, 0xb3,
6588
+	0x70, 0x58, 0x6c, 0xd5, 0x4f, 0xec, 0x76, 0x3c, 0x6e, 0x0f, 0x90, 0x43, 0x8d, 0x80, 0x1c, 0x42,
6589
+	0x89, 0x5d, 0x33, 0xaf, 0x69, 0x1d, 0x16, 0x5a, 0xf5, 0x13, 0x40, 0x40, 0xff, 0x9a, 0x79, 0xc3,
6590
+	0xf8, 0x6c, 0x8b, 0x2a, 0x09, 0xf9, 0x08, 0x2a, 0x09, 0x5f, 0x08, 0x8f, 0x35, 0x8b, 0x0a, 0xb3,
6591
+	0x8d, 0x98, 0x91, 0xe2, 0x28, 0x94, 0x91, 0xa2, 0xa6, 0x49, 0x30, 0x67, 0xcd, 0x52, 0xa6, 0xe9,
6592
+	0x41, 0x30, 0xd7, 0x18, 0x25, 0x21, 0x1f, 0x40, 0x79, 0xbc, 0x08, 0xe6, 0x7e, 0xb3, 0xac, 0x20,
6593
+	0x75, 0x84, 0x74, 0x90, 0xa1, 0x30, 0x5a, 0x46, 0x5a, 0x50, 0x8b, 0xe7, 0xae, 0x9c, 0x70, 0x11,
6594
+	0x36, 0x21, 0x3b, 0xf0, 0xc2, 0xf0, 0xe8, 0x4a, 0x4a, 0x3e, 0x83, 0xba, 0xc7, 0xa3, 0x44, 0x0a,
6595
+	0x37, 0x88, 0x64, 0xd2, 0xac, 0x2b, 0xf0, 0xbb, 0x08, 0xfe, 0x86, 0x8b, 0x19, 0x13, 0xdd, 0x4c,
6596
+	0x48, 0xf3, 0xc8, 0x4e, 0x09, 0x2c, 0x1e, 0x3b, 0x7f, 0x28, 0x40, 0x2d, 0xd5, 0x4a, 0x1c, 0xd8,
6597
+	0x3e, 0x15, 0xde, 0x65, 0x20, 0x99, 0x27, 0x17, 0x82, 0x35, 0x0b, 0x87, 0x85, 0x96, 0x4d, 0xd7,
6598
+	0x78, 0x64, 0x17, 0xac, 0xe1, 0x48, 0x39, 0xca, 0xa6, 0xd6, 0x70, 0x44, 0x9a, 0x50, 0x7d, 0xe2,
6599
+	0x8a, 0xc0, 0x8d, 0xa4, 0xf2, 0x8c, 0x4d, 0xd3, 0x25, 0xb9, 0x03, 0xf6, 0x70, 0xf4, 0x84, 0x89,
6600
+	0x24, 0xe0, 0x91, 0xf2, 0x87, 0x4d, 0x33, 0x06, 0xd9, 0x07, 0x18, 0x8e, 0x1e, 0x30, 0x17, 0x95,
6601
+	0x26, 0xcd, 0xf2, 0x61, 0xb1, 0x65, 0xd3, 0x1c, 0xc7, 0xf9, 0x1d, 0x94, 0x55, 0x8c, 0xc8, 0x97,
6602
+	0x50, 0xf1, 0x83, 0x29, 0x4b, 0xa4, 0x36, 0xa7, 0x73, 0xf2, 0xdd, 0x8f, 0x07, 0x5b, 0xff, 0xfc,
6603
+	0xf1, 0xe0, 0x28, 0x97, 0x0c, 0x3c, 0x66, 0x91, 0xc7, 0x23, 0xe9, 0x06, 0x11, 0x13, 0xc9, 0xf1,
6604
+	0x94, 0xdf, 0xd7, 0x5b, 0xda, 0x3d, 0xf5, 0x43, 0x8d, 0x06, 0xf2, 0x31, 0x94, 0x83, 0xc8, 0x67,
6605
+	0xd7, 0xca, 0xfe, 0x62, 0xe7, 0x1d, 0xa3, 0xaa, 0x3e, 0x5c, 0xc8, 0x78, 0x21, 0x07, 0x28, 0xa2,
6606
+	0x1a, 0xe1, 0xc4, 0x50, 0xd1, 0x29, 0x40, 0xee, 0x40, 0x29, 0x64, 0xd2, 0x55, 0xc7, 0xd7, 0x4f,
6607
+	0x6a, 0xe8, 0xda, 0x87, 0x4c, 0xba, 0x54, 0x71, 0x31, 0xbb, 0x42, 0xbe, 0x40, 0xd7, 0x5b, 0x59,
6608
+	0x76, 0x3d, 0x44, 0x0e, 0x35, 0x02, 0xf2, 0x6b, 0xa8, 0x46, 0x4c, 0x5e, 0x71, 0x31, 0x53, 0x2e,
6609
+	0xda, 0xd5, 0x31, 0x3f, 0x67, 0xf2, 0x21, 0xf7, 0x19, 0x4d, 0x65, 0xce, 0x5f, 0x0a, 0x50, 0x42,
6610
+	0xc5, 0x84, 0x40, 0xc9, 0x15, 0x53, 0x9d, 0xae, 0x36, 0x55, 0x34, 0x69, 0x40, 0x91, 0x45, 0xcf,
6611
+	0xd4, 0x19, 0x36, 0x45, 0x12, 0x39, 0xde, 0x95, 0x6f, 0x9c, 0x8e, 0x24, 0xee, 0x5b, 0x24, 0x4c,
6612
+	0x18, 0x5f, 0x2b, 0x9a, 0x7c, 0x0c, 0x76, 0x2c, 0xf8, 0xf5, 0xf2, 0x29, 0xee, 0x2e, 0xe7, 0x32,
6613
+	0x09, 0x99, 0xfd, 0xe8, 0x19, 0xad, 0xc5, 0x86, 0x22, 0x47, 0x00, 0xec, 0x5a, 0x0a, 0xf7, 0x8c,
6614
+	0x27, 0x32, 0x69, 0x56, 0xd4, 0x6d, 0x54, 0x02, 0x23, 0x63, 0x70, 0x41, 0x73, 0x52, 0xe7, 0x6f,
6615
+	0x16, 0x94, 0xd5, 0x25, 0x49, 0x0b, 0x5d, 0x1a, 0x2f, 0x74, 0x74, 0x8a, 0x1d, 0x62, 0x5c, 0x0a,
6616
+	0x2a, 0x78, 0x2b, 0x8f, 0x62, 0x20, 0xf7, 0xa0, 0x96, 0xb0, 0x39, 0xf3, 0x24, 0x17, 0x26, 0x7f,
6617
+	0x56, 0x6b, 0x34, 0xdd, 0xc7, 0x10, 0xeb, 0xdb, 0x28, 0x9a, 0xdc, 0x85, 0x0a, 0x57, 0x71, 0x51,
6618
+	0x17, 0xfa, 0x89, 0x68, 0x19, 0x08, 0x2a, 0x17, 0xcc, 0xf5, 0x79, 0x34, 0x5f, 0xaa, 0x6b, 0xd6,
6619
+	0xe8, 0x6a, 0x4d, 0xee, 0x82, 0xad, 0x22, 0xf1, 0x68, 0x19, 0xb3, 0x66, 0x45, 0x45, 0x60, 0x67,
6620
+	0x15, 0x25, 0x64, 0xd2, 0x4c, 0x8e, 0x2f, 0xcf, 0x73, 0xbd, 0x4b, 0x36, 0x8c, 0x65, 0xf3, 0x76,
6621
+	0xe6, 0xaf, 0xae, 0xe1, 0xd1, 0x95, 0x14, 0xd5, 0x26, 0xcc, 0x13, 0x4c, 0x22, 0xf4, 0x5d, 0x05,
6622
+	0x55, 0x6a, 0x47, 0x29, 0x93, 0x66, 0x72, 0xe2, 0x40, 0x65, 0x34, 0x3a, 0x43, 0xe4, 0x7b, 0x59,
6623
+	0x65, 0xd0, 0x1c, 0x6a, 0x24, 0xce, 0x00, 0x6a, 0xe9, 0x31, 0xf8, 0xcc, 0x06, 0x3d, 0xf3, 0x00,
6624
+	0xad, 0x41, 0x8f, 0xdc, 0x87, 0x6a, 0x72, 0xe9, 0x8a, 0x20, 0x9a, 0x2a, 0xdf, 0xed, 0x9e, 0xbc,
6625
+	0xb3, 0xb2, 0x6a, 0xa4, 0xf9, 0xa8, 0x29, 0xc5, 0x38, 0x1c, 0xec, 0x95, 0x19, 0xaf, 0xe8, 0x6a,
6626
+	0x40, 0x71, 0x11, 0xf8, 0x4a, 0xcf, 0x0e, 0x45, 0x12, 0x39, 0xd3, 0x40, 0xe7, 0xd2, 0x0e, 0x45,
6627
+	0x12, 0x03, 0x12, 0x72, 0x5f, 0xd7, 0xb1, 0x1d, 0xaa, 0x68, 0xf4, 0x31, 0x8f, 0x65, 0xc0, 0x23,
6628
+	0x77, 0x9e, 0xfa, 0x38, 0x5d, 0x3b, 0xf3, 0xf4, 0x7e, 0xff, 0x97, 0xd3, 0x7e, 0x5f, 0x80, 0x5a,
6629
+	0x5a, 0x7c, 0xb1, 0x92, 0x04, 0x3e, 0x8b, 0x64, 0x30, 0x09, 0x98, 0x30, 0x07, 0xe7, 0x38, 0xe4,
6630
+	0x3e, 0x94, 0x5d, 0x29, 0x45, 0xfa, 0x40, 0x7f, 0x9e, 0xaf, 0xdc, 0xed, 0x53, 0x94, 0xf4, 0x23,
6631
+	0x29, 0x96, 0x54, 0xa3, 0xf6, 0x3e, 0x07, 0xc8, 0x98, 0x68, 0xeb, 0x8c, 0x2d, 0x8d, 0x56, 0x24,
6632
+	0xc9, 0x6d, 0x28, 0x3f, 0x73, 0xe7, 0x0b, 0x66, 0x72, 0x58, 0x2f, 0xbe, 0xb0, 0x3e, 0x2f, 0x38,
6633
+	0x7f, 0xb5, 0xa0, 0x6a, 0x2a, 0x39, 0xb9, 0x07, 0x55, 0x55, 0xc9, 0x8d, 0x45, 0x37, 0x3f, 0x8c,
6634
+	0x14, 0x42, 0x8e, 0x57, 0x2d, 0x2a, 0x67, 0xa3, 0x51, 0xa5, 0x5b, 0x95, 0xb1, 0x31, 0x6b, 0x58,
6635
+	0x45, 0x9f, 0x4d, 0x4c, 0x2f, 0xda, 0x45, 0x74, 0x8f, 0x4d, 0x82, 0x28, 0x40, 0xff, 0x50, 0x14,
6636
+	0x91, 0x7b, 0xe9, 0xad, 0x4b, 0x4a, 0xe3, 0x7b, 0x79, 0x8d, 0xaf, 0x5e, 0x7a, 0x00, 0xf5, 0xdc,
6637
+	0x31, 0x37, 0xdc, 0xfa, 0xc3, 0xfc, 0xad, 0xcd, 0x91, 0x4a, 0x9d, 0x6e, 0xa4, 0x99, 0x17, 0xfe,
6638
+	0x0b, 0xff, 0x7d, 0x0a, 0x90, 0xa9, 0x7c, 0xf3, 0xc2, 0xe2, 0x3c, 0x2f, 0x02, 0x0c, 0x63, 0x2c,
6639
+	0x9d, 0xbe, 0xab, 0x2a, 0xf2, 0x76, 0x30, 0x8d, 0xb8, 0x60, 0x4f, 0xd5, 0x53, 0x55, 0xfb, 0x6b,
6640
+	0xb4, 0xae, 0x79, 0xea, 0xc5, 0x90, 0x53, 0xa8, 0xfb, 0x2c, 0xf1, 0x44, 0xa0, 0x12, 0xca, 0x38,
6641
+	0xfd, 0x00, 0xef, 0x94, 0xe9, 0x69, 0xf7, 0x32, 0x84, 0xf6, 0x55, 0x7e, 0x0f, 0x39, 0x81, 0x6d,
6642
+	0x76, 0x1d, 0x73, 0x21, 0xcd, 0x29, 0xba, 0xe1, 0xdf, 0xd2, 0xa3, 0x03, 0xf2, 0xd5, 0x49, 0xb4,
6643
+	0xce, 0xb2, 0x05, 0x71, 0xa1, 0xe4, 0xb9, 0xb1, 0xee, 0x76, 0xf5, 0x93, 0xe6, 0xc6, 0x79, 0x5d,
6644
+	0x37, 0xd6, 0x4e, 0xeb, 0x7c, 0x82, 0x77, 0x7d, 0xfe, 0xaf, 0x83, 0xbb, 0xb9, 0x16, 0x17, 0xf2,
6645
+	0xf1, 0xf2, 0x58, 0xe5, 0xcb, 0x2c, 0x90, 0xc7, 0x0b, 0x19, 0xcc, 0x8f, 0xdd, 0x38, 0x40, 0x75,
6646
+	0xb8, 0x71, 0xd0, 0xa3, 0x4a, 0xf5, 0xde, 0x6f, 0xa0, 0xb1, 0x69, 0xf7, 0xdb, 0xc4, 0x60, 0xef,
6647
+	0x33, 0xb0, 0x57, 0x76, 0xbc, 0x6e, 0x63, 0x2d, 0x1f, 0xbc, 0x0f, 0xa0, 0x9e, 0xbb, 0x37, 0x02,
6648
+	0x9f, 0x28, 0xa0, 0xf6, 0xbe, 0x5e, 0x38, 0xcf, 0x71, 0xda, 0x48, 0xfb, 0xcd, 0xaf, 0x00, 0x2e,
6649
+	0xa5, 0x8c, 0x9f, 0xaa, 0x06, 0x64, 0x0e, 0xb1, 0x91, 0xa3, 0x10, 0xe4, 0x00, 0xea, 0xb8, 0x48,
6650
+	0x8c, 0x5c, 0x5b, 0xaa, 0x76, 0x24, 0x1a, 0xf0, 0x4b, 0xb0, 0x27, 0xab, 0xed, 0xba, 0x71, 0xd4,
6651
+	0x26, 0xe9, 0xee, 0x5f, 0x40, 0x2d, 0xe2, 0x46, 0xa6, 0xfb, 0x61, 0x35, 0xe2, 0x4a, 0xe4, 0xdc,
6652
+	0x85, 0x9f, 0xbd, 0x32, 0x1a, 0x91, 0xf7, 0xa0, 0x32, 0x09, 0xe6, 0x52, 0x3d, 0x57, 0x6c, 0xb1,
6653
+	0x66, 0xe5, 0xfc, 0xa3, 0x00, 0x90, 0x3d, 0x2d, 0xf4, 0x08, 0xbe, 0x3b, 0xc4, 0x6c, 0xeb, 0x77,
6654
+	0x36, 0x87, 0x5a, 0x68, 0x22, 0x68, 0xf2, 0xe8, 0xce, 0xfa, 0x73, 0x6c, 0xa7, 0x01, 0xd6, 0xb1,
6655
+	0x3d, 0x31, 0xb1, 0x7d, 0x9b, 0xf1, 0x65, 0x75, 0xc2, 0xde, 0x57, 0xb0, 0xb3, 0xa6, 0xee, 0x0d,
6656
+	0x5f, 0x6a, 0x96, 0x65, 0xf9, 0x90, 0xdd, 0x83, 0x8a, 0x6e, 0xed, 0x58, 0x7f, 0x91, 0x32, 0x6a,
6657
+	0x14, 0xad, 0xea, 0xf8, 0x45, 0x3a, 0xe8, 0x0d, 0x2e, 0x9c, 0x13, 0xa8, 0xe8, 0x49, 0x96, 0xb4,
6658
+	0xa0, 0xea, 0x7a, 0x78, 0xb5, 0xb4, 0x5c, 0xed, 0xa6, 0x63, 0xee, 0xa9, 0x62, 0xd3, 0x54, 0xec,
6659
+	0xfc, 0xdd, 0x02, 0xc8, 0xf8, 0x6f, 0x31, 0x2b, 0x7c, 0x01, 0xbb, 0x09, 0xf3, 0x78, 0xe4, 0xbb,
6660
+	0x62, 0xa9, 0xa4, 0x66, 0x62, 0xbb, 0x69, 0xcb, 0x06, 0x32, 0x37, 0x37, 0x14, 0x5f, 0x3f, 0x37,
6661
+	0xb4, 0xa0, 0xe4, 0xf1, 0x78, 0x69, 0x9e, 0x2f, 0x59, 0xbf, 0x48, 0x97, 0xc7, 0x4b, 0x9c, 0xdb,
6662
+	0x11, 0x41, 0xda, 0x50, 0x09, 0x67, 0x6a, 0xb6, 0xd7, 0x63, 0xd4, 0xed, 0x75, 0xec, 0xc3, 0x19,
6663
+	0xd2, 0xf8, 0x25, 0xa0, 0x51, 0xe4, 0x2e, 0x94, 0xc3, 0x99, 0x1f, 0x08, 0x35, 0x71, 0xd4, 0x75,
6664
+	0xbf, 0xce, 0xc3, 0x7b, 0x81, 0xc0, 0x79, 0x5f, 0x61, 0x88, 0x03, 0x96, 0x08, 0x9b, 0x55, 0x85,
6665
+	0x6c, 0x6c, 0x78, 0x33, 0x3c, 0xdb, 0xa2, 0x96, 0x08, 0x3b, 0x35, 0xa8, 0x68, 0xbf, 0x3a, 0x7f,
6666
+	0x2e, 0xc2, 0xee, 0xba, 0x95, 0x98, 0x07, 0x89, 0xf0, 0xd2, 0x3c, 0x48, 0x84, 0xb7, 0x1a, 0xa9,
6667
+	0xac, 0xdc, 0x48, 0xe5, 0x40, 0x99, 0x5f, 0x45, 0x4c, 0xe4, 0x3f, 0x62, 0xba, 0x97, 0xfc, 0x2a,
6668
+	0xc2, 0xe1, 0x41, 0x8b, 0xd6, 0x7a, 0x71, 0xd9, 0xf4, 0xe2, 0x0f, 0x61, 0x67, 0xc2, 0xe7, 0x73,
6669
+	0x7e, 0x35, 0x5a, 0x86, 0xf3, 0x20, 0x9a, 0x99, 0x86, 0xbc, 0xce, 0x24, 0x2d, 0xb8, 0xe5, 0x07,
6670
+	0x02, 0xcd, 0xe9, 0xf2, 0x48, 0xb2, 0x48, 0x4d, 0x91, 0x88, 0xdb, 0x64, 0x93, 0x2f, 0xe1, 0xd0,
6671
+	0x95, 0x92, 0x85, 0xb1, 0x7c, 0x1c, 0xc5, 0xae, 0x37, 0xeb, 0x71, 0x4f, 0xbd, 0xc7, 0x30, 0x76,
6672
+	0x65, 0x30, 0x0e, 0xe6, 0x81, 0x5c, 0x2a, 0x67, 0xd4, 0xe8, 0x6b, 0x71, 0xe4, 0x23, 0xd8, 0xf5,
6673
+	0x04, 0x73, 0x25, 0xeb, 0xb1, 0x44, 0x5e, 0xb8, 0xf2, 0xb2, 0x59, 0x53, 0x3b, 0x37, 0xb8, 0x78,
6674
+	0x07, 0x17, 0xad, 0xfd, 0x26, 0x98, 0xfb, 0x9e, 0x2b, 0xfc, 0xa6, 0xad, 0xef, 0xb0, 0xc6, 0x24,
6675
+	0x6d, 0x20, 0x8a, 0xd1, 0x0f, 0x63, 0xb9, 0x5c, 0x41, 0x41, 0x41, 0x6f, 0x90, 0xe0, 0x47, 0x8e,
6676
+	0x0c, 0x42, 0x96, 0x48, 0x37, 0x8c, 0xd5, 0xc7, 0x57, 0x91, 0x66, 0x0c, 0xe7, 0xdb, 0x02, 0x34,
6677
+	0x36, 0x53, 0x04, 0x1d, 0x1c, 0xa3, 0x99, 0xe6, 0xb1, 0x21, 0xbd, 0x72, 0xba, 0x95, 0x73, 0x3a,
6678
+	0x06, 0x10, 0xab, 0x0a, 0xc6, 0x6a, 0x9b, 0x2a, 0x3a, 0x0b, 0x60, 0xe9, 0xa7, 0x03, 0xb8, 0x66,
6679
+	0x52, 0x79, 0xd3, 0xa4, 0x3f, 0x16, 0xe0, 0xd6, 0x46, 0x1a, 0xbe, 0xb1, 0x45, 0x87, 0x50, 0x0f,
6680
+	0xdd, 0x19, 0xbb, 0x70, 0x85, 0x0a, 0x6e, 0x51, 0x37, 0xd6, 0x1c, 0xeb, 0x7f, 0x60, 0x5f, 0x04,
6681
+	0xdb, 0xf9, 0xdc, 0xbf, 0xd1, 0xb6, 0x34, 0x94, 0xe7, 0x5c, 0x3e, 0xe0, 0x8b, 0xc8, 0x37, 0xdd,
6682
+	0x68, 0x9d, 0xf9, 0x6a, 0xc0, 0x8b, 0x37, 0x04, 0xdc, 0x39, 0x87, 0x5a, 0x6a, 0x20, 0x39, 0x30,
6683
+	0x1f, 0x50, 0x85, 0xec, 0xcb, 0xfc, 0x71, 0xc2, 0x04, 0xda, 0xae, 0xbf, 0xa6, 0xde, 0x87, 0xf2,
6684
+	0x54, 0xf0, 0x45, 0x6c, 0x6a, 0xeb, 0x1a, 0x42, 0x4b, 0x9c, 0x11, 0x54, 0x0d, 0x87, 0x1c, 0x41,
6685
+	0x65, 0xbc, 0x3c, 0x77, 0x43, 0x66, 0x14, 0xaa, 0x87, 0x8d, 0x6b, 0xdf, 0x20, 0xb0, 0x5a, 0x68,
6686
+	0x04, 0xb9, 0x0d, 0xa5, 0xf1, 0x72, 0xd0, 0xd3, 0x63, 0x32, 0xd6, 0x1c, 0x5c, 0x75, 0x2a, 0xda,
6687
+	0x20, 0xe7, 0x6b, 0xd8, 0xce, 0xef, 0x43, 0xa7, 0x44, 0xa9, 0x5e, 0x9b, 0x2a, 0x3a, 0x2b, 0xae,
6688
+	0xd6, 0x6b, 0x8a, 0xeb, 0x51, 0x0b, 0xaa, 0xe6, 0xe3, 0x93, 0xd8, 0x50, 0x7e, 0x7c, 0x3e, 0xea,
6689
+	0x3f, 0x6a, 0x6c, 0x91, 0x1a, 0x94, 0xce, 0x86, 0xa3, 0x47, 0x8d, 0x02, 0x52, 0xe7, 0xc3, 0xf3,
6690
+	0x7e, 0xc3, 0x3a, 0xfa, 0x2d, 0xd8, 0xab, 0x8f, 0x24, 0x64, 0x77, 0x06, 0xe7, 0xbd, 0xc6, 0x16,
6691
+	0x01, 0xa8, 0x8c, 0xfa, 0x5d, 0xda, 0x47, 0x70, 0x15, 0x8a, 0xa3, 0xd1, 0x59, 0xc3, 0x42, 0x55,
6692
+	0xdd, 0xd3, 0xee, 0x59, 0xbf, 0x51, 0x44, 0xf2, 0xd1, 0xc3, 0x8b, 0x07, 0xa3, 0x46, 0xe9, 0xe8,
6693
+	0x53, 0xb8, 0xb5, 0xf1, 0x91, 0xa2, 0x76, 0x9f, 0x9d, 0xd2, 0x3e, 0x6a, 0xaa, 0x43, 0xf5, 0x82,
6694
+	0x0e, 0x9e, 0x9c, 0x3e, 0xea, 0x37, 0x0a, 0x28, 0xf8, 0x7a, 0xd8, 0xfd, 0xaa, 0xdf, 0x6b, 0x58,
6695
+	0x9d, 0x3b, 0xdf, 0xbd, 0xd8, 0x2f, 0x7c, 0xff, 0x62, 0xbf, 0xf0, 0xc3, 0x8b, 0xfd, 0xc2, 0xbf,
6696
+	0x5f, 0xec, 0x17, 0xbe, 0x7d, 0xb9, 0xbf, 0xf5, 0xfd, 0xcb, 0xfd, 0xad, 0x1f, 0x5e, 0xee, 0x6f,
6697
+	0x8d, 0x2b, 0xea, 0x0f, 0x9e, 0x4f, 0xfe, 0x13, 0x00, 0x00, 0xff, 0xff, 0x9b, 0x24, 0xd0, 0xaa,
6698
+	0x20, 0x12, 0x00, 0x00,
6669 6699
 }
... ...
@@ -15,7 +15,7 @@ message Op {
15 15
 	oneof op {
16 16
 		ExecOp exec = 2;
17 17
 		SourceOp source = 3;
18
-		CopyOp copy = 4;
18
+		FileOp file = 4;
19 19
 		BuildOp build = 5;
20 20
 	}
21 21
 	Platform platform = 10;
... ...
@@ -134,18 +134,6 @@ message SSHOpt {
134 134
 	bool optional = 5;
135 135
 }
136 136
 
137
-// CopyOp copies files across Ops.
138
-message CopyOp {
139
-	repeated CopySource src = 1;
140
-	string dest = 2;
141
-}
142
-
143
-// CopySource specifies a source for CopyOp.
144
-message CopySource {
145
-	int64 input = 1 [(gogoproto.customtype) = "InputIndex", (gogoproto.nullable) = false];
146
-	string selector = 2;
147
-}
148
-
149 137
 // SourceOp specifies a source such as build contexts and images.
150 138
 message SourceOp {
151 139
 	// TODO: use source type or any type instead of URL protocol.
... ...
@@ -211,4 +199,101 @@ message Definition {
211 211
 message HostIP {
212 212
 	string Host = 1;
213 213
 	string IP = 2;
214
+}
215
+
216
+message FileOp {
217
+	repeated FileAction actions = 2;
218
+}
219
+
220
+message FileAction {
221
+	int64 input = 1 [(gogoproto.customtype) = "InputIndex", (gogoproto.nullable) = false]; // could be real input or target (target index + max input index)
222
+	int64 secondaryInput = 2 [(gogoproto.customtype) = "InputIndex", (gogoproto.nullable) = false]; // --//--
223
+	int64 output = 3 [(gogoproto.customtype) = "OutputIndex", (gogoproto.nullable) = false];
224
+	oneof action {
225
+		// FileActionCopy copies files from secondaryInput on top of input
226
+		FileActionCopy copy = 4;
227
+		// FileActionMkFile creates a new file
228
+		FileActionMkFile mkfile = 5;
229
+		// FileActionMkDir creates a new directory
230
+		FileActionMkDir mkdir = 6;
231
+		// FileActionRm removes a file
232
+		FileActionRm rm = 7;
233
+	}
234
+}
235
+
236
+message FileActionCopy {
237
+	// src is the source path
238
+	string src = 1;
239
+	// dest path
240
+	string dest = 2;
241
+	// optional owner override
242
+	ChownOpt owner = 3;
243
+	// optional permission bits override
244
+	int32 mode = 4;
245
+	// followSymlink resolves symlinks in src
246
+	bool followSymlink = 5;
247
+	// dirCopyContents only copies contents if src is a directory
248
+	bool dirCopyContents = 6;
249
+	// attemptUnpackDockerCompatibility detects if src is an archive to unpack it instead
250
+	bool attemptUnpackDockerCompatibility = 7;
251
+	// createDestPath creates dest path directories if needed
252
+	bool createDestPath = 8;
253
+	// allowWildcard allows filepath.Match wildcards in src path
254
+	bool allowWildcard = 9;
255
+	// allowEmptyWildcard doesn't fail the whole copy if wildcard doesn't resolve to files
256
+	bool allowEmptyWildcard = 10;
257
+	// optional created time override
258
+	int64 timestamp = 11;
259
+}
260
+
261
+message FileActionMkFile {
262
+	// path for the new file
263
+	string path = 1;
264
+	// permission bits
265
+	int32 mode = 2;
266
+	// data is the new file contents
267
+	bytes data = 3;
268
+	// optional owner for the new file
269
+	ChownOpt owner = 4;
270
+	// optional created time override
271
+	int64 timestamp = 5;
272
+}
273
+
274
+message FileActionMkDir {
275
+	// path for the new directory
276
+	string path = 1;
277
+	// permission bits
278
+	int32 mode = 2;
279
+	// makeParents creates parent directories as well if needed
280
+	bool makeParents = 3;
281
+	// optional owner for the new directory
282
+	ChownOpt owner = 4;
283
+	// optional created time override
284
+	int64 timestamp = 5;
285
+}
286
+
287
+message FileActionRm {
288
+	// path to remove
289
+	string path = 1;
290
+	// allowNotFound doesn't fail the rm if file is not found
291
+	bool allowNotFound = 2;
292
+	// allowWildcard allows filepath.Match wildcards in path
293
+	bool allowWildcard = 3;
294
+}
295
+
296
+message ChownOpt {
297
+	UserOpt user = 1;
298
+	UserOpt group = 2;
299
+}
300
+
301
+message UserOpt {
302
+	oneof user {
303
+		NamedUserOpt byName = 1;
304
+		uint32 byID = 2;
305
+	}
306
+}
307
+
308
+message NamedUserOpt {
309
+	string name = 1;
310
+	int64 input = 2 [(gogoproto.customtype) = "InputIndex", (gogoproto.nullable) = false];
214 311
 }
215 312
\ No newline at end of file
... ...
@@ -24,7 +24,7 @@ type contextKeyT string
24 24
 
25 25
 var contextKey = contextKeyT("buildkit/util/flightcontrol.progress")
26 26
 
27
-// Group is a flightcontrol syncronization group
27
+// Group is a flightcontrol synchronization group
28 28
 type Group struct {
29 29
 	mu sync.Mutex       // protects m
30 30
 	m  map[string]*call // lazily initialized
... ...
@@ -63,7 +63,7 @@ func Config(ctx context.Context, str string, resolver remotes.Resolver, cache Co
63 63
 	}
64 64
 
65 65
 	handlers := []images.Handler{
66
-		remotes.FetchHandler(cache, fetcher),
66
+		fetchWithoutRoot(remotes.FetchHandler(cache, fetcher)),
67 67
 		childrenConfigHandler(cache, platform),
68 68
 	}
69 69
 	if err := images.Dispatch(ctx, images.Handlers(handlers...), nil, desc); err != nil {
... ...
@@ -82,6 +82,16 @@ func Config(ctx context.Context, str string, resolver remotes.Resolver, cache Co
82 82
 	return desc.Digest, dt, nil
83 83
 }
84 84
 
85
+func fetchWithoutRoot(fetch images.HandlerFunc) images.HandlerFunc {
86
+	return func(ctx context.Context, desc specs.Descriptor) ([]specs.Descriptor, error) {
87
+		if desc.Annotations == nil {
88
+			desc.Annotations = map[string]string{}
89
+		}
90
+		desc.Annotations["buildkit/noroot"] = "true"
91
+		return fetch(ctx, desc)
92
+	}
93
+}
94
+
85 95
 func childrenConfigHandler(provider content.Provider, platform platforms.MatchComparer) images.HandlerFunc {
86 96
 	return func(ctx context.Context, desc specs.Descriptor) ([]specs.Descriptor, error) {
87 97
 		var descs []specs.Descriptor
88 98
new file mode 100644
... ...
@@ -0,0 +1,423 @@
0
+package fs
1
+
2
+import (
3
+	"context"
4
+	"io/ioutil"
5
+	"os"
6
+	"path/filepath"
7
+	"runtime"
8
+	"strings"
9
+	"sync"
10
+	"time"
11
+
12
+	"github.com/containerd/continuity/fs"
13
+	"github.com/pkg/errors"
14
+)
15
+
16
+var bufferPool = &sync.Pool{
17
+	New: func() interface{} {
18
+		buffer := make([]byte, 32*1024)
19
+		return &buffer
20
+	},
21
+}
22
+
23
+func rootPath(root, p string, followLinks bool) (string, error) {
24
+	p = filepath.Join("/", p)
25
+	if p == "/" {
26
+		return root, nil
27
+	}
28
+	if followLinks {
29
+		return fs.RootPath(root, p)
30
+	}
31
+	d, f := filepath.Split(p)
32
+	ppath, err := fs.RootPath(root, d)
33
+	if err != nil {
34
+		return "", err
35
+	}
36
+	return filepath.Join(ppath, f), nil
37
+}
38
+
39
+func ResolveWildcards(root, src string, followLinks bool) ([]string, error) {
40
+	d1, d2 := splitWildcards(src)
41
+	if d2 != "" {
42
+		p, err := rootPath(root, d1, followLinks)
43
+		if err != nil {
44
+			return nil, err
45
+		}
46
+		matches, err := resolveWildcards(p, d2)
47
+		if err != nil {
48
+			return nil, err
49
+		}
50
+		for i, m := range matches {
51
+			p, err := rel(root, m)
52
+			if err != nil {
53
+				return nil, err
54
+			}
55
+			matches[i] = p
56
+		}
57
+		return matches, nil
58
+	}
59
+	return []string{d1}, nil
60
+}
61
+
62
+// Copy copies files using `cp -a` semantics.
63
+// Copy is likely unsafe to be used in non-containerized environments.
64
+func Copy(ctx context.Context, srcRoot, src, dstRoot, dst string, opts ...Opt) error {
65
+	var ci CopyInfo
66
+	for _, o := range opts {
67
+		o(&ci)
68
+	}
69
+	ensureDstPath := dst
70
+	if d, f := filepath.Split(dst); f != "" && f != "." {
71
+		ensureDstPath = d
72
+	}
73
+	if ensureDstPath != "" {
74
+		ensureDstPath, err := fs.RootPath(dstRoot, ensureDstPath)
75
+		if err != nil {
76
+			return err
77
+		}
78
+		if err := MkdirAll(ensureDstPath, 0755, ci.Chown, ci.Utime); err != nil {
79
+			return err
80
+		}
81
+	}
82
+
83
+	dst, err := fs.RootPath(dstRoot, filepath.Clean(dst))
84
+	if err != nil {
85
+		return err
86
+	}
87
+
88
+	c := newCopier(ci.Chown, ci.Utime, ci.Mode, ci.XAttrErrorHandler)
89
+	srcs := []string{src}
90
+
91
+	if ci.AllowWildcards {
92
+		matches, err := ResolveWildcards(srcRoot, src, ci.FollowLinks)
93
+		if err != nil {
94
+			return err
95
+		}
96
+		if len(matches) == 0 {
97
+			return errors.Errorf("no matches found: %s", src)
98
+		}
99
+		srcs = matches
100
+	}
101
+
102
+	for _, src := range srcs {
103
+		srcFollowed, err := rootPath(srcRoot, src, ci.FollowLinks)
104
+		if err != nil {
105
+			return err
106
+		}
107
+		dst, err := c.prepareTargetDir(srcFollowed, src, dst, ci.CopyDirContents)
108
+		if err != nil {
109
+			return err
110
+		}
111
+		if err := c.copy(ctx, srcFollowed, dst, false); err != nil {
112
+			return err
113
+		}
114
+	}
115
+
116
+	return nil
117
+}
118
+
119
+func (c *copier) prepareTargetDir(srcFollowed, src, destPath string, copyDirContents bool) (string, error) {
120
+	fiSrc, err := os.Lstat(srcFollowed)
121
+	if err != nil {
122
+		return "", err
123
+	}
124
+
125
+	fiDest, err := os.Stat(destPath)
126
+	if err != nil {
127
+		if !os.IsNotExist(err) {
128
+			return "", errors.Wrap(err, "failed to lstat destination path")
129
+		}
130
+	}
131
+
132
+	if (!copyDirContents && fiSrc.IsDir() && fiDest != nil) || (!fiSrc.IsDir() && fiDest != nil && fiDest.IsDir()) {
133
+		destPath = filepath.Join(destPath, filepath.Base(src))
134
+	}
135
+
136
+	target := filepath.Dir(destPath)
137
+
138
+	if copyDirContents && fiSrc.IsDir() && fiDest == nil {
139
+		target = destPath
140
+	}
141
+	if err := MkdirAll(target, 0755, c.chown, c.utime); err != nil {
142
+		return "", err
143
+	}
144
+
145
+	return destPath, nil
146
+}
147
+
148
+type ChownOpt struct {
149
+	Uid, Gid int
150
+}
151
+
152
+type XAttrErrorHandler func(dst, src, xattrKey string, err error) error
153
+
154
+type CopyInfo struct {
155
+	Chown             *ChownOpt
156
+	Utime             *time.Time
157
+	AllowWildcards    bool
158
+	Mode              *int
159
+	XAttrErrorHandler XAttrErrorHandler
160
+	CopyDirContents   bool
161
+	FollowLinks       bool
162
+}
163
+
164
+type Opt func(*CopyInfo)
165
+
166
+func WithCopyInfo(ci CopyInfo) func(*CopyInfo) {
167
+	return func(c *CopyInfo) {
168
+		*c = ci
169
+	}
170
+}
171
+
172
+func WithChown(uid, gid int) Opt {
173
+	return func(ci *CopyInfo) {
174
+		ci.Chown = &ChownOpt{Uid: uid, Gid: gid}
175
+	}
176
+}
177
+
178
+func AllowWildcards(ci *CopyInfo) {
179
+	ci.AllowWildcards = true
180
+}
181
+
182
+func WithXAttrErrorHandler(h XAttrErrorHandler) Opt {
183
+	return func(ci *CopyInfo) {
184
+		ci.XAttrErrorHandler = h
185
+	}
186
+}
187
+
188
+func AllowXAttrErrors(ci *CopyInfo) {
189
+	h := func(string, string, string, error) error {
190
+		return nil
191
+	}
192
+	WithXAttrErrorHandler(h)(ci)
193
+}
194
+
195
+type copier struct {
196
+	chown             *ChownOpt
197
+	utime             *time.Time
198
+	mode              *int
199
+	inodes            map[uint64]string
200
+	xattrErrorHandler XAttrErrorHandler
201
+}
202
+
203
+func newCopier(chown *ChownOpt, tm *time.Time, mode *int, xeh XAttrErrorHandler) *copier {
204
+	if xeh == nil {
205
+		xeh = func(dst, src, key string, err error) error {
206
+			return err
207
+		}
208
+	}
209
+	return &copier{inodes: map[uint64]string{}, chown: chown, utime: tm, xattrErrorHandler: xeh, mode: mode}
210
+}
211
+
212
+// dest is always clean
213
+func (c *copier) copy(ctx context.Context, src, target string, overwriteTargetMetadata bool) error {
214
+	select {
215
+	case <-ctx.Done():
216
+		return ctx.Err()
217
+	default:
218
+	}
219
+	fi, err := os.Lstat(src)
220
+	if err != nil {
221
+		return errors.Wrapf(err, "failed to stat %s", src)
222
+	}
223
+
224
+	if !fi.IsDir() {
225
+		if err := ensureEmptyFileTarget(target); err != nil {
226
+			return err
227
+		}
228
+	}
229
+
230
+	copyFileInfo := true
231
+
232
+	switch {
233
+	case fi.IsDir():
234
+		if created, err := c.copyDirectory(ctx, src, target, fi, overwriteTargetMetadata); err != nil {
235
+			return err
236
+		} else if !overwriteTargetMetadata {
237
+			copyFileInfo = created
238
+		}
239
+	case (fi.Mode() & os.ModeType) == 0:
240
+		link, err := getLinkSource(target, fi, c.inodes)
241
+		if err != nil {
242
+			return errors.Wrap(err, "failed to get hardlink")
243
+		}
244
+		if link != "" {
245
+			if err := os.Link(link, target); err != nil {
246
+				return errors.Wrap(err, "failed to create hard link")
247
+			}
248
+		} else if err := copyFile(src, target); err != nil {
249
+			return errors.Wrap(err, "failed to copy files")
250
+		}
251
+	case (fi.Mode() & os.ModeSymlink) == os.ModeSymlink:
252
+		link, err := os.Readlink(src)
253
+		if err != nil {
254
+			return errors.Wrapf(err, "failed to read link: %s", src)
255
+		}
256
+		if err := os.Symlink(link, target); err != nil {
257
+			return errors.Wrapf(err, "failed to create symlink: %s", target)
258
+		}
259
+	case (fi.Mode() & os.ModeDevice) == os.ModeDevice:
260
+		if err := copyDevice(target, fi); err != nil {
261
+			return errors.Wrapf(err, "failed to create device")
262
+		}
263
+	default:
264
+		// TODO: Support pipes and sockets
265
+		return errors.Wrapf(err, "unsupported mode %s", fi.Mode())
266
+	}
267
+
268
+	if copyFileInfo {
269
+		if err := c.copyFileInfo(fi, target); err != nil {
270
+			return errors.Wrap(err, "failed to copy file info")
271
+		}
272
+
273
+		if err := copyXAttrs(target, src, c.xattrErrorHandler); err != nil {
274
+			return errors.Wrap(err, "failed to copy xattrs")
275
+		}
276
+	}
277
+	return nil
278
+}
279
+
280
+func (c *copier) copyDirectory(ctx context.Context, src, dst string, stat os.FileInfo, overwriteTargetMetadata bool) (bool, error) {
281
+	if !stat.IsDir() {
282
+		return false, errors.Errorf("source is not directory")
283
+	}
284
+
285
+	created := false
286
+
287
+	if st, err := os.Lstat(dst); err != nil {
288
+		if !os.IsNotExist(err) {
289
+			return false, err
290
+		}
291
+		created = true
292
+		if err := os.Mkdir(dst, stat.Mode()); err != nil {
293
+			return created, errors.Wrapf(err, "failed to mkdir %s", dst)
294
+		}
295
+	} else if !st.IsDir() {
296
+		return false, errors.Errorf("cannot copy to non-directory: %s", dst)
297
+	} else if overwriteTargetMetadata {
298
+		if err := os.Chmod(dst, stat.Mode()); err != nil {
299
+			return false, errors.Wrapf(err, "failed to chmod on %s", dst)
300
+		}
301
+	}
302
+
303
+	fis, err := ioutil.ReadDir(src)
304
+	if err != nil {
305
+		return false, errors.Wrapf(err, "failed to read %s", src)
306
+	}
307
+
308
+	for _, fi := range fis {
309
+		if err := c.copy(ctx, filepath.Join(src, fi.Name()), filepath.Join(dst, fi.Name()), true); err != nil {
310
+			return false, err
311
+		}
312
+	}
313
+
314
+	return created, nil
315
+}
316
+
317
+func ensureEmptyFileTarget(dst string) error {
318
+	fi, err := os.Lstat(dst)
319
+	if err != nil {
320
+		if os.IsNotExist(err) {
321
+			return nil
322
+		}
323
+		return errors.Wrap(err, "failed to lstat file target")
324
+	}
325
+	if fi.IsDir() {
326
+		return errors.Errorf("cannot replace to directory %s with file", dst)
327
+	}
328
+	return os.Remove(dst)
329
+}
330
+
331
+func copyFile(source, target string) error {
332
+	src, err := os.Open(source)
333
+	if err != nil {
334
+		return errors.Wrapf(err, "failed to open source %s", source)
335
+	}
336
+	defer src.Close()
337
+	tgt, err := os.Create(target)
338
+	if err != nil {
339
+		return errors.Wrapf(err, "failed to open target %s", target)
340
+	}
341
+	defer tgt.Close()
342
+
343
+	return copyFileContent(tgt, src)
344
+}
345
+
346
+func containsWildcards(name string) bool {
347
+	isWindows := runtime.GOOS == "windows"
348
+	for i := 0; i < len(name); i++ {
349
+		ch := name[i]
350
+		if ch == '\\' && !isWindows {
351
+			i++
352
+		} else if ch == '*' || ch == '?' || ch == '[' {
353
+			return true
354
+		}
355
+	}
356
+	return false
357
+}
358
+
359
+func splitWildcards(p string) (d1, d2 string) {
360
+	parts := strings.Split(filepath.Join(p), string(filepath.Separator))
361
+	var p1, p2 []string
362
+	var found bool
363
+	for _, p := range parts {
364
+		if !found && containsWildcards(p) {
365
+			found = true
366
+		}
367
+		if p == "" {
368
+			p = "/"
369
+		}
370
+		if !found {
371
+			p1 = append(p1, p)
372
+		} else {
373
+			p2 = append(p2, p)
374
+		}
375
+	}
376
+	return filepath.Join(p1...), filepath.Join(p2...)
377
+}
378
+
379
+func resolveWildcards(basePath, comp string) ([]string, error) {
380
+	var out []string
381
+	err := filepath.Walk(basePath, func(path string, info os.FileInfo, err error) error {
382
+		if err != nil {
383
+			return err
384
+		}
385
+		rel, err := rel(basePath, path)
386
+		if err != nil {
387
+			return err
388
+		}
389
+		if rel == "." {
390
+			return nil
391
+		}
392
+		if match, _ := filepath.Match(comp, rel); !match {
393
+			return nil
394
+		}
395
+		out = append(out, path)
396
+		if info.IsDir() {
397
+			return filepath.SkipDir
398
+		}
399
+		return nil
400
+	})
401
+	if err != nil {
402
+		return nil, err
403
+	}
404
+	return out, nil
405
+}
406
+
407
+// rel makes a path relative to base path. Same as `filepath.Rel` but can also
408
+// handle UUID paths in windows.
409
+func rel(basepath, targpath string) (string, error) {
410
+	// filepath.Rel can't handle UUID paths in windows
411
+	if runtime.GOOS == "windows" {
412
+		pfx := basepath + `\`
413
+		if strings.HasPrefix(targpath, pfx) {
414
+			p := strings.TrimPrefix(targpath, pfx)
415
+			if p == "" {
416
+				p = "."
417
+			}
418
+			return p, nil
419
+		}
420
+	}
421
+	return filepath.Rel(basepath, targpath)
422
+}
0 423
new file mode 100644
... ...
@@ -0,0 +1,97 @@
0
+package fs
1
+
2
+import (
3
+	"io"
4
+	"math"
5
+	"os"
6
+	"syscall"
7
+
8
+	"github.com/containerd/containerd/sys"
9
+	"github.com/pkg/errors"
10
+	"golang.org/x/sys/unix"
11
+)
12
+
13
+func getUidGid(fi os.FileInfo) (uid, gid int) {
14
+	st := fi.Sys().(*syscall.Stat_t)
15
+	return int(st.Uid), int(st.Gid)
16
+}
17
+
18
+func (c *copier) copyFileInfo(fi os.FileInfo, name string) error {
19
+	st := fi.Sys().(*syscall.Stat_t)
20
+
21
+	chown := c.chown
22
+	if chown == nil {
23
+		uid, gid := getUidGid(fi)
24
+		chown = &ChownOpt{Uid: uid, Gid: gid}
25
+	}
26
+	if err := Chown(name, chown); err != nil {
27
+		return errors.Wrapf(err, "failed to chown %s", name)
28
+	}
29
+
30
+	m := fi.Mode()
31
+	if c.mode != nil {
32
+		m = (m & ^os.FileMode(0777)) | os.FileMode(*c.mode&0777)
33
+	}
34
+	if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink {
35
+		if err := os.Chmod(name, m); err != nil {
36
+			return errors.Wrapf(err, "failed to chmod %s", name)
37
+		}
38
+	}
39
+
40
+	if c.utime != nil {
41
+		if err := Utimes(name, c.utime); err != nil {
42
+			return err
43
+		}
44
+	} else {
45
+		timespec := []unix.Timespec{unix.Timespec(sys.StatAtime(st)), unix.Timespec(sys.StatMtime(st))}
46
+		if err := unix.UtimesNanoAt(unix.AT_FDCWD, name, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil {
47
+			return errors.Wrapf(err, "failed to utime %s", name)
48
+		}
49
+	}
50
+
51
+	return nil
52
+}
53
+
54
+func copyFileContent(dst, src *os.File) error {
55
+	st, err := src.Stat()
56
+	if err != nil {
57
+		return errors.Wrap(err, "unable to stat source")
58
+	}
59
+
60
+	var written int64
61
+	size := st.Size()
62
+	first := true
63
+
64
+	for written < size {
65
+		var desired int
66
+		if size-written > math.MaxInt32 {
67
+			desired = int(math.MaxInt32)
68
+		} else {
69
+			desired = int(size - written)
70
+		}
71
+
72
+		n, err := unix.CopyFileRange(int(src.Fd()), nil, int(dst.Fd()), nil, desired, 0)
73
+		if err != nil {
74
+			if (err != unix.ENOSYS && err != unix.EXDEV && err != unix.EPERM) || !first {
75
+				return errors.Wrap(err, "copy file range failed")
76
+			}
77
+
78
+			buf := bufferPool.Get().(*[]byte)
79
+			_, err = io.CopyBuffer(dst, src, *buf)
80
+			bufferPool.Put(buf)
81
+			return errors.Wrap(err, "userspace copy failed")
82
+		}
83
+
84
+		first = false
85
+		written += int64(n)
86
+	}
87
+	return nil
88
+}
89
+
90
+func copyDevice(dst string, fi os.FileInfo) error {
91
+	st, ok := fi.Sys().(*syscall.Stat_t)
92
+	if !ok {
93
+		return errors.New("unsupported stat type")
94
+	}
95
+	return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev))
96
+}
0 97
new file mode 100644
... ...
@@ -0,0 +1,28 @@
0
+// +build !windows
1
+
2
+package fs
3
+
4
+import (
5
+	"github.com/pkg/errors"
6
+
7
+	"github.com/containerd/continuity/sysx"
8
+)
9
+
10
+// copyXAttrs requires xeh to be non-nil
11
+func copyXAttrs(dst, src string, xeh XAttrErrorHandler) error {
12
+	xattrKeys, err := sysx.LListxattr(src)
13
+	if err != nil {
14
+		return xeh(dst, src, "", errors.Wrapf(err, "failed to list xattrs on %s", src))
15
+	}
16
+	for _, xattr := range xattrKeys {
17
+		data, err := sysx.LGetxattr(src, xattr)
18
+		if err != nil {
19
+			return xeh(dst, src, xattr, errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src))
20
+		}
21
+		if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil {
22
+			return xeh(dst, src, xattr, errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst))
23
+		}
24
+	}
25
+
26
+	return nil
27
+}
0 28
new file mode 100644
... ...
@@ -0,0 +1,68 @@
0
+// +build solaris darwin freebsd
1
+
2
+package fs
3
+
4
+import (
5
+	"io"
6
+	"os"
7
+	"syscall"
8
+
9
+	"github.com/containerd/containerd/sys"
10
+	"github.com/pkg/errors"
11
+	"golang.org/x/sys/unix"
12
+)
13
+
14
+func getUidGid(fi os.FileInfo) (uid, gid int) {
15
+	st := fi.Sys().(*syscall.Stat_t)
16
+	return int(st.Uid), int(st.Gid)
17
+}
18
+
19
+func (c *copier) copyFileInfo(fi os.FileInfo, name string) error {
20
+	st := fi.Sys().(*syscall.Stat_t)
21
+	chown := c.chown
22
+	if chown == nil {
23
+		uid, gid := getUidGid(fi)
24
+		chown = &ChownOpt{Uid: uid, Gid: gid}
25
+	}
26
+	if err := Chown(name, chown); err != nil {
27
+		return errors.Wrapf(err, "failed to chown %s", name)
28
+	}
29
+
30
+	m := fi.Mode()
31
+	if c.mode != nil {
32
+		m = (m & ^os.FileMode(0777)) | os.FileMode(*c.mode&0777)
33
+	}
34
+	if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink {
35
+		if err := os.Chmod(name, m); err != nil {
36
+			return errors.Wrapf(err, "failed to chmod %s", name)
37
+		}
38
+	}
39
+
40
+	if c.utime != nil {
41
+		if err := Utimes(name, c.utime); err != nil {
42
+			return err
43
+		}
44
+	} else {
45
+		timespec := []unix.Timespec{unix.Timespec(sys.StatAtime(st)), unix.Timespec(sys.StatMtime(st))}
46
+		if err := unix.UtimesNanoAt(unix.AT_FDCWD, name, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil {
47
+			return errors.Wrapf(err, "failed to utime %s", name)
48
+		}
49
+	}
50
+	return nil
51
+}
52
+
53
+func copyFileContent(dst, src *os.File) error {
54
+	buf := bufferPool.Get().(*[]byte)
55
+	_, err := io.CopyBuffer(dst, src, *buf)
56
+	bufferPool.Put(buf)
57
+
58
+	return err
59
+}
60
+
61
+func copyDevice(dst string, fi os.FileInfo) error {
62
+	st, ok := fi.Sys().(*syscall.Stat_t)
63
+	if !ok {
64
+		return errors.New("unsupported stat type")
65
+	}
66
+	return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev))
67
+}
0 68
new file mode 100644
... ...
@@ -0,0 +1,33 @@
0
+package fs
1
+
2
+import (
3
+	"io"
4
+	"os"
5
+
6
+	"github.com/pkg/errors"
7
+)
8
+
9
+func (c *copier) copyFileInfo(fi os.FileInfo, name string) error {
10
+	if err := os.Chmod(name, fi.Mode()); err != nil {
11
+		return errors.Wrapf(err, "failed to chmod %s", name)
12
+	}
13
+
14
+	// TODO: copy windows specific metadata
15
+
16
+	return nil
17
+}
18
+
19
+func copyFileContent(dst, src *os.File) error {
20
+	buf := bufferPool.Get().(*[]byte)
21
+	_, err := io.CopyBuffer(dst, src, *buf)
22
+	bufferPool.Put(buf)
23
+	return err
24
+}
25
+
26
+func copyXAttrs(dst, src string, xeh XAttrErrorHandler) error {
27
+	return nil
28
+}
29
+
30
+func copyDevice(dst string, fi os.FileInfo) error {
31
+	return errors.New("device copy not supported")
32
+}
0 33
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+package fs
1
+
2
+import "os"
3
+
4
+// GetLinkInfo returns an identifier representing the node a hardlink is pointing
5
+// to. If the file is not hard linked then 0 will be returned.
6
+func GetLinkInfo(fi os.FileInfo) (uint64, bool) {
7
+	return getLinkInfo(fi)
8
+}
9
+
10
+// getLinkSource returns a path for the given name and
11
+// file info to its link source in the provided inode
12
+// map. If the given file name is not in the map and
13
+// has other links, it is added to the inode map
14
+// to be a source for other link locations.
15
+func getLinkSource(name string, fi os.FileInfo, inodes map[uint64]string) (string, error) {
16
+	inode, isHardlink := getLinkInfo(fi)
17
+	if !isHardlink {
18
+		return "", nil
19
+	}
20
+
21
+	path, ok := inodes[inode]
22
+	if !ok {
23
+		inodes[inode] = name
24
+	}
25
+	return path, nil
26
+}
0 27
new file mode 100644
... ...
@@ -0,0 +1,17 @@
0
+// +build !windows
1
+
2
+package fs
3
+
4
+import (
5
+	"os"
6
+	"syscall"
7
+)
8
+
9
+func getLinkInfo(fi os.FileInfo) (uint64, bool) {
10
+	s, ok := fi.Sys().(*syscall.Stat_t)
11
+	if !ok {
12
+		return 0, false
13
+	}
14
+
15
+	return uint64(s.Ino), !fi.IsDir() && s.Nlink > 1
16
+}
0 17
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+package fs
1
+
2
+import "os"
3
+
4
+func getLinkInfo(fi os.FileInfo) (uint64, bool) {
5
+	return 0, false
6
+}
0 7
new file mode 100644
... ...
@@ -0,0 +1,74 @@
0
+package fs
1
+
2
+import (
3
+	"os"
4
+	"syscall"
5
+	"time"
6
+)
7
+
8
+func Chown(p string, user *ChownOpt) error {
9
+	if user != nil {
10
+		if err := os.Lchown(p, user.Uid, user.Gid); err != nil {
11
+			return err
12
+		}
13
+	}
14
+	return nil
15
+}
16
+
17
+// MkdirAll is forked os.MkdirAll
18
+func MkdirAll(path string, perm os.FileMode, user *ChownOpt, tm *time.Time) error {
19
+	// Fast path: if we can tell whether path is a directory or file, stop with success or error.
20
+	dir, err := os.Stat(path)
21
+	if err == nil {
22
+		if dir.IsDir() {
23
+			return nil
24
+		}
25
+		return &os.PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR}
26
+	}
27
+
28
+	// Slow path: make sure parent exists and then call Mkdir for path.
29
+	i := len(path)
30
+	for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator.
31
+		i--
32
+	}
33
+
34
+	j := i
35
+	for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element.
36
+		j--
37
+	}
38
+
39
+	if j > 1 {
40
+		// Create parent.
41
+		err = MkdirAll(fixRootDirectory(path[:j-1]), perm, user, tm)
42
+		if err != nil {
43
+			return err
44
+		}
45
+	}
46
+
47
+	dir, err1 := os.Lstat(path)
48
+	if err1 == nil && dir.IsDir() {
49
+		return nil
50
+	}
51
+
52
+	// Parent now exists; invoke Mkdir and use its result.
53
+	err = os.Mkdir(path, perm)
54
+	if err != nil {
55
+		// Handle arguments like "foo/." by
56
+		// double-checking that directory doesn't exist.
57
+		dir, err1 := os.Lstat(path)
58
+		if err1 == nil && dir.IsDir() {
59
+			return nil
60
+		}
61
+		return err
62
+	}
63
+
64
+	if err := Chown(path, user); err != nil {
65
+		return err
66
+	}
67
+
68
+	if err := Utimes(path, tm); err != nil {
69
+		return err
70
+	}
71
+
72
+	return nil
73
+}
0 74
new file mode 100644
... ...
@@ -0,0 +1,32 @@
0
+// +build !windows
1
+
2
+package fs
3
+
4
+import (
5
+	"time"
6
+
7
+	"github.com/pkg/errors"
8
+	"golang.org/x/sys/unix"
9
+)
10
+
11
+func fixRootDirectory(p string) string {
12
+	return p
13
+}
14
+
15
+func Utimes(p string, tm *time.Time) error {
16
+	if tm == nil {
17
+		return nil
18
+	}
19
+
20
+	ts, err := unix.TimeToTimespec(*tm)
21
+	if err != nil {
22
+		return err
23
+	}
24
+
25
+	timespec := []unix.Timespec{ts, ts}
26
+	if err := unix.UtimesNanoAt(unix.AT_FDCWD, p, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil {
27
+		return errors.Wrapf(err, "failed to utime %s", p)
28
+	}
29
+
30
+	return nil
31
+}
0 32
new file mode 100644
... ...
@@ -0,0 +1,21 @@
0
+// +build windows
1
+
2
+package fs
3
+
4
+import (
5
+	"os"
6
+	"time"
7
+)
8
+
9
+func fixRootDirectory(p string) string {
10
+	if len(p) == len(`\\?\c:`) {
11
+		if os.IsPathSeparator(p[0]) && os.IsPathSeparator(p[1]) && p[2] == '?' && os.IsPathSeparator(p[3]) && p[5] == ':' {
12
+			return p + `\`
13
+		}
14
+	}
15
+	return p
16
+}
17
+
18
+func Utimes(p string, tm *time.Time) error {
19
+	return nil
20
+}
... ...
@@ -26,7 +26,7 @@ type DiskWriterOpt struct {
26 26
 	Filter        FilterFunc
27 27
 }
28 28
 
29
-type FilterFunc func(*types.Stat) bool
29
+type FilterFunc func(string, *types.Stat) bool
30 30
 
31 31
 type DiskWriter struct {
32 32
 	opt  DiskWriterOpt
... ...
@@ -84,6 +84,12 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er
84 84
 	destPath := filepath.Join(dw.dest, filepath.FromSlash(p))
85 85
 
86 86
 	if kind == ChangeKindDelete {
87
+		if dw.filter != nil {
88
+			var empty types.Stat
89
+			if ok := dw.filter(p, &empty); !ok {
90
+				return nil
91
+			}
92
+		}
87 93
 		// todo: no need to validate if diff is trusted but is it always?
88 94
 		if err := os.RemoveAll(destPath); err != nil {
89 95
 			return errors.Wrapf(err, "failed to remove: %s", destPath)
... ...
@@ -104,7 +110,7 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er
104 104
 	statCopy := *stat
105 105
 
106 106
 	if dw.filter != nil {
107
-		if ok := dw.filter(&statCopy); !ok {
107
+		if ok := dw.filter(p, &statCopy); !ok {
108 108
 			return nil
109 109
 		}
110 110
 	}
... ...
@@ -46,14 +46,18 @@ func setUnixOpt(fi os.FileInfo, stat *types.Stat, path string, seenFiles map[uin
46 46
 		}
47 47
 
48 48
 		ino := s.Ino
49
+		linked := false
49 50
 		if seenFiles != nil {
50 51
 			if s.Nlink > 1 {
51 52
 				if oldpath, ok := seenFiles[ino]; ok {
52 53
 					stat.Linkname = oldpath
53 54
 					stat.Size_ = 0
55
+					linked = true
54 56
 				}
55 57
 			}
56
-			seenFiles[ino] = path
58
+			if !linked {
59
+				seenFiles[ino] = path
60
+			}
57 61
 		}
58 62
 	}
59 63
 }
... ...
@@ -19,7 +19,7 @@ type WalkOpt struct {
19 19
 	// FollowPaths contains symlinks that are resolved into include patterns
20 20
 	// before performing the fs walk
21 21
 	FollowPaths []string
22
-	Map         func(*types.Stat) bool
22
+	Map         FilterFunc
23 23
 }
24 24
 
25 25
 func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) error {
... ...
@@ -157,7 +157,7 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
157 157
 			return ctx.Err()
158 158
 		default:
159 159
 			if opt != nil && opt.Map != nil {
160
-				if allowed := opt.Map(stat); !allowed {
160
+				if allowed := opt.Map(stat.Path, stat); !allowed {
161 161
 					return nil
162 162
 				}
163 163
 			}