Skip to content

Commit db55716

Browse files
committed
Update interface to fit into user package
Removed duplicated structs Removed deprecated functionality Simplified calls which previously relied on unused functionality Signed-off-by: Derek McGowan <derek@mcg.dev>
1 parent 8e08d6b commit db55716

4 files changed

Lines changed: 195 additions & 224 deletions

File tree

user/idtools.go

Lines changed: 54 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,53 @@
1-
package idtools
1+
package user
22

33
import (
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
142139
func (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-
}

user/idtools_unix.go

Lines changed: 20 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
//go:build !windows
22

3-
package idtools
3+
package user
44

55
import (
66
"fmt"
77
"os"
88
"path/filepath"
99
"strconv"
1010
"syscall"
11-
12-
"github.com/moby/sys/user"
1311
)
1412

15-
func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting bool) error {
13+
func mkdirAs(path string, mode os.FileMode, uid, gid int, mkAll, onlyNew bool) error {
1614
path, err := filepath.Abs(path)
1715
if err != nil {
1816
return err
@@ -23,21 +21,21 @@ func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting
2321
if !stat.IsDir() {
2422
return &os.PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR}
2523
}
26-
if !chownExisting {
24+
if onlyNew {
2725
return nil
2826
}
2927

3028
// short-circuit -- we were called with an existing directory and chown was requested
31-
return setPermissions(path, mode, owner, stat)
29+
return setPermissions(path, mode, uid, gid, stat)
3230
}
3331

3432
// make an array containing the original path asked for, plus (for mkAll == true)
3533
// all path components leading up to the complete path that don't exist before we MkdirAll
36-
// so that we can chown all of them properly at the end. If chownExisting is false, we won't
34+
// so that we can chown all of them properly at the end. If onlyNew is true, we won't
3735
// chown the full directory path if it exists
3836
var paths []string
3937
if os.IsNotExist(err) {
40-
paths = []string{path}
38+
paths = append(paths, path)
4139
}
4240

4341
if mkAll {
@@ -49,7 +47,7 @@ func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting
4947
if dirPath == "/" {
5048
break
5149
}
52-
if _, err = os.Stat(dirPath); err != nil && os.IsNotExist(err) {
50+
if _, err = os.Stat(dirPath); os.IsNotExist(err) {
5351
paths = append(paths, dirPath)
5452
}
5553
}
@@ -62,39 +60,18 @@ func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting
6260
// even if it existed, we will chown the requested path + any subpaths that
6361
// didn't exist when we called MkdirAll
6462
for _, pathComponent := range paths {
65-
if err = setPermissions(pathComponent, mode, owner, nil); err != nil {
63+
if err = setPermissions(pathComponent, mode, uid, gid, nil); err != nil {
6664
return err
6765
}
6866
}
6967
return nil
7068
}
7169

72-
// LookupUser uses traditional local system files lookup (from libcontainer/user) on a username
73-
//
74-
// Deprecated: use [user.LookupUser] instead
75-
func LookupUser(name string) (user.User, error) {
76-
return user.LookupUser(name)
77-
}
78-
79-
// LookupUID uses traditional local system files lookup (from libcontainer/user) on a uid
80-
//
81-
// Deprecated: use [user.LookupUid] instead
82-
func LookupUID(uid int) (user.User, error) {
83-
return user.LookupUid(uid)
84-
}
85-
86-
// LookupGroup uses traditional local system files lookup (from libcontainer/user) on a group name,
87-
//
88-
// Deprecated: use [user.LookupGroup] instead
89-
func LookupGroup(name string) (user.Group, error) {
90-
return user.LookupGroup(name)
91-
}
92-
9370
// setPermissions performs a chown/chmod only if the uid/gid don't match what's requested
9471
// Normally a Chown is a no-op if uid/gid match, but in some cases this can still cause an error, e.g. if the
9572
// dir is on an NFS share, so don't call chown unless we absolutely must.
9673
// Likewise for setting permissions.
97-
func setPermissions(p string, mode os.FileMode, owner Identity, stat os.FileInfo) error {
74+
func setPermissions(p string, mode os.FileMode, uid, gid int, stat os.FileInfo) error {
9875
if stat == nil {
9976
var err error
10077
stat, err = os.Stat(p)
@@ -108,20 +85,20 @@ func setPermissions(p string, mode os.FileMode, owner Identity, stat os.FileInfo
10885
}
10986
}
11087
ssi := stat.Sys().(*syscall.Stat_t)
111-
if ssi.Uid == uint32(owner.UID) && ssi.Gid == uint32(owner.GID) {
88+
if ssi.Uid == uint32(uid) && ssi.Gid == uint32(gid) {
11289
return nil
11390
}
114-
return os.Chown(p, owner.UID, owner.GID)
91+
return os.Chown(p, uid, gid)
11592
}
11693

11794
// LoadIdentityMapping takes a requested username and
11895
// using the data from /etc/sub{uid,gid} ranges, creates the
11996
// proper uid and gid remapping ranges for that user/group pair
12097
func LoadIdentityMapping(name string) (IdentityMapping, error) {
12198
// TODO: Consider adding support for calling out to "getent"
122-
usr, err := user.LookupUser(name)
99+
usr, err := LookupUser(name)
123100
if err != nil {
124-
return IdentityMapping{}, fmt.Errorf("could not get user for username %s: %v", name, err)
101+
return IdentityMapping{}, fmt.Errorf("could not get user for username %s: %w", name, err)
125102
}
126103

127104
subuidRanges, err := lookupSubRangesFile("/etc/subuid", usr)
@@ -139,9 +116,9 @@ func LoadIdentityMapping(name string) (IdentityMapping, error) {
139116
}, nil
140117
}
141118

142-
func lookupSubRangesFile(path string, usr user.User) ([]IDMap, error) {
119+
func lookupSubRangesFile(path string, usr User) ([]IDMap, error) {
143120
uidstr := strconv.Itoa(usr.Uid)
144-
rangeList, err := user.ParseSubIDFileFilter(path, func(sid user.SubID) bool {
121+
rangeList, err := ParseSubIDFileFilter(path, func(sid SubID) bool {
145122
return sid.Name == usr.Name || sid.Name == uidstr
146123
})
147124
if err != nil {
@@ -153,14 +130,14 @@ func lookupSubRangesFile(path string, usr user.User) ([]IDMap, error) {
153130

154131
idMap := []IDMap{}
155132

156-
containerID := 0
133+
var containerID int64
157134
for _, idrange := range rangeList {
158135
idMap = append(idMap, IDMap{
159-
ContainerID: containerID,
160-
HostID: int(idrange.SubID),
161-
Size: int(idrange.Count),
136+
ID: containerID,
137+
ParentID: idrange.SubID,
138+
Count: idrange.Count,
162139
})
163-
containerID = containerID + int(idrange.Count)
140+
containerID = containerID + idrange.Count
164141
}
165142
return idMap, nil
166143
}

0 commit comments

Comments
 (0)