github.com/swiftstack/proxyfs@v0.0.0-20201223034610-5434d919416e/fs/resolve_path_test.go (about) 1 package fs 2 3 import ( 4 "testing" 5 6 "github.com/swiftstack/ProxyFS/inode" 7 ) 8 9 func TestResolvePath(t *testing.T) { 10 var ( 11 canonicalizedPathSplit []string 12 dirA inode.InodeNumber 13 dirEntryBasename string 14 dirEntryInodeNumber inode.InodeNumber 15 dirEntryInodeType inode.InodeType 16 dirInodeNumber inode.InodeNumber 17 err error 18 fileA inode.InodeNumber 19 fileB inode.InodeNumber 20 heldLocks *heldLocksStruct 21 foundInMap bool 22 reCanonicalizedPathSplit []string 23 retryRequired bool 24 symlinkA inode.InodeNumber 25 testContainer inode.InodeNumber 26 ) 27 28 // Validate canonicalizePath() & reCanonicalizePathForSymlink() 29 30 canonicalizedPathSplit, err = canonicalizePath("//.//a/b/c/../d") 31 if nil != err { 32 t.Fatalf("canonicalizePath(\"//.//a/b/c/../d\") failed: %v", err) 33 } 34 if (3 != len(canonicalizedPathSplit)) || 35 ("a" != canonicalizedPathSplit[0]) || 36 ("b" != canonicalizedPathSplit[1]) || 37 ("d" != canonicalizedPathSplit[2]) { 38 t.Fatalf("canonicalizedPathSplit(\"//.//a/b/c/../d\") returned unexpected results: %v", canonicalizedPathSplit) 39 } 40 41 reCanonicalizedPathSplit, err = reCanonicalizePathForSymlink(canonicalizedPathSplit, 1, "e/f") 42 if nil != err { 43 t.Fatalf("reCanonicalizePathForSymlink(canonicalizedPathSplit, 1, \"e/f\") failed: %v", err) 44 } 45 if (4 != len(reCanonicalizedPathSplit)) || 46 ("a" != reCanonicalizedPathSplit[0]) || 47 ("e" != reCanonicalizedPathSplit[1]) || 48 ("f" != reCanonicalizedPathSplit[2]) || 49 ("d" != reCanonicalizedPathSplit[3]) { 50 t.Fatalf("reCanonicalizePathForSymlink(canonicalizedPathSplit, 1, \"e/f\") returned unexpected results: %v", reCanonicalizedPathSplit) 51 } 52 53 // Build out directory hierarchy underneath "/TestResolvePathContainer/": 54 // 55 // / 56 // TestResolvePathContainer/ 57 // FileA 58 // SimlinkA --> FileA 59 // SimlinkB --> ../DirA 60 // 61 // During testing, a PUT of /TestResolvePathContainer/SimlinkB/FileB 62 // will implicitly create /TestResolvePathContainer/DirA/FileB: 63 // 64 // / 65 // TestResolvePathContainer/ 66 // FileA 67 // SimlinkA --> FileA 68 // SimlinkB --> ../DirA 69 // DirA\ 70 // FileB 71 72 testSetup(t, false) 73 74 testContainer, err = testVolumeStruct.Mkdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inode.RootDirInodeNumber, "TestResolvePathContainer", inode.PosixModePerm) 75 if nil != err { 76 t.Fatalf("Mkdir(,,,,\"TestResolvePathContainer\",) failed: %v", err) 77 } 78 fileA, err = testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, testContainer, "FileA", inode.PosixModePerm) 79 if nil != err { 80 t.Fatalf("Create(,,,,\"FileA\",) failed: %v", err) 81 } 82 symlinkA, err = testVolumeStruct.Symlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, testContainer, "SymlinkA", "FileA") 83 if nil != err { 84 t.Fatalf("Symlink(,,,,\"SymlinkA\",) failed: %v", err) 85 } 86 87 // GET or HEAD on "/TestResolvePathContainer/SymlinkA" should find FileA 88 89 heldLocks = newHeldLocks() 90 91 dirInodeNumber, dirEntryInodeNumber, dirEntryBasename, dirEntryInodeType, retryRequired, err = 92 testVolumeStruct.resolvePath( 93 inode.RootDirInodeNumber, 94 "/TestResolvePathContainer/SymlinkA", 95 heldLocks, 96 resolvePathFollowDirEntrySymlinks| 97 resolvePathFollowDirSymlinks) 98 99 if nil != err { 100 t.Fatalf("resolvePath() for GET or HEAD failed: %v", err) 101 } 102 if (testContainer != dirInodeNumber) || 103 (fileA != dirEntryInodeNumber) || 104 ("FileA" != dirEntryBasename) || 105 (inode.FileType != dirEntryInodeType) || 106 retryRequired || 107 (0 != len(heldLocks.exclusive)) || 108 (1 != len(heldLocks.shared)) { 109 t.Fatalf("resolvePath() for GET or HEAD returned unexpected results") 110 } 111 112 _, foundInMap = heldLocks.shared[fileA] 113 if !foundInMap { 114 t.Fatalf("resolvePath() for GET or HEAD returned heldLocks.shared missing fileA") 115 } 116 117 heldLocks.free() 118 119 // POST or COALESCE (destPath) on "/TestResolvePathContainer/SymlinkA" should find FileA 120 121 heldLocks = newHeldLocks() 122 123 dirInodeNumber, dirEntryInodeNumber, dirEntryBasename, dirEntryInodeType, retryRequired, err = 124 testVolumeStruct.resolvePath( 125 inode.RootDirInodeNumber, 126 "/TestResolvePathContainer/SymlinkA", 127 heldLocks, 128 resolvePathFollowDirEntrySymlinks| 129 resolvePathFollowDirSymlinks| 130 resolvePathCreateMissingPathElements| 131 resolvePathRequireExclusiveLockOnDirEntryInode) 132 133 if nil != err { 134 t.Fatalf("resolvePath() for POST or COALESCE (destPath) failed: %v", err) 135 } 136 if (testContainer != dirInodeNumber) || 137 (fileA != dirEntryInodeNumber) || 138 ("FileA" != dirEntryBasename) || 139 (inode.FileType != dirEntryInodeType) || 140 retryRequired || 141 (1 != len(heldLocks.exclusive)) || 142 (0 != len(heldLocks.shared)) { 143 t.Fatalf("resolvePath() for POST or COALESCE (destPath) returned unexpected results") 144 } 145 146 _, foundInMap = heldLocks.exclusive[fileA] 147 if !foundInMap { 148 t.Fatalf("resolvePath() for POST or COALESCE (destPath) returned heldLocks.exclusive missing fileA") 149 } 150 151 heldLocks.free() 152 153 // DELETE on "/TestResolvePathContainer/SymlinkA" should find SymlinkA 154 155 heldLocks = newHeldLocks() 156 157 dirInodeNumber, dirEntryInodeNumber, dirEntryBasename, dirEntryInodeType, retryRequired, err = 158 testVolumeStruct.resolvePath( 159 inode.RootDirInodeNumber, 160 "/TestResolvePathContainer/SymlinkA", 161 heldLocks, 162 resolvePathFollowDirSymlinks| 163 resolvePathRequireExclusiveLockOnDirEntryInode| 164 resolvePathRequireExclusiveLockOnDirInode) 165 166 if nil != err { 167 t.Fatalf("resolvePath() for DELETE failed: %v", err) 168 } 169 if (testContainer != dirInodeNumber) || 170 (symlinkA != dirEntryInodeNumber) || 171 ("SymlinkA" != dirEntryBasename) || 172 (inode.SymlinkType != dirEntryInodeType) || 173 retryRequired || 174 (2 != len(heldLocks.exclusive)) || 175 (0 != len(heldLocks.shared)) { 176 t.Fatalf("resolvePath() for DELETE returned unexpected results") 177 } 178 179 _, foundInMap = heldLocks.exclusive[symlinkA] 180 if !foundInMap { 181 t.Fatalf("resolvePath() for DELETE returned heldLocks.shared missing symlinkA") 182 } 183 _, foundInMap = heldLocks.exclusive[testContainer] 184 if !foundInMap { 185 t.Fatalf("resolvePath() for DELETE returned heldLocks.shared missing symlinkA") 186 } 187 188 heldLocks.free() 189 190 // PUT on /TestResolvePathContainer/SimlinkB/FileB should create /TestResolvePathContainer/DirA/FileB 191 192 heldLocks = newHeldLocks() 193 194 dirInodeNumber, dirEntryInodeNumber, dirEntryBasename, dirEntryInodeType, retryRequired, err = 195 testVolumeStruct.resolvePath( 196 inode.RootDirInodeNumber, 197 "/TestResolvePathContainer/DirA/FileB", 198 heldLocks, 199 resolvePathFollowDirEntrySymlinks| 200 resolvePathFollowDirSymlinks| 201 resolvePathCreateMissingPathElements| 202 resolvePathRequireExclusiveLockOnDirEntryInode) 203 204 if nil != err { 205 t.Fatalf("resolvePath() for PUT (thru symlink & missing dir) failed: %v", err) 206 } 207 if ("FileB" != dirEntryBasename) || 208 (inode.FileType != dirEntryInodeType) || 209 retryRequired || 210 (3 != len(heldLocks.exclusive)) || 211 (0 != len(heldLocks.shared)) { 212 t.Fatalf("resolvePath() for PUT (thru symlink & missing dir) returned unexpected results") 213 } 214 215 dirA = dirInodeNumber 216 fileB = dirEntryInodeNumber 217 218 _, foundInMap = heldLocks.exclusive[fileB] 219 if !foundInMap { 220 t.Fatalf("resolvePath() for PUT (thru symlink & missing dir) returned heldLocks.exclusive missing fileB") 221 } 222 _, foundInMap = heldLocks.exclusive[dirA] 223 if !foundInMap { 224 t.Fatalf("resolvePath() for PUT (thru symlink & missing dir) returned heldLocks.exclusive missing dirA") 225 } 226 _, foundInMap = heldLocks.exclusive[testContainer] 227 if !foundInMap { 228 t.Fatalf("resolvePath() for PUT (thru symlink & missing dir) returned heldLocks.exclusive missing testContainer") 229 } 230 231 heldLocks.free() 232 233 dirEntryInodeNumber, err = testVolumeStruct.Lookup(inode.InodeRootUserID, inode.InodeGroupID(0), nil, testContainer, "DirA") 234 if nil != err { 235 t.Fatalf("Lookup(,,,,\"DirA\") failed: %v", err) 236 } 237 if dirEntryInodeNumber != dirA { 238 t.Fatalf("Lookup(,,,,\"DirA\") returned 0x%016X... expected 0x%016X", dirEntryInodeNumber, dirA) 239 } 240 241 dirEntryInodeNumber, err = testVolumeStruct.Lookup(inode.InodeRootUserID, inode.InodeGroupID(0), nil, dirA, "FileB") 242 if nil != err { 243 t.Fatalf("Lookup(,,,,\"FileB\") failed: %v", err) 244 } 245 if dirEntryInodeNumber != fileB { 246 t.Fatalf("Lookup(,,,,\"FileB\") returned 0x%016X... expected 0x%016X", dirEntryInodeNumber, fileB) 247 } 248 249 // Destroy directory hierachy underneath "/TestResolvePathContainer/" 250 251 err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, dirA, "FileB") 252 if nil != err { 253 t.Fatalf("Unlink(,,,,\"FileB\") failed: %v", err) 254 } 255 err = testVolumeStruct.Rmdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, testContainer, "DirA") 256 if nil != err { 257 t.Fatalf("Rmdir(,,,,\"DirA\") failed: %v", err) 258 } 259 err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, testContainer, "SymlinkA") 260 if nil != err { 261 t.Fatalf("Unlink(,,,,\"SymlinkA\") failed: %v", err) 262 } 263 err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, testContainer, "FileA") 264 if nil != err { 265 t.Fatalf("Unlink(,,,,\"FileA\") failed: %v", err) 266 } 267 err = testVolumeStruct.Rmdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inode.RootDirInodeNumber, "TestResolvePathContainer") 268 if nil != err { 269 t.Fatalf("Rmdir(,,,,\"TestResolvePathContainer\") failed: %v", err) 270 } 271 272 testTeardown(t) 273 } 274 275 type testCanonicalizePathItemStruct struct { 276 path string 277 shouldSucceed bool 278 canonicalizedPathSplit []string 279 dirInodeIndex int 280 } 281 282 func TestCanonicalizePath(t *testing.T) { 283 var ( 284 canonicalizedPathSplit []string 285 containerInodeNumber inode.InodeNumber 286 dirInodeIndex int 287 directoryInodeNumber inode.InodeNumber 288 err error 289 pathSplitIndex int 290 testCanonicalizePathItem *testCanonicalizePathItemStruct 291 testCanonicalizePathList []*testCanonicalizePathItemStruct 292 ) 293 294 testSetup(t, false) 295 296 _, err = testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inode.RootDirInodeNumber, "RootFileName", inode.PosixModePerm) 297 if nil != err { 298 t.Fatal(err) 299 } 300 containerInodeNumber, err = testVolumeStruct.Mkdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inode.RootDirInodeNumber, "ContainerName", inode.PosixModePerm) 301 if nil != err { 302 t.Fatal(err) 303 } 304 _, err = testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, containerInodeNumber, "ContainerFileName", inode.PosixModePerm) 305 if nil != err { 306 t.Fatal(err) 307 } 308 directoryInodeNumber, err = testVolumeStruct.Mkdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, containerInodeNumber, "DirectoryName", inode.PosixModePerm) 309 if nil != err { 310 t.Fatal(err) 311 } 312 _, err = testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, directoryInodeNumber, "DirectoryFileName", inode.PosixModePerm) 313 if nil != err { 314 t.Fatal(err) 315 } 316 317 testCanonicalizePathList = []*testCanonicalizePathItemStruct{ 318 &testCanonicalizePathItemStruct{ 319 path: "", 320 shouldSucceed: false, 321 canonicalizedPathSplit: []string{}, // Not actually returned 322 dirInodeIndex: 0, // Not actually returned 323 }, 324 &testCanonicalizePathItemStruct{ 325 path: "MissingNameInRoot", 326 shouldSucceed: true, 327 canonicalizedPathSplit: []string{"MissingNameInRoot"}, 328 dirInodeIndex: -1, 329 }, 330 &testCanonicalizePathItemStruct{ 331 path: "RootFileName", 332 shouldSucceed: true, 333 canonicalizedPathSplit: []string{"RootFileName"}, 334 dirInodeIndex: -1, 335 }, 336 &testCanonicalizePathItemStruct{ 337 path: "ContainerName", 338 shouldSucceed: true, 339 canonicalizedPathSplit: []string{"ContainerName"}, 340 dirInodeIndex: 0, 341 }, 342 &testCanonicalizePathItemStruct{ 343 path: "ContainerName/MissingNameInContainer", 344 shouldSucceed: true, 345 canonicalizedPathSplit: []string{"ContainerName", "MissingNameInContainer"}, 346 dirInodeIndex: 0, 347 }, 348 &testCanonicalizePathItemStruct{ 349 path: "ContainerName/DirectoryName", 350 shouldSucceed: true, 351 canonicalizedPathSplit: []string{"ContainerName", "DirectoryName"}, 352 dirInodeIndex: 1, 353 }, 354 &testCanonicalizePathItemStruct{ 355 path: "ContainerName/DirectoryName/MissingNameInDirectory", 356 shouldSucceed: true, 357 canonicalizedPathSplit: []string{"ContainerName", "DirectoryName", "MissingNameInDirectory"}, 358 dirInodeIndex: 1, 359 }, 360 &testCanonicalizePathItemStruct{ 361 path: "ContainerName/DirectoryName/DirectoryFileName", 362 shouldSucceed: true, 363 canonicalizedPathSplit: []string{"ContainerName", "DirectoryName", "DirectoryFileName"}, 364 dirInodeIndex: 1, 365 }, 366 &testCanonicalizePathItemStruct{ 367 path: "ContainerName/DirectoryName/MissingNameInDirectory/MissingSubDirectoryName", 368 shouldSucceed: true, 369 canonicalizedPathSplit: []string{"ContainerName", "DirectoryName", "MissingNameInDirectory", "MissingSubDirectoryName"}, 370 dirInodeIndex: 2, 371 }, 372 } 373 374 for _, testCanonicalizePathItem = range testCanonicalizePathList { 375 canonicalizedPathSplit, dirInodeIndex, err = testVolumeStruct.canonicalizePathAndLocateLeafDirInode(testCanonicalizePathItem.path) 376 if testCanonicalizePathItem.shouldSucceed { 377 if nil == err { 378 if len(canonicalizedPathSplit) != len(testCanonicalizePathItem.canonicalizedPathSplit) { 379 t.Fatalf("canonicalizePathAndLocateLeafDirInode(\"%s\") received unexpected canonicalizedPathSplit: %v", testCanonicalizePathItem.path, canonicalizedPathSplit) 380 } 381 for pathSplitIndex = range canonicalizedPathSplit { 382 if canonicalizedPathSplit[pathSplitIndex] != testCanonicalizePathItem.canonicalizedPathSplit[pathSplitIndex] { 383 t.Fatalf("canonicalizePathAndLocateLeafDirInode(\"%s\") received unexpected canonicalizedPathSplit: %v", testCanonicalizePathItem.path, canonicalizedPathSplit) 384 } 385 } 386 if dirInodeIndex != testCanonicalizePathItem.dirInodeIndex { 387 t.Fatalf("canonicalizePathAndLocateLeafDirInode(\"%s\") received unexpected canonicalizedPathSplit: %v", testCanonicalizePathItem.path, dirInodeIndex) 388 } 389 } else { 390 t.Fatalf("canonicalizePathAndLocateLeafDirInode(\"%s\") unexpectadly failed: %v", testCanonicalizePathItem.path, err) 391 } 392 } else { // !testCanonicalizePathItem.shouldSucceed 393 if nil == err { 394 t.Fatalf("canonicalizePathAndLocateLeafDirInode(\"%s\") unexpectadly succeeded", testCanonicalizePathItem.path) 395 } 396 } 397 } 398 399 err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, directoryInodeNumber, "DirectoryFileName") 400 if nil != err { 401 t.Fatal(err) 402 } 403 err = testVolumeStruct.Rmdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, containerInodeNumber, "DirectoryName") 404 if nil != err { 405 t.Fatal(err) 406 } 407 err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, containerInodeNumber, "ContainerFileName") 408 if nil != err { 409 t.Fatal(err) 410 } 411 err = testVolumeStruct.Rmdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inode.RootDirInodeNumber, "ContainerName") 412 if nil != err { 413 t.Fatal(err) 414 } 415 err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inode.RootDirInodeNumber, "RootFileName") 416 if nil != err { 417 t.Fatal(err) 418 } 419 420 testTeardown(t) 421 }