@@ -3,6 +3,21 @@ package user
33import (
44 "fmt"
55 "os"
6+ "path/filepath"
7+ )
8+
9+ // FS is the filesystem contract used by the Mkdir*AndChownFS helpers.
10+ type FS interface {
11+ Stat (name string ) (os.FileInfo , error )
12+ Mkdir (name string , perm os.FileMode ) error
13+ MkdirAll (name string , perm os.FileMode ) error
14+ Chmod (name string , mode os.FileMode ) error
15+ Chown (name string , uid , gid int ) error
16+ }
17+
18+ var (
19+ _ FS = & os.Root {}
20+ _ FS = & hostFS {}
621)
722
823// MkdirOpt is a type for options to pass to Mkdir calls
@@ -23,12 +38,24 @@ func WithOnlyNew(o *mkdirOptions) {
2338// function will still change ownership and permissions. If WithOnlyNew is passed as an
2439// option, then only the newly created directories will have ownership and permissions changed.
2540func MkdirAllAndChown (path string , mode os.FileMode , uid , gid int , opts ... MkdirOpt ) error {
26- var options mkdirOptions
27- for _ , opt := range opts {
28- opt (& options )
41+ return MkdirAllAndChownFS (nil , path , mode , uid , gid , opts ... )
42+ }
43+
44+ // MkdirAllAndChownFS creates a directory (including any along the path) on the
45+ // provided filesystem and then modifies ownership to the requested uid/gid. If
46+ // fsys is nil, the host filesystem is used.
47+ func MkdirAllAndChownFS (fsys FS , path string , mode os.FileMode , uid , gid int , opts ... MkdirOpt ) error {
48+ if fsys == nil {
49+ absPath , err := filepath .Abs (path )
50+ if err != nil {
51+ return err
52+ }
53+ fsys = & hostFS {}
54+ path = absPath
2955 }
3056
31- return mkdirAs (path , mode , uid , gid , true , options .onlyNew )
57+ options := mkdirOpts (opts )
58+ return mkdirAs (fsys , path , mode , uid , gid , true , options .onlyNew )
3259}
3360
3461// MkdirAndChown creates a directory and then modifies ownership to the requested uid/gid.
@@ -38,11 +65,32 @@ func MkdirAllAndChown(path string, mode os.FileMode, uid, gid int, opts ...Mkdir
3865// Note that unlike os.Mkdir(), this function does not return IsExist error
3966// in case path already exists.
4067func MkdirAndChown (path string , mode os.FileMode , uid , gid int , opts ... MkdirOpt ) error {
68+ return MkdirAndChownFS (nil , path , mode , uid , gid , opts ... )
69+ }
70+
71+ // MkdirAndChownFS creates a directory on the provided filesystem and then
72+ // modifies ownership to the requested uid/gid. If fsys is nil, the host
73+ // filesystem is used.
74+ func MkdirAndChownFS (fsys FS , path string , mode os.FileMode , uid , gid int , opts ... MkdirOpt ) error {
75+ if fsys == nil {
76+ absPath , err := filepath .Abs (path )
77+ if err != nil {
78+ return err
79+ }
80+ fsys = & hostFS {}
81+ path = absPath
82+ }
83+
84+ options := mkdirOpts (opts )
85+ return mkdirAs (fsys , path , mode , uid , gid , false , options .onlyNew )
86+ }
87+
88+ func mkdirOpts (opts []MkdirOpt ) mkdirOptions {
4189 var options mkdirOptions
4290 for _ , opt := range opts {
4391 opt (& options )
4492 }
45- return mkdirAs ( path , mode , uid , gid , false , options . onlyNew )
93+ return options
4694}
4795
4896// getRootUIDGID retrieves the remapped root uid/gid pair from the set of maps.
@@ -139,3 +187,25 @@ func (i IdentityMapping) ToContainer(uid, gid int) (int, int, error) {
139187func (i IdentityMapping ) Empty () bool {
140188 return len (i .UIDMaps ) == 0 && len (i .GIDMaps ) == 0
141189}
190+
191+ type hostFS struct {}
192+
193+ func (* hostFS ) Stat (name string ) (os.FileInfo , error ) {
194+ return os .Stat (name )
195+ }
196+
197+ func (* hostFS ) Mkdir (name string , perm os.FileMode ) error {
198+ return os .Mkdir (name , perm )
199+ }
200+
201+ func (* hostFS ) MkdirAll (name string , perm os.FileMode ) error {
202+ return os .MkdirAll (name , perm )
203+ }
204+
205+ func (* hostFS ) Chmod (name string , mode os.FileMode ) error {
206+ return os .Chmod (name , mode )
207+ }
208+
209+ func (* hostFS ) Chown (name string , uid , gid int ) error {
210+ return os .Chown (name , uid , gid )
211+ }
0 commit comments