From db55716d7f03d3b169b94d5f099405d857b5560b Mon Sep 17 00:00:00 2001 From: Derek McGowan Date: Fri, 10 Jan 2025 12:45:36 -0800 Subject: [PATCH] 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 --- user/idtools.go | 116 +++++++++----------- user/idtools_unix.go | 63 ++++------- user/idtools_unix_test.go | 223 ++++++++++++++++++++------------------ user/idtools_windows.go | 17 +-- 4 files changed, 195 insertions(+), 224 deletions(-) diff --git a/user/idtools.go b/user/idtools.go index d2fbd94..595b7a9 100644 --- a/user/idtools.go +++ b/user/idtools.go @@ -1,44 +1,53 @@ -package idtools +package user import ( "fmt" "os" ) -// IDMap contains a single entry for user namespace range remapping. An array -// of IDMap entries represents the structure that will be provided to the Linux -// kernel for creating a user namespace. -type IDMap struct { - ContainerID int `json:"container_id"` - HostID int `json:"host_id"` - Size int `json:"size"` +// MkdirOpt is a type for options to pass to Mkdir calls +type MkdirOpt func(*mkdirOptions) + +type mkdirOptions struct { + onlyNew bool +} + +// WithOnlyNew is an option for MkdirAllAndChown that will only change ownership and permissions +// on newly created directories. If the directory already exists, it will not be modified +func WithOnlyNew(o *mkdirOptions) { + o.onlyNew = true } // MkdirAllAndChown creates a directory (include any along the path) and then modifies -// ownership to the requested uid/gid. If the directory already exists, this -// function will still change ownership and permissions. -func MkdirAllAndChown(path string, mode os.FileMode, owner Identity) error { - return mkdirAs(path, mode, owner, true, true) +// ownership to the requested uid/gid. By default, if the directory already exists, this +// function will still change ownership and permissions. If WithOnlyNew is passed as an +// option, then only the newly created directories will have ownership and permissions changed. +func MkdirAllAndChown(path string, mode os.FileMode, uid, gid int, opts ...MkdirOpt) error { + var options mkdirOptions + for _, opt := range opts { + opt(&options) + } + + return mkdirAs(path, mode, uid, gid, true, options.onlyNew) } // MkdirAndChown creates a directory and then modifies ownership to the requested uid/gid. -// If the directory already exists, this function still changes ownership and permissions. +// By default, if the directory already exists, this function still changes ownership and permissions. +// If WithOnlyNew is passed as an option, then only the newly created directory will have ownership +// and permissions changed. // Note that unlike os.Mkdir(), this function does not return IsExist error // in case path already exists. -func MkdirAndChown(path string, mode os.FileMode, owner Identity) error { - return mkdirAs(path, mode, owner, false, true) -} - -// MkdirAllAndChownNew creates a directory (include any along the path) and then modifies -// ownership ONLY of newly created directories to the requested uid/gid. If the -// directories along the path exist, no change of ownership or permissions will be performed -func MkdirAllAndChownNew(path string, mode os.FileMode, owner Identity) error { - return mkdirAs(path, mode, owner, true, false) +func MkdirAndChown(path string, mode os.FileMode, uid, gid int, opts ...MkdirOpt) error { + var options mkdirOptions + for _, opt := range opts { + opt(&options) + } + return mkdirAs(path, mode, uid, gid, false, options.onlyNew) } -// GetRootUIDGID retrieves the remapped root uid/gid pair from the set of maps. +// getRootUIDGID retrieves the remapped root uid/gid pair from the set of maps. // If the maps are empty, then the root uid/gid will default to "real" 0/0 -func GetRootUIDGID(uidMap, gidMap []IDMap) (int, int, error) { +func getRootUIDGID(uidMap, gidMap []IDMap) (int, int, error) { uid, err := toHost(0, uidMap) if err != nil { return -1, -1, err @@ -58,12 +67,12 @@ func toContainer(hostID int, idMap []IDMap) (int, error) { return hostID, nil } for _, m := range idMap { - if (hostID >= m.HostID) && (hostID <= (m.HostID + m.Size - 1)) { - contID := m.ContainerID + (hostID - m.HostID) + if (int64(hostID) >= m.ParentID) && (int64(hostID) <= (m.ParentID + m.Count - 1)) { + contID := int(m.ID + (int64(hostID) - m.ParentID)) return contID, nil } } - return -1, fmt.Errorf("Host ID %d cannot be mapped to a container ID", hostID) + return -1, fmt.Errorf("host ID %d cannot be mapped to a container ID", hostID) } // toHost takes an id mapping and a remapped ID, and translates the @@ -74,24 +83,12 @@ func toHost(contID int, idMap []IDMap) (int, error) { return contID, nil } for _, m := range idMap { - if (contID >= m.ContainerID) && (contID <= (m.ContainerID + m.Size - 1)) { - hostID := m.HostID + (contID - m.ContainerID) + if (int64(contID) >= m.ID) && (int64(contID) <= (m.ID + m.Count - 1)) { + hostID := int(m.ParentID + (int64(contID) - m.ID)) return hostID, nil } } - return -1, fmt.Errorf("Container ID %d cannot be mapped to a host ID", contID) -} - -// Identity is either a UID and GID pair or a SID (but not both) -type Identity struct { - UID int - GID int - SID string -} - -// Chown changes the numeric uid and gid of the named file to id.UID and id.GID. -func (id Identity) Chown(name string) error { - return os.Chown(name, id.UID, id.GID) + return -1, fmt.Errorf("container ID %d cannot be mapped to a host ID", contID) } // IdentityMapping contains a mappings of UIDs and GIDs. @@ -104,46 +101,41 @@ type IdentityMapping struct { // RootPair returns a uid and gid pair for the root user. The error is ignored // because a root user always exists, and the defaults are correct when the uid // and gid maps are empty. -func (i IdentityMapping) RootPair() Identity { - uid, gid, _ := GetRootUIDGID(i.UIDMaps, i.GIDMaps) - return Identity{UID: uid, GID: gid} +func (i IdentityMapping) RootPair() (int, int) { + uid, gid, _ := getRootUIDGID(i.UIDMaps, i.GIDMaps) + return uid, gid } // ToHost returns the host UID and GID for the container uid, gid. // Remapping is only performed if the ids aren't already the remapped root ids -func (i IdentityMapping) ToHost(pair Identity) (Identity, error) { +func (i IdentityMapping) ToHost(uid, gid int) (int, int, error) { var err error - target := i.RootPair() + ruid, rgid := i.RootPair() - if pair.UID != target.UID { - target.UID, err = toHost(pair.UID, i.UIDMaps) + if uid != ruid { + ruid, err = toHost(uid, i.UIDMaps) if err != nil { - return target, err + return ruid, rgid, err } } - if pair.GID != target.GID { - target.GID, err = toHost(pair.GID, i.GIDMaps) + if gid != rgid { + rgid, err = toHost(gid, i.GIDMaps) } - return target, err + return ruid, rgid, err } // ToContainer returns the container UID and GID for the host uid and gid -func (i IdentityMapping) ToContainer(pair Identity) (int, int, error) { - uid, err := toContainer(pair.UID, i.UIDMaps) +func (i IdentityMapping) ToContainer(uid, gid int) (int, int, error) { + ruid, err := toContainer(uid, i.UIDMaps) if err != nil { return -1, -1, err } - gid, err := toContainer(pair.GID, i.GIDMaps) - return uid, gid, err + rgid, err := toContainer(gid, i.GIDMaps) + return ruid, rgid, err } // Empty returns true if there are no id mappings func (i IdentityMapping) Empty() bool { return len(i.UIDMaps) == 0 && len(i.GIDMaps) == 0 } - -// CurrentIdentity returns the identity of the current process -func CurrentIdentity() Identity { - return Identity{UID: os.Getuid(), GID: os.Getegid()} -} diff --git a/user/idtools_unix.go b/user/idtools_unix.go index 1f11fe4..4e39d24 100644 --- a/user/idtools_unix.go +++ b/user/idtools_unix.go @@ -1,6 +1,6 @@ //go:build !windows -package idtools +package user import ( "fmt" @@ -8,11 +8,9 @@ import ( "path/filepath" "strconv" "syscall" - - "github.com/moby/sys/user" ) -func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting bool) error { +func mkdirAs(path string, mode os.FileMode, uid, gid int, mkAll, onlyNew bool) error { path, err := filepath.Abs(path) if err != nil { return err @@ -23,21 +21,21 @@ func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting if !stat.IsDir() { return &os.PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR} } - if !chownExisting { + if onlyNew { return nil } // short-circuit -- we were called with an existing directory and chown was requested - return setPermissions(path, mode, owner, stat) + return setPermissions(path, mode, uid, gid, stat) } // make an array containing the original path asked for, plus (for mkAll == true) // all path components leading up to the complete path that don't exist before we MkdirAll - // so that we can chown all of them properly at the end. If chownExisting is false, we won't + // so that we can chown all of them properly at the end. If onlyNew is true, we won't // chown the full directory path if it exists var paths []string if os.IsNotExist(err) { - paths = []string{path} + paths = append(paths, path) } if mkAll { @@ -49,7 +47,7 @@ func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting if dirPath == "/" { break } - if _, err = os.Stat(dirPath); err != nil && os.IsNotExist(err) { + if _, err = os.Stat(dirPath); os.IsNotExist(err) { paths = append(paths, dirPath) } } @@ -62,39 +60,18 @@ func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting // even if it existed, we will chown the requested path + any subpaths that // didn't exist when we called MkdirAll for _, pathComponent := range paths { - if err = setPermissions(pathComponent, mode, owner, nil); err != nil { + if err = setPermissions(pathComponent, mode, uid, gid, nil); err != nil { return err } } return nil } -// LookupUser uses traditional local system files lookup (from libcontainer/user) on a username -// -// Deprecated: use [user.LookupUser] instead -func LookupUser(name string) (user.User, error) { - return user.LookupUser(name) -} - -// LookupUID uses traditional local system files lookup (from libcontainer/user) on a uid -// -// Deprecated: use [user.LookupUid] instead -func LookupUID(uid int) (user.User, error) { - return user.LookupUid(uid) -} - -// LookupGroup uses traditional local system files lookup (from libcontainer/user) on a group name, -// -// Deprecated: use [user.LookupGroup] instead -func LookupGroup(name string) (user.Group, error) { - return user.LookupGroup(name) -} - // setPermissions performs a chown/chmod only if the uid/gid don't match what's requested // 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 // dir is on an NFS share, so don't call chown unless we absolutely must. // Likewise for setting permissions. -func setPermissions(p string, mode os.FileMode, owner Identity, stat os.FileInfo) error { +func setPermissions(p string, mode os.FileMode, uid, gid int, stat os.FileInfo) error { if stat == nil { var err error stat, err = os.Stat(p) @@ -108,10 +85,10 @@ func setPermissions(p string, mode os.FileMode, owner Identity, stat os.FileInfo } } ssi := stat.Sys().(*syscall.Stat_t) - if ssi.Uid == uint32(owner.UID) && ssi.Gid == uint32(owner.GID) { + if ssi.Uid == uint32(uid) && ssi.Gid == uint32(gid) { return nil } - return os.Chown(p, owner.UID, owner.GID) + return os.Chown(p, uid, gid) } // LoadIdentityMapping takes a requested username and @@ -119,9 +96,9 @@ func setPermissions(p string, mode os.FileMode, owner Identity, stat os.FileInfo // proper uid and gid remapping ranges for that user/group pair func LoadIdentityMapping(name string) (IdentityMapping, error) { // TODO: Consider adding support for calling out to "getent" - usr, err := user.LookupUser(name) + usr, err := LookupUser(name) if err != nil { - return IdentityMapping{}, fmt.Errorf("could not get user for username %s: %v", name, err) + return IdentityMapping{}, fmt.Errorf("could not get user for username %s: %w", name, err) } subuidRanges, err := lookupSubRangesFile("/etc/subuid", usr) @@ -139,9 +116,9 @@ func LoadIdentityMapping(name string) (IdentityMapping, error) { }, nil } -func lookupSubRangesFile(path string, usr user.User) ([]IDMap, error) { +func lookupSubRangesFile(path string, usr User) ([]IDMap, error) { uidstr := strconv.Itoa(usr.Uid) - rangeList, err := user.ParseSubIDFileFilter(path, func(sid user.SubID) bool { + rangeList, err := ParseSubIDFileFilter(path, func(sid SubID) bool { return sid.Name == usr.Name || sid.Name == uidstr }) if err != nil { @@ -153,14 +130,14 @@ func lookupSubRangesFile(path string, usr user.User) ([]IDMap, error) { idMap := []IDMap{} - containerID := 0 + var containerID int64 for _, idrange := range rangeList { idMap = append(idMap, IDMap{ - ContainerID: containerID, - HostID: int(idrange.SubID), - Size: int(idrange.Count), + ID: containerID, + ParentID: idrange.SubID, + Count: idrange.Count, }) - containerID = containerID + int(idrange.Count) + containerID = containerID + idrange.Count } return idMap, nil } diff --git a/user/idtools_unix_test.go b/user/idtools_unix_test.go index 381a1d7..db7fc42 100644 --- a/user/idtools_unix_test.go +++ b/user/idtools_unix_test.go @@ -1,17 +1,15 @@ //go:build !windows -package idtools +package user import ( + "errors" "fmt" "os" "path/filepath" "testing" "golang.org/x/sys/unix" - "gotest.tools/v3/assert" - is "gotest.tools/v3/assert/cmp" - "gotest.tools/v3/skip" ) type node struct { @@ -20,12 +18,8 @@ type node struct { } func TestMkdirAllAndChown(t *testing.T) { - RequiresRoot(t) - dirName, err := os.MkdirTemp("", "mkdirall") - if err != nil { - t.Fatalf("Couldn't create temp dir: %v", err) - } - defer os.RemoveAll(dirName) + requiresRoot(t) + dirName := t.TempDir() testTree := map[string]node{ "usr": {0, 0}, @@ -40,7 +34,7 @@ func TestMkdirAllAndChown(t *testing.T) { } // test adding a directory to a pre-existing dir; only the new dir is owned by the uid/gid - if err := MkdirAllAndChown(filepath.Join(dirName, "usr", "share"), 0o755, Identity{UID: 99, GID: 99}); err != nil { + if err := MkdirAllAndChown(filepath.Join(dirName, "usr", "share"), 0o755, 99, 99); err != nil { t.Fatal(err) } testTree["usr/share"] = node{99, 99} @@ -48,12 +42,10 @@ func TestMkdirAllAndChown(t *testing.T) { if err != nil { t.Fatal(err) } - if err := compareTrees(testTree, verifyTree); err != nil { - t.Fatal(err) - } + compareTrees(t, testTree, verifyTree) // test 2-deep new directories--both should be owned by the uid/gid pair - if err := MkdirAllAndChown(filepath.Join(dirName, "lib", "some", "other"), 0o755, Identity{UID: 101, GID: 101}); err != nil { + if err := MkdirAllAndChown(filepath.Join(dirName, "lib", "some", "other"), 0o755, 101, 101); err != nil { t.Fatal(err) } testTree["lib/some"] = node{101, 101} @@ -62,12 +54,10 @@ func TestMkdirAllAndChown(t *testing.T) { if err != nil { t.Fatal(err) } - if err := compareTrees(testTree, verifyTree); err != nil { - t.Fatal(err) - } + compareTrees(t, testTree, verifyTree) // test a directory that already exists; should be chowned, but nothing else - if err := MkdirAllAndChown(filepath.Join(dirName, "usr"), 0o755, Identity{UID: 102, GID: 102}); err != nil { + if err := MkdirAllAndChown(filepath.Join(dirName, "usr"), 0o755, 102, 102); err != nil { t.Fatal(err) } testTree["usr"] = node{102, 102} @@ -75,16 +65,12 @@ func TestMkdirAllAndChown(t *testing.T) { if err != nil { t.Fatal(err) } - if err := compareTrees(testTree, verifyTree); err != nil { - t.Fatal(err) - } + compareTrees(t, testTree, verifyTree) } func TestMkdirAllAndChownNew(t *testing.T) { - RequiresRoot(t) - dirName, err := os.MkdirTemp("", "mkdirnew") - assert.NilError(t, err) - defer os.RemoveAll(dirName) + requiresRoot(t) + dirName := t.TempDir() testTree := map[string]node{ "usr": {0, 0}, @@ -93,36 +79,45 @@ func TestMkdirAllAndChownNew(t *testing.T) { "lib/x86_64": {45, 45}, "lib/x86_64/share": {1, 1}, } - assert.NilError(t, buildTree(dirName, testTree)) + if err := buildTree(dirName, testTree); err != nil { + t.Fatal(err) + } // test adding a directory to a pre-existing dir; only the new dir is owned by the uid/gid - err = MkdirAllAndChownNew(filepath.Join(dirName, "usr", "share"), 0o755, Identity{UID: 99, GID: 99}) - assert.NilError(t, err) + if err := MkdirAllAndChown(filepath.Join(dirName, "usr", "share"), 0o755, 99, 99, WithOnlyNew); err != nil { + t.Fatal(err) + } testTree["usr/share"] = node{99, 99} verifyTree, err := readTree(dirName, "") - assert.NilError(t, err) - assert.NilError(t, compareTrees(testTree, verifyTree)) + if err != nil { + t.Fatal(err) + } + compareTrees(t, testTree, verifyTree) // test 2-deep new directories--both should be owned by the uid/gid pair - err = MkdirAllAndChownNew(filepath.Join(dirName, "lib", "some", "other"), 0o755, Identity{UID: 101, GID: 101}) - assert.NilError(t, err) + if err = MkdirAllAndChown(filepath.Join(dirName, "lib", "some", "other"), 0o755, 101, 101, WithOnlyNew); err != nil { + t.Fatal(err) + } testTree["lib/some"] = node{101, 101} testTree["lib/some/other"] = node{101, 101} - verifyTree, err = readTree(dirName, "") - assert.NilError(t, err) - assert.NilError(t, compareTrees(testTree, verifyTree)) + if verifyTree, err = readTree(dirName, ""); err != nil { + t.Fatal(err) + } + compareTrees(t, testTree, verifyTree) // test a directory that already exists; should NOT be chowned - err = MkdirAllAndChownNew(filepath.Join(dirName, "usr"), 0o755, Identity{UID: 102, GID: 102}) - assert.NilError(t, err) - verifyTree, err = readTree(dirName, "") - assert.NilError(t, err) - assert.NilError(t, compareTrees(testTree, verifyTree)) + if err = MkdirAllAndChown(filepath.Join(dirName, "usr"), 0o755, 102, 102, WithOnlyNew); err != nil { + t.Fatal(err) + } + if verifyTree, err = readTree(dirName, ""); err != nil { + t.Fatal(err) + } + compareTrees(t, testTree, verifyTree) } func TestMkdirAllAndChownNewRelative(t *testing.T) { - RequiresRoot(t) + requiresRoot(t) tests := []struct { in string @@ -181,18 +176,26 @@ func TestMkdirAllAndChownNewRelative(t *testing.T) { t.Run(tc.in, func(t *testing.T) { for _, p := range tc.out { _, err := os.Stat(p) - assert.ErrorIs(t, err, os.ErrNotExist) + if !errors.Is(err, os.ErrNotExist) { + t.Fatalf("expected file not exists for %v, got %v", p, err) + } } - err := MkdirAllAndChownNew(tc.in, 0o755, Identity{UID: expectedUIDGID, GID: expectedUIDGID}) - assert.Check(t, err) + if err := MkdirAllAndChown(tc.in, 0o755, expectedUIDGID, expectedUIDGID, WithOnlyNew); err != nil { + t.Fatal(err) + } for _, p := range tc.out { s := &unix.Stat_t{} - err = unix.Stat(p, s) - if assert.Check(t, err) { - assert.Check(t, is.Equal(uint64(s.Uid), uint64(expectedUIDGID))) - assert.Check(t, is.Equal(uint64(s.Gid), uint64(expectedUIDGID))) + if err := unix.Stat(p, s); err != nil { + t.Errorf("stat %v: %v", p, err) + continue + } + if s.Uid != expectedUIDGID { + t.Errorf("expected UID: %d, got: %d", expectedUIDGID, s.Uid) + } + if s.Gid != expectedUIDGID { + t.Errorf("expected GID: %d, got: %d", expectedUIDGID, s.Gid) } } }) @@ -204,21 +207,22 @@ func TestMkdirAllAndChownNewRelative(t *testing.T) { func setWorkingDirectory(t *testing.T, dir string) { t.Helper() cwd, err := os.Getwd() - assert.NilError(t, err) + if err != nil { + t.Fatal(err) + } t.Cleanup(func() { - assert.NilError(t, os.Chdir(cwd)) + if err := os.Chdir(cwd); err != nil { + t.Error(err) + } }) - err = os.Chdir(dir) - assert.NilError(t, err) + if err = os.Chdir(dir); err != nil { + t.Fatal(err) + } } func TestMkdirAndChown(t *testing.T) { - RequiresRoot(t) - dirName, err := os.MkdirTemp("", "mkdir") - if err != nil { - t.Fatalf("Couldn't create temp dir: %v", err) - } - defer os.RemoveAll(dirName) + requiresRoot(t) + dirName := t.TempDir() testTree := map[string]node{ "usr": {0, 0}, @@ -228,7 +232,7 @@ func TestMkdirAndChown(t *testing.T) { } // test a directory that already exists; should just chown to the requested uid/gid - if err := MkdirAndChown(filepath.Join(dirName, "usr"), 0o755, Identity{UID: 99, GID: 99}); err != nil { + if err := MkdirAndChown(filepath.Join(dirName, "usr"), 0o755, 99, 99); err != nil { t.Fatal(err) } testTree["usr"] = node{99, 99} @@ -236,17 +240,15 @@ func TestMkdirAndChown(t *testing.T) { if err != nil { t.Fatal(err) } - if err := compareTrees(testTree, verifyTree); err != nil { - t.Fatal(err) - } + compareTrees(t, testTree, verifyTree) // create a subdir under a dir which doesn't exist--should fail - if err := MkdirAndChown(filepath.Join(dirName, "usr", "bin", "subdir"), 0o755, Identity{UID: 102, GID: 102}); err == nil { - t.Fatalf("Trying to create a directory with Mkdir where the parent doesn't exist should have failed") + if err := MkdirAndChown(filepath.Join(dirName, "usr", "bin", "subdir"), 0o755, 102, 102); err == nil { + t.Fatal("Trying to create a directory with Mkdir where the parent doesn't exist should have failed") } // create a subdir under an existing dir; should only change the ownership of the new subdir - if err := MkdirAndChown(filepath.Join(dirName, "usr", "bin"), 0o755, Identity{UID: 102, GID: 102}); err != nil { + if err := MkdirAndChown(filepath.Join(dirName, "usr", "bin"), 0o755, 102, 102); err != nil { t.Fatal(err) } testTree["usr/bin"] = node{102, 102} @@ -254,19 +256,17 @@ func TestMkdirAndChown(t *testing.T) { if err != nil { t.Fatal(err) } - if err := compareTrees(testTree, verifyTree); err != nil { - t.Fatal(err) - } + compareTrees(t, testTree, verifyTree) } func buildTree(base string, tree map[string]node) error { for path, node := range tree { fullPath := filepath.Join(base, path) if err := os.MkdirAll(fullPath, 0o755); err != nil { - return fmt.Errorf("couldn't create path: %s; error: %v", fullPath, err) + return err } if err := os.Chown(fullPath, node.uid, node.gid); err != nil { - return fmt.Errorf("couldn't chown path: %s; error: %v", fullPath, err) + return err } } return nil @@ -277,13 +277,13 @@ func readTree(base, root string) (map[string]node, error) { dirInfos, err := os.ReadDir(base) if err != nil { - return nil, fmt.Errorf("couldn't read directory entries for %q: %v", base, err) + return nil, err } for _, info := range dirInfos { s := &unix.Stat_t{} if err := unix.Stat(filepath.Join(base, info.Name()), s); err != nil { - return nil, fmt.Errorf("can't stat file %q: %v", filepath.Join(base, info.Name()), err) + return nil, fmt.Errorf("can't stat file %q: %w", filepath.Join(base, info.Name()), err) } tree[filepath.Join(root, info.Name())] = node{int(s.Uid), int(s.Gid)} if info.IsDir() { @@ -300,84 +300,99 @@ func readTree(base, root string) (map[string]node, error) { return tree, nil } -func compareTrees(left, right map[string]node) error { +func compareTrees(t testing.TB, left, right map[string]node) { + t.Helper() if len(left) != len(right) { - return fmt.Errorf("trees aren't the same size") + t.Fatal("trees aren't the same size") } for path, nodeLeft := range left { if nodeRight, ok := right[path]; ok { if nodeRight.uid != nodeLeft.uid || nodeRight.gid != nodeLeft.gid { // mismatch - return fmt.Errorf("mismatched ownership for %q: expected: %d:%d, got: %d:%d", path, + t.Fatalf("mismatched ownership for %q: expected: %d:%d, got: %d:%d", path, nodeLeft.uid, nodeLeft.gid, nodeRight.uid, nodeRight.gid) } continue } - return fmt.Errorf("right tree didn't contain path %q", path) + t.Fatalf("right tree didn't contain path %q", path) } - return nil } func TestGetRootUIDGID(t *testing.T) { uidMap := []IDMap{ { - ContainerID: 0, - HostID: os.Getuid(), - Size: 1, + ID: 0, + ParentID: int64(os.Getuid()), + Count: 1, }, } gidMap := []IDMap{ { - ContainerID: 0, - HostID: os.Getgid(), - Size: 1, + ID: 0, + ParentID: int64(os.Getgid()), + Count: 1, }, } - uid, gid, err := GetRootUIDGID(uidMap, gidMap) - assert.Check(t, err) - assert.Check(t, is.Equal(os.Geteuid(), uid)) - assert.Check(t, is.Equal(os.Getegid(), gid)) + uid, gid, err := getRootUIDGID(uidMap, gidMap) + if err != nil { + t.Fatal(err) + } + if uid != os.Getuid() { + t.Fatalf("expected %d, got %d", os.Getuid(), uid) + } + if gid != os.Getgid() { + t.Fatalf("expected %d, got %d", os.Getgid(), gid) + } uidMapError := []IDMap{ { - ContainerID: 1, - HostID: os.Getuid(), - Size: 1, + ID: 1, + ParentID: int64(os.Getuid()), + Count: 1, }, } - _, _, err = GetRootUIDGID(uidMapError, gidMap) - assert.Check(t, is.Error(err, "Container ID 0 cannot be mapped to a host ID")) + _, _, err = getRootUIDGID(uidMapError, gidMap) + if expected := "container ID 0 cannot be mapped to a host ID"; err.Error() != expected { + t.Fatalf("expected error: %v, got: %v", expected, err) + } } func TestToContainer(t *testing.T) { uidMap := []IDMap{ { - ContainerID: 2, - HostID: 2, - Size: 1, + ID: 2, + ParentID: 2, + Count: 1, }, } containerID, err := toContainer(2, uidMap) - assert.Check(t, err) - assert.Check(t, is.Equal(uidMap[0].ContainerID, containerID)) + if err != nil { + t.Fatal(err) + } + if uidMap[0].ID != int64(containerID) { + t.Fatalf("expected %d, got %d", uidMap[0].ID, containerID) + } } // TestMkdirIsNotDir checks that mkdirAs() function (used by MkdirAll...) // returns a correct error in case a directory which it is about to create // already exists but is a file (rather than a directory). func TestMkdirIsNotDir(t *testing.T) { - file, err := os.CreateTemp("", t.Name()) + file, err := os.CreateTemp(t.TempDir(), t.Name()) if err != nil { t.Fatalf("Couldn't create temp dir: %v", err) } - defer os.Remove(file.Name()) - err = mkdirAs(file.Name(), 0o755, Identity{UID: 0, GID: 0}, false, false) - assert.Check(t, is.Error(err, "mkdir "+file.Name()+": not a directory")) + err = mkdirAs(file.Name(), 0o755, 0, 0, false, false) + if expected := "mkdir " + file.Name() + ": not a directory"; err.Error() != expected { + t.Fatalf("expected error: %v, got: %v", expected, err) + } } -func RequiresRoot(t *testing.T) { - skip.If(t, os.Getuid() != 0, "skipping test that requires root") +func requiresRoot(t *testing.T) { + if os.Getuid() != 0 { + t.Skip("skipping test that requires root") + } } diff --git a/user/idtools_windows.go b/user/idtools_windows.go index 43702f7..9de730c 100644 --- a/user/idtools_windows.go +++ b/user/idtools_windows.go @@ -1,26 +1,13 @@ -package idtools +package user import ( "os" ) -const ( - // Deprecated: copy value locally - SeTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege" -) - -const ( - // Deprecated: copy value locally - ContainerAdministratorSidString = "S-1-5-93-2-1" - - // Deprecated: copy value locally - ContainerUserSidString = "S-1-5-93-2-2" -) - // This is currently a wrapper around [os.MkdirAll] since currently // permissions aren't set through this path, the identity isn't utilized. // Ownership is handled elsewhere, but in the future could be support here // too. -func mkdirAs(path string, _ os.FileMode, _ Identity, _, _ bool) error { +func mkdirAs(path string, _ os.FileMode, _, _ int, _, _ bool) error { return os.MkdirAll(path, 0) }