Skip to content

Commit

Permalink
add support for non-octal mode setting
Browse files Browse the repository at this point in the history
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
  • Loading branch information
tonistiigi committed Oct 1, 2024
1 parent aca4ca6 commit 3a8c87e
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 10 deletions.
2 changes: 1 addition & 1 deletion bench/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/tonistiigi/fsutil/bench

go 1.20
go 1.21

require (
github.com/containerd/continuity v0.4.1
Expand Down
2 changes: 2 additions & 0 deletions bench/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,7 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
Expand Down Expand Up @@ -888,6 +889,7 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
Expand Down
26 changes: 20 additions & 6 deletions copy/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/containerd/continuity/fs"
"github.com/moby/patternmatcher"
"github.com/pkg/errors"
mode "github.com/tonistiigi/dchapes-mode"
"github.com/tonistiigi/fsutil"
)

Expand Down Expand Up @@ -83,12 +84,21 @@ func Copy(ctx context.Context, srcRoot, src, dstRoot, dst string, opts ...Opt) e
}
}

var modeSet *mode.Set
if ci.ModeStr != "" {
ms, err := mode.ParseWithUmask(ci.ModeStr, 0)
if err != nil {
return err
}
modeSet = &ms
}

dst, err := fs.RootPath(dstRoot, filepath.Clean(dst))
if err != nil {
return err
}

c, err := newCopier(dstRoot, ci.Chown, ci.Utime, ci.Mode, ci.XAttrErrorHandler, ci.IncludePatterns, ci.ExcludePatterns, ci.AlwaysReplaceExistingDestPaths, ci.ChangeFunc)
c, err := newCopier(dstRoot, ci.Chown, ci.Utime, ci.Mode, modeSet, ci.XAttrErrorHandler, ci.IncludePatterns, ci.ExcludePatterns, ci.AlwaysReplaceExistingDestPaths, ci.ChangeFunc)
if err != nil {
return err
}
Expand Down Expand Up @@ -161,10 +171,12 @@ type Chowner func(*User) (*User, error)
type XAttrErrorHandler func(dst, src, xattrKey string, err error) error

