github.com/cdoern/storage@v1.12.13/pkg/idtools/idtools_unix_test.go (about) 1 // +build !windows 2 3 package idtools 4 5 import ( 6 "fmt" 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 "testing" 11 12 "github.com/stretchr/testify/require" 13 "golang.org/x/sys/unix" 14 ) 15 16 type node struct { 17 uid int 18 gid int 19 } 20 21 func TestMkdirAllAs(t *testing.T) { 22 dirName, err := ioutil.TempDir("", "mkdirall") 23 if err != nil { 24 t.Fatalf("Couldn't create temp dir: %v", err) 25 } 26 defer os.RemoveAll(dirName) 27 28 testTree := map[string]node{ 29 "usr": {0, 0}, 30 "usr/bin": {0, 0}, 31 "lib": {33, 33}, 32 "lib/x86_64": {45, 45}, 33 "lib/x86_64/share": {1, 1}, 34 } 35 36 if err := buildTree(dirName, testTree); err != nil { 37 t.Fatal(err) 38 } 39 40 // test adding a directory to a pre-existing dir; only the new dir is owned by the uid/gid 41 if err := MkdirAllAs(filepath.Join(dirName, "usr", "share"), 0755, 99, 99); err != nil { 42 t.Fatal(err) 43 } 44 testTree["usr/share"] = node{99, 99} 45 verifyTree, err := readTree(dirName, "") 46 if err != nil { 47 t.Fatal(err) 48 } 49 if err := compareTrees(testTree, verifyTree); err != nil { 50 t.Fatal(err) 51 } 52 53 // test 2-deep new directories--both should be owned by the uid/gid pair 54 if err := MkdirAllAs(filepath.Join(dirName, "lib", "some", "other"), 0755, 101, 101); err != nil { 55 t.Fatal(err) 56 } 57 testTree["lib/some"] = node{101, 101} 58 testTree["lib/some/other"] = node{101, 101} 59 verifyTree, err = readTree(dirName, "") 60 if err != nil { 61 t.Fatal(err) 62 } 63 if err := compareTrees(testTree, verifyTree); err != nil { 64 t.Fatal(err) 65 } 66 67 // test a directory that already exists; should be chowned, but nothing else 68 if err := MkdirAllAs(filepath.Join(dirName, "usr"), 0755, 102, 102); err != nil { 69 t.Fatal(err) 70 } 71 testTree["usr"] = node{102, 102} 72 verifyTree, err = readTree(dirName, "") 73 if err != nil { 74 t.Fatal(err) 75 } 76 if err := compareTrees(testTree, verifyTree); err != nil { 77 t.Fatal(err) 78 } 79 } 80 81 func TestMkdirAllAndChownNew(t *testing.T) { 82 dirName, err := ioutil.TempDir("", "mkdirnew") 83 require.NoError(t, err) 84 defer os.RemoveAll(dirName) 85 86 testTree := map[string]node{ 87 "usr": {0, 0}, 88 "usr/bin": {0, 0}, 89 "lib": {33, 33}, 90 "lib/x86_64": {45, 45}, 91 "lib/x86_64/share": {1, 1}, 92 } 93 require.NoError(t, buildTree(dirName, testTree)) 94 95 // test adding a directory to a pre-existing dir; only the new dir is owned by the uid/gid 96 err = MkdirAllAndChownNew(filepath.Join(dirName, "usr", "share"), 0755, IDPair{99, 99}) 97 require.NoError(t, err) 98 99 testTree["usr/share"] = node{99, 99} 100 verifyTree, err := readTree(dirName, "") 101 require.NoError(t, err) 102 require.NoError(t, compareTrees(testTree, verifyTree)) 103 104 // test 2-deep new directories--both should be owned by the uid/gid pair 105 err = MkdirAllAndChownNew(filepath.Join(dirName, "lib", "some", "other"), 0755, IDPair{101, 101}) 106 require.NoError(t, err) 107 testTree["lib/some"] = node{101, 101} 108 testTree["lib/some/other"] = node{101, 101} 109 verifyTree, err = readTree(dirName, "") 110 require.NoError(t, err) 111 require.NoError(t, compareTrees(testTree, verifyTree)) 112 113 // test a directory that already exists; should NOT be chowned 114 err = MkdirAllAndChownNew(filepath.Join(dirName, "usr"), 0755, IDPair{102, 102}) 115 require.NoError(t, err) 116 verifyTree, err = readTree(dirName, "") 117 require.NoError(t, err) 118 require.NoError(t, compareTrees(testTree, verifyTree)) 119 } 120 121 func TestMkdirAs(t *testing.T) { 122 123 dirName, err := ioutil.TempDir("", "mkdir") 124 if err != nil { 125 t.Fatalf("Couldn't create temp dir: %v", err) 126 } 127 defer os.RemoveAll(dirName) 128 129 testTree := map[string]node{ 130 "usr": {0, 0}, 131 } 132 if err := buildTree(dirName, testTree); err != nil { 133 t.Fatal(err) 134 } 135 136 // test a directory that already exists; should just chown to the requested uid/gid 137 if err := MkdirAs(filepath.Join(dirName, "usr"), 0755, 99, 99); err != nil { 138 t.Fatal(err) 139 } 140 testTree["usr"] = node{99, 99} 141 verifyTree, err := readTree(dirName, "") 142 if err != nil { 143 t.Fatal(err) 144 } 145 if err := compareTrees(testTree, verifyTree); err != nil { 146 t.Fatal(err) 147 } 148 149 // create a subdir under a dir which doesn't exist--should fail 150 if err := MkdirAs(filepath.Join(dirName, "usr", "bin", "subdir"), 0755, 102, 102); err == nil { 151 t.Fatalf("Trying to create a directory with Mkdir where the parent doesn't exist should have failed") 152 } 153 154 // create a subdir under an existing dir; should only change the ownership of the new subdir 155 if err := MkdirAs(filepath.Join(dirName, "usr", "bin"), 0755, 102, 102); err != nil { 156 t.Fatal(err) 157 } 158 testTree["usr/bin"] = node{102, 102} 159 verifyTree, err = readTree(dirName, "") 160 if err != nil { 161 t.Fatal(err) 162 } 163 if err := compareTrees(testTree, verifyTree); err != nil { 164 t.Fatal(err) 165 } 166 } 167 168 func buildTree(base string, tree map[string]node) error { 169 for path, node := range tree { 170 fullPath := filepath.Join(base, path) 171 if err := os.MkdirAll(fullPath, 0755); err != nil { 172 return fmt.Errorf("Couldn't create path: %s; error: %v", fullPath, err) 173 } 174 if err := os.Chown(fullPath, node.uid, node.gid); err != nil { 175 return fmt.Errorf("Couldn't chown path: %s; error: %v", fullPath, err) 176 } 177 } 178 return nil 179 } 180 181 func readTree(base, root string) (map[string]node, error) { 182 tree := make(map[string]node) 183 184 dirInfos, err := ioutil.ReadDir(base) 185 if err != nil { 186 return nil, fmt.Errorf("Couldn't read directory entries for %q: %v", base, err) 187 } 188 189 for _, info := range dirInfos { 190 s := &unix.Stat_t{} 191 if err := unix.Stat(filepath.Join(base, info.Name()), s); err != nil { 192 return nil, fmt.Errorf("Can't stat file %q: %v", filepath.Join(base, info.Name()), err) 193 } 194 tree[filepath.Join(root, info.Name())] = node{int(s.Uid), int(s.Gid)} 195 if info.IsDir() { 196 // read the subdirectory 197 subtree, err := readTree(filepath.Join(base, info.Name()), filepath.Join(root, info.Name())) 198 if err != nil { 199 return nil, err 200 } 201 for path, nodeinfo := range subtree { 202 tree[path] = nodeinfo 203 } 204 } 205 } 206 return tree, nil 207 } 208 209 func compareTrees(left, right map[string]node) error { 210 if len(left) != len(right) { 211 return fmt.Errorf("Trees aren't the same size") 212 } 213 for path, nodeLeft := range left { 214 if nodeRight, ok := right[path]; ok { 215 if nodeRight.uid != nodeLeft.uid || nodeRight.gid != nodeLeft.gid { 216 // mismatch 217 return fmt.Errorf("mismatched ownership for %q: expected: %d:%d, got: %d:%d", path, 218 nodeLeft.uid, nodeLeft.gid, nodeRight.uid, nodeRight.gid) 219 } 220 continue 221 } 222 return fmt.Errorf("right tree didn't contain path %q", path) 223 } 224 return nil 225 } 226 227 func TestParseSubidFileWithNewlinesAndComments(t *testing.T) { 228 tmpDir, err := ioutil.TempDir("", "parsesubid") 229 if err != nil { 230 t.Fatal(err) 231 } 232 fnamePath := filepath.Join(tmpDir, "testsubuid") 233 fcontent := `tss:100000:65536 234 # empty default subuid/subgid file 235 236 dockremap:231072:65536` 237 if err := ioutil.WriteFile(fnamePath, []byte(fcontent), 0644); err != nil { 238 t.Fatal(err) 239 } 240 ranges, err := parseSubidFile(fnamePath, "dockremap") 241 if err != nil { 242 t.Fatal(err) 243 } 244 if len(ranges) != 1 { 245 t.Fatalf("wanted 1 element in ranges, got %d instead", len(ranges)) 246 } 247 if ranges[0].Start != 231072 { 248 t.Fatalf("wanted 231072, got %d instead", ranges[0].Start) 249 } 250 if ranges[0].Length != 65536 { 251 t.Fatalf("wanted 65536, got %d instead", ranges[0].Length) 252 } 253 }