Skip to content

Commit c7db68a

Browse files
authored
Merge pull request moby#5375 from tonistiigi/non-octal-chmod-copy
client: allow non-octal chmod config for fileop.copy
2 parents fdf5d37 + 25632fb commit c7db68a

File tree

22 files changed

+928
-98
lines changed

22 files changed

+928
-98
lines changed

client/client_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ var allTests = []func(t *testing.T, sb integration.Sandbox){
101101
testFileOpCopyAlwaysReplaceExistingDestPaths,
102102
testFileOpRmWildcard,
103103
testFileOpCopyUIDCache,
104+
testFileOpCopyChmodText,
104105
testCallDiskUsage,
105106
testBuildMultiMount,
106107
testBuildHTTPSource,
@@ -1705,6 +1706,48 @@ func testFileOpCopyRm(t *testing.T, sb integration.Sandbox) {
17051706
require.Equal(t, []byte("file2"), dt)
17061707
}
17071708

1709+
func testFileOpCopyChmodText(t *testing.T, sb integration.Sandbox) {
1710+
requiresLinux(t)
1711+
c, err := New(sb.Context(), sb.Address())
1712+
require.NoError(t, err)
1713+
defer c.Close()
1714+
1715+
tcases := []struct {
1716+
src string
1717+
dest string
1718+
mode string
1719+
}{
1720+
{"file", "f1", "go-w"},
1721+
{"file", "f2", "o-rwx,g+x"},
1722+
{"file", "f3", "u=rwx,g=,o="},
1723+
{"file", "f4", "u+rw,g+r,o-x,o+w"},
1724+
{"dir", "d1", "a+X"},
1725+
{"dir", "d2", "g+rw,o+rw"},
1726+
}
1727+
1728+
st := llb.Image("alpine").
1729+
Run(llb.Shlex(`sh -c "mkdir /input && touch /input/file && mkdir /input/dir && chmod 0400 /input/dir && mkdir /expected"`))
1730+
1731+
for _, tc := range tcases {
1732+
st = st.Run(llb.Shlex(`sh -c "cp -a /input/` + tc.src + ` /expected/` + tc.dest + ` && chmod ` + tc.mode + ` /expected/` + tc.dest + `"`))
1733+
}
1734+
cp := llb.Scratch()
1735+
1736+
for _, tc := range tcases {
1737+
cp = cp.File(llb.Copy(st.Root(), "/input/"+tc.src, "/"+tc.dest, llb.ChmodOpt{ModeStr: tc.mode}))
1738+
}
1739+
1740+
for _, tc := range tcases {
1741+
st = st.Run(llb.Shlex(`sh -c '[ "$(stat -c '%A' /expected/`+tc.dest+`)" == "$(stat -c '%A' /actual/`+tc.dest+`)" ]'`), llb.AddMount("/actual", cp, llb.Readonly))
1742+
}
1743+
1744+
def, err := st.Marshal(sb.Context())
1745+
require.NoError(t, err)
1746+
1747+
_, err = c.Solve(sb.Context(), def, SolveOpt{}, nil)
1748+
require.NoError(t, err)
1749+
}
1750+
17081751
// moby/buildkit#3291
17091752
func testFileOpCopyUIDCache(t *testing.T, sb integration.Sandbox) {
17101753
requiresLinux(t)

client/llb/fileop.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,15 @@ func WithUIDGID(uid, gid int) ChownOption {
264264
}
265265
}
266266

267+
type ChmodOpt struct {
268+
Mode os.FileMode
269+
ModeStr string
270+
}
271+
272+
func (co ChmodOpt) SetCopyOption(mi *CopyInfo) {
273+
mi.Mode = &co
274+
}
275+
267276
type ChownOpt struct {
268277
User *UserOpt
269278
Group *UserOpt
@@ -492,7 +501,7 @@ type CopyOption interface {
492501
}
493502

494503
type CopyInfo struct {
495-
Mode *os.FileMode
504+
Mode *ChmodOpt
496505
FollowSymlinks bool
497506
CopyDirContentsOnly bool
498507
IncludePatterns []string
@@ -541,7 +550,11 @@ func (a *fileActionCopy) toProtoAction(ctx context.Context, parent string, base
541550
AlwaysReplaceExistingDestPaths: a.info.AlwaysReplaceExistingDestPaths,
542551
}
543552
if a.info.Mode != nil {
544-
c.Mode = int32(*a.info.Mode)
553+
if a.info.Mode.ModeStr != "" {
554+
c.ModeStr = a.info.Mode.ModeStr
555+
} else {
556+
c.Mode = int32(a.info.Mode.Mode)
557+
}
545558
} else {
546559
c.Mode = -1
547560
}
@@ -574,6 +587,9 @@ func (a *fileActionCopy) addCaps(f *FileOp) {
574587
if a.info.AlwaysReplaceExistingDestPaths {
575588
addCap(&f.constraints, pb.CapFileCopyAlwaysReplaceExistingDestPaths)
576589
}
590+
if a.info.Mode.ModeStr != "" {
591+
addCap(&f.constraints, pb.CapFileCopyModeStringFormat)
592+
}
577593
}
578594

579595
type CreatedTime time.Time

frontend/dockerfile/dockerfile2llb/convert.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,14 +1361,14 @@ func dispatchCopy(d *dispatchState, cfg copyConfig) error {
13611361
copyOpt = append(copyOpt, llb.WithExcludePatterns(cfg.excludePatterns))
13621362
}
13631363

1364-
var mode *os.FileMode
1364+
var mode *llb.ChmodOpt
13651365
if cfg.chmod != "" {
13661366
p, err := strconv.ParseUint(cfg.chmod, 8, 32)
13671367
if err != nil || p > 0o7777 {
13681368
return errors.Errorf("invalid chmod parameter: '%v'. it should be octal string and between 0 and 07777", cfg.chmod)
13691369
}
13701370
perm := os.FileMode(p)
1371-
mode = &perm
1371+
mode = &llb.ChmodOpt{Mode: perm}
13721372
}
13731373

13741374
if cfg.checksum != "" {

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ require (
6868
github.com/sirupsen/logrus v1.9.3
6969
github.com/spdx/tools-golang v0.5.3
7070
github.com/stretchr/testify v1.9.0
71-
github.com/tonistiigi/fsutil v0.0.0-20240926161958-8754824c3c4f
71+
github.com/tonistiigi/fsutil v0.0.0-20241001141129-e98dfb603c6f
7272
github.com/tonistiigi/go-actions-cache v0.0.0-20240327122527-58651d5e11d6
7373
github.com/tonistiigi/go-archvariant v1.0.0
7474
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4
@@ -166,6 +166,7 @@ require (
166166
github.com/russross/blackfriday/v2 v2.1.0 // indirect
167167
github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect
168168
github.com/shibumi/go-pathspec v1.3.0 // indirect
169+
github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205 // indirect
169170
github.com/vbatts/tar-split v0.11.5 // indirect
170171
github.com/vishvananda/netns v0.0.4 // indirect
171172
go.opencensus.io v0.24.0 // indirect

go.sum

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -379,8 +379,10 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
379379
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
380380
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
381381
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
382-
github.com/tonistiigi/fsutil v0.0.0-20240926161958-8754824c3c4f h1:scejvzjNA30X9ufWPUH/a2MhWg1sQPxeC6N6wm7nWEE=
383-
github.com/tonistiigi/fsutil v0.0.0-20240926161958-8754824c3c4f/go.mod h1:xnG7rCC28GVN8efEm5ijNp56TnNtrYCv75EtTH42yz4=
382+
github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205 h1:eUk79E1w8yMtXeHSzjKorxuC8qJOnyXQnLaJehxpJaI=
383+
github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY=
384+
github.com/tonistiigi/fsutil v0.0.0-20241001141129-e98dfb603c6f h1:LuAOoRH31KqSNuYFl1grkcl3Sr3+Rv4uQ+e65P0SFLA=
385+
github.com/tonistiigi/fsutil v0.0.0-20241001141129-e98dfb603c6f/go.mod h1:BwWj5HZxE1/r8WnLfSLIMDNyXE9y/XKwQwRqmgx4nKY=
384386
github.com/tonistiigi/go-actions-cache v0.0.0-20240327122527-58651d5e11d6 h1:XFG/Wmm5dFYoqUiVChLumRjRzJm0P9k/qDMhxLqdupU=
385387
github.com/tonistiigi/go-actions-cache v0.0.0-20240327122527-58651d5e11d6/go.mod h1:anhKd3mnC1shAbQj1Q4IJ+w6xqezxnyDYlx/yKa7IXM=
386388
github.com/tonistiigi/go-archvariant v1.0.0 h1:5LC1eDWiBNflnTF1prCiX09yfNHIxDC/aukdhCdTyb0=

solver/llbsolver/file/backend.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,9 @@ func docopy(ctx context.Context, src, dest string, action *pb.FileActionCopy, u
190190
ci.ExcludePatterns = action.ExcludePatterns
191191
ci.Chown = ch
192192
ci.Utime = timestampToTime(action.Timestamp)
193-
if m := int(action.Mode); m != -1 {
193+
if action.ModeStr != "" {
194+
ci.ModeStr = action.ModeStr
195+
} else if m := int(action.Mode); m != -1 {
194196
ci.Mode = &m
195197
}
196198
ci.CopyDirContents = action.DirCopyContents

solver/pb/caps.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ const (
6767
CapFileCopyIncludeExcludePatterns apicaps.CapID = "file.copy.includeexcludepatterns"
6868
CapFileRmNoFollowSymlink apicaps.CapID = "file.rm.nofollowsymlink"
6969
CapFileCopyAlwaysReplaceExistingDestPaths apicaps.CapID = "file.copy.alwaysreplaceexistingdestpaths"
70+
CapFileCopyModeStringFormat apicaps.CapID = "file.copy.modestring"
7071

7172
CapConstraints apicaps.CapID = "constraints"
7273
CapPlatform apicaps.CapID = "platform"

0 commit comments

Comments
 (0)