type CopyInfo struct {
Chown Chowner
Utime *time.Time
AllowWildcards bool
Mode *int
Chown Chowner
Utime *time.Time
AllowWildcards bool
Mode *int
// ModeStr is mode in non-octal format. Overrides Mode if non-empty.
ModeStr string
XAttrErrorHandler XAttrErrorHandler
CopyDirContents bool
FollowLinks bool
Expand Down Expand Up @@ -234,6 +246,7 @@ type copier struct {
chown Chowner
utime *time.Time
mode *int
modeSet *mode.Set
inodes map[uint64]string
xattrErrorHandler XAttrErrorHandler
includePatternMatcher *patternmatcher.PatternMatcher
Expand All @@ -250,7 +263,7 @@ type parentDir struct {
copied bool
}

func newCopier(root string, chown Chowner, tm *time.Time, mode *int, xeh XAttrErrorHandler, includePatterns, excludePatterns []string, alwaysReplaceExistingDestPaths bool, changeFunc fsutil.ChangeFunc) (*copier, error) {
func newCopier(root string, chown Chowner, tm *time.Time, mode *int, modeSet *mode.Set, xeh XAttrErrorHandler, includePatterns, excludePatterns []string, alwaysReplaceExistingDestPaths bool, changeFunc fsutil.ChangeFunc) (*copier, error) {
if xeh == nil {
xeh = func(dst, src, key string, err error) error {
return err
Expand Down Expand Up @@ -282,6 +295,7 @@ func newCopier(root string, chown Chowner, tm *time.Time, mode *int, xeh XAttrEr
utime: tm,
xattrErrorHandler: xeh,
mode: mode,
modeSet: modeSet,
includePatternMatcher: includePatternMatcher,
excludePatternMatcher: excludePatternMatcher,
changefn: changeFunc,
Expand Down
4 changes: 3 additions & 1 deletion copy/copy_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ func (c *copier) copyFileInfo(fi os.FileInfo, src, name string) error {
}

m := fi.Mode()
if c.mode != nil {
if c.modeSet != nil {
m = c.modeSet.Apply(m)
} else if c.mode != nil {
m = os.FileMode(*c.mode).Perm()
if *c.mode&syscall.S_ISGID != 0 {
m |= os.ModeSetgid
Expand Down
4 changes: 3 additions & 1 deletion copy/copy_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ func (c *copier) copyFileInfo(fi os.FileInfo, src, name string) error {
}

m := fi.Mode()
if c.mode != nil {
if c.modeSet != nil {
m = c.modeSet.Apply(m)
} else if c.mode != nil {
m = os.FileMode(*c.mode).Perm()
if *c.mode&syscall.S_ISGID != 0 {
m |= os.ModeSetgid
Expand Down
68 changes: 68 additions & 0 deletions copy/copy_unix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,71 @@ func TestCopySetuid(t *testing.T) {
assert.Equal(t, os.FileMode(0), fi.Mode()&os.ModeSetgid)
assert.Equal(t, os.FileMode(0), fi.Mode()&os.ModeSticky)
}

func TestCopyModeTextFormat(t *testing.T) {
t1 := t.TempDir()

err := os.WriteFile(filepath.Join(t1, "file"), []byte("hello"), 0644)
require.NoError(t, err)

err = os.WriteFile(filepath.Join(t1, "executable_file"), []byte("world"), 0755)
require.NoError(t, err)

err = os.Mkdir(filepath.Join(t1, "dir"), 0750)
require.NoError(t, err)

err = os.Mkdir(filepath.Join(t1, "restricted_dir"), 0700)
require.NoError(t, err)

testCases := []struct {
name string
modeStr string
expectedFilePerm os.FileMode
expectedExecPerm os.FileMode
expectedDirPerm os.FileMode
expectedRestrictDirPerm os.FileMode
}{
{"remove write for others", "go-w", 0644, 0755, 0750, 0700},
{"add execute for user", "u+x", 0744, 0755, 0750, 0700},
{"remove all permissions for group", "g-rwx", 0604, 0705, 0700, 0700},
{"add read for others", "o+r", 0644, 0755, 0754, 0704},
{"remove execute for all", "a-x", 0644, 0644, 0640, 0600},
{"remove others and add execute for group", "o-rwx,g+x", 0650, 0750, 0750, 0710},
{"capital X (apply execute only if directory)", "a+X", 0644, 0755, 0751, 0711},
{"remove execute and add write for user", "u-x,u+w", 0644, 0655, 0650, 0600},
{"add execute for user and others", "u+x,o+x", 0745, 0755, 0751, 0701},
{"add write and read for group and others", "g+rw,o+rw", 0666, 0777, 0776, 0766},
{"set read-only for all", "a=r", 0444, 0444, 0444, 0444},
{"set full permissions for user only", "u=rwx,g=,o=", 0700, 0700, 0700, 0700},
{"remove all permissions for others", "o-rwx", 0640, 0750, 0750, 0700},
{"remove read for group, add execute for all", "g-r,a+x", 0715, 0715, 0711, 0711},
{"complex permissions change", "u+rw,g+r,o-x,o+w", 0646, 0756, 0752, 0742},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t2 := t.TempDir()

err := Copy(context.TODO(), t1, ".", t2, ".", WithCopyInfo(CopyInfo{
ModeStr: tc.modeStr,
}))
require.NoError(t, err)

fi, err := os.Lstat(filepath.Join(t2, "file"))
require.NoError(t, err)
assert.Equal(t, tc.expectedFilePerm, fi.Mode().Perm(), "file %04o, got %04o", tc.expectedFilePerm, fi.Mode().Perm())

execFileInfo, err := os.Lstat(filepath.Join(t2, "executable_file"))
require.NoError(t, err)
assert.Equal(t, tc.expectedExecPerm, execFileInfo.Mode().Perm(), "executable file %04o, got %04o", tc.expectedExecPerm, execFileInfo.Mode().Perm())

dirInfo, err := os.Lstat(filepath.Join(t2, "dir"))
require.NoError(t, err)
assert.Equal(t, tc.expectedDirPerm, dirInfo.Mode().Perm(), "dir %04o, got %04o", tc.expectedDirPerm, dirInfo.Mode().Perm())

restrictDirInfo, err := os.Lstat(filepath.Join(t2, "restricted_dir"))
require.NoError(t, err)
assert.Equal(t, tc.expectedRestrictDirPerm, restrictDirInfo.Mode().Perm(), "restricted dir %04o, got %04o", tc.expectedRestrictDirPerm, restrictDirInfo.Mode().Perm())
})
}
}
4 changes: 4 additions & 0 deletions copy/copy_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ func getFileSecurityInfo(name string) (*windows.SID, *windows.ACL, error) {
}

func (c *copier) copyFileInfo(fi os.FileInfo, src, name string) error {
if c.modeSet != nil {
return errors.Errorf("non-octal mode not supported on windows")
}

if err := os.Chmod(name, fi.Mode()); err != nil {
return errors.Wrapf(err, "failed to chmod %s", name)
}
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/tonistiigi/fsutil

go 1.20
go 1.21

require (
github.com/Microsoft/go-winio v0.5.2
Expand All @@ -9,6 +9,7 @@ require (
github.com/opencontainers/go-digest v1.0.0
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.8.4
github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205
golang.org/x/sync v0.1.0
golang.org/x/sys v0.11.0
google.golang.org/protobuf v1.31.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205 h1:eUk79E1w8yMtXeHSzjKorxuC8qJOnyXQnLaJehxpJaI=
github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down

0 comments on commit 3a8c87e

Please sign in to comment.