@@ -23,6 +23,7 @@ import (
2323 "unicode"
2424
2525 "github.com/sirupsen/logrus"
26+ "github.com/tonistiigi/dchapes-mode"
2627 "go.podman.io/image/v5/pkg/compression"
2728 "go.podman.io/image/v5/types"
2829 "go.podman.io/storage/pkg/archive"
@@ -386,6 +387,7 @@ type GetOptions struct {
386387 UIDMap , GIDMap []idtools.IDMap // map from hostIDs to containerIDs in the output archive
387388 Excludes []string // contents to pretend don't exist, using the OS-specific path separator
388389 ExpandArchives bool // extract the contents of named items that are archives
390+ Chmod string // set permissions in octal or symbolic notation. overrides ChmodDirs and ChmodFiles if set. no effect on archives being extracted
389391 ChownDirs * idtools.IDPair // set ownership on directories. no effect on archives being extracted
390392 ChmodDirs * os.FileMode // set permissions on directories. no effect on archives being extracted
391393 ChownFiles * idtools.IDPair // set ownership of files. no effect on archives being extracted
@@ -444,7 +446,8 @@ func Get(root string, directory string, options GetOptions, globs []string, bulk
444446type PutOptions struct {
445447 UIDMap , GIDMap []idtools.IDMap // map from containerIDs to hostIDs when writing contents to disk
446448 DefaultDirOwner * idtools.IDPair // set ownership of implicitly-created directories, default is ChownDirs, or 0:0 if ChownDirs not set
447- DefaultDirMode * os.FileMode // set permissions on implicitly-created directories, default is ChmodDirs, or 0755 if ChmodDirs not set
449+ DefaultDirMode * os.FileMode // set permissions on implicitly-created directories, default is Chmod or ChmodDirs, or 0755 if neither is set
450+ Chmod string // set permissions in octal or symbolic notation. overrides ChmodDirs and ChmodFiles if set
448451 ChownDirs * idtools.IDPair // set ownership of newly-created directories
449452 ChmodDirs * os.FileMode // set permissions on newly-created directories
450453 ChownFiles * idtools.IDPair // set ownership of newly-created files
@@ -1408,6 +1411,14 @@ func copierHandlerGet(bulkWriter io.Writer, req request, pm *fileutils.PatternMa
14081411 if err != nil {
14091412 return errorResponse ("copier: get: error reading info about directory %q: %v" , req .Directory , err )
14101413 }
1414+ var chmod * mode.Set
1415+ if req .GetOptions .Chmod != "" {
1416+ p , err := mode .Parse (req .GetOptions .Chmod )
1417+ if err != nil {
1418+ return errorResponse ("copier: get: parsing chmod %q: %v" , req .GetOptions .Chmod , err )
1419+ }
1420+ chmod = & p
1421+ }
14111422 cb := func () error {
14121423 tw := tar .NewWriter (bulkWriter )
14131424 defer tw .Close ()
@@ -1461,7 +1472,7 @@ func copierHandlerGet(bulkWriter io.Writer, req request, pm *fileutils.PatternMa
14611472 return fmt .Errorf ("copier: get: %w" , err )
14621473 }
14631474
1464- if err := copierHandlerGetOne (parentInfo , parentSymlinkTarget , parentName , parent , req .GetOptions , tw , hardlinkChecker , idMappings ); err != nil {
1475+ if err := copierHandlerGetOne (parentInfo , parentSymlinkTarget , parentName , parent , req .GetOptions , tw , hardlinkChecker , idMappings , chmod ); err != nil {
14651476 if req .GetOptions .IgnoreUnreadable && errorIsPermission (err ) {
14661477 continue
14671478 } else if errors .Is (err , os .ErrNotExist ) {
@@ -1584,7 +1595,7 @@ func copierHandlerGet(bulkWriter io.Writer, req request, pm *fileutils.PatternMa
15841595 }
15851596 }
15861597 // add the item to the outgoing tar stream
1587- if err := copierHandlerGetOne (info , symlinkTarget , rel , path , options , tw , hardlinkChecker , idMappings ); err != nil {
1598+ if err := copierHandlerGetOne (info , symlinkTarget , rel , path , options , tw , hardlinkChecker , idMappings , chmod ); err != nil {
15881599 if req .GetOptions .IgnoreUnreadable && errorIsPermission (err ) {
15891600 return ok
15901601 } else if errors .Is (err , os .ErrNotExist ) {
@@ -1626,7 +1637,7 @@ func copierHandlerGet(bulkWriter io.Writer, req request, pm *fileutils.PatternMa
16261637 return fmt .Errorf ("copier: get: %w" , err )
16271638 }
16281639
1629- if err := copierHandlerGetOne (info , symlinkTarget , name , item , req .GetOptions , tw , hardlinkChecker , idMappings ); err != nil {
1640+ if err := copierHandlerGetOne (info , symlinkTarget , name , item , req .GetOptions , tw , hardlinkChecker , idMappings , chmod ); err != nil {
16301641 if req .GetOptions .IgnoreUnreadable && errorIsPermission (err ) {
16311642 continue
16321643 }
@@ -1697,7 +1708,7 @@ func getTargetIfSymlink(path string, info os.FileInfo) (string, error) {
16971708 return "" , nil
16981709}
16991710
1700- func copierHandlerGetOne (srcfi os.FileInfo , symlinkTarget , name , contentPath string , options GetOptions , tw * tar.Writer , hardlinkChecker * hardlinkChecker , idMappings * idtools.IDMappings ) error {
1711+ func copierHandlerGetOne (srcfi os.FileInfo , symlinkTarget , name , contentPath string , options GetOptions , tw * tar.Writer , hardlinkChecker * hardlinkChecker , idMappings * idtools.IDMappings , chmod * mode. Set ) error {
17011712 // build the header using the name provided
17021713 hdr , err := tar .FileInfoHeader (srcfi , symlinkTarget )
17031714 if err != nil {
@@ -1807,11 +1818,14 @@ func copierHandlerGetOne(srcfi os.FileInfo, symlinkTarget, name, contentPath str
18071818 }
18081819 }
18091820 // force ownership and/or permissions, if requested
1821+ if chmod != nil {
1822+ hdr .Mode = int64 (chmod .Apply (srcfi .Mode ()))
1823+ }
18101824 if hdr .Typeflag == tar .TypeDir {
18111825 if options .ChownDirs != nil {
18121826 hdr .Uid , hdr .Gid = options .ChownDirs .UID , options .ChownDirs .GID
18131827 }
1814- if options .ChmodDirs != nil {
1828+ if options .ChmodDirs != nil && chmod == nil {
18151829 hdr .Mode = int64 (* options .ChmodDirs )
18161830 }
18171831 if ! strings .HasSuffix (hdr .Name , "/" ) {
@@ -1821,7 +1835,7 @@ func copierHandlerGetOne(srcfi os.FileInfo, symlinkTarget, name, contentPath str
18211835 if options .ChownFiles != nil {
18221836 hdr .Uid , hdr .Gid = options .ChownFiles .UID , options .ChownFiles .GID
18231837 }
1824- if options .ChmodFiles != nil {
1838+ if options .ChmodFiles != nil && chmod == nil {
18251839 hdr .Mode = int64 (* options .ChmodFiles )
18261840 }
18271841 }
@@ -1887,6 +1901,15 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM
18871901 if req .PutOptions .ChmodDirs != nil {
18881902 defaultDirMode = * req .PutOptions .ChmodDirs
18891903 }
1904+ var chmod * mode.Set
1905+ if req .PutOptions .Chmod != "" {
1906+ p , err := mode .Parse (req .PutOptions .Chmod )
1907+ if err != nil {
1908+ return errorResponse ("parsing chmod %q: %v" , req .PutOptions .Chmod , err )
1909+ }
1910+ chmod = & p
1911+ defaultDirMode = chmod .Apply (defaultDirMode )
1912+ }
18901913 if req .PutOptions .DefaultDirOwner != nil {
18911914 defaultDirUID , defaultDirGID = req .PutOptions .DefaultDirOwner .UID , req .PutOptions .DefaultDirOwner .GID
18921915 }
@@ -2078,13 +2101,17 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM
20782101 if req .PutOptions .StripStickyBit && hdr .Mode & cISVTX == cISVTX {
20792102 hdr .Mode &^= cISVTX
20802103 }
2081- if hdr .Typeflag == tar .TypeDir {
2082- if req .PutOptions .ChmodDirs != nil {
2083- hdr .Mode = int64 (* req .PutOptions .ChmodDirs )
2084- }
2104+ if chmod != nil {
2105+ hdr .Mode = int64 (chmod .Apply (os .FileMode (hdr .Mode )))
20852106 } else {
2086- if req .PutOptions .ChmodFiles != nil {
2087- hdr .Mode = int64 (* req .PutOptions .ChmodFiles )
2107+ if hdr .Typeflag == tar .TypeDir {
2108+ if req .PutOptions .ChmodDirs != nil {
2109+ hdr .Mode = int64 (* req .PutOptions .ChmodDirs )
2110+ }
2111+ } else {
2112+ if req .PutOptions .ChmodFiles != nil {
2113+ hdr .Mode = int64 (* req .PutOptions .ChmodFiles )
2114+ }
20882115 }
20892116 }
20902117 // create the new item
0 commit comments