github.com/swiftstack/proxyfs@v0.0.0-20201223034610-5434d919416e/fs/api_test.go (about) 1 package fs 2 3 import ( 4 "bytes" 5 "math" 6 "strings" 7 "syscall" 8 "testing" 9 "time" 10 11 "golang.org/x/sys/unix" 12 13 "github.com/swiftstack/ProxyFS/blunder" 14 "github.com/swiftstack/ProxyFS/inode" 15 ) 16 17 // TODO: Enhance this to do a stat() as well and check number of files 18 func expectDirectory(t *testing.T, userID inode.InodeUserID, groupID inode.InodeGroupID, inodeNum inode.InodeNumber, expectedEntries []string) { 19 readdirEntries, numEntries, moreEntries, err := testVolumeStruct.Readdir(userID, groupID, nil, inodeNum, 0, "") 20 if nil != err { 21 t.Fatalf("Readdir() [#1] returned error: %v", err) 22 } 23 if uint64(len(expectedEntries)) != numEntries { 24 t.Fatalf("Readdir() [#1] returned unexpected number of entries (%v) - should have been %v", numEntries, len(expectedEntries)) 25 } 26 if moreEntries { 27 t.Fatalf("Readdir() [#1] returned moreEntries == true... should have been false") 28 } 29 30 entriesFound := make(map[string]bool) 31 for i := uint64(0); i < numEntries; i++ { 32 entriesFound[readdirEntries[i].Basename] = true 33 } 34 35 for i := 0; i < len(expectedEntries); i++ { 36 expected := expectedEntries[i] 37 _, found := entriesFound[expected] 38 if !found { 39 t.Errorf("Expected entry %s not found in readdirEntries", expected) 40 } 41 } 42 } 43 44 func createTestDirectory(t *testing.T, dirname string) (dirInode inode.InodeNumber) { 45 var err error 46 47 // Get root dir inode number 48 rootDirInodeNumber := inode.RootDirInodeNumber 49 50 dirInode, err = testVolumeStruct.Mkdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, rootDirInodeNumber, dirname, inode.PosixModePerm) 51 if nil != err { 52 t.Fatalf("Mkdir() returned error: %v", err) 53 } 54 55 return dirInode 56 } 57 58 // TODO: Ultimately, each of these tests should at least run in their own directory 59 // a la createTestDirectory(), or preferably some stronger effort should be 60 // made to insulate them from each other. 61 func TestCreateAndLookup(t *testing.T) { 62 testSetup(t, false) 63 64 rootDirInodeNumber := inode.RootDirInodeNumber 65 basename := "create_lookup.test" 66 67 createdFileInodeNumber, err := testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, rootDirInodeNumber, basename, inode.PosixModePerm) 68 if err != nil { 69 t.Fatalf("Unexpectedly couldn't create file: %v", err) 70 } 71 72 foundFileInodeNumber, err := testVolumeStruct.Lookup(inode.InodeRootUserID, inode.InodeGroupID(0), nil, rootDirInodeNumber, basename) 73 if err != nil { 74 t.Fatalf("Unexpectedly failed to look up %v", basename) 75 } 76 77 if createdFileInodeNumber != foundFileInodeNumber { 78 t.Fatalf("Expected created inode number %v to equal found inode number %v", createdFileInodeNumber, foundFileInodeNumber) 79 } 80 81 err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, rootDirInodeNumber, basename) 82 if nil != err { 83 t.Fatalf("Unlink() returned error: %v", err) 84 } 85 86 testTeardown(t) 87 } 88 89 func TestGetstat(t *testing.T) { 90 testSetup(t, false) 91 92 rootDirInodeNumber := inode.RootDirInodeNumber 93 basename := "getstat.test" 94 timeBeforeCreation := uint64(time.Now().UnixNano()) 95 96 inodeNumber, err := testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, rootDirInodeNumber, basename, inode.PosixModePerm) 97 if err != nil { 98 t.Fatalf("couldn't create file: %v", err) 99 } 100 101 stat, err := testVolumeStruct.Getstat(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inodeNumber) 102 if err != nil { 103 t.Fatalf("couldn't stat inode %v: %v", inodeNumber, err) 104 } 105 106 if !(math.Abs(float64(int64(timeBeforeCreation)-int64(stat[StatCRTime]))) < 0.1*1000000000) { // nanoseconds 107 t.Errorf("unexpectedly skewed StatCRTime %v is not close to %v", stat[StatCRTime], timeBeforeCreation) 108 } 109 if !(math.Abs(float64(int64(timeBeforeCreation)-int64(stat[StatMTime]))) < 0.1*1000000000) { // nanoseconds 110 t.Errorf("unexpectedly skewed StatMTime %v is not close to %v", stat[StatMTime], timeBeforeCreation) 111 } 112 if stat[StatSize] != 0 { 113 t.Errorf("expected size to be 0") 114 } 115 if stat[StatNLink] != 1 { 116 t.Errorf("expected number of links to be one, got %v", stat[StatNLink]) 117 } 118 119 // TODO: perform a write, check that size has changed accordingly 120 // TODO: make and delete hardlinks, check that link count has changed accordingly 121 122 err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, rootDirInodeNumber, basename) 123 if nil != err { 124 t.Fatalf("Unlink() returned error: %v", err) 125 } 126 127 testTeardown(t) 128 } 129 130 // TestAllAPIPositiveCases() follows the following "positive" test steps: 131 // 132 // Mount A : mount the specified test Volume (must be empty) 133 // Mkdir A/B/ : create a subdirectory within Volume directory 134 // Create #1 A/C : create and open a normal file within Volume directory 135 // Lookup #1 A/C : fetch the inode name of the just created normal file 136 // Write A/C : write something to normal file 137 // Read A/C : read back what was just written to normal file 138 // FetchExtentMapChunk A/C : fetch extentMapChunk for entire file 139 // Getstat #1 A/C : check the current size of the normal file 140 // Resize A/C : truncate the file 141 // Getstat #2 A/C : verify the size of the normal file is now zero 142 // Symlink A/D->A/C : create a symlink to the normal file 143 // Lookup #2 A/D : fetch the inode name of the just created symlink 144 // Readsymlink A/D : read the symlink to ensure it points to the normal file 145 // Lookup #3 A/B/ : fetch the inode name of the subdirectory 146 // Create #2 A/B/E : create a normal file within subdirectory 147 // Readdir #1 A/B/ (prev == "", max_entries == 0) : ensure we get only ".", "..", and "E" 148 // Statfs A : should report A has 4 "files" (normal & symlink) and 1 directory "ideally" 149 // Unlink #1 A/B/E : delete the normal file within the subdirectory 150 // Readdir #2 A/ (prev == "", max_entries == 3) : ensure we get only ".", ".." & "B" 151 // Readdir #3 A/ (prev == "B", max_entries == 3) : ensure we get only "C" & "D" 152 // Unlink #2 A/D : delete the symlink 153 // Unlink #3 A/C : delete the normal file 154 // Unlink #4 A/B : delete the subdirectory 155 // Unmount A : unmount the Volume 156 // 157 // TODO: Rename(), Link() tests 158 159 var tempVolumeName string // TODO: This is currently the local file system full path 160 161 func TestAllAPIPositiveCases(t *testing.T) { 162 var ( 163 err error 164 ) 165 166 testSetup(t, false) 167 168 // Get root dir inode number 169 rootDirInodeNumber := inode.RootDirInodeNumber 170 171 // Mkdir A/B/ : create a subdirectory within Volume directory 172 _, err = testVolumeStruct.Mkdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, rootDirInodeNumber, "TestSubDirectory", inode.PosixModePerm) 173 // newDirInodeNum, err := testVolumeStruct.Mkdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, rootDirInodeNumber, "TestSubDirectory") 174 if nil != err { 175 t.Fatalf("Mkdir() returned error: %v", err) 176 } 177 178 // Create #1 A/C : create and open a normal file within Volume directory 179 basename := "TestNormalFile" 180 createdFileInodeNumber, err := testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, rootDirInodeNumber, basename, inode.PosixModePerm) 181 if err != nil { 182 t.Fatalf("Create() [#1] returned error: %v", err) 183 } 184 185 // Lookup #1 A/C : fetch the inode name of the just created normal file 186 foundFileInodeNumber, err := testVolumeStruct.Lookup(inode.InodeRootUserID, inode.InodeGroupID(0), nil, rootDirInodeNumber, basename) 187 if err != nil { 188 t.Fatalf("Lookup() [#1] returned error: %v", err) 189 } 190 if createdFileInodeNumber != foundFileInodeNumber { 191 t.Fatalf("Expected created inode number %v to equal found inode number %v", createdFileInodeNumber, foundFileInodeNumber) 192 } 193 194 // Write A/C : write something to normal file 195 bufToWrite := []byte{0x41, 0x42, 0x43} 196 write_rspSize, err := testVolumeStruct.Write(inode.InodeRootUserID, inode.InodeGroupID(0), nil, createdFileInodeNumber, 0, bufToWrite, nil) 197 if nil != err { 198 t.Fatalf("Write() returned error: %v", err) 199 } 200 if uint64(len(bufToWrite)) != write_rspSize { 201 t.Fatalf("Write() expected to write %v bytes but actually wrote %v bytes", len(bufToWrite), write_rspSize) 202 } 203 204 // don't forget to flush 205 err = testVolumeStruct.Flush(inode.InodeRootUserID, inode.InodeGroupID(0), nil, createdFileInodeNumber) 206 if err != nil { 207 t.Fatalf("Flush() returned error: %v", err) 208 } 209 210 // Read A/C : read back what was just written to normal file 211 read_buf, err := testVolumeStruct.Read(inode.InodeRootUserID, inode.InodeGroupID(0), nil, createdFileInodeNumber, 0, uint64(len(bufToWrite)), nil) 212 if nil != err { 213 t.Fatalf("Read() returned error: %v", err) 214 } 215 if len(bufToWrite) != len(read_buf) { 216 t.Fatalf("Read() expected to read %v bytes but actually read %v bytes", len(bufToWrite), len(read_buf)) 217 } 218 if 0 != bytes.Compare(bufToWrite, read_buf) { 219 t.Fatalf("Read() returned data different from what was written") 220 } 221 222 extent_map_chunk, err := testVolumeStruct.FetchExtentMapChunk(inode.InodeRootUserID, inode.InodeGroupID(0), nil, createdFileInodeNumber, uint64(0), int64(1), int64(0)) 223 if nil != err { 224 t.Fatalf("FetchExtentMapChunk() returned error: %v", err) 225 } 226 if 0 != extent_map_chunk.FileOffsetRangeStart { 227 t.Fatalf("FetchExtentMapChunk() returned unexpected FileOffsetRangeStart: %v (should be 0)", extent_map_chunk.FileOffsetRangeStart) 228 } 229 if uint64(len(bufToWrite)) != extent_map_chunk.FileOffsetRangeEnd { 230 t.Fatalf("FetchExtentMapChunk() returned unexpected FileOffsetRangeEnd: %v (should be %v)", len(bufToWrite), extent_map_chunk.FileOffsetRangeEnd) 231 } 232 if uint64(len(bufToWrite)) != extent_map_chunk.FileSize { 233 t.Fatalf("FetchExtentMapChunk() returned unexpected FileSize: %v (should be %v)", len(bufToWrite), extent_map_chunk.FileSize) 234 } 235 if 1 != len(extent_map_chunk.ExtentMapEntry) { 236 t.Fatalf("FetchExtentMapChunk() returned unexpected len(ExtentMapEntry slice): %v (should be 1)", len(extent_map_chunk.ExtentMapEntry)) 237 } 238 if uint64(0) != extent_map_chunk.ExtentMapEntry[0].FileOffset { 239 t.Fatalf("FetchExtentMapChunk() returned unexpected ExtentMapEntry[0].FileOffset: %v (should be 0)", extent_map_chunk.ExtentMapEntry[0].FileOffset) 240 } 241 if uint64(len(bufToWrite)) != extent_map_chunk.ExtentMapEntry[0].Length { 242 t.Fatalf("FetchExtentMapChunk() returned unexpected ExtentMapEntry[0].Length: %v (should be %v)", extent_map_chunk.ExtentMapEntry[0].Length, uint64(len(bufToWrite))) 243 } 244 if uint64(0) != extent_map_chunk.ExtentMapEntry[0].LogSegmentOffset { 245 t.Fatalf("FetchExtentMapChunk() returned unexpected ExtentMapEntry[0].LogSegmentOffset: %v (should be 0)", extent_map_chunk.ExtentMapEntry[0].LogSegmentOffset) 246 } 247 248 // Getstat #1 A/C : check the current size of the normal file 249 getstat_1_rspStat, err := testVolumeStruct.Getstat(inode.InodeRootUserID, inode.InodeGroupID(0), nil, foundFileInodeNumber) 250 if nil != err { 251 t.Fatalf("Getstat() returned error: %v", err) 252 } 253 getstat_1_size, getstat_1_size_ok := getstat_1_rspStat[StatSize] 254 if !getstat_1_size_ok { 255 t.Fatalf("Getstat() returned no StatSize") 256 } 257 if uint64(len(bufToWrite)) != getstat_1_size { 258 t.Fatalf("Getstat() returned StatSize == %v instead of the expected %v", getstat_1_size, len(bufToWrite)) 259 } 260 261 // Resize A/C : truncate the file 262 err = testVolumeStruct.Resize(inode.InodeRootUserID, inode.InodeGroupID(0), nil, foundFileInodeNumber, 0) 263 if nil != err { 264 t.Fatalf("Resize() returned error: %v", err) 265 } 266 267 // Getstat #2 A/C : verify the size of the normal file is now zero 268 getstat_2_rspStat, err := testVolumeStruct.Getstat(inode.InodeRootUserID, inode.InodeGroupID(0), nil, foundFileInodeNumber) 269 if nil != err { 270 t.Fatalf("Getstat() [#2] returned error: %v", err) 271 } 272 getstat_2_size, getstat_2_size_ok := getstat_2_rspStat[StatSize] 273 if !getstat_2_size_ok { 274 t.Fatalf("Getstat() [#2] returned no StatSize") 275 } 276 if 0 != getstat_2_size { 277 t.Fatalf("Getstat() [#2] returned StatSize == %v instead of the expected %v", getstat_2_size, 0) 278 } 279 280 // Symlink A/D->A/C : create a symlink to the normal file 281 createdSymlinkInodeNumber, err := testVolumeStruct.Symlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, rootDirInodeNumber, "TestSymlink", "TestNormalFile") 282 if nil != err { 283 t.Fatalf("Symlink() returned error: %v", err) 284 } 285 286 // Lookup #2 A/D : fetch the inode name of the just created symlink 287 lookup_2_inodeHandle, err := testVolumeStruct.Lookup(inode.InodeRootUserID, inode.InodeGroupID(0), nil, rootDirInodeNumber, "TestSymlink") 288 if nil != err { 289 t.Fatalf("Lookup() [#2] returned error: %v", err) 290 } 291 if lookup_2_inodeHandle != createdSymlinkInodeNumber { 292 t.Fatalf("Lookup() [#2] returned unexpected InodeNumber") 293 } 294 295 // Readsymlink A/D : read the symlink to ensure it points to the normal file 296 readsymlink_target, err := testVolumeStruct.Readsymlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lookup_2_inodeHandle) 297 if nil != err { 298 t.Fatalf("Readsymlink() returned error: %v", err) 299 } 300 if 0 != strings.Compare("TestNormalFile", readsymlink_target) { 301 t.Fatalf("Readsymlink() data different from what was written") 302 } 303 304 // Lookup #3 A/B/ : fetch the inode name of the subdirectory 305 lookup_3_inodeHandle, err := testVolumeStruct.Lookup(inode.InodeRootUserID, inode.InodeGroupID(0), nil, rootDirInodeNumber, "TestSubDirectory") 306 if nil != err { 307 t.Fatalf("Lookup() [#3] returned error: %v", err) 308 } 309 310 // Create #2 A/B/E : create a normal file within subdirectory 311 testSubDirectoryFileInode, err := testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lookup_3_inodeHandle, "TestSubDirectoryFile", inode.PosixModePerm) 312 if nil != err { 313 t.Fatalf("Create() [#2] returned error: %v", err) 314 } 315 316 // Readdir and examine contents 317 entriesExpected := []string{".", "..", "TestSubDirectoryFile"} 318 expectDirectory(t, inode.InodeRootUserID, inode.InodeGroupID(0), lookup_3_inodeHandle, entriesExpected) 319 320 // Link A/B/E 321 err = testVolumeStruct.Link(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lookup_3_inodeHandle, "TestSubDirectoryFileHardLink", testSubDirectoryFileInode) 322 if nil != err { 323 t.Fatalf("Link() returned error: %v", err) 324 } 325 326 entriesExpected = []string{".", "..", "TestSubDirectoryFile", "TestSubDirectoryFileHardLink"} 327 expectDirectory(t, inode.InodeRootUserID, inode.InodeGroupID(0), lookup_3_inodeHandle, entriesExpected) 328 329 // Unlink #1 A/B/E : delete the normal file within the subdirectory 330 err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lookup_3_inodeHandle, "TestSubDirectoryFile") 331 if nil != err { 332 t.Fatalf("Unlink() [#1] returned error: %v", err) 333 } 334 335 entriesExpected = []string{".", "..", "TestSubDirectoryFileHardLink"} 336 expectDirectory(t, inode.InodeRootUserID, inode.InodeGroupID(0), lookup_3_inodeHandle, entriesExpected) 337 338 // Unlink #1.5 A/B/E : delete the normal file within the subdirectory 339 err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lookup_3_inodeHandle, "TestSubDirectoryFileHardLink") 340 if nil != err { 341 t.Fatalf("Unlink() [#1.5] returned error: %v", err) 342 } 343 344 entriesExpected = []string{".", "..", "TestSymlink", "TestNormalFile", "TestSubDirectory"} 345 expectDirectory(t, inode.InodeRootUserID, inode.InodeGroupID(0), rootDirInodeNumber, entriesExpected) 346 347 // Unlink #2 A/D : delete the symlink 348 err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, rootDirInodeNumber, "TestSymlink") 349 350 if nil != err { 351 t.Fatalf("Unlink() [#2] returned error: %v", err) 352 } 353 354 // Unlink #3 A/C : delete the normal file 355 err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, rootDirInodeNumber, "TestNormalFile") 356 if nil != err { 357 t.Fatalf("Unlink() [#3] returned error: %v", err) 358 } 359 360 // Rmdir #4 A/B : delete the subdirectory 361 err = testVolumeStruct.Rmdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, rootDirInodeNumber, "TestSubDirectory") 362 if nil != err { 363 t.Fatalf("Unlink() [#4] returned error: %v", err) 364 } 365 366 entriesExpected = []string{".", ".."} 367 expectDirectory(t, inode.InodeRootUserID, inode.InodeGroupID(0), rootDirInodeNumber, entriesExpected) 368 369 testTeardown(t) 370 } 371 372 // TODO: flesh this out with other boundary condition testing for Link 373 func TestBadLinks(t *testing.T) { 374 testSetup(t, false) 375 376 testDirInode := createTestDirectory(t, "BadLinks") 377 378 validFile := "PerfectlyValidFile" 379 validFileInodeNumber, err := testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, testDirInode, validFile, inode.PosixModePerm) 380 if err != nil { 381 t.Fatalf("Create() returned error: %v", err) 382 } 383 384 nameTooLong := strings.Repeat("x", FileNameMax+1) 385 err = testVolumeStruct.Link(inode.InodeRootUserID, inode.InodeGroupID(0), nil, testDirInode, nameTooLong, validFileInodeNumber) 386 if nil != err { 387 if blunder.IsNot(err, blunder.NameTooLongError) { 388 t.Fatalf("Link() returned error %v, expected %v(%d).", blunder.Errno(err), blunder.NameTooLongError, blunder.NameTooLongError.Value()) 389 } 390 } else { 391 t.Fatal("Link() unexpectedly succeeded on too-long filename!") 392 } 393 394 entriesExpected := []string{".", "..", validFile} 395 expectDirectory(t, inode.InodeRootUserID, inode.InodeGroupID(0), testDirInode, entriesExpected) 396 397 testTeardown(t) 398 } 399 400 func TestMkdir(t *testing.T) { 401 testSetup(t, false) 402 403 testDirInode := createTestDirectory(t, "Mkdir") 404 longButLegalFilename := strings.Repeat("x", FileNameMax) 405 nameTooLong := strings.Repeat("x", FileNameMax+1) 406 407 _, err := testVolumeStruct.Mkdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, testDirInode, nameTooLong, inode.PosixModePerm) 408 if nil != err { 409 if blunder.IsNot(err, blunder.NameTooLongError) { 410 t.Fatalf("Mkdir() returned error %v, expected %v(%d).", blunder.Errno(err), blunder.NameTooLongError, blunder.NameTooLongError.Value()) 411 } 412 } else { 413 t.Fatal("Mkdir() unexpectedly succeeded on too-long filename!") 414 } 415 416 _, err = testVolumeStruct.Mkdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, testDirInode, longButLegalFilename, inode.PosixModePerm) 417 if err != nil { 418 t.Fatalf("Mkdir() returned error: %v", err) 419 } 420 421 entriesExpected := []string{".", "..", longButLegalFilename} 422 expectDirectory(t, inode.InodeRootUserID, inode.InodeGroupID(0), testDirInode, entriesExpected) 423 424 longButLegalFullPath := "/Mkdir/" + longButLegalFilename 425 ino, err := testVolumeStruct.LookupPath(inode.InodeRootUserID, inode.InodeGroupID(0), nil, longButLegalFullPath) 426 if err != nil { 427 t.Fatalf("LookupPath() returned error: %v", err) 428 } 429 430 _, err = testVolumeStruct.Getstat(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inode.InodeNumber(ino)) 431 if err != nil { 432 t.Fatalf("GetStat() returned error: %v", err) 433 } 434 435 // trying to make the directory a second time should fail with EEXIST 436 _, err = testVolumeStruct.Mkdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 437 testDirInode, longButLegalFilename, inode.PosixModePerm) 438 if err == nil { 439 t.Fatalf("Mkdir() of existing entry returned success") 440 } 441 if blunder.IsNot(err, blunder.FileExistsError) { 442 t.Fatalf("Mkdir() of existing entry should return FileExistsError, but got %v", err) 443 } 444 445 testTeardown(t) 446 } 447 448 func TestRmdir(t *testing.T) { 449 testSetup(t, false) 450 defer testTeardown(t) 451 452 testDirInode := createTestDirectory(t, "Rmdir") 453 454 _, err := testVolumeStruct.Mkdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 455 testDirInode, "test1", inode.PosixModePerm) 456 if err != nil { 457 t.Fatalf("Mkdir(\"test1\") returned error: %v", err) 458 } 459 460 // the test directory can't be removed until its empty 461 err = testVolumeStruct.Rmdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 462 inode.RootDirInodeNumber, "Rmdir") 463 if err == nil { 464 t.Fatalf("Rmdir() [#0] should have failed") 465 } 466 if blunder.IsNot(err, blunder.NotEmptyError) { 467 t.Fatalf("Rmdir() [#0] should have returned 'NotEmptyError', err: %v", err) 468 } 469 470 // empty the test directory 471 err = testVolumeStruct.Rmdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 472 testDirInode, "test1") 473 if err != nil { 474 t.Fatalf("Rmdir() [#1] returned error: %v", err) 475 } 476 477 err = testVolumeStruct.Rmdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 478 inode.RootDirInodeNumber, "Rmdir") 479 if err != nil { 480 t.Fatalf("Rmdir() [#2] returned error: %v", err) 481 } 482 } 483 484 // TODO: flesh this out with other boundary condition testing for Rename 485 func TestBadRename(t *testing.T) { 486 testSetup(t, false) 487 488 testDirInode := createTestDirectory(t, "BadRename") 489 nameTooLong := strings.Repeat("x", FileNameMax+1) 490 491 validFile := "PerfectlyValidFile" 492 _, err := testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, testDirInode, validFile, inode.PosixModePerm) 493 if nil != err { 494 t.Fatalf("Create() returned error: %v", err) 495 } 496 497 // Try to rename a valid file to a name that is too long 498 err = testVolumeStruct.Rename(inode.InodeRootUserID, inode.InodeGroupID(0), nil, testDirInode, validFile, testDirInode, nameTooLong) 499 if nil != err { 500 if blunder.IsNot(err, blunder.NameTooLongError) { 501 t.Fatalf("Link() returned error %v, expected %v(%d).", blunder.Errno(err), blunder.NameTooLongError, blunder.NameTooLongError.Value()) 502 } 503 } else { 504 t.Fatal("Link() unexpectedly succeeded on too-long filename!") 505 } 506 507 entriesExpected := []string{".", "..", validFile} 508 expectDirectory(t, inode.InodeRootUserID, inode.InodeGroupID(0), testDirInode, entriesExpected) 509 510 // Try to rename a nonexistent file with a name that is too long 511 err = testVolumeStruct.Rename(inode.InodeRootUserID, inode.InodeGroupID(0), nil, testDirInode, nameTooLong, testDirInode, "AlsoAGoodFilename") 512 if nil != err { 513 if blunder.IsNot(err, blunder.NameTooLongError) { 514 t.Fatalf("Link() returned error %v, expected %v(%d).", blunder.Errno(err), blunder.NameTooLongError, blunder.NameTooLongError.Value()) 515 } 516 } else { 517 t.Fatal("Link() unexpectedly succeeded on too-long filename!") 518 } 519 520 entriesExpected = []string{".", "..", validFile} 521 expectDirectory(t, inode.InodeRootUserID, inode.InodeGroupID(0), testDirInode, entriesExpected) 522 523 testTeardown(t) 524 } 525 526 func TestBadChownChmod(t *testing.T) { 527 var ( 528 err error 529 ) 530 531 testSetup(t, false) 532 533 // Get root dir inode number 534 rootDirInodeNumber := inode.RootDirInodeNumber 535 536 // Create file to play with 537 basename := "TestFile" 538 createdFileInodeNumber, err := testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, rootDirInodeNumber, basename, inode.PosixModePerm) 539 if err != nil { 540 t.Fatalf("Create() %v returned error: %v", basename, err) 541 } 542 543 // Since we are playing some games with size of mode/userid/groupid, make sure that we 544 // correctly handle cases where the value is > uint32 545 var tooBigForUint32 uint64 = math.MaxUint32 + 7<<48 546 547 // Validate too-big Mode 548 stat := make(Stat) 549 stat[StatMode] = tooBigForUint32 550 err = testVolumeStruct.Setstat(inode.InodeRootUserID, inode.InodeGroupID(0), nil, createdFileInodeNumber, stat) 551 if blunder.IsNot(err, blunder.InvalidFileModeError) { 552 t.Fatalf("Setstat() %v returned error %v, expected %v(%d).", basename, blunder.Errno(err), blunder.InvalidFileModeError, blunder.InvalidFileModeError.Value()) 553 } 554 delete(stat, StatMode) 555 556 // Validate too-big UserID 557 stat[StatUserID] = tooBigForUint32 558 err = testVolumeStruct.Setstat(inode.InodeRootUserID, inode.InodeGroupID(0), nil, createdFileInodeNumber, stat) 559 if blunder.Errno(err) != int(blunder.InvalidFileModeError) { 560 t.Fatalf("Setstat() %v returned error %v, expected %v(%d).", basename, blunder.Errno(err), blunder.InvalidFileModeError, blunder.InvalidFileModeError.Value()) 561 } 562 delete(stat, StatUserID) 563 564 // Validate too-big GroupID 565 stat[StatGroupID] = tooBigForUint32 566 err = testVolumeStruct.Setstat(inode.InodeRootUserID, inode.InodeGroupID(0), nil, createdFileInodeNumber, stat) 567 if blunder.Errno(err) != int(blunder.InvalidFileModeError) { 568 t.Fatalf("Setstat() %v returned error %v, expected %v(%d).", basename, blunder.Errno(err), blunder.InvalidFileModeError, blunder.InvalidFileModeError.Value()) 569 } 570 delete(stat, StatGroupID) 571 572 testTeardown(t) 573 } 574 575 func TestFlock(t *testing.T) { 576 var ( 577 err error 578 ) 579 580 testSetup(t, false) 581 582 rootDirInodeNumber := inode.RootDirInodeNumber 583 584 // Create file to play with 585 basename := "TestLockFile" 586 lockFileInodeNumber, err := testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, rootDirInodeNumber, basename, inode.PosixModePerm) 587 if err != nil { 588 t.Fatalf("Create() %v returned error: %v", basename, err) 589 } 590 591 // Resize the file to a 1M so that we can apply byte range locks: 592 err = testVolumeStruct.Resize(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, 1024*1024) 593 if err != nil { 594 t.Fatalf("Resize() %v returned error: %v", basename, err) 595 } 596 597 // Write lock test: 598 var lock FlockStruct 599 lock.Type = syscall.F_WRLCK 600 lock.Start = 0 601 lock.Len = 0 602 lock.Pid = 1 603 604 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock) 605 if err != nil { 606 t.Fatalf("Write lock on file failed: %v", err) 607 } 608 609 lock.Type = syscall.F_UNLCK 610 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock) 611 if err != nil { 612 t.Fatalf("Unlock on file failed: %v", blunder.Errno(err)) 613 } 614 615 lock.Type = syscall.F_WRLCK 616 lock.Pid = 1 617 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock) 618 if err != nil { 619 t.Fatalf("Write lock on file failed: %v", err) 620 } 621 622 // Try another write lock from a different pid, it should fail: 623 var lock1 FlockStruct 624 lock1 = lock 625 lock1.Pid = 2 626 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock1) 627 if blunder.Errno(err) != int(blunder.TryAgainError) { 628 t.Fatalf("Write lock on a locked file should fail with EAGAIN instead got : %v", err) 629 } 630 631 // Lock again from pid1, it should succeed: 632 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock) 633 if err != nil { 634 t.Fatalf("Relocking from same PID on file failed: %v", err) 635 } 636 637 lock.Type = syscall.F_UNLCK 638 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock) 639 if err != nil { 640 t.Fatalf("Unlock failed : %v", err) 641 } 642 643 // Read lock test: 644 lock.Type = syscall.F_RDLCK 645 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock) 646 if err != nil { 647 t.Fatalf("Read lock pid - 1 failed: %v", err) 648 } 649 650 lock1.Type = syscall.F_RDLCK 651 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock1) 652 if err != nil { 653 t.Fatalf("Read lock pid - 2 failed: %v", err) 654 } 655 656 // Try write lock it should fail: 657 lock3 := lock 658 lock3.Type = syscall.F_WRLCK 659 lock3.Pid = 3 660 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock3) 661 if blunder.Errno(err) != int(blunder.TryAgainError) { 662 t.Fatalf("Write lock should have failed with EAGAIN instead got - %v", err) 663 } 664 665 lock11 := lock1 666 lock11.Type = syscall.F_UNLCK 667 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock11) 668 if err != nil { 669 t.Fatalf("Unlock of (readlock) - 2 failed: %v", err) 670 } 671 672 lock01 := lock 673 lock01.Type = syscall.F_UNLCK 674 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock01) 675 if err != nil { 676 t.Fatalf("Unlock of (readlock) - 1 failed: %v", err) 677 } 678 679 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock3) 680 if err != nil { 681 t.Fatalf("Write lock should have succeeded instead got - %v", err.Error()) 682 } 683 684 lock3.Type = syscall.F_UNLCK 685 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock3) 686 if err != nil { 687 t.Fatalf("Unlock of (write after read) failed: %v", err) 688 } 689 690 // Multiple Range lock testing: 691 692 var lock10 FlockStruct 693 lock10.Pid = 1 694 lock10.Start = 100 695 lock10.Len = 100 696 lock10.Type = syscall.F_WRLCK 697 lock10.Whence = 0 698 699 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock10) 700 if err != nil { 701 t.Fatalf("Range test failed to lock range (100 - 200), err %v", err) 702 } 703 704 lock201 := lock10 705 lock201.Pid = 2 706 lock201.Type = syscall.F_RDLCK 707 lock201.Start = 10 708 lock201.Len = 10 709 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock201) 710 if err != nil { 711 t.Fatalf("Range test failed to read lock range (10 - 20) by pid2, err %v", err) 712 } 713 714 lock202 := lock201 715 lock202.Start = 90 716 lock202.Len = 10 717 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock202) 718 if err != nil { 719 t.Fatalf("Range test failed to read lock range (90 - 100) by pid2, err %v", err) 720 } 721 722 lock203 := lock202 723 lock203.Start = 80 724 lock203.Len = 40 725 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock203) 726 if err == nil { 727 t.Fatalf("Range test read lock of range (80 - 120) should have failed for pid2 err %v", err) 728 } 729 730 lock204 := lock203 731 lock204.Start = 180 732 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock204) 733 if err == nil { 734 t.Fatalf("Range test read lock of range (180 - 220) should have failed for pid2 err %v", err) 735 } 736 737 lock205 := lock204 738 lock205.Start = 200 739 lock205.Len = 10 740 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock205) 741 if err != nil { 742 t.Fatalf("Range test read lock of range (200 - 210) should have succeeded for pid2 err %v", err) 743 } 744 745 lock206 := lock205 746 lock206.Start = 240 747 lock206.Len = 10 748 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock206) 749 if err != nil { 750 t.Fatalf("Range test read lock of range (240 - 250) should have succeeded for pid2 err %v", err) 751 } 752 753 lock101 := lock10 754 lock101.Type = syscall.F_RDLCK 755 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock101) 756 if err != nil { 757 t.Fatalf("Range test converting write lock to read lock of pid1 range 100 - 200 failed, err %v", err) 758 } 759 760 // Now, lock 203 and 204 should succceed. 761 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock203) 762 if err != nil { 763 t.Fatalf("Range test read lock of range (80 - 120) should have succeeded for pid2 err %v", err) 764 } 765 766 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock204) 767 if err != nil { 768 t.Fatalf("Range test read lock of range (180 - 220) should have succeeded for pid2 err %v", err) 769 } 770 771 lock30 := lock10 772 lock30.Pid = 3 773 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock30) 774 if err == nil { 775 t.Fatalf("Range test write lock of range 100 - 200 should have failed for pid3 err %v", err) 776 } 777 778 lock102 := lock10 779 lock102.Type = syscall.F_UNLCK 780 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock102) 781 if err != nil { 782 t.Fatalf("Range test unlock of range 100 - 200 for pid1 should have succeeded, err - %v", err) 783 } 784 785 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock30) 786 if err == nil { 787 t.Fatalf("Range test write lock of range 100 - 200 should have failed for pid3 err %v", err) 788 } 789 790 lock207 := lock10 791 lock207.Type = syscall.F_UNLCK 792 lock207.Pid = 2 793 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock207) 794 if err != nil { 795 t.Fatalf("Range test unlock of range 100 - 200 for pid2 should have succeeded, err - %v", err) 796 } 797 798 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock30) 799 if err != nil { 800 t.Fatalf("Range test write lock of range 100 - 200 should have succeeded for pid3 err %v", err) 801 } 802 803 lock301 := lock30 804 lock301.Type = syscall.F_UNLCK 805 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock301) 806 if err != nil { 807 t.Fatalf("Range test unlock of range 100 - 200 should have succeeded for pid3 err %v", err) 808 } 809 810 lock2u1 := lock201 811 lock2u1.Type = syscall.F_UNLCK 812 lock2u1.Start = 0 813 lock2u1.Len = 150 814 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock2u1) 815 if err != nil { 816 t.Fatalf("Range test unlock of range 0 - 150 should have succeeded for pid2 err %v", err) 817 } 818 819 lock2u2 := lock2u1 820 lock2u2.Start = 150 821 lock2u2.Len = 150 822 _, err = testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_SETLK, &lock2u2) 823 if err != nil { 824 t.Fatalf("Range test unlock of range 150 - 300 should have succeeded for pid2 err %v", err) 825 } 826 827 lock30.Start = 0 828 lock30.Len = 250 829 lock30.Type = syscall.F_WRLCK 830 lockHeld, err := testVolumeStruct.Flock(inode.InodeRootUserID, inode.InodeGroupID(0), nil, lockFileInodeNumber, syscall.F_GETLK, &lock30) 831 if err != nil { 832 t.Fatalf("Range test GET write lock of range 0 - 250 should have succeeded for pid3 err %v lockHeld %+v", err, lockHeld) 833 } 834 835 if lock30.Type != syscall.F_UNLCK { 836 t.Fatalf("GetLock should have succeeded for range 0 - 250 for pid 3, err %v", err) 837 } 838 839 err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, rootDirInodeNumber, basename) 840 if err != nil { 841 t.Fatalf("Unlink() %v returned error: %v", basename, err) 842 } 843 844 testTeardown(t) 845 } 846 847 // Verify that the file system API works correctly with stale inode numbers, 848 // as can happen if an NFS client cache gets out of sync because another NFS 849 // client as removed a file or directory. 850 func TestStaleInodes(t *testing.T) { 851 var ( 852 rootDirInodeNumber inode.InodeNumber = inode.RootDirInodeNumber 853 testDirname string = "stale_inodes_test" 854 testFileName string = "valid_file" 855 staleDirName string = "dir" 856 staleFileName string = "file" 857 testDirInodeNumber inode.InodeNumber 858 testFileInodeNumber inode.InodeNumber 859 staleDirInodeNumber inode.InodeNumber 860 staleFileInodeNumber inode.InodeNumber 861 err error 862 ) 863 864 testSetup(t, false) 865 866 // scratchpad directory for testing 867 testDirInodeNumber, err = testVolumeStruct.Mkdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 868 rootDirInodeNumber, testDirname, 0755) 869 if nil != err { 870 t.Fatalf("Mkdir() '%s' returned error: %v", testDirname, err) 871 } 872 873 // create a valid test file 874 testFileInodeNumber, err = testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 875 testDirInodeNumber, testFileName, 0644) 876 if nil != err { 877 t.Fatalf("Create() '%s' returned error: %v", testFileName, err) 878 } 879 880 // get an inode number that used to belong to a dirctory 881 _, err = testVolumeStruct.Mkdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 882 testDirInodeNumber, staleDirName, 0755) 883 if nil != err { 884 t.Fatalf("Mkdir() '%s' returned error: %v", testDirname, err) 885 } 886 staleDirInodeNumber, err = testVolumeStruct.Lookup(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 887 testDirInodeNumber, staleDirName) 888 if err != nil { 889 t.Fatalf("Unexpectedly failed to look up of '%s': %v", testDirname, err) 890 } 891 err = testVolumeStruct.Rmdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 892 testDirInodeNumber, staleDirName) 893 if nil != err { 894 t.Fatalf("Rmdir() of '%s' returned error: %v", staleDirName, err) 895 } 896 897 // get an inode number that used to belong to a file (it shouldn't 898 // really matter which type of file the inode used to be, but it doesn't 899 // hurt to have two to play with) 900 _, err = testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 901 testDirInodeNumber, staleFileName, 0644) 902 if nil != err { 903 t.Fatalf("Mkdir() '%s' returned error: %v", testDirname, err) 904 } 905 staleFileInodeNumber, err = testVolumeStruct.Lookup(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 906 testDirInodeNumber, staleFileName) 907 if err != nil { 908 t.Fatalf("Unexpectedly failed to look up of '%s': %v", testDirname, err) 909 } 910 err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 911 testDirInodeNumber, staleFileName) 912 if nil != err { 913 t.Fatalf("Unlink() of '%s' returned error: %v", staleFileName, err) 914 } 915 916 // Stat 917 _, err = testVolumeStruct.Getstat(inode.InodeRootUserID, inode.InodeGroupID(0), nil, staleFileInodeNumber) 918 if nil == err { 919 t.Fatalf("Getstat() should not have returned success") 920 } 921 if blunder.IsNot(err, blunder.NotFoundError) { 922 t.Fatalf("Getstat() should have failed with NotFoundError, instead got: %v", err) 923 } 924 925 // Mkdir 926 _, err = testVolumeStruct.Mkdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 927 staleDirInodeNumber, "TestSubDirectory", 0755) 928 if nil == err { 929 t.Fatalf("Mkdir() should not have returned success") 930 } 931 if blunder.IsNot(err, blunder.NotFoundError) { 932 t.Fatalf("Mkdir() should have failed with NotFoundError, instead got: %v", err) 933 } 934 935 // Rmdir 936 err = testVolumeStruct.Rmdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 937 staleDirInodeNumber, "fubar") 938 if nil == err { 939 t.Fatalf("Rmdir() should not have returned success") 940 } 941 if blunder.IsNot(err, blunder.NotFoundError) { 942 t.Fatalf("Rmdir() should have failed with NotFoundError, instead got: %v", err) 943 } 944 945 // Create 946 _, err = testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 947 staleDirInodeNumber, "fubar", 0644) 948 if nil == err { 949 t.Fatalf("Create() should not have returned success") 950 } 951 if blunder.IsNot(err, blunder.NotFoundError) { 952 t.Fatalf("Create() should have failed with NotFoundError, instead got: %v", err) 953 } 954 955 // Lookup 956 _, err = testVolumeStruct.Lookup(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 957 staleDirInodeNumber, "fubar") 958 if nil == err { 959 t.Fatalf("Lookup() should not have returned success") 960 } 961 if blunder.IsNot(err, blunder.NotFoundError) { 962 t.Fatalf("Lookup() should have failed with NotFoundError, instead got: %v", err) 963 } 964 965 // Write 966 bufToWrite := []byte{0x41, 0x42, 0x43} 967 _, err = testVolumeStruct.Write(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 968 staleFileInodeNumber, 0, bufToWrite, nil) 969 if nil == err { 970 t.Fatalf("Write() should not have returned success") 971 } 972 if blunder.IsNot(err, blunder.NotFoundError) { 973 t.Fatalf("Write() should have failed with NotFoundError, instead got: %v", err) 974 } 975 976 // Read 977 _, err = testVolumeStruct.Read(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 978 staleFileInodeNumber, 0, uint64(len(bufToWrite)), nil) 979 if nil == err { 980 t.Fatalf("Read() should not have returned success") 981 } 982 if blunder.IsNot(err, blunder.NotFoundError) { 983 t.Fatalf("Read() should have failed with NotFoundError, instead got: %v", err) 984 } 985 986 // Trunc 987 err = testVolumeStruct.Resize(inode.InodeRootUserID, inode.InodeGroupID(0), nil, staleFileInodeNumber, 77) 988 if nil == err { 989 t.Fatalf("Resize() should not have returned success") 990 } 991 if blunder.IsNot(err, blunder.NotFoundError) { 992 t.Fatalf("Resize() should have failed with NotFoundError, instead got: %v", err) 993 } 994 995 // Symlink 996 _, err = testVolumeStruct.Symlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 997 staleDirInodeNumber, "TestSymlink", "fubar") 998 if nil == err { 999 t.Fatalf("Symlink() should not have returned success") 1000 } 1001 if blunder.IsNot(err, blunder.NotFoundError) { 1002 t.Fatalf("Symlink() should have failed with NotFoundError, instead got: %v", err) 1003 } 1004 1005 // Readsymlink (that we didn't create) 1006 _, err = testVolumeStruct.Readsymlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, staleFileInodeNumber) 1007 if nil == err { 1008 t.Fatalf("Readsymlink() should not have returned success") 1009 } 1010 if blunder.IsNot(err, blunder.NotFoundError) { 1011 t.Fatalf("Readsymlink() should have failed with NotFoundError, instead got: %v", err) 1012 } 1013 1014 // Readdir 1015 _, _, _, err = testVolumeStruct.Readdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, staleDirInodeNumber, 0, "") 1016 if nil == err { 1017 t.Fatalf("Readdir() should not have returned success") 1018 } 1019 if blunder.IsNot(err, blunder.NotFoundError) { 1020 t.Fatalf("Readdir() should have failed with NotFoundError, instead got: %v", err) 1021 } 1022 1023 // Link -- two cases, one with stale directory and one with stale file 1024 err = testVolumeStruct.Link(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 1025 staleDirInodeNumber, "fubar", testFileInodeNumber) 1026 if nil == err { 1027 t.Fatalf("Link(1) should not have returned success") 1028 } 1029 if blunder.IsNot(err, blunder.NotFoundError) { 1030 t.Fatalf("Link(1) should have failed with NotFoundError, instead got: %v", err) 1031 } 1032 1033 err = testVolumeStruct.Link(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 1034 testDirInodeNumber, testFileName, staleFileInodeNumber) 1035 if nil == err { 1036 t.Fatalf("Link(2) should not have returned success") 1037 } 1038 if blunder.IsNot(err, blunder.NotFoundError) { 1039 t.Fatalf("Link(2) should have failed with NotFoundError, instead got: %v", err) 1040 } 1041 1042 // Unlink 1043 err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 1044 staleDirInodeNumber, "fubar") 1045 if nil == err { 1046 t.Fatalf("Unlink() should not have returned success") 1047 } 1048 if blunder.IsNot(err, blunder.NotFoundError) { 1049 t.Fatalf("Unlink() should have failed with NotFoundError, instead got: %v", err) 1050 } 1051 1052 // Rename -- two cases, one with stale src directory and one with stale dest 1053 err = testVolumeStruct.Rename(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 1054 testDirInodeNumber, "fubar", staleDirInodeNumber, "barfu") 1055 if nil == err { 1056 t.Fatalf("Rename(1) should not have returned success") 1057 } 1058 if blunder.IsNot(err, blunder.NotFoundError) { 1059 t.Fatalf("Rename(1) should have failed with NotFoundError, instead got: %v", err) 1060 } 1061 1062 err = testVolumeStruct.Rename(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 1063 staleDirInodeNumber, "fubar", testDirInodeNumber, "barfu") 1064 if nil == err { 1065 t.Fatalf("Rename(2) should not have returned success") 1066 } 1067 if blunder.IsNot(err, blunder.NotFoundError) { 1068 t.Fatalf("Rename(2) should have failed with NotFoundError, instead got: %v", err) 1069 } 1070 1071 // cleanup test file and directory 1072 err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 1073 testDirInodeNumber, testFileName) 1074 if nil != err { 1075 t.Fatalf("Unlink() of '%s' returned error: %v", testFileName, err) 1076 } 1077 err = testVolumeStruct.Rmdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, 1078 rootDirInodeNumber, testDirname) 1079 if nil != err { 1080 t.Fatalf("Rmdir() of '%s' returned error: %v", testDirname, err) 1081 } 1082 1083 testTeardown(t) 1084 } 1085 1086 func TestMiddlewareGetContainer(t *testing.T) { 1087 var ents []ContainerEntry 1088 testSetup(t, false) 1089 1090 testDirInode := createTestDirectory(t, "container") 1091 1092 marker1 := "a_marker" 1093 _, err := testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, testDirInode, marker1, inode.PosixModePerm) 1094 if err != nil { 1095 t.Fatalf("Create() returned error: %v", err) 1096 } 1097 marker2 := "b_marker" 1098 _, err = testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, testDirInode, marker2, inode.PosixModePerm) 1099 if err != nil { 1100 t.Fatalf("Create() returned error: %v", err) 1101 } 1102 1103 ents, err = testVolumeStruct.MiddlewareGetContainer("container", 10, "a", "", "", "") 1104 if nil != err { 1105 t.Fatalf("got some error: %v", err) 1106 } 1107 if 2 != len(ents) { 1108 t.Fatalf("marker a gave wrong number of entries: %v", ents) 1109 } 1110 1111 ents, err = testVolumeStruct.MiddlewareGetContainer("container", 10, "b", "", "", "") 1112 if nil != err { 1113 t.Fatalf("got some error: %v", err) 1114 } 1115 if 1 != len(ents) { 1116 t.Fatalf("marker b gave wrong number of entries: %v", ents) 1117 } 1118 1119 ents, err = testVolumeStruct.MiddlewareGetContainer("container", 10, "a_marker", "", "", "") 1120 if nil != err { 1121 t.Fatalf("got some error: %v", err) 1122 } 1123 if 1 != len(ents) { 1124 t.Fatalf("marker a_marker gave wrong number of entries: %v", ents) 1125 } 1126 1127 testTeardown(t) 1128 } 1129 1130 // Verify that the metadata for the object at containerObjPath, as returned by 1131 // MiddlewareHeadResponse(), matches the metadata in opMetdata. opMetadata is 1132 // presumably returned by some middleware operation named opName, but it could 1133 // come from somewhere else. 1134 // 1135 // Throw testing errors if anything doesn't match. stepName and opName are used 1136 // in the error strings. 1137 func verifyMetadata(t *testing.T, containerObjPath string, 1138 stepName string, opName string, opMeta *HeadResponse) { 1139 1140 // how to print time stamps (the date is dropped) 1141 timeFormat := "15:04:05.000000000" 1142 1143 var ( 1144 headMeta HeadResponse 1145 err error 1146 ) 1147 1148 // fetch the current metadata (implicit and explicit) 1149 headMeta, err = testVolumeStruct.MiddlewareHeadResponse(containerObjPath) 1150 if err != nil { 1151 t.Errorf("MiddlewareHeadResponse() for '%s' op %s '%s' failed: %v", 1152 containerObjPath, opName, stepName, err) 1153 return 1154 } 1155 1156 // validate the "explicit" metadata 1157 if !bytes.Equal(opMeta.Metadata, headMeta.Metadata) { 1158 t.Errorf("object '%s' op %s '%s' op metadata '%s' does not match stat metadata '%s'", 1159 containerObjPath, opName, stepName, opMeta.Metadata, headMeta.Metadata) 1160 } 1161 1162 // check the rest of the attributes and quit after the first mismatch 1163 if opMeta.IsDir != headMeta.IsDir { 1164 t.Errorf("object '%s' op %s '%s' op IsDir '%v' does not match stat IsDir '%v'", 1165 containerObjPath, opName, stepName, opMeta.IsDir, headMeta.IsDir) 1166 return 1167 } 1168 if opMeta.FileSize != headMeta.FileSize { 1169 t.Errorf("object '%s' op %s '%s' op FileSize '%d' does not match stat FileSize '%d'", 1170 containerObjPath, opName, stepName, opMeta.FileSize, headMeta.FileSize) 1171 return 1172 } 1173 if opMeta.NumWrites != headMeta.NumWrites { 1174 t.Errorf("object '%s' op %s '%s' op NumWrites '%d' does not match stat NumWrites '%d'", 1175 containerObjPath, opName, stepName, opMeta.NumWrites, headMeta.NumWrites) 1176 return 1177 } 1178 if opMeta.InodeNumber != headMeta.InodeNumber { 1179 t.Errorf("object '%s' op %s '%s' op InodeNumber '%d' does not match stat InodeNumber '%d'", 1180 containerObjPath, opName, stepName, opMeta.InodeNumber, headMeta.InodeNumber) 1181 return 1182 } 1183 1184 if opMeta.AttrChangeTime != headMeta.AttrChangeTime { 1185 t.Errorf("object '%s' op %s '%s' op AttrChangeTime '%s' does not match stat AttrChangeTime '%s'", 1186 containerObjPath, opName, stepName, 1187 time.Unix(0, int64(opMeta.AttrChangeTime)).Format(timeFormat), 1188 time.Unix(0, int64(headMeta.AttrChangeTime)).Format(timeFormat)) 1189 return 1190 } 1191 if opMeta.ModificationTime != headMeta.ModificationTime { 1192 t.Errorf("object '%s' op %s '%s' op ModificationTime '%s' does not match stat ModificationTime '%s'", 1193 containerObjPath, opName, stepName, 1194 time.Unix(0, int64(opMeta.ModificationTime)).Format(timeFormat), 1195 time.Unix(0, int64(headMeta.ModificationTime)).Format(timeFormat)) 1196 return 1197 } 1198 return 1199 } 1200 1201 // Test MiddlewareMkdir() and MiddlewarePutComplete(). 1202 // 1203 // A Swift PUT operation can create a file or a directory, depending on the 1204 // arguments. Further, in Swift a PUT on an object deletes the object and 1205 // replaces it with a new object with the new metadata. 1206 // 1207 // We interpret the "replacement rule" to mean that a PUT can delete a file and 1208 // replace it with a directory or vice versa, but it cannot cause the delete of 1209 // a directory that is not empty. (Its not clear how symlinks figure into this, 1210 // but they should probably follow the same rule.) 1211 // 1212 // The code in pfs_middleware determines whether a PUT request intends to 1213 // create a file or a directory. 1214 // 1215 // Test other behaviors, like automatically creating a path to the specified 1216 // object. This does not test concatenating 1 or more objects to make up the 1217 // contents of the file. 1218 func TestMiddlewarePuts(t *testing.T) { 1219 1220 testSetup(t, false) 1221 defer testTeardown(t) 1222 1223 initialMetadata := []byte("initial metadata") 1224 updatedMetadata := []byte("updated metadata") 1225 updatedMetadata2 := []byte("really new metadata") 1226 containerName := "MiddlewarePuts" 1227 objectPath := "dir0/dir1/dir2/file0" 1228 containerObjectPath := containerName + "/" + objectPath 1229 1230 var ( 1231 opMeta HeadResponse 1232 err error 1233 ) 1234 1235 // make a container for testing and verify the explicit metadata 1236 err = testVolumeStruct.MiddlewarePutContainer(containerName, []byte(""), initialMetadata) 1237 if err != nil { 1238 t.Fatalf("MiddlewarePutContainer() failed: %v", err) 1239 } 1240 opMeta, err = testVolumeStruct.MiddlewareHeadResponse(containerName) 1241 if err != nil { 1242 t.Fatalf("MiddlewareHeadResponse() for container '%s' failed: %v", containerName, err) 1243 } 1244 if !bytes.Equal(opMeta.Metadata, initialMetadata) { 1245 t.Errorf("MiddlewareHeadResponse() for container '%s' metadata '%s' does not match '%s'", 1246 containerName, opMeta.Metadata, initialMetadata) 1247 } 1248 1249 // create a file object and then verify the explicit metadata and 1250 // returned attributes are correct 1251 opMeta.ModificationTime, opMeta.AttrChangeTime, opMeta.InodeNumber, opMeta.NumWrites, err = 1252 testVolumeStruct.MiddlewarePutComplete(containerName, objectPath, nil, nil, initialMetadata) 1253 if err != nil { 1254 t.Errorf("MiddlewarePutComplete() for container '%s' object '%s' failed: %v", 1255 containerName, objectPath, err) 1256 } else { 1257 opMeta.IsDir = false 1258 opMeta.FileSize = 0 1259 opMeta.Metadata = initialMetadata 1260 1261 verifyMetadata(t, containerObjectPath, "step 0", "MiddlewarePutComplete", &opMeta) 1262 } 1263 1264 // replace the file object with a directory object then verify the 1265 // explicit metadata and returned attributes 1266 opMeta.ModificationTime, opMeta.AttrChangeTime, opMeta.InodeNumber, opMeta.NumWrites, err = 1267 testVolumeStruct.MiddlewareMkdir(containerName, objectPath, updatedMetadata) 1268 if err != nil { 1269 t.Errorf("MiddlewareMkdir() for container '%s' object '%s' failed: %v", 1270 containerName, objectPath, err) 1271 } else { 1272 opMeta.IsDir = true 1273 opMeta.FileSize = 0 1274 opMeta.Metadata = updatedMetadata 1275 1276 verifyMetadata(t, containerObjectPath, "step 1", "MiddlewareMkdir", &opMeta) 1277 } 1278 1279 // verify the metadata (explicit and implicit) returned by 1280 // MiddlewareGetObject() matches MiddlewareHeadResponse() for a 1281 // directory 1282 opMeta, err = testVolumeStruct.MiddlewareGetObject(containerObjectPath, 1283 []ReadRangeIn{}, &[]inode.ReadPlanStep{}) 1284 if err != nil { 1285 t.Errorf("MiddlewareGetObject() for object '%s' failed: %v", containerObjectPath, err) 1286 } else { 1287 verifyMetadata(t, containerObjectPath, "step 1.5", "MiddlewareGetObject", &opMeta) 1288 } 1289 1290 // change the directory object back to a file object and verify the 1291 // explicit metadata and returned attributes 1292 opMeta.ModificationTime, opMeta.AttrChangeTime, opMeta.InodeNumber, opMeta.NumWrites, err = 1293 testVolumeStruct.MiddlewarePutComplete(containerName, objectPath, nil, nil, updatedMetadata2) 1294 if err != nil { 1295 t.Errorf("MiddlewarePutComplete() for container '%s' object '%s' failed: %v", 1296 containerName, objectPath, err) 1297 } else { 1298 opMeta.IsDir = false 1299 opMeta.FileSize = 0 1300 opMeta.Metadata = updatedMetadata2 1301 1302 verifyMetadata(t, containerObjectPath, "step 2", "MiddlewarePutComplete", &opMeta) 1303 } 1304 1305 // verify the metadata (explicit and implicit) returned by 1306 // MiddlewareGetObject() matches MiddlewareHeadResponse() 1307 opMeta, err = testVolumeStruct.MiddlewareGetObject(containerObjectPath, 1308 []ReadRangeIn{}, &[]inode.ReadPlanStep{}) 1309 if err != nil { 1310 t.Errorf("MiddlewareGetObject() for object '%s' failed: %v", containerObjectPath, err) 1311 } else { 1312 verifyMetadata(t, containerObjectPath, "step 3", "MiddlewareGetObject", &opMeta) 1313 } 1314 1315 // save the file's metadata for later validation 1316 file0Meta := opMeta 1317 1318 // a "file" PUT to a directory object that is not empty should fail 1319 // (because we cannot convert a non-empty directory to a file object) 1320 pathComponents := strings.Split(objectPath, "/") 1321 dirPath := strings.Join(pathComponents[:len(pathComponents)-1], "/") 1322 containerDirPath := containerName + "/" + dirPath 1323 1324 opMeta.ModificationTime, opMeta.AttrChangeTime, opMeta.InodeNumber, opMeta.NumWrites, err = 1325 testVolumeStruct.MiddlewarePutComplete(containerName, dirPath, nil, nil, initialMetadata) 1326 if err == nil { 1327 t.Errorf("MiddlewarePutComplete() for container '%s' non-empty object '%s' should have failed", 1328 containerName, objectPath) 1329 } else if blunder.IsNot(err, blunder.NotEmptyError) { 1330 t.Errorf("MiddlewarePutComplete() for container '%s' non-empty object '%s' should have failed "+ 1331 "with error '%s' but failed with error '%s' (%s)", 1332 containerName, objectPath, 1333 blunder.FsError(int(unix.ENOTEMPTY)), 1334 blunder.FsError(blunder.Errno(err)), 1335 blunder.ErrorString(err)) 1336 } 1337 1338 // a "directory PUT" to a directory object that is not empty should 1339 // succeed and update the explicit metadata (but should not delete 1340 // existing directory entries) 1341 opMeta.ModificationTime, opMeta.AttrChangeTime, opMeta.InodeNumber, opMeta.NumWrites, err = 1342 testVolumeStruct.MiddlewareMkdir(containerName, dirPath, updatedMetadata) 1343 if err != nil { 1344 t.Errorf("MiddlewareMkdir() for object '%s' failed: %v", containerDirPath, err) 1345 } else { 1346 opMeta.IsDir = true 1347 opMeta.FileSize = 0 1348 opMeta.Metadata = updatedMetadata 1349 1350 verifyMetadata(t, containerDirPath, "step 4", "MiddlewareMkdir", &opMeta) 1351 } 1352 1353 // verify that the file (child of the directory) is unchanged 1354 verifyMetadata(t, containerObjectPath, "step 5", "verify", &file0Meta) 1355 }