1- package idtools
1+ package user
22
33import (
44 "fmt"
55 "os"
66)
77
8- // IDMap contains a single entry for user namespace range remapping. An array
9- // of IDMap entries represents the structure that will be provided to the Linux
10- // kernel for creating a user namespace.
11- type IDMap struct {
12- ContainerID int `json:"container_id"`
13- HostID int `json:"host_id"`
14- Size int `json:"size"`
8+ // MkdirOpt is a type for options to pass to Mkdir calls
9+ type MkdirOpt func (* mkdirOptions )
10+
11+ type mkdirOptions struct {
12+ onlyNew bool
13+ }
14+
15+ // WithOnlyNew is an option for MkdirAllAndChown that will only change ownership and permissions
16+ // on newly created directories. If the directory already exists, it will not be modified
17+ func WithOnlyNew (o * mkdirOptions ) {
18+ o .onlyNew = true
1519}
1620
1721// MkdirAllAndChown creates a directory (include any along the path) and then modifies
18- // ownership to the requested uid/gid. If the directory already exists, this
19- // function will still change ownership and permissions.
20- func MkdirAllAndChown (path string , mode os.FileMode , owner Identity ) error {
21- return mkdirAs (path , mode , owner , true , true )
22+ // ownership to the requested uid/gid. By default, if the directory already exists, this
23+ // function will still change ownership and permissions. If WithOnlyNew is passed as an
24+ // option, then only the newly created directories will have ownership and permissions changed.
25+ func MkdirAllAndChown (path string , mode os.FileMode , uid , gid int , opts ... MkdirOpt ) error {
26+ var options mkdirOptions
27+ for _ , opt := range opts {
28+ opt (& options )
29+ }
30+
31+ return mkdirAs (path , mode , uid , gid , true , options .onlyNew )
2232}
2333
2434// MkdirAndChown creates a directory and then modifies ownership to the requested uid/gid.
25- // If the directory already exists, this function still changes ownership and permissions.
35+ // By default, if the directory already exists, this function still changes ownership and permissions.
36+ // If WithOnlyNew is passed as an option, then only the newly created directory will have ownership
37+ // and permissions changed.
2638// Note that unlike os.Mkdir(), this function does not return IsExist error
2739// in case path already exists.
28- func MkdirAndChown (path string , mode os.FileMode , owner Identity ) error {
29- return mkdirAs (path , mode , owner , false , true )
30- }
31-
32- // MkdirAllAndChownNew creates a directory (include any along the path) and then modifies
33- // ownership ONLY of newly created directories to the requested uid/gid. If the
34- // directories along the path exist, no change of ownership or permissions will be performed
35- func MkdirAllAndChownNew (path string , mode os.FileMode , owner Identity ) error {
36- return mkdirAs (path , mode , owner , true , false )
40+ func MkdirAndChown (path string , mode os.FileMode , uid , gid int , opts ... MkdirOpt ) error {
41+ var options mkdirOptions
42+ for _ , opt := range opts {
43+ opt (& options )
44+ }
45+ return mkdirAs (path , mode , uid , gid , false , options .onlyNew )
3746}
3847
39- // GetRootUIDGID retrieves the remapped root uid/gid pair from the set of maps.
48+ // getRootUIDGID retrieves the remapped root uid/gid pair from the set of maps.
4049// If the maps are empty, then the root uid/gid will default to "real" 0/0
41- func GetRootUIDGID (uidMap , gidMap []IDMap ) (int , int , error ) {
50+ func getRootUIDGID (uidMap , gidMap []IDMap ) (int , int , error ) {
4251 uid , err := toHost (0 , uidMap )
4352 if err != nil {
4453 return - 1 , - 1 , err
@@ -58,12 +67,12 @@ func toContainer(hostID int, idMap []IDMap) (int, error) {
5867 return hostID , nil
5968 }
6069 for _ , m := range idMap {
61- if (hostID >= m .HostID ) && (hostID <= (m .HostID + m .Size - 1 )) {
62- contID := m . ContainerID + (hostID - m .HostID )
70+ if (int64 ( hostID ) >= m .ParentID ) && (int64 ( hostID ) <= (m .ParentID + m .Count - 1 )) {
71+ contID := int ( m . ID + (int64 ( hostID ) - m .ParentID ) )
6372 return contID , nil
6473 }
6574 }
66- return - 1 , fmt .Errorf ("Host ID %d cannot be mapped to a container ID" , hostID )
75+ return - 1 , fmt .Errorf ("host ID %d cannot be mapped to a container ID" , hostID )
6776}
6877
6978// toHost takes an id mapping and a remapped ID, and translates the
@@ -74,24 +83,12 @@ func toHost(contID int, idMap []IDMap) (int, error) {
7483 return contID , nil
7584 }
7685 for _ , m := range idMap {
77- if (contID >= m .ContainerID ) && (contID <= (m .ContainerID + m .Size - 1 )) {
78- hostID := m . HostID + (contID - m .ContainerID )
86+ if (int64 ( contID ) >= m .ID ) && (int64 ( contID ) <= (m .ID + m .Count - 1 )) {
87+ hostID := int ( m . ParentID + (int64 ( contID ) - m .ID ) )
7988 return hostID , nil
8089 }
8190 }
82- return - 1 , fmt .Errorf ("Container ID %d cannot be mapped to a host ID" , contID )
83- }
84-
85- // Identity is either a UID and GID pair or a SID (but not both)
86- type Identity struct {
87- UID int
88- GID int
89- SID string
90- }
91-
92- // Chown changes the numeric uid and gid of the named file to id.UID and id.GID.
93- func (id Identity ) Chown (name string ) error {
94- return os .Chown (name , id .UID , id .GID )
91+ return - 1 , fmt .Errorf ("container ID %d cannot be mapped to a host ID" , contID )
9592}
9693
9794// IdentityMapping contains a mappings of UIDs and GIDs.
@@ -104,46 +101,41 @@ type IdentityMapping struct {
104101// RootPair returns a uid and gid pair for the root user. The error is ignored
105102// because a root user always exists, and the defaults are correct when the uid
106103// and gid maps are empty.
107- func (i IdentityMapping ) RootPair () Identity {
108- uid , gid , _ := GetRootUIDGID (i .UIDMaps , i .GIDMaps )
109- return Identity { UID : uid , GID : gid }
104+ func (i IdentityMapping ) RootPair () ( int , int ) {
105+ uid , gid , _ := getRootUIDGID (i .UIDMaps , i .GIDMaps )
106+ return uid , gid
110107}
111108
112109// ToHost returns the host UID and GID for the container uid, gid.
113110// Remapping is only performed if the ids aren't already the remapped root ids
114- func (i IdentityMapping ) ToHost (pair Identity ) (Identity , error ) {
111+ func (i IdentityMapping ) ToHost (uid , gid int ) (int , int , error ) {
115112 var err error
116- target := i .RootPair ()
113+ ruid , rgid := i .RootPair ()
117114
118- if pair . UID != target . UID {
119- target . UID , err = toHost (pair . UID , i .UIDMaps )
115+ if uid != ruid {
116+ ruid , err = toHost (uid , i .UIDMaps )
120117 if err != nil {
121- return target , err
118+ return ruid , rgid , err
122119 }
123120 }
124121
125- if pair . GID != target . GID {
126- target . GID , err = toHost (pair . GID , i .GIDMaps )
122+ if gid != rgid {
123+ rgid , err = toHost (gid , i .GIDMaps )
127124 }
128- return target , err
125+ return ruid , rgid , err
129126}
130127
131128// ToContainer returns the container UID and GID for the host uid and gid
132- func (i IdentityMapping ) ToContainer (pair Identity ) (int , int , error ) {
133- uid , err := toContainer (pair . UID , i .UIDMaps )
129+ func (i IdentityMapping ) ToContainer (uid , gid int ) (int , int , error ) {
130+ ruid , err := toContainer (uid , i .UIDMaps )
134131 if err != nil {
135132 return - 1 , - 1 , err
136133 }
137- gid , err := toContainer (pair . GID , i .GIDMaps )
138- return uid , gid , err
134+ rgid , err := toContainer (gid , i .GIDMaps )
135+ return ruid , rgid , err
139136}
140137
141138// Empty returns true if there are no id mappings
142139func (i IdentityMapping ) Empty () bool {
143140 return len (i .UIDMaps ) == 0 && len (i .GIDMaps ) == 0
144141}
145-
146- // CurrentIdentity returns the identity of the current process
147- func CurrentIdentity () Identity {
148- return Identity {UID : os .Getuid (), GID : os .Getegid ()}
149- }
0 commit comments