gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/filesystem/filesystem_test.go (about) 1 package filesystem 2 3 import ( 4 "bytes" 5 "encoding/hex" 6 "fmt" 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 "reflect" 11 "sort" 12 "strings" 13 "sync" 14 "sync/atomic" 15 "testing" 16 "time" 17 18 "gitlab.com/NebulousLabs/errors" 19 "gitlab.com/NebulousLabs/fastrand" 20 "gitlab.com/NebulousLabs/writeaheadlog" 21 skydPersist "gitlab.com/SkynetLabs/skyd/persist" 22 "gitlab.com/SkynetLabs/skyd/skymodules" 23 "gitlab.com/SkynetLabs/skyd/skymodules/renter/filesystem/siadir" 24 "gitlab.com/SkynetLabs/skyd/skymodules/renter/filesystem/siafile" 25 "go.sia.tech/siad/crypto" 26 "go.sia.tech/siad/persist" 27 28 "gitlab.com/SkynetLabs/skyd/build" 29 ) 30 31 // CachedListCollect calls CachedList but collects the returned infos into 32 // slices which are then sorted by siapath. This should only be used in testing 33 // since it might result in a large memory allocation. 34 func (fs *FileSystem) CachedListCollect(siaPath skymodules.SiaPath, recursive bool) (fis []skymodules.FileInfo, dis []skymodules.DirectoryInfo, err error) { 35 var fmu, dmu sync.Mutex 36 flf := func(fi skymodules.FileInfo) { 37 fmu.Lock() 38 fis = append(fis, fi) 39 fmu.Unlock() 40 } 41 dlf := func(di skymodules.DirectoryInfo) { 42 dmu.Lock() 43 dis = append(dis, di) 44 dmu.Unlock() 45 } 46 err = fs.CachedList(siaPath, recursive, flf, dlf) 47 48 // Sort slices by SiaPath. 49 sort.Slice(dis, func(i, j int) bool { 50 return dis[i].SiaPath.String() < dis[j].SiaPath.String() 51 }) 52 sort.Slice(fis, func(i, j int) bool { 53 return fis[i].SiaPath.String() < fis[j].SiaPath.String() 54 }) 55 return 56 } 57 58 // newTestFileSystemWithFile creates a new FileSystem and SiaFile and makes sure 59 // that they are linked 60 func newTestFileSystemWithFile(name string) (*FileNode, *FileSystem, error) { 61 dir := testDir(name) 62 fs := newTestFileSystem(dir) 63 sp := skymodules.RandomSiaPath() 64 fs.addTestSiaFile(sp) 65 sf, err := fs.OpenSiaFile(sp) 66 return sf, fs, err 67 } 68 69 // newTestFileSystemWithDir creates a new FileSystem and SiaDir and makes sure 70 // that they are linked 71 func newTestFileSystemWithDir(name string) (*DirNode, *FileSystem, error) { 72 dir := testDir(name) 73 fs := newTestFileSystem(dir) 74 sp := skymodules.RandomSiaPath() 75 if err := fs.NewSiaDir(sp, skymodules.DefaultDirPerm); err != nil { 76 panic(err) // Reflect behavior of newTestFileSystemWithFile. 77 } 78 sd, err := fs.OpenSiaDir(sp) 79 return sd, fs, err 80 } 81 82 // testDir creates a testing directory for a filesystem test. 83 func testDir(name string) string { 84 dir := build.TempDir(name, filepath.Join("filesystem")) 85 if err := os.MkdirAll(dir, persist.DefaultDiskPermissionsTest); err != nil { 86 panic(err) 87 } 88 return dir 89 } 90 91 // newSiaPath creates a new siapath from the specified string. 92 func newSiaPath(path string) skymodules.SiaPath { 93 sp, err := skymodules.NewSiaPath(path) 94 if err != nil { 95 panic(err) 96 } 97 return sp 98 } 99 100 // newTestFileSystem creates a new filesystem for testing. 101 func newTestFileSystem(root string) *FileSystem { 102 wal, _ := newTestWAL() 103 logger, err := skydPersist.NewLogger(ioutil.Discard) 104 if err != nil { 105 panic(err.Error()) 106 } 107 fs, err := New(root, logger, wal) 108 if err != nil { 109 panic(err.Error()) 110 } 111 return fs 112 } 113 114 // newTestWal is a helper method to create a WAL for testing. 115 func newTestWAL() (*writeaheadlog.WAL, string) { 116 // Create the wal. 117 walsDir := filepath.Join(os.TempDir(), "wals") 118 if err := os.MkdirAll(walsDir, 0700); err != nil { 119 panic(err) 120 } 121 walFilePath := filepath.Join(walsDir, hex.EncodeToString(fastrand.Bytes(8))) 122 _, wal, err := writeaheadlog.New(walFilePath) 123 if err != nil { 124 panic(err) 125 } 126 return wal, walFilePath 127 } 128 129 // addTestSiaFile is a convenience method to add a SiaFile for testing to a 130 // FileSystem. 131 func (fs *FileSystem) addTestSiaFile(siaPath skymodules.SiaPath) { 132 if err := fs.addTestSiaFileWithErr(siaPath); err != nil { 133 panic(err) 134 } 135 } 136 137 // addTestSiaFileWithErr is a convenience method to add a SiaFile for testing to 138 // a FileSystem. 139 func (fs *FileSystem) addTestSiaFileWithErr(siaPath skymodules.SiaPath) error { 140 ec, err := skymodules.NewRSSubCode(10, 20, crypto.SegmentSize) 141 if err != nil { 142 return err 143 } 144 err = fs.NewSiaFile(siaPath, "", ec, crypto.GenerateSiaKey(crypto.TypeDefaultRenter), uint64(fastrand.Intn(100)), persist.DefaultDiskPermissionsTest) 145 if err != nil { 146 return err 147 } 148 return nil 149 } 150 151 // TestNew tests creating a new FileSystem. 152 func TestNew(t *testing.T) { 153 if testing.Short() && !build.VLONG { 154 t.SkipNow() 155 } 156 t.Parallel() 157 // Create filesystem. 158 root := filepath.Join(testDir(t.Name()), "fs-root") 159 fs := newTestFileSystem(root) 160 // Check fields. 161 if fs.parent != nil { 162 t.Fatalf("fs.parent shoud be 'nil' but wasn't") 163 } 164 if *fs.name != "" { 165 t.Fatalf("fs.staticName should be %v but was %v", "", *fs.name) 166 } 167 if *fs.path != root { 168 t.Fatalf("fs.path should be %v but was %v", root, *fs.path) 169 } 170 if fs.threads == nil || len(fs.threads) != 0 { 171 t.Fatal("fs.threads is not an empty initialized map") 172 } 173 if fs.threadUID != 0 { 174 t.Fatalf("fs.threadUID should be 0 but was %v", fs.threadUID) 175 } 176 if fs.directories == nil || len(fs.directories) != 0 { 177 t.Fatal("fs.directories is not an empty initialized map") 178 } 179 if fs.files == nil || len(fs.files) != 0 { 180 t.Fatal("fs.files is not an empty initialized map") 181 } 182 // Create the filesystem again at the same location. 183 _ = newTestFileSystem(*fs.path) 184 } 185 186 // TestNewSiaDir tests if creating a new directory using NewSiaDir creates the 187 // correct folder structure. 188 func TestNewSiaDir(t *testing.T) { 189 if testing.Short() && !build.VLONG { 190 t.SkipNow() 191 } 192 t.Parallel() 193 // Create filesystem. 194 root := filepath.Join(testDir(t.Name()), "fs-root") 195 fs := newTestFileSystem(root) 196 // Create dir /sub/foo 197 sp := newSiaPath("sub/foo") 198 if err := fs.NewSiaDir(sp, skymodules.DefaultDirPerm); err != nil { 199 t.Fatal(err) 200 } 201 // The whole path should exist. 202 if _, err := os.Stat(filepath.Join(root, sp.String())); err != nil { 203 t.Fatal(err) 204 } 205 } 206 207 // TestNewSiaFile tests if creating a new file using NewSiaFiles creates the 208 // correct folder structure and file. 209 func TestNewSiaFile(t *testing.T) { 210 if testing.Short() && !build.VLONG { 211 t.SkipNow() 212 } 213 t.Parallel() 214 // Create filesystem. 215 root := filepath.Join(testDir(t.Name()), "fs-root") 216 fs := newTestFileSystem(root) 217 // Create file /sub/foo/file 218 sp := newSiaPath("sub/foo/file") 219 fs.addTestSiaFile(sp) 220 if err := fs.NewSiaDir(sp, skymodules.DefaultDirPerm); !errors.Contains(err, ErrExists) { 221 t.Fatal("err should be ErrExists but was", err) 222 } 223 if _, err := os.Stat(filepath.Join(root, sp.String())); !os.IsNotExist(err) { 224 t.Fatal("there should be no dir on disk") 225 } 226 if _, err := os.Stat(filepath.Join(root, sp.String()+skymodules.SiaFileExtension)); err != nil { 227 t.Fatal(err) 228 } 229 // Create a file in the root dir. 230 sp = newSiaPath("file") 231 fs.addTestSiaFile(sp) 232 if err := fs.NewSiaDir(sp, skymodules.DefaultDirPerm); !errors.Contains(err, ErrExists) { 233 t.Fatal("err should be ErrExists but was", err) 234 } 235 if _, err := os.Stat(filepath.Join(root, sp.String())); !os.IsNotExist(err) { 236 t.Fatal("there should be no dir on disk") 237 } 238 if _, err := os.Stat(filepath.Join(root, sp.String()+skymodules.SiaFileExtension)); err != nil { 239 t.Fatal(err) 240 } 241 } 242 243 func (d *DirNode) checkNode(numThreads, numDirs, numFiles int) error { 244 if len(d.threads) != numThreads { 245 return fmt.Errorf("Expected d.threads to have length %v but was %v", numThreads, len(d.threads)) 246 } 247 if len(d.directories) != numDirs { 248 return fmt.Errorf("Expected %v subdirectories in the root but got %v", numDirs, len(d.directories)) 249 } 250 if len(d.files) != numFiles { 251 return fmt.Errorf("Expected %v files in the root but got %v", numFiles, len(d.files)) 252 } 253 return nil 254 } 255 256 // TestOpenSiaDir confirms that a previoiusly created SiaDir can be opened and 257 // that the filesystem tree is extended accordingly in the process. 258 func TestOpenSiaDir(t *testing.T) { 259 if testing.Short() && !build.VLONG { 260 t.SkipNow() 261 } 262 t.Parallel() 263 // Create filesystem. 264 root := filepath.Join(testDir(t.Name()), "fs-root") 265 fs := newTestFileSystem(root) 266 // Create dir /foo 267 sp := newSiaPath("foo") 268 if err := fs.NewSiaDir(sp, skymodules.DefaultDirPerm); err != nil { 269 t.Fatal(err) 270 } 271 // Open the newly created dir. 272 foo, err := fs.OpenSiaDir(sp) 273 if err != nil { 274 t.Fatal(err) 275 } 276 defer func() { 277 if err := foo.Close(); err != nil { 278 t.Fatal(err) 279 } 280 }() 281 // Create dir /sub/foo. This time don't use NewSiaDir but OpenSiaDir with 282 // the create flag set to `true`. 283 sp = newSiaPath("sub/foo") 284 sd, err := fs.OpenSiaDirCustom(sp, true) 285 if err != nil { 286 t.Fatal(err) 287 } 288 defer func() { 289 if err := sd.Close(); err != nil { 290 t.Fatal(err) 291 } 292 }() 293 // Confirm the integrity of the root node. 294 if err := fs.checkNode(0, 2, 0); err != nil { 295 t.Fatal(err) 296 } 297 // Open the root node manually and confirm that they are the same. 298 rootSD, err := fs.OpenSiaDir(skymodules.RootSiaPath()) 299 if err != nil { 300 t.Fatal(err) 301 } 302 if err := fs.checkNode(len(rootSD.threads), len(rootSD.directories), len(rootSD.files)); err != nil { 303 t.Fatal(err) 304 } 305 // Confirm the integrity of the /sub node. 306 subNode, exists := fs.directories["sub"] 307 if !exists { 308 t.Fatal("expected root to contain the 'sub' node") 309 } 310 if *subNode.name != "sub" { 311 t.Fatalf("subNode name should be 'sub' but was %v", *subNode.name) 312 } 313 if path := filepath.Join(*subNode.parent.path, *subNode.name); path != *subNode.path { 314 t.Fatalf("subNode path should be %v but was %v", path, *subNode.path) 315 } 316 if err := subNode.checkNode(0, 1, 0); err != nil { 317 t.Fatal(err) 318 } 319 // Confirm the integrity of the /sub/foo node. 320 fooNode, exists := subNode.directories["foo"] 321 if !exists { 322 t.Fatal("expected /sub to contain /sub/foo") 323 } 324 if *fooNode.name != "foo" { 325 t.Fatalf("fooNode name should be 'foo' but was %v", *fooNode.name) 326 } 327 if path := filepath.Join(*fooNode.parent.path, *fooNode.name); path != *fooNode.path { 328 t.Fatalf("fooNode path should be %v but was %v", path, *fooNode.path) 329 } 330 if err := fooNode.checkNode(1, 0, 0); err != nil { 331 t.Fatal(err) 332 } 333 // Open the newly created dir again. 334 sd2, err := fs.OpenSiaDir(sp) 335 if err != nil { 336 t.Fatal(err) 337 } 338 defer func() { 339 if err := sd2.Close(); err != nil { 340 t.Fatal(err) 341 } 342 }() 343 // They should have different UIDs. 344 if sd.threadUID == 0 { 345 t.Fatal("threaduid shouldn't be 0") 346 } 347 if sd2.threadUID == 0 { 348 t.Fatal("threaduid shouldn't be 0") 349 } 350 if sd.threadUID == sd2.threadUID { 351 t.Fatal("sd and sd2 should have different threaduids") 352 } 353 if len(sd.threads) != 2 || len(sd2.threads) != 2 { 354 t.Fatal("sd and sd2 should both have 2 threads registered") 355 } 356 _, exists1 := sd.threads[sd.threadUID] 357 _, exists2 := sd.threads[sd2.threadUID] 358 _, exists3 := sd2.threads[sd.threadUID] 359 _, exists4 := sd2.threads[sd2.threadUID] 360 if exists := exists1 && exists2 && exists3 && exists4; !exists { 361 t.Fatal("sd and sd1's threads don't contain the right uids") 362 } 363 // Open /sub manually and make sure that subDir and sdSub are consistent. 364 sdSub, err := fs.OpenSiaDir(newSiaPath("sub")) 365 if err != nil { 366 t.Fatal(err) 367 } 368 defer func() { 369 if err := sdSub.Close(); err != nil { 370 t.Fatal(err) 371 } 372 }() 373 if err := subNode.checkNode(1, 1, 0); err != nil { 374 t.Fatal(err) 375 } 376 if err := sdSub.checkNode(1, 1, 0); err != nil { 377 t.Fatal(err) 378 } 379 } 380 381 // TestOpenSiaFile confirms that a previously created SiaFile can be opened and 382 // that the filesystem tree is extended accordingly in the process. 383 func TestOpenSiaFile(t *testing.T) { 384 if testing.Short() && !build.VLONG { 385 t.SkipNow() 386 } 387 t.Parallel() 388 // Create filesystem. 389 root := filepath.Join(testDir(t.Name()), "fs-root") 390 fs := newTestFileSystem(root) 391 // Create file /file 392 sp := newSiaPath("file") 393 fs.addTestSiaFile(sp) 394 // Open the newly created file. 395 sf, err := fs.OpenSiaFile(sp) 396 if err != nil { 397 t.Fatal(err) 398 } 399 defer func() { 400 if err := sf.Close(); err != nil { 401 t.Fatal(err) 402 } 403 }() 404 // Confirm the integrity of the file. 405 if *sf.name != "file" { 406 t.Fatalf("name of file should be file but was %v", *sf.name) 407 } 408 if *sf.path != filepath.Join(root, (*sf.name)+skymodules.SiaFileExtension) { 409 t.Fatal("file has wrong path", *sf.path) 410 } 411 if sf.parent != &fs.DirNode { 412 t.Fatalf("parent of file should be %v but was %v", &fs.node, sf.parent) 413 } 414 if sf.threadUID == 0 { 415 t.Fatal("threaduid wasn't set") 416 } 417 if len(sf.threads) != 1 { 418 t.Fatalf("len(threads) should be 1 but was %v", len(sf.threads)) 419 } 420 if _, exists := sf.threads[sf.threadUID]; !exists { 421 t.Fatal("threaduid doesn't exist in threads map") 422 } 423 // Confirm the integrity of the root node. 424 if len(fs.threads) != 0 { 425 t.Fatalf("Expected fs.threads to have length 0 but was %v", len(fs.threads)) 426 } 427 if len(fs.directories) != 0 { 428 t.Fatalf("Expected 0 subdirectories in the root but got %v", len(fs.directories)) 429 } 430 if len(fs.files) != 1 { 431 t.Fatalf("Expected 1 file in the root but got %v", len(fs.files)) 432 } 433 // Create file /sub/file 434 sp = newSiaPath("/sub1/sub2/file") 435 fs.addTestSiaFile(sp) 436 // Open the newly created file. 437 sf2, err := fs.OpenSiaFile(sp) 438 if err != nil { 439 t.Fatal(err) 440 } 441 defer func() { 442 if err := sf2.Close(); err != nil { 443 t.Fatal(err) 444 } 445 }() 446 // Confirm the integrity of the file. 447 if *sf2.name != "file" { 448 t.Fatalf("name of file should be file but was %v", *sf2.name) 449 } 450 if *sf2.parent.name != "sub2" { 451 t.Fatalf("parent of file should be %v but was %v", "sub", *sf2.parent.name) 452 } 453 if sf2.threadUID == 0 { 454 t.Fatal("threaduid wasn't set") 455 } 456 if len(sf2.threads) != 1 { 457 t.Fatalf("len(threads) should be 1 but was %v", len(sf2.threads)) 458 } 459 // Confirm the integrity of the "sub2" folder. 460 sub2 := sf2.parent 461 if err := sub2.checkNode(0, 0, 1); err != nil { 462 t.Fatal(err) 463 } 464 if _, exists := sf2.threads[sf2.threadUID]; !exists { 465 t.Fatal("threaduid doesn't exist in threads map") 466 } 467 // Confirm the integrity of the "sub1" folder. 468 sub1 := sub2.parent 469 if err := sub1.checkNode(0, 1, 0); err != nil { 470 t.Fatal(err) 471 } 472 if _, exists := sf2.threads[sf2.threadUID]; !exists { 473 t.Fatal("threaduid doesn't exist in threads map") 474 } 475 } 476 477 // TestCloseSiaDir tests that closing an opened directory shrinks the tree 478 // accordingly. 479 func TestCloseSiaDir(t *testing.T) { 480 if testing.Short() && !build.VLONG { 481 t.SkipNow() 482 } 483 t.Parallel() 484 // Create filesystem. 485 root := filepath.Join(testDir(t.Name()), "fs-root") 486 fs := newTestFileSystem(root) 487 // Create dir /sub/foo 488 sp := newSiaPath("sub1/foo") 489 if err := fs.NewSiaDir(sp, skymodules.DefaultDirPerm); err != nil { 490 t.Fatal(err) 491 } 492 // Open the newly created dir. 493 sd, err := fs.OpenSiaDir(sp) 494 if err != nil { 495 t.Fatal(err) 496 } 497 if len(sd.threads) != 1 { 498 t.Fatalf("There should be 1 thread in sd.threads but got %v", len(sd.threads)) 499 } 500 if len(sd.parent.threads) != 0 { 501 t.Fatalf("The parent shouldn't have any threads but had %v", len(sd.parent.threads)) 502 } 503 if len(fs.directories) != 1 { 504 t.Fatalf("There should be 1 directory in fs.directories but got %v", len(fs.directories)) 505 } 506 if len(sd.parent.directories) != 1 { 507 t.Fatalf("The parent should have 1 directory but got %v", len(sd.parent.directories)) 508 } 509 // After closing it the thread should be gone. 510 sd.Close() 511 if err := fs.checkNode(0, 0, 0); err != nil { 512 t.Fatal(err) 513 } 514 // Open the dir again. This time twice. 515 sd1, err := fs.OpenSiaDir(sp) 516 if err != nil { 517 t.Fatal(err) 518 } 519 sd2, err := fs.OpenSiaDirCustom(sp, true) 520 if err != nil { 521 t.Fatal(err) 522 } 523 if len(sd1.threads) != 2 || len(sd2.threads) != 2 { 524 t.Fatalf("There should be 2 threads in sd.threads but got %v", len(sd1.threads)) 525 } 526 if len(fs.directories) != 1 { 527 t.Fatalf("There should be 1 directory in fs.directories but got %v", len(fs.directories)) 528 } 529 if len(sd1.parent.directories) != 1 || len(sd2.parent.directories) != 1 { 530 t.Fatalf("The parent should have 1 directory but got %v", len(sd.parent.directories)) 531 } 532 // Close one instance. 533 sd1.Close() 534 if len(sd1.threads) != 1 || len(sd2.threads) != 1 { 535 t.Fatalf("There should be 1 thread in sd.threads but got %v", len(sd1.threads)) 536 } 537 if len(fs.directories) != 1 { 538 t.Fatalf("There should be 1 directory in fs.directories but got %v", len(fs.directories)) 539 } 540 if len(sd1.parent.directories) != 1 || len(sd2.parent.directories) != 1 { 541 t.Fatalf("The parent should have 1 directory but got %v", len(sd.parent.directories)) 542 } 543 // Close the second one. 544 if err := sd2.Close(); err != nil { 545 t.Fatal(err) 546 } 547 if len(fs.threads) != 0 { 548 t.Fatalf("There should be 0 threads in fs.threads but got %v", len(fs.threads)) 549 } 550 if len(sd1.threads) != 0 || len(sd2.threads) != 0 { 551 t.Fatalf("There should be 0 threads in sd.threads but got %v", len(sd1.threads)) 552 } 553 if len(fs.directories) != 0 { 554 t.Fatalf("There should be 0 directories in fs.directories but got %v", len(fs.directories)) 555 } 556 } 557 558 // TestCloseSiaFile tests that closing an opened file shrinks the tree 559 // accordingly. 560 func TestCloseSiaFile(t *testing.T) { 561 if testing.Short() && !build.VLONG { 562 t.SkipNow() 563 } 564 t.Parallel() 565 // Create filesystem. 566 root := filepath.Join(testDir(t.Name()), "fs-root") 567 fs := newTestFileSystem(root) 568 // Create file /sub/file 569 sp := newSiaPath("sub/file") 570 fs.addTestSiaFile(sp) 571 // Open the newly created file. 572 sf, err := fs.OpenSiaFile(sp) 573 if err != nil { 574 t.Fatal(err) 575 } 576 if len(sf.threads) != 1 { 577 t.Fatalf("There should be 1 thread in sf.threads but got %v", len(sf.threads)) 578 } 579 if len(sf.parent.threads) != 0 { 580 t.Fatalf("The parent shouldn't have any threads but had %v", len(sf.parent.threads)) 581 } 582 if len(fs.directories) != 1 { 583 t.Fatalf("There should be 1 directory in fs.directories but got %v", len(fs.directories)) 584 } 585 if len(sf.parent.files) != 1 { 586 t.Fatalf("The parent should have 1 file but got %v", len(sf.parent.files)) 587 } 588 // After closing it the thread should be gone. 589 sf.Close() 590 if len(fs.threads) != 0 { 591 t.Fatalf("There should be 0 threads in fs.threads but got %v", len(fs.threads)) 592 } 593 if len(sf.threads) != 0 { 594 t.Fatalf("There should be 0 threads in sd.threads but got %v", len(sf.threads)) 595 } 596 if len(fs.files) != 0 { 597 t.Fatalf("There should be 0 files in fs.files but got %v", len(fs.files)) 598 } 599 // Open the file again. This time twice. 600 sf1, err := fs.OpenSiaFile(sp) 601 if err != nil { 602 t.Fatal(err) 603 } 604 sf2, err := fs.OpenSiaFile(sp) 605 if err != nil { 606 t.Fatal(err) 607 } 608 if len(sf1.threads) != 2 || len(sf2.threads) != 2 { 609 t.Fatalf("There should be 2 threads in sf1.threads but got %v", len(sf1.threads)) 610 } 611 if len(fs.directories) != 1 { 612 t.Fatalf("There should be 1 directory in fs.directories but got %v", len(fs.directories)) 613 } 614 if len(sf1.parent.files) != 1 || len(sf2.parent.files) != 1 { 615 t.Fatalf("The parent should have 1 file but got %v", len(sf1.parent.files)) 616 } 617 // Close one instance. 618 sf1.Close() 619 if len(sf1.threads) != 1 || len(sf2.threads) != 1 { 620 t.Fatalf("There should be 1 thread in sf1.threads but got %v", len(sf1.threads)) 621 } 622 if len(fs.directories) != 1 { 623 t.Fatalf("There should be 1 dir in fs.directories but got %v", len(fs.directories)) 624 } 625 if len(sf1.parent.files) != 1 || len(sf2.parent.files) != 1 { 626 t.Fatalf("The parent should have 1 file but got %v", len(sf1.parent.files)) 627 } 628 if len(sf1.parent.parent.directories) != 1 { 629 t.Fatalf("The root should have 1 directory but had %v", len(sf1.parent.parent.directories)) 630 } 631 // Close the second one. 632 sf2.Close() 633 if len(fs.threads) != 0 { 634 t.Fatalf("There should be 0 threads in fs.threads but got %v", len(fs.threads)) 635 } 636 if len(sf1.threads) != 0 || len(sf2.threads) != 0 { 637 t.Fatalf("There should be 0 threads in sd.threads but got %v", len(sf1.threads)) 638 } 639 if len(fs.directories) != 0 { 640 t.Fatalf("There should be 0 directories in fs.directories but got %v", len(fs.directories)) 641 } 642 if len(sf1.parent.files) != 0 || len(sf2.parent.files) != 0 { 643 t.Fatalf("The parent should have 0 files but got %v", len(sf1.parent.files)) 644 } 645 if len(sf1.parent.parent.directories) != 0 { 646 t.Fatalf("The root should have 0 directories but had %v", len(sf1.parent.parent.directories)) 647 } 648 } 649 650 // TestDeleteFile tests that deleting a file works as expected and that certain 651 // edge cases are covered. 652 func TestDeleteFile(t *testing.T) { 653 if testing.Short() && !build.VLONG { 654 t.SkipNow() 655 } 656 t.Parallel() 657 // Create filesystem. 658 root := filepath.Join(testDir(t.Name()), "fs-root") 659 fs := newTestFileSystem(root) 660 // Add a file to the root dir. 661 sp := newSiaPath("foo") 662 fs.addTestSiaFile(sp) 663 // Open the file. 664 sf, err := fs.OpenSiaFile(sp) 665 if err != nil { 666 t.Fatal(err) 667 } 668 // File shouldn't be deleted yet. 669 if sf.Deleted() { 670 t.Fatal("foo is deleted before calling delete") 671 } 672 // Delete it using the filesystem. 673 if err := fs.DeleteFile(sp); err != nil { 674 t.Fatal(err) 675 } 676 // Check that the open instance is marked as deleted. 677 if !sf.Deleted() { 678 t.Fatal("foo should be marked as deleted but wasn't") 679 } 680 // Check that we can't open another instance of foo and that we can't create 681 // a new file at the same path. 682 if _, err := fs.OpenSiaFile(sp); !errors.Contains(err, ErrNotExist) { 683 t.Fatal("err should be ErrNotExist but was:", err) 684 } 685 if err := fs.addTestSiaFileWithErr(sp); err != nil { 686 t.Fatal("err should be nil but was:", err) 687 } 688 } 689 690 // TestDeleteDirectory tests if deleting a directory correctly and recursively 691 // removes the dir. 692 func TestDeleteDirectory(t *testing.T) { 693 if testing.Short() && !build.VLONG { 694 t.SkipNow() 695 } 696 t.Parallel() 697 // Create filesystem. 698 root := filepath.Join(testDir(t.Name()), "fs-root") 699 fs := newTestFileSystem(root) 700 // Add some files. 701 fs.addTestSiaFile(newSiaPath("dir/foo/bar/file1")) 702 fs.addTestSiaFile(newSiaPath("dir/foo/bar/file2")) 703 fs.addTestSiaFile(newSiaPath("dir/foo/bar/file3")) 704 // Delete "foo" 705 if err := fs.DeleteDir(newSiaPath("/dir/foo")); err != nil { 706 t.Fatal(err) 707 } 708 // Check that /dir still exists. 709 if _, err := os.Stat(filepath.Join(root, "dir")); err != nil { 710 t.Fatal(err) 711 } 712 // Check that /dir is empty. 713 if fis, err := ioutil.ReadDir(filepath.Join(root, "dir")); err != nil { 714 t.Fatal(err) 715 } else if len(fis) != 1 { 716 for i, fi := range fis { 717 t.Logf("fi%v: %v", i, fi.Name()) 718 } 719 t.Fatalf("expected 1 file in 'dir' but contains %v files", len(fis)) 720 } 721 } 722 723 // TestRenameFile tests if renaming a single file works as expected. 724 func TestRenameFile(t *testing.T) { 725 if testing.Short() && !build.VLONG { 726 t.SkipNow() 727 } 728 t.Parallel() 729 // Create filesystem. 730 root := filepath.Join(testDir(t.Name()), "fs-root") 731 fs := newTestFileSystem(root) 732 // Add a file to the root dir. 733 foo := newSiaPath("foo") 734 foobar := newSiaPath("foobar") 735 barfoo := newSiaPath("bar/foo") 736 fs.addTestSiaFile(foo) 737 // Rename the file. 738 if err := fs.RenameFile(foo, foobar); err != nil { 739 t.Fatal(err) 740 } 741 // Check if the file was renamed. 742 if _, err := fs.OpenSiaFile(foo); !errors.Contains(err, ErrNotExist) { 743 t.Fatal("expected ErrNotExist but got:", err) 744 } 745 sf, err := fs.OpenSiaFile(foobar) 746 if err != nil { 747 t.Fatal("expected ErrNotExist but got:", err) 748 } 749 sf.Close() 750 // Rename the file again. This time it changes to a non-existent folder. 751 if err := fs.RenameFile(foobar, barfoo); err != nil { 752 t.Fatal(err) 753 } 754 sf, err = fs.OpenSiaFile(barfoo) 755 if err != nil { 756 t.Fatal("expected ErrNotExist but got:", err) 757 } 758 sf.Close() 759 } 760 761 // TestThreadedAccess tests rapidly opening and closing files and directories 762 // from multiple threads to check the locking conventions. 763 func TestThreadedAccess(t *testing.T) { 764 if testing.Short() && !build.VLONG { 765 t.SkipNow() 766 } 767 t.Parallel() 768 // Specify the file structure for the test. 769 filePaths := []string{ 770 "f0", 771 "f1", 772 "f2", 773 774 "d0/f0", "d0/f1", "d0/f2", 775 "d1/f0", "d1/f1", "d1/f2", 776 "d2/f0", "d2/f1", "d2/f2", 777 778 "d0/d0/f0", "d0/d0/f1", "d0/d0/f2", 779 "d0/d1/f0", "d0/d1/f1", "d0/d1/f2", 780 "d0/d2/f0", "d0/d2/f1", "d0/d2/f2", 781 782 "d1/d0/f0", "d1/d0/f1", "d1/d0/f2", 783 "d1/d1/f0", "d1/d1/f1", "d1/d1/f2", 784 "d1/d2/f0", "d1/d2/f1", "d1/d2/f2", 785 786 "d2/d0/f0", "d2/d0/f1", "d2/d0/f2", 787 "d2/d1/f0", "d2/d1/f1", "d2/d1/f2", 788 "d2/d2/f0", "d2/d2/f1", "d2/d2/f2", 789 } 790 // Create filesystem. 791 root := filepath.Join(testDir(t.Name()), "fs-root") 792 fs := newTestFileSystem(root) 793 for _, fp := range filePaths { 794 fs.addTestSiaFile(newSiaPath(fp)) 795 } 796 // Create a few threads which open files 797 var wg sync.WaitGroup 798 numThreads := 5 799 maxNumActions := uint64(50000) 800 numActions := uint64(0) 801 for i := 0; i < numThreads; i++ { 802 wg.Add(1) 803 go func() { 804 defer wg.Done() 805 for { 806 if atomic.LoadUint64(&numActions) >= maxNumActions { 807 break 808 } 809 atomic.AddUint64(&numActions, 1) 810 sp := newSiaPath(filePaths[fastrand.Intn(len(filePaths))]) 811 sf, err := fs.OpenSiaFile(sp) 812 if err != nil { 813 t.Error(err) 814 return 815 } 816 sf.Close() 817 } 818 }() 819 } 820 // Create a few threads which open dirs 821 for i := 0; i < numThreads; i++ { 822 wg.Add(1) 823 go func() { 824 defer wg.Done() 825 for { 826 if atomic.LoadUint64(&numActions) >= maxNumActions { 827 break 828 } 829 atomic.AddUint64(&numActions, 1) 830 sp := newSiaPath(filePaths[fastrand.Intn(len(filePaths))]) 831 sp, err := sp.Dir() 832 if err != nil { 833 t.Error(err) 834 return 835 } 836 sd, err := fs.OpenSiaDir(sp) 837 if err != nil { 838 t.Error(err) 839 return 840 } 841 sd.Close() 842 } 843 }() 844 } 845 wg.Wait() 846 847 // Check the root's integrity. Since all files and dirs were closed, the 848 // node's maps should reflect that. 849 if len(fs.threads) != 0 { 850 t.Fatalf("fs should have 0 threads but had %v", len(fs.threads)) 851 } 852 if len(fs.directories) != 0 { 853 t.Fatalf("fs should have 0 directories but had %v", len(fs.directories)) 854 } 855 if len(fs.files) != 0 { 856 t.Fatalf("fs should have 0 files but had %v", len(fs.files)) 857 } 858 } 859 860 // TestSiaDirRename tests the Rename method of the siadirset. 861 func TestSiaDirRename(t *testing.T) { 862 if testing.Short() { 863 t.SkipNow() 864 } 865 866 // Prepare a filesystem. 867 root := filepath.Join(testDir(t.Name()), "fs-root") 868 os.RemoveAll(root) 869 fs := newTestFileSystem(root) 870 871 // Specify a directory structure for this test. 872 var dirStructure = []string{ 873 "dir1", 874 "dir1/subdir1", 875 "dir1/subdir1/subsubdir1", 876 "dir1/subdir1/subsubdir2", 877 "dir1/subdir1/subsubdir3", 878 "dir1/subdir2", 879 "dir1/subdir2/subsubdir1", 880 "dir1/subdir2/subsubdir2", 881 "dir1/subdir2/subsubdir3", 882 "dir1/subdir3", 883 "dir1/subdir3/subsubdir1", 884 "dir1/subdir3/subsubdir2", 885 "dir1/subdir3/subsubdir3", 886 } 887 // Specify a function that's executed in parallel which continuously saves dirs 888 // to disk. 889 stop := make(chan struct{}) 890 wg := new(sync.WaitGroup) 891 f := func(entry *DirNode) { 892 defer wg.Done() 893 defer func() { 894 if err := entry.Close(); err != nil { 895 t.Error(err) 896 return 897 } 898 }() 899 for { 900 select { 901 case <-stop: 902 return 903 default: 904 } 905 err := entry.UpdateMetadata(siadir.Metadata{}) 906 if err != nil { 907 t.Error(err) 908 return 909 } 910 time.Sleep(50 * time.Millisecond) 911 } 912 } 913 // Create the structure and spawn a goroutine that keeps saving the structure 914 // to disk for each directory. 915 for _, dir := range dirStructure { 916 sp, err := skymodules.NewSiaPath(dir) 917 if err != nil { 918 t.Fatal(err) 919 } 920 err = fs.NewSiaDir(sp, skymodules.DefaultDirPerm) 921 if err != nil { 922 t.Fatal(err) 923 } 924 entry, err := fs.OpenSiaDir(sp) 925 if err != nil { 926 t.Fatal(err) 927 } 928 // 50% chance to spawn goroutine. It's not realistic to assume that all dirs 929 // are loaded. 930 if fastrand.Intn(2) == 0 { 931 wg.Add(1) 932 go f(entry) 933 } else { 934 if err := entry.Close(); err != nil { 935 t.Fatal(err) 936 } 937 } 938 } 939 // Wait a second for the goroutines to write to disk a few times. 940 time.Sleep(time.Second) 941 // Rename dir1 to dir2. 942 oldPath, err1 := skymodules.NewSiaPath(dirStructure[0]) 943 newPath, err2 := skymodules.NewSiaPath("dir2") 944 if err := errors.Compose(err1, err2); err != nil { 945 t.Fatal(err) 946 } 947 if err := fs.RenameDir(oldPath, newPath); err != nil { 948 t.Fatal(err) 949 } 950 // Wait another second for more writes to disk after renaming the dir before 951 // killing the goroutines. 952 time.Sleep(time.Second) 953 close(stop) 954 wg.Wait() 955 time.Sleep(time.Second) 956 // Make sure we can't open any of the old folders on disk but we can open the 957 // new ones. 958 for _, dir := range dirStructure { 959 oldDir, err1 := skymodules.NewSiaPath(dir) 960 newDir, err2 := oldDir.Rebase(oldPath, newPath) 961 if err := errors.Compose(err1, err2); err != nil { 962 t.Fatal(err) 963 } 964 // Open entry with old dir. Shouldn't work. 965 _, err := fs.OpenSiaDir(oldDir) 966 if !errors.Contains(err, ErrNotExist) { 967 t.Fatal("shouldn't be able to open old path", oldDir.String(), err) 968 } 969 // Open entry with new dir. Should succeed. 970 entry, err := fs.OpenSiaDir(newDir) 971 if err != nil { 972 t.Fatal(err) 973 } 974 defer func() { 975 if err := entry.Close(); err != nil { 976 t.Fatal(err) 977 } 978 }() 979 // Check path of entry. 980 if expectedPath := fs.DirPath(newDir); *entry.path != expectedPath { 981 t.Fatalf("entry should have path '%v' but was '%v'", expectedPath, entry.path) 982 } 983 } 984 } 985 986 // TestAddSiaFileFromReader tests the AddSiaFileFromReader method's behavior. 987 func TestAddSiaFileFromReader(t *testing.T) { 988 if testing.Short() { 989 t.SkipNow() 990 } 991 t.Parallel() 992 // Create a fileset with file. 993 sf, sfs, err := newTestFileSystemWithFile(t.Name()) 994 if err != nil { 995 t.Fatal(err) 996 } 997 // Add the existing file to the set again this shouldn't do anything. 998 sr, err := sf.SnapshotReader() 999 if err != nil { 1000 t.Fatal(err) 1001 } 1002 d, err := ioutil.ReadAll(sr) 1003 sr.Close() 1004 if err != nil { 1005 t.Fatal(err) 1006 } 1007 if err := sfs.AddSiaFileFromReader(bytes.NewReader(d), sfs.FileSiaPath(sf)); err != nil { 1008 t.Fatal(err) 1009 } 1010 numSiaFiles := 0 1011 err = sfs.Walk(skymodules.RootSiaPath(), func(path string, info os.FileInfo, err error) error { 1012 if filepath.Ext(path) == skymodules.SiaFileExtension { 1013 numSiaFiles++ 1014 } 1015 return nil 1016 }) 1017 if err != nil { 1018 t.Fatal(err) 1019 } 1020 // There should be 1 siafile. 1021 if numSiaFiles != 1 { 1022 t.Fatalf("Found %v siafiles but expected %v", numSiaFiles, 1) 1023 } 1024 // Load the same siafile again, but change the UID. 1025 b, err := ioutil.ReadFile(sf.SiaFilePath()) 1026 if err != nil { 1027 t.Fatal(err) 1028 } 1029 reader := bytes.NewReader(b) 1030 newSF, newChunks, err := siafile.LoadSiaFileFromReaderWithChunks(reader, sf.SiaFilePath(), sfs.staticWal) 1031 if err != nil { 1032 t.Fatal(err) 1033 } 1034 // Save the file to a temporary location with the new uid. 1035 newSF.UpdateUniqueID() 1036 newSF.SetSiaFilePath(sf.SiaFilePath() + "_tmp") 1037 if err := newSF.SaveWithChunks(newChunks); err != nil { 1038 t.Fatal(err) 1039 } 1040 // Grab the pre-import UID after changing it. 1041 preImportUID := newSF.UID() 1042 // Import the file. This should work because the files no longer share the same 1043 // UID. 1044 b, err = ioutil.ReadFile(newSF.SiaFilePath()) 1045 if err != nil { 1046 t.Fatal(err) 1047 } 1048 // Remove file at temporary location after reading it. 1049 if err := os.Remove(newSF.SiaFilePath()); err != nil { 1050 t.Fatal(err) 1051 } 1052 reader = bytes.NewReader(b) 1053 var newSFSiaPath skymodules.SiaPath 1054 if err := newSFSiaPath.FromSysPath(sf.SiaFilePath(), sfs.Root()); err != nil { 1055 t.Fatal(err) 1056 } 1057 if err := sfs.AddSiaFileFromReader(reader, newSFSiaPath); err != nil { 1058 t.Fatal(err) 1059 } 1060 // Reload newSF with the new expected path. 1061 newSFPath := filepath.Join(filepath.Dir(sf.SiaFilePath()), newSFSiaPath.String()+"_1"+skymodules.SiaFileExtension) 1062 newSF, err = siafile.LoadSiaFile(newSFPath, sfs.staticWal) 1063 if err != nil { 1064 t.Fatal(err) 1065 } 1066 // sf and newSF should have the same pieces. 1067 for chunkIndex := uint64(0); chunkIndex < sf.NumChunks(); chunkIndex++ { 1068 piecesOld, err1 := sf.Pieces(chunkIndex) 1069 piecesNew, err2 := newSF.Pieces(chunkIndex) 1070 if err := errors.Compose(err1, err2); err != nil { 1071 t.Fatal(err) 1072 } 1073 if !reflect.DeepEqual(piecesOld, piecesNew) { 1074 t.Log("piecesOld: ", piecesOld) 1075 t.Log("piecesNew: ", piecesNew) 1076 t.Fatal("old pieces don't match new pieces") 1077 } 1078 } 1079 numSiaFiles = 0 1080 err = sfs.Walk(skymodules.RootSiaPath(), func(path string, info os.FileInfo, err error) error { 1081 if filepath.Ext(path) == skymodules.SiaFileExtension { 1082 numSiaFiles++ 1083 } 1084 return nil 1085 }) 1086 if err != nil { 1087 t.Fatal(err) 1088 } 1089 // There should be 2 siafiles. 1090 if numSiaFiles != 2 { 1091 t.Fatalf("Found %v siafiles but expected %v", numSiaFiles, 2) 1092 } 1093 // The UID should have changed. 1094 if newSF.UID() == preImportUID { 1095 t.Fatal("newSF UID should have changed after importing the file") 1096 } 1097 if !strings.HasSuffix(newSF.SiaFilePath(), "_1"+skymodules.SiaFileExtension) { 1098 t.Fatal("SiaFile should have a suffix but didn't") 1099 } 1100 // Should be able to open the new file from disk. 1101 if _, err := os.Stat(newSF.SiaFilePath()); err != nil { 1102 t.Fatal(err) 1103 } 1104 } 1105 1106 // TestSiaFileSetDeleteOpen checks that deleting an entry from the set followed 1107 // by creating a Siafile with the same name without closing the deleted entry 1108 // works as expected. 1109 func TestSiaFileSetDeleteOpen(t *testing.T) { 1110 if testing.Short() { 1111 t.SkipNow() 1112 } 1113 t.Parallel() 1114 1115 // Create filesystem. 1116 sfs := newTestFileSystem(testDir(t.Name())) 1117 siaPath := skymodules.RandomSiaPath() 1118 rc, _ := skymodules.NewRSSubCode(10, 20, crypto.SegmentSize) 1119 fileSize := uint64(100) 1120 source := "" 1121 sk := crypto.GenerateSiaKey(crypto.TypeDefaultRenter) 1122 fileMode := os.FileMode(persist.DefaultDiskPermissionsTest) 1123 1124 // Repeatedly create a SiaFile and delete it while still keeping the entry 1125 // around. That should only be possible without errors if the correctly 1126 // delete the entry from the set. 1127 var entries []*FileNode 1128 for i := 0; i < 10; i++ { 1129 // Create SiaFile 1130 up := skymodules.FileUploadParams{ 1131 Source: source, 1132 SiaPath: siaPath, 1133 ErasureCode: rc, 1134 } 1135 err := sfs.NewSiaFile(up.SiaPath, up.Source, up.ErasureCode, sk, fileSize, fileMode) 1136 if err != nil { 1137 t.Fatal(err) 1138 } 1139 entry, err := sfs.OpenSiaFile(up.SiaPath) 1140 if err != nil { 1141 t.Fatal(err) 1142 } 1143 // Delete SiaFile 1144 if err := sfs.DeleteFile(sfs.FileSiaPath(entry)); err != nil { 1145 t.Fatal(err) 1146 } 1147 // The map should be empty. 1148 if len(sfs.files) != 0 { 1149 t.Fatal("SiaFileMap should have 1 file") 1150 } 1151 // Append the entry to make sure we can close it later. 1152 entries = append(entries, entry) 1153 } 1154 // The SiaFile shouldn't exist anymore. 1155 _, err := sfs.OpenSiaFile(siaPath) 1156 if !errors.Contains(err, ErrNotExist) { 1157 t.Fatal("SiaFile shouldn't exist anymore") 1158 } 1159 // Close the entries. 1160 for _, entry := range entries { 1161 if err := entry.Close(); err != nil { 1162 t.Fatal(err) 1163 } 1164 } 1165 } 1166 1167 // TestSiaFileSetOpenClose tests that the threadCount of the siafile is 1168 // incremented and decremented properly when Open() and Close() are called 1169 func TestSiaFileSetOpenClose(t *testing.T) { 1170 if testing.Short() { 1171 t.SkipNow() 1172 } 1173 t.Parallel() 1174 1175 // Create SiaFileSet with SiaFile 1176 entry, sfs, err := newTestFileSystemWithFile(t.Name()) 1177 if err != nil { 1178 t.Fatal(err) 1179 } 1180 siaPath := sfs.FileSiaPath(entry) 1181 exists, _ := sfs.FileExists(siaPath) 1182 if !exists { 1183 t.Fatal("No SiaFileSetEntry found") 1184 } 1185 if err != nil { 1186 t.Fatal(err) 1187 } 1188 1189 // Confirm 1 file is in memory 1190 if len(sfs.files) != 1 { 1191 t.Fatalf("Expected SiaFileSet map to be of length 1, instead is length %v", len(sfs.files)) 1192 } 1193 1194 // Confirm threadCount is incremented properly 1195 if len(entry.threads) != 1 { 1196 t.Fatalf("Expected threadMap to be of length 1, got %v", len(entry.threads)) 1197 } 1198 1199 // Close SiaFileSetEntry 1200 if err := entry.Close(); err != nil { 1201 t.Fatal(err) 1202 } 1203 1204 // Confirm that threadCount was decremented 1205 if len(entry.threads) != 0 { 1206 t.Fatalf("Expected threadCount to be 0, got %v", len(entry.threads)) 1207 } 1208 1209 // Confirm file was removed from memory 1210 if len(sfs.files) != 0 { 1211 t.Fatalf("Expected SiaFileSet map to contain 0 files, instead is length %v", len(sfs.files)) 1212 } 1213 1214 // Open siafile again and confirm threadCount was incremented 1215 entry, err = sfs.OpenSiaFile(siaPath) 1216 if err != nil { 1217 t.Fatal(err) 1218 } 1219 if len(entry.threads) != 1 { 1220 t.Fatalf("Expected threadCount to be 1, got %v", len(entry.threads)) 1221 } 1222 } 1223 1224 // TestFilesInMemory confirms that files are added and removed from memory 1225 // as expected when files are in use and not in use 1226 func TestFilesInMemory(t *testing.T) { 1227 if testing.Short() { 1228 t.SkipNow() 1229 } 1230 t.Parallel() 1231 1232 // Create SiaFileSet with SiaFile 1233 entry, sfs, err := newTestFileSystemWithFile(t.Name()) 1234 if err != nil { 1235 t.Fatal(err) 1236 } 1237 siaPath := sfs.FileSiaPath(entry) 1238 exists, _ := sfs.FileExists(siaPath) 1239 if !exists { 1240 t.Fatal("No SiaFileSetEntry found") 1241 } 1242 if err != nil { 1243 t.Fatal(err) 1244 } 1245 // Confirm there is 1 file in memory. 1246 if len(sfs.files) != 1 { 1247 t.Fatal("Expected 1 files in memory, got:", len(sfs.files)) 1248 } 1249 // Close File 1250 if err := entry.Close(); err != nil { 1251 t.Fatal(err) 1252 } 1253 // Confirm there are no files in memory 1254 if len(sfs.files) != 0 { 1255 t.Fatal("Expected 0 files in memory, got:", len(sfs.files)) 1256 } 1257 1258 // Test accessing the same file from two separate threads 1259 // 1260 // Open file 1261 entry1, err := sfs.OpenSiaFile(siaPath) 1262 if err != nil { 1263 t.Fatal(err) 1264 } 1265 // Confirm there is 1 file in memory 1266 if len(sfs.files) != 1 { 1267 t.Fatal("Expected 1 file in memory, got:", len(sfs.files)) 1268 } 1269 // Access the file again 1270 entry2, err := sfs.OpenSiaFile(siaPath) 1271 if err != nil { 1272 t.Fatal(err) 1273 } 1274 // Confirm there is still only has 1 file in memory 1275 if len(sfs.files) != 1 { 1276 t.Fatal("Expected 1 file in memory, got:", len(sfs.files)) 1277 } 1278 // Close one of the file instances 1279 entry1.Close() 1280 // Confirm there is still only has 1 file in memory 1281 if len(sfs.files) != 1 { 1282 t.Fatal("Expected 1 file in memory, got:", len(sfs.files)) 1283 } 1284 1285 // Confirm closing out remaining files removes all files from memory 1286 // 1287 // Close last instance of the first file 1288 entry2.Close() 1289 // Confirm there is no file in memory 1290 if len(sfs.files) != 0 { 1291 t.Fatal("Expected 0 files in memory, got:", len(sfs.files)) 1292 } 1293 } 1294 1295 // TestRenameFileInMemory confirms that threads that have access to a file 1296 // will continue to have access to the file even it another thread renames it 1297 func TestRenameFileInMemory(t *testing.T) { 1298 if testing.Short() { 1299 t.SkipNow() 1300 } 1301 t.Parallel() 1302 1303 // Create FileSystem with corresponding siafile. 1304 entry, sfs, err := newTestFileSystemWithFile(t.Name()) 1305 if err != nil { 1306 t.Fatal(err) 1307 } 1308 siaPath := sfs.FileSiaPath(entry) 1309 exists, _ := sfs.FileExists(siaPath) 1310 if !exists { 1311 t.Fatal("No SiaFile found") 1312 } 1313 if err != nil { 1314 t.Fatal(err) 1315 } 1316 1317 // Confirm there is 1 file in memory. 1318 if len(sfs.files) != 1 { 1319 t.Fatal("Expected 1 file in memory, got:", len(sfs.files)) 1320 } 1321 1322 // Test renaming an instance of a file 1323 // 1324 // Access file with another instance 1325 entry2, err := sfs.OpenSiaFile(siaPath) 1326 if err != nil { 1327 t.Fatal(err) 1328 } 1329 // Confirm that renter still only has 1 file in memory. 1330 if len(sfs.files) != 1 { 1331 t.Fatal("Expected 1 file in memory, got:", len(sfs.files)) 1332 } 1333 _, err = os.Stat(entry.SiaFilePath()) 1334 if err != nil { 1335 t.Fatal(err) 1336 } 1337 // Rename second instance 1338 newSiaPath := skymodules.RandomSiaPath() 1339 err = sfs.RenameFile(siaPath, newSiaPath) 1340 if err != nil { 1341 t.Fatal(err) 1342 } 1343 // Confirm there are still only 1 file in memory as renaming doesn't add 1344 // the new name to memory 1345 if len(sfs.files) != 1 { 1346 t.Fatal("Expected 1 file in memory, got:", len(sfs.files)) 1347 } 1348 // Close instance of renamed file 1349 err = entry2.Close() 1350 if err != nil { 1351 t.Fatal(err) 1352 } 1353 // Confirm there are still only 1 file in memory 1354 if len(sfs.files) != 1 { 1355 t.Fatal("Expected 1 file in memory, got:", len(sfs.files)) 1356 } 1357 // Close other instance of second file 1358 err = entry.Close() 1359 if err != nil { 1360 t.Fatal(err) 1361 } 1362 // Confirm there is no file in memory 1363 if len(sfs.files) != 0 { 1364 t.Fatal("Expected 0 files in memory, got:", len(sfs.files)) 1365 } 1366 } 1367 1368 // TestDeleteFileInMemory confirms that threads that have access to a file 1369 // will continue to have access to the file even if another thread deletes it 1370 func TestDeleteFileInMemory(t *testing.T) { 1371 if testing.Short() { 1372 t.SkipNow() 1373 } 1374 t.Parallel() 1375 1376 // Create FileSystem with SiaFile 1377 entry, sfs, err := newTestFileSystemWithFile(t.Name()) 1378 if err != nil { 1379 t.Fatal(err) 1380 } 1381 siaPath := sfs.FileSiaPath(entry) 1382 exists, _ := sfs.FileExists(siaPath) 1383 if !exists { 1384 t.Fatal("No SiaFileSetEntry found") 1385 } 1386 if err != nil { 1387 t.Fatal(err) 1388 } 1389 1390 // Confirm there is 1 file in memory 1391 if len(sfs.files) != 1 { 1392 t.Fatal("Expected 1 file in memory, got:", len(sfs.files)) 1393 } 1394 1395 // Test deleting an instance of a file 1396 // 1397 // Access the file again 1398 entry2, err := sfs.OpenSiaFile(siaPath) 1399 if err != nil { 1400 t.Fatal(err) 1401 } 1402 // Confirm there is still only has 1 file in memory 1403 if len(sfs.files) != 1 { 1404 t.Fatal("Expected 1 file in memory, got:", len(sfs.files)) 1405 } 1406 // Delete and close instance of file. 1407 if err := sfs.DeleteFile(siaPath); err != nil { 1408 t.Fatal(err) 1409 } 1410 err = entry2.Close() 1411 if err != nil { 1412 t.Fatal(err) 1413 } 1414 // There should be no file in the filesystem after deleting it. 1415 if len(sfs.files) != 0 { 1416 t.Fatal("Expected 0 files in memory, got:", len(sfs.files)) 1417 } 1418 // Confirm other instance is still in memory by calling methods on it. 1419 if !entry.Deleted() { 1420 t.Fatal("Expected file to be deleted") 1421 } 1422 1423 // Confirm closing out remaining files removes all files from memory 1424 // 1425 // Close last instance of the first file 1426 err = entry.Close() 1427 if err != nil { 1428 t.Fatal(err) 1429 } 1430 // Confirm renter has one file in memory 1431 if len(sfs.files) != 0 { 1432 t.Fatal("Expected 0 file in memory, got:", len(sfs.files)) 1433 } 1434 } 1435 1436 // TestDeleteCorruptSiaFile confirms that the siafileset will delete a siafile 1437 // even if it cannot be opened 1438 func TestDeleteCorruptSiaFile(t *testing.T) { 1439 if testing.Short() { 1440 t.SkipNow() 1441 } 1442 t.Parallel() 1443 1444 // Create siafileset 1445 _, sfs, err := newTestFileSystemWithFile(t.Name()) 1446 if err != nil { 1447 t.Fatal(err) 1448 } 1449 1450 // Create siafile on disk with random bytes 1451 siaPath, err := skymodules.NewSiaPath("badFile") 1452 if err != nil { 1453 t.Fatal(err) 1454 } 1455 siaFilePath := siaPath.SiaFileSysPath(sfs.Root()) 1456 err = ioutil.WriteFile(siaFilePath, fastrand.Bytes(100), persist.DefaultDiskPermissionsTest) 1457 if err != nil { 1458 t.Fatal(err) 1459 } 1460 1461 // Confirm the siafile cannot be opened 1462 _, err = sfs.OpenSiaFile(siaPath) 1463 if err == nil || errors.Contains(err, ErrNotExist) { 1464 t.Fatal("expected open to fail for read error but instead got:", err) 1465 } 1466 1467 // Delete the siafile 1468 err = sfs.DeleteFile(siaPath) 1469 if err != nil { 1470 t.Fatal(err) 1471 } 1472 1473 // Confirm the file is no longer on disk 1474 _, err = os.Stat(siaFilePath) 1475 if !os.IsNotExist(err) { 1476 t.Fatal("Expected err to be that file does not exists but was:", err) 1477 } 1478 } 1479 1480 // TestSiaDirDelete tests the DeleteDir method of the siafileset. 1481 func TestSiaDirDelete(t *testing.T) { 1482 if testing.Short() { 1483 t.SkipNow() 1484 } 1485 t.Parallel() 1486 1487 // Prepare a siadirset 1488 root := filepath.Join(testDir(t.Name()), "fs-root") 1489 os.RemoveAll(root) 1490 fs := newTestFileSystem(root) 1491 1492 // Specify a directory structure for this test. 1493 var dirStructure = []string{ 1494 "dir1", 1495 "dir1/subdir1", 1496 "dir1/subdir1/subsubdir1", 1497 "dir1/subdir1/subsubdir2", 1498 "dir1/subdir1/subsubdir3", 1499 "dir1/subdir2", 1500 "dir1/subdir2/subsubdir1", 1501 "dir1/subdir2/subsubdir2", 1502 "dir1/subdir2/subsubdir3", 1503 "dir1/subdir3", 1504 "dir1/subdir3/subsubdir1", 1505 "dir1/subdir3/subsubdir2", 1506 "dir1/subdir3/subsubdir3", 1507 } 1508 // Specify a function that's executed in parallel which continuously saves a 1509 // file to disk. 1510 stop := make(chan struct{}) 1511 wg := new(sync.WaitGroup) 1512 f := func(entry *FileNode) { 1513 defer wg.Done() 1514 defer func() { 1515 if err := entry.Close(); err != nil { 1516 t.Error(err) 1517 return 1518 } 1519 }() 1520 for { 1521 select { 1522 case <-stop: 1523 return 1524 default: 1525 } 1526 err := entry.SaveHeader() 1527 if err != nil && !errors.Contains(err, siafile.ErrDeleted) { 1528 t.Error(err) 1529 return 1530 } 1531 time.Sleep(50 * time.Millisecond) 1532 } 1533 } 1534 // Create the structure and spawn a goroutine that keeps saving the structure 1535 // to disk for each directory. 1536 for _, dir := range dirStructure { 1537 sp, err := skymodules.NewSiaPath(dir) 1538 if err != nil { 1539 t.Fatal(err) 1540 } 1541 err = fs.NewSiaDir(sp, skymodules.DefaultDirPerm) 1542 if err != nil { 1543 t.Fatal(err) 1544 } 1545 entry, err := fs.OpenSiaDir(sp) 1546 if err != nil { 1547 t.Fatal(err) 1548 } 1549 // 50% chance to close the dir. 1550 if fastrand.Intn(2) == 0 { 1551 if err := entry.Close(); err != nil { 1552 t.Fatal(err) 1553 } 1554 } 1555 // Create a file in the dir. 1556 fileSP, err := sp.Join(hex.EncodeToString(fastrand.Bytes(16))) 1557 if err != nil { 1558 t.Fatal(err) 1559 } 1560 ec, _ := skymodules.NewRSSubCode(10, 20, crypto.SegmentSize) 1561 up := skymodules.FileUploadParams{Source: "", SiaPath: fileSP, ErasureCode: ec} 1562 err = fs.NewSiaFile(up.SiaPath, up.Source, up.ErasureCode, crypto.GenerateSiaKey(crypto.TypeDefaultRenter), 100, persist.DefaultDiskPermissionsTest) 1563 if err != nil { 1564 t.Fatal(err) 1565 } 1566 sf, err := fs.OpenSiaFile(up.SiaPath) 1567 if err != nil { 1568 t.Fatal(err) 1569 } 1570 // 50% chance to spawn goroutine. It's not realistic to assume that all dirs 1571 // are loaded. 1572 if fastrand.Intn(2) == 0 { 1573 wg.Add(1) 1574 go f(sf) 1575 } else { 1576 sf.Close() 1577 } 1578 } 1579 // Wait a second for the goroutines to write to disk a few times. 1580 time.Sleep(time.Second) 1581 // Delete dir1. 1582 sp, err := skymodules.NewSiaPath("dir1") 1583 if err != nil { 1584 t.Fatal(err) 1585 } 1586 if err := fs.DeleteDir(sp); err != nil { 1587 t.Fatal(err) 1588 } 1589 1590 // Wait another second for more writes to disk after renaming the dir before 1591 // killing the goroutines. 1592 time.Sleep(time.Second) 1593 close(stop) 1594 wg.Wait() 1595 time.Sleep(time.Second) 1596 // The root siafile dir should be empty except for 1 .siadir file. 1597 files, err := fs.ReadDir(skymodules.RootSiaPath()) 1598 if err != nil { 1599 t.Fatal(err) 1600 } 1601 if len(files) != 1 { 1602 for _, file := range files { 1603 t.Log("Found ", file.Name()) 1604 } 1605 t.Fatalf("There should be %v files/folders in the root dir but found %v\n", 1, len(files)) 1606 } 1607 for _, file := range files { 1608 if filepath.Ext(file.Name()) != skymodules.SiaDirExtension { 1609 t.Fatal("Encountered unexpected file:", file.Name()) 1610 } 1611 } 1612 } 1613 1614 // TestSiaDirRenameWithFiles tests the RenameDir method of the filesystem with 1615 // files. 1616 func TestSiaDirRenameWithFiles(t *testing.T) { 1617 if testing.Short() { 1618 t.SkipNow() 1619 } 1620 t.Parallel() 1621 1622 // Prepare a filesystem. 1623 root := filepath.Join(testDir(t.Name()), "fs-root") 1624 os.RemoveAll(root) 1625 fs := newTestFileSystem(root) 1626 1627 // Prepare parameters for siafiles. 1628 rc, _ := skymodules.NewRSSubCode(10, 20, crypto.SegmentSize) 1629 fileSize := uint64(100) 1630 source := "" 1631 sk := crypto.GenerateSiaKey(crypto.TypeDefaultRenter) 1632 fileMode := os.FileMode(persist.DefaultDiskPermissionsTest) 1633 1634 // Specify a directory structure for this test. 1635 var dirStructure = []string{ 1636 "dir1", 1637 "dir1/subdir1", 1638 "dir1/subdir1/subsubdir1", 1639 "dir1/subdir1/subsubdir2", 1640 "dir1/subdir1/subsubdir3", 1641 "dir1/subdir2", 1642 "dir1/subdir2/subsubdir1", 1643 "dir1/subdir2/subsubdir2", 1644 "dir1/subdir2/subsubdir3", 1645 "dir1/subdir3", 1646 "dir1/subdir3/subsubdir1", 1647 "dir1/subdir3/subsubdir2", 1648 "dir1/subdir3/subsubdir3", 1649 } 1650 // Specify a function that's executed in parallel which continuously saves a 1651 // file to disk. 1652 stop := make(chan struct{}) 1653 wg := new(sync.WaitGroup) 1654 f := func(entry *FileNode) { 1655 defer wg.Done() 1656 defer func() { 1657 if err := entry.Close(); err != nil { 1658 t.Error(err) 1659 return 1660 } 1661 }() 1662 for { 1663 select { 1664 case <-stop: 1665 return 1666 default: 1667 } 1668 err := entry.SaveHeader() 1669 if err != nil { 1670 t.Error(err) 1671 return 1672 } 1673 time.Sleep(50 * time.Millisecond) 1674 } 1675 } 1676 // Create the structure and spawn a goroutine that keeps saving the structure 1677 // to disk for each directory. 1678 for _, dir := range dirStructure { 1679 sp, err := skymodules.NewSiaPath(dir) 1680 if err != nil { 1681 t.Fatal(err) 1682 } 1683 err = fs.NewSiaDir(sp, skymodules.DefaultDirPerm) 1684 if err != nil { 1685 t.Fatal(err) 1686 } 1687 entry, err := fs.OpenSiaDir(sp) 1688 // 50% chance to close the dir. 1689 if fastrand.Intn(2) == 0 { 1690 if err := entry.Close(); err != nil { 1691 t.Fatal(err) 1692 } 1693 } 1694 // Create a file in the dir. 1695 fileSP, err := sp.Join(hex.EncodeToString(fastrand.Bytes(16))) 1696 if err != nil { 1697 t.Fatal(err) 1698 } 1699 err = fs.NewSiaFile(fileSP, source, rc, sk, fileSize, fileMode) 1700 if err != nil { 1701 t.Fatal(err) 1702 } 1703 sf, err := fs.OpenSiaFile(fileSP) 1704 if err != nil { 1705 t.Fatal(err) 1706 } 1707 // 50% chance to spawn goroutine. It's not realistic to assume that all dirs 1708 // are loaded. 1709 if fastrand.Intn(2) == 0 { 1710 wg.Add(1) 1711 go f(sf) 1712 } else { 1713 sf.Close() 1714 } 1715 } 1716 // Wait a second for the goroutines to write to disk a few times. 1717 time.Sleep(time.Second) 1718 // Rename dir1 to dir2. 1719 oldPath, err1 := skymodules.NewSiaPath(dirStructure[0]) 1720 newPath, err2 := skymodules.NewSiaPath("dir2") 1721 if err := errors.Compose(err1, err2); err != nil { 1722 t.Fatal(err) 1723 } 1724 if err := fs.RenameDir(oldPath, newPath); err != nil { 1725 t.Fatal(err) 1726 } 1727 // Wait another second for more writes to disk after renaming the dir before 1728 // killing the goroutines. 1729 time.Sleep(time.Second) 1730 close(stop) 1731 wg.Wait() 1732 time.Sleep(time.Second) 1733 // Make sure we can't open any of the old folders/files on disk but we can open 1734 // the new ones. 1735 for _, dir := range dirStructure { 1736 oldDir, err1 := skymodules.NewSiaPath(dir) 1737 newDir, err2 := oldDir.Rebase(oldPath, newPath) 1738 if err := errors.Compose(err1, err2); err != nil { 1739 t.Fatal(err) 1740 } 1741 // Open entry with old dir. Shouldn't work. 1742 _, err := fs.OpenSiaDir(oldDir) 1743 if !errors.Contains(err, ErrNotExist) { 1744 t.Fatal("shouldn't be able to open old path", oldDir.String(), err) 1745 } 1746 // Old dir shouldn't exist. 1747 if _, err = fs.Stat(oldDir); !os.IsNotExist(err) { 1748 t.Fatal(err) 1749 } 1750 // Open entry with new dir. Should succeed. 1751 entry, err := fs.OpenSiaDir(newDir) 1752 if err != nil { 1753 t.Fatal(err) 1754 } 1755 defer func() { 1756 err = entry.Close() 1757 if err != nil { 1758 t.Fatal(err) 1759 } 1760 }() 1761 // New dir should contain 1 siafile. 1762 fis, err := fs.ReadDir(newDir) 1763 if err != nil { 1764 t.Fatal(err) 1765 } 1766 numFiles := 0 1767 for _, fi := range fis { 1768 if !fi.IsDir() && filepath.Ext(fi.Name()) == skymodules.SiaFileExtension { 1769 numFiles++ 1770 } 1771 } 1772 if numFiles != 1 { 1773 t.Fatalf("there should be 1 file in the new dir not %v", numFiles) 1774 } 1775 // Check siapath of entry. 1776 if entry.managedAbsPath() != fs.DirPath(newDir) { 1777 t.Fatalf("entry should have path '%v' but was '%v'", fs.DirPath(newDir), entry.managedAbsPath()) 1778 } 1779 } 1780 } 1781 1782 // TestRenameDirInMemory confirms that threads that have access to a dir 1783 // will continue to have access to the dir even if the dir is renamed. 1784 func TestRenameDirInMemory(t *testing.T) { 1785 if testing.Short() { 1786 t.SkipNow() 1787 } 1788 t.Parallel() 1789 1790 // Create FileSystem with corresponding siadir. 1791 entry, fs, err := newTestFileSystemWithDir(t.Name()) 1792 if err != nil { 1793 t.Fatal(err) 1794 } 1795 siaPath := fs.DirSiaPath(entry) 1796 exists, _ := fs.DirExists(siaPath) 1797 if !exists { 1798 t.Fatal("No SiaDir found") 1799 } 1800 1801 // Confirm there are 1 dir in memory 1802 if len(fs.directories) != 1 { 1803 t.Fatal("Expected 1 dir in memory, got:", len(fs.directories)) 1804 } 1805 1806 // Access dir with another instance 1807 entry2, err := fs.OpenSiaDir(siaPath) 1808 if err != nil { 1809 t.Fatal(err) 1810 } 1811 // Confirm that renter still only has 1 dir in memory 1812 if len(fs.directories) != 1 { 1813 t.Fatal("Expected 1 dir in memory, got:", len(fs.directories)) 1814 } 1815 path, err := entry2.Path() 1816 if err != nil { 1817 t.Fatal(err) 1818 } 1819 _, err = os.Stat(path) 1820 if err != nil { 1821 t.Fatal(err) 1822 } 1823 // Rename the directory. 1824 newSiaPath := skymodules.RandomSiaPath() 1825 err = fs.RenameDir(siaPath, newSiaPath) 1826 if err != nil { 1827 t.Fatal(err) 1828 } 1829 // Confirm both instances are still in memory. 1830 deleted, err := entry.Deleted() 1831 if err != nil { 1832 t.Fatal(err) 1833 } 1834 if deleted { 1835 t.Fatal("Expected file to not be deleted") 1836 } 1837 deleted, err = entry2.Deleted() 1838 if err != nil { 1839 t.Fatal(err) 1840 } 1841 if deleted { 1842 t.Fatal("Expected file to not be deleted") 1843 } 1844 1845 // Create a new dir at the same path. 1846 err = fs.NewSiaDir(siaPath, skymodules.DefaultDirPerm) 1847 if err != nil { 1848 t.Fatal(err) 1849 } 1850 // Confirm the directory is still in memory. 1851 deleted, err = entry.Deleted() 1852 if err != nil { 1853 t.Fatal(err) 1854 } 1855 if deleted { 1856 t.Fatal("Expected file to not be deleted") 1857 } 1858 1859 // Confirm there is still only 1 dir in memory as renaming doesn't add 1860 // the new name to memory. 1861 if len(fs.directories) != 1 { 1862 t.Fatal("Expected 1 dir in memory, got:", len(fs.directories)) 1863 } 1864 // Close instance of renamed dir. 1865 err = entry2.Close() 1866 if err != nil { 1867 t.Fatal(err) 1868 } 1869 // Confirm there are still only 1 dir in memory. 1870 if len(fs.directories) != 1 { 1871 t.Fatal("Expected 1 dir in memory, got:", len(fs.directories)) 1872 } 1873 // Close other instance of second dir. 1874 err = entry.Close() 1875 if err != nil { 1876 t.Fatal(err) 1877 } 1878 // Confirm there is no dir in memory. 1879 if len(fs.directories) != 0 { 1880 t.Fatal("Expected 0 dirs in memory, got:", len(fs.directories)) 1881 } 1882 } 1883 1884 // TestDeleteDirInMemory confirms that threads that have access to a dir 1885 // will continue to have access to the dir even if another thread deletes it 1886 func TestDeleteDirInMemory(t *testing.T) { 1887 if testing.Short() { 1888 t.SkipNow() 1889 } 1890 t.Parallel() 1891 1892 // Create FileSystem with corresponding siadir. 1893 entry, fs, err := newTestFileSystemWithDir(t.Name()) 1894 if err != nil { 1895 t.Fatal(err) 1896 } 1897 dirPath := fs.DirSiaPath(entry) 1898 exists, _ := fs.DirExists(dirPath) 1899 if !exists { 1900 t.Fatal("No SiaDirSetEntry found") 1901 } 1902 1903 // Confirm there is 1 dir in memory 1904 if len(fs.directories) != 1 { 1905 t.Fatal("Expected 1 dir in memory, got:", len(fs.directories)) 1906 } 1907 1908 // Add files to the dir and open them. 1909 filepath1, err := dirPath.Join("file1") 1910 if err != nil { 1911 t.Fatal(err) 1912 } 1913 fs.addTestSiaFile(filepath1) 1914 fileEntry1, err := fs.OpenSiaFile(filepath1) 1915 if err != nil { 1916 t.Fatal(err) 1917 } 1918 filepath2, err := dirPath.Join("file2") 1919 if err != nil { 1920 t.Fatal(err) 1921 } 1922 fs.addTestSiaFile(filepath2) 1923 fileEntry2, err := fs.OpenSiaFile(filepath2) 1924 if err != nil { 1925 t.Fatal(err) 1926 } 1927 1928 // Confirm the files are in the filesystem. 1929 if len(entry.files) != 2 { 1930 t.Fatal("Expected 2 files in memory, got:", len(entry.files)) 1931 } 1932 1933 // Test deleting an instance of a dir 1934 // 1935 // Access the dir again 1936 entry2, err := fs.OpenSiaDir(dirPath) 1937 if err != nil { 1938 t.Fatal(err) 1939 } 1940 // Confirm there is still only has 1 dir in memory 1941 if len(fs.directories) != 1 { 1942 t.Fatal("Expected 1 dir in memory, got:", len(fs.directories)) 1943 } 1944 // The files should not have been deleted yet. 1945 if fileEntry1.Deleted() { 1946 t.Fatal("expected file1 not to be marked deleted") 1947 } 1948 if fileEntry2.Deleted() { 1949 t.Fatal("expected file2 not to be marked deleted") 1950 } 1951 // Delete and close instance of dir. 1952 err = fs.DeleteDir(dirPath) 1953 if err != nil { 1954 t.Fatal(err) 1955 } 1956 err = entry2.Close() 1957 if err != nil { 1958 t.Fatal(err) 1959 } 1960 // There should be no dir in the filesystem after deleting it. 1961 if len(fs.directories) != 0 { 1962 t.Fatal("Expected 0 dirs in memory, got:", len(fs.directories)) 1963 } 1964 // The files should have been deleted as well. 1965 if !fileEntry1.Deleted() { 1966 t.Fatal("expected file1 to be marked deleted") 1967 } 1968 if !fileEntry2.Deleted() { 1969 t.Fatal("expected file2 to be marked deleted") 1970 } 1971 1972 // Confirm other instance is still in memory by calling methods on it. 1973 deleted, err := entry.Deleted() 1974 if err != nil { 1975 t.Fatal(err) 1976 } 1977 if !deleted { 1978 t.Fatal("Expected dir to be deleted") 1979 } 1980 1981 // Create a new dir at the same path. 1982 err = fs.NewSiaDir(dirPath, skymodules.DefaultDirPerm) 1983 if err != nil { 1984 t.Fatal(err) 1985 } 1986 // Get the entry. 1987 entry3, err := fs.OpenSiaDir(dirPath) 1988 if err != nil { 1989 t.Fatal(err) 1990 } 1991 // Confirm the other instance is still in memory. 1992 deleted, err = entry.Deleted() 1993 if err != nil { 1994 t.Fatal(err) 1995 } 1996 if !deleted { 1997 t.Fatal("Expected dir to be deleted") 1998 } 1999 2000 // New dir should not link to the files of the old dir. 2001 if len(entry3.files) != 0 { 2002 t.Fatal("Expected 0 files in memory, got:", len(entry3.files)) 2003 } 2004 2005 // Confirm closing out remaining dirs removes all dirs from memory 2006 // 2007 // Close last instance of the first dir 2008 err = entry.Close() 2009 if err != nil { 2010 t.Fatal(err) 2011 } 2012 err = entry3.Close() 2013 if err != nil { 2014 t.Fatal(err) 2015 } 2016 // Confirm renter has no dirs in memory 2017 if len(fs.directories) != 0 { 2018 t.Fatal("Expected 0 dirs in memory, got:", len(fs.directories)) 2019 } 2020 } 2021 2022 // TestLazySiaDir tests that siaDir correctly reads and sets the lazySiaDir 2023 // field. 2024 func TestLazySiaDir(t *testing.T) { 2025 if testing.Short() && !build.VLONG { 2026 t.SkipNow() 2027 } 2028 t.Parallel() 2029 // Create filesystem. 2030 root := filepath.Join(testDir(t.Name()), "fs-root") 2031 fs := newTestFileSystem(root) 2032 // Create dir /foo 2033 sp := newSiaPath("foo") 2034 if err := fs.NewSiaDir(sp, skymodules.DefaultDirPerm); err != nil { 2035 t.Fatal(err) 2036 } 2037 // Open the newly created dir. 2038 foo, err := fs.OpenSiaDir(sp) 2039 if err != nil { 2040 t.Fatal(err) 2041 } 2042 defer func() { 2043 if err := foo.Close(); err != nil { 2044 t.Fatal(err) 2045 } 2046 }() 2047 // Get the siadir. 2048 sd, err := foo.siaDir() 2049 if err != nil { 2050 t.Fatal(err) 2051 } 2052 // Lazydir should be set. 2053 if *foo.lazySiaDir != sd { 2054 t.Fatal(err) 2055 } 2056 // Fetching foo from root should also have lazydir set. 2057 fooRoot := fs.directories["foo"] 2058 if *fooRoot.lazySiaDir != sd { 2059 t.Fatal("fooRoot doesn't have lazydir set") 2060 } 2061 // Open foo again. 2062 foo2, err := fs.OpenSiaDir(sp) 2063 if err != nil { 2064 t.Fatal(err) 2065 } 2066 defer func() { 2067 if err := foo2.Close(); err != nil { 2068 t.Fatal(err) 2069 } 2070 }() 2071 // Lazydir should already be loaded. 2072 if *foo2.lazySiaDir != sd { 2073 t.Fatal("foo2.lazySiaDir isn't set correctly", foo2.lazySiaDir) 2074 } 2075 } 2076 2077 // TestLazySiaDir tests that siaDir correctly reads and sets the lazySiaDir 2078 // field. 2079 func TestOpenCloseRoot(t *testing.T) { 2080 if testing.Short() && !build.VLONG { 2081 t.SkipNow() 2082 } 2083 t.Parallel() 2084 // Create filesystem. 2085 root := filepath.Join(testDir(t.Name()), "fs-root") 2086 fs := newTestFileSystem(root) 2087 2088 rootNode, err := fs.OpenSiaDir(skymodules.RootSiaPath()) 2089 if err != nil { 2090 t.Fatal(err) 2091 } 2092 err = rootNode.Close() 2093 if err != nil { 2094 t.Fatal(err) 2095 } 2096 } 2097 2098 // TestFailedOpenFileFolder makes sure that a failed call to OpenSiaFile or 2099 // OpensiaDir doesn't leave any nodes dangling in memory. 2100 func TestFailedOpenFileFolder(t *testing.T) { 2101 if testing.Short() && !build.VLONG { 2102 t.SkipNow() 2103 } 2104 t.Parallel() 2105 // Create filesystem. 2106 root := filepath.Join(testDir(t.Name()), "fs-root") 2107 fs := newTestFileSystem(root) 2108 // Create dir /sub1/sub2 2109 sp := newSiaPath("sub1/sub2") 2110 if err := fs.NewSiaDir(sp, skymodules.DefaultDirPerm); err != nil { 2111 t.Fatal(err) 2112 } 2113 // Prepare a path to "foo" 2114 foo, err := sp.Join("foo") 2115 if err != nil { 2116 t.Fatal(err) 2117 } 2118 // Open "foo" as a dir. 2119 _, err = fs.OpenSiaDir(foo) 2120 if !errors.Contains(err, ErrNotExist) { 2121 t.Fatal("err should be ErrNotExist but was", err) 2122 } 2123 if len(fs.files) != 0 || len(fs.directories) != 0 { 2124 t.Fatal("Expected 0 files and folders but got", len(fs.files), len(fs.directories)) 2125 } 2126 // Open "foo" as a file. 2127 _, err = fs.OpenSiaFile(foo) 2128 if !errors.Contains(err, ErrNotExist) { 2129 t.Fatal("err should be ErrNotExist but was", err) 2130 } 2131 if len(fs.files) != 0 || len(fs.directories) != 0 { 2132 t.Fatal("Expected 0 files and folders but got", len(fs.files), len(fs.directories)) 2133 } 2134 } 2135 2136 // TestFileDirConflict makes sure that files and dirs cannot share the same 2137 // name. 2138 func TestFileDirConflict(t *testing.T) { 2139 if testing.Short() { 2140 t.SkipNow() 2141 } 2142 2143 testFileDirConflict(t, false) 2144 testFileDirConflict(t, true) 2145 } 2146 2147 // testFileDirConflict runs a subtest for TestFileDirConflict. When `open` is 2148 // true we first open the already existing file/dir before trying to produce 2149 // `ErrExist` and delete it. 2150 func testFileDirConflict(t *testing.T, open bool) { 2151 // Prepare a filesystem. 2152 root := filepath.Join(testDir(t.Name()), fmt.Sprintf("open-%v", open), "fs-root") 2153 err := os.RemoveAll(root) 2154 if err != nil { 2155 t.Fatal(err) 2156 } 2157 fs := newTestFileSystem(root) 2158 2159 // Create a file. 2160 filepath := newSiaPath("dir1/file1") 2161 fs.addTestSiaFile(filepath) 2162 2163 if open { 2164 // Open the file. This shouldn't affect later checks. 2165 node, err := fs.OpenSiaFile(filepath) 2166 if err != nil { 2167 t.Fatal(err) 2168 } 2169 defer func() { 2170 err := node.Close() 2171 if err != nil { 2172 t.Fatal(err) 2173 } 2174 }() 2175 } 2176 2177 // Make sure we can't create another file with the same name. 2178 err = fs.addTestSiaFileWithErr(filepath) 2179 if !errors.Contains(err, ErrExists) { 2180 t.Fatalf("Expected err %v, got %v", ErrExists, err) 2181 } 2182 2183 // Make sure we can't rename another file to the same name. 2184 filepath2 := newSiaPath("dir1/file2") 2185 fs.addTestSiaFile(filepath2) 2186 err = fs.RenameFile(filepath2, filepath) 2187 if !errors.Contains(err, ErrExists) { 2188 t.Fatalf("Expected err %v, got %v", ErrExists, err) 2189 } 2190 2191 // Make sure we (still) can't create another file with the same name. 2192 err = fs.addTestSiaFileWithErr(filepath) 2193 if !errors.Contains(err, ErrExists) { 2194 t.Fatalf("Expected err %v, got %v", ErrExists, err) 2195 } 2196 2197 // Make sure we can't create a dir with the same name. 2198 err = fs.NewSiaDir(filepath, skymodules.DefaultDirPerm) 2199 if !errors.Contains(err, ErrExists) { 2200 t.Fatalf("Expected err %v, got %v", ErrExists, err) 2201 } 2202 2203 // Create a dir. 2204 dirpath := newSiaPath("dir2/dir3") 2205 err = fs.NewSiaDir(dirpath, skymodules.DefaultDirPerm) 2206 if err != nil { 2207 t.Fatal(err) 2208 } 2209 2210 if open { 2211 // Open the dir. This shouldn't affect later checks. 2212 node, err := fs.OpenSiaDir(dirpath) 2213 if err != nil { 2214 t.Fatal(err) 2215 } 2216 defer func() { 2217 err := node.Close() 2218 if err != nil { 2219 t.Fatal(err) 2220 } 2221 }() 2222 } 2223 2224 // Make sure we CAN create another dir with the same name as the first 2225 // dir. 2226 err = fs.NewSiaDir(dirpath, skymodules.DefaultDirPerm) 2227 if err != nil { 2228 t.Fatal(err) 2229 } 2230 2231 // Make sure we can't rename a dir to the same name as the first file. 2232 err = fs.RenameDir(dirpath, filepath) 2233 if !errors.Contains(err, ErrExists) { 2234 t.Fatalf("Expected err %v, got %v", ErrExists, err) 2235 } 2236 2237 // Make sure we still CAN create another dir with the same name as the first 2238 // dir. 2239 err = fs.NewSiaDir(dirpath, skymodules.DefaultDirPerm) 2240 if err != nil { 2241 t.Fatal(err) 2242 } 2243 2244 // Make sure we can't create a file with the same name as the dir. 2245 err = fs.addTestSiaFileWithErr(dirpath) 2246 if !errors.Contains(err, ErrExists) { 2247 t.Fatalf("Expected err %v, got %v", ErrExists, err) 2248 } 2249 2250 // Make sure we can't rename a file to the same name as the first dir. 2251 err = fs.RenameFile(filepath, dirpath) 2252 if !errors.Contains(err, ErrExists) { 2253 t.Fatalf("Expected err %v, got %v", ErrExists, err) 2254 } 2255 2256 // Make sure we can't rename another dir to the same name as the first dir. 2257 dirpath2 := newSiaPath("dir2/dir4") 2258 err = fs.NewSiaDir(dirpath2, skymodules.DefaultDirPerm) 2259 if err != nil { 2260 t.Fatal(err) 2261 } 2262 err = fs.RenameDir(dirpath2, dirpath) 2263 if !errors.Contains(err, ErrExists) { 2264 t.Fatalf("Expected err %v, got %v", ErrExists, err) 2265 } 2266 } 2267 2268 // TestList tests that the list method of the filesystem returns the correct 2269 // number of file and directory information 2270 func TestList(t *testing.T) { 2271 if testing.Short() { 2272 t.SkipNow() 2273 } 2274 // Prepare a siadirset 2275 root := filepath.Join(testDir(t.Name()), "fs-root") 2276 os.RemoveAll(root) 2277 fs := newTestFileSystem(root) 2278 2279 // Specify a directory structure for this test. 2280 var dirStructure = []string{ 2281 "dir1", 2282 "dir1/subdir1", 2283 "dir1/subdir1/subsubdir1", 2284 "dir1/subdir1/subsubdir2", 2285 "dir1/subdir1/subsubdir3", 2286 "dir1/subdir2", 2287 "dir1/subdir2/subsubdir1", 2288 "dir1/subdir2/subsubdir2", 2289 "dir1/subdir2/subsubdir3", 2290 "dir1/subdir3", 2291 "dir1/subdir3/subsubdir1", 2292 "dir1/subdir3/subsubdir2", 2293 "dir1/subdir3/subsubdir3", 2294 } 2295 2296 // Create filesystem 2297 for _, d := range dirStructure { 2298 // Create directory 2299 siaPath := newSiaPath(d) 2300 err := fs.NewSiaDir(siaPath, persist.DefaultDiskPermissionsTest) 2301 if err != nil { 2302 t.Fatal(err) 2303 } 2304 2305 // Add a file 2306 fileSiaPath, err := siaPath.Join("file") 2307 if err != nil { 2308 t.Fatal(err) 2309 } 2310 fs.addTestSiaFile(fileSiaPath) 2311 } 2312 2313 // Get the cached information 2314 fis, dis, err := fs.CachedListCollect(newSiaPath(dirStructure[0]), true) 2315 if err != nil { 2316 t.Fatal(err) 2317 } 2318 if len(fis) != len(dirStructure) { 2319 t.Fatal("wrong number of files", len(fis), len(dirStructure)) 2320 } 2321 if len(dis) != len(dirStructure) { 2322 t.Fatal("wrong number of dirs", len(dis), len(dirStructure)) 2323 } 2324 }