github.com/hanwen/go-fuse@v1.0.0/unionfs/unionfs_test.go (about) 1 // Copyright 2016 the Go-FUSE Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package unionfs 6 7 import ( 8 "bytes" 9 "fmt" 10 "io/ioutil" 11 "os" 12 "os/exec" 13 "path/filepath" 14 "regexp" 15 "strconv" 16 "strings" 17 "syscall" 18 "testing" 19 "time" 20 21 "github.com/hanwen/go-fuse/fuse" 22 "github.com/hanwen/go-fuse/fuse/nodefs" 23 "github.com/hanwen/go-fuse/fuse/pathfs" 24 "github.com/hanwen/go-fuse/internal/testutil" 25 ) 26 27 func TestFilePathHash(t *testing.T) { 28 got := filePathHash("xyz/abc") 29 want := "34d52a6371ee5c79-abc" 30 if got != want { 31 t.Errorf("got %q want %q", got, want) 32 } 33 } 34 35 var testOpts = UnionFsOptions{ 36 DeletionCacheTTL: entryTTL, 37 DeletionDirName: "DELETIONS", 38 BranchCacheTTL: entryTTL, 39 HiddenFiles: []string{"hidden"}, 40 } 41 42 func setRecursiveWritable(t *testing.T, dir string, writable bool) { 43 err := filepath.Walk( 44 dir, 45 func(path string, fi os.FileInfo, err error) error { 46 var newMode uint32 47 if writable { 48 newMode = uint32(fi.Mode().Perm()) | 0200 49 } else { 50 newMode = uint32(fi.Mode().Perm()) &^ 0222 51 } 52 if fi.Mode()|os.ModeSymlink != 0 { 53 return nil 54 } 55 return os.Chmod(path, os.FileMode(newMode)) 56 }) 57 if err != nil { 58 t.Fatalf("Walk: %v", err) 59 } 60 } 61 62 // Creates a temporary dir "wd" with 3 directories: 63 // mnt ... overlayed (unionfs) mount 64 // rw .... modifiable data 65 // ro .... read-only data 66 func setupUfs(t *testing.T) (wd string, cleanup func()) { 67 // Make sure system setting does not affect test. 68 syscall.Umask(0) 69 70 wd = testutil.TempDir() 71 err := os.Mkdir(wd+"/mnt", 0700) 72 if err != nil { 73 t.Fatalf("Mkdir: %v", err) 74 } 75 76 err = os.Mkdir(wd+"/rw", 0700) 77 if err != nil { 78 t.Fatalf("Mkdir: %v", err) 79 } 80 81 os.Mkdir(wd+"/ro", 0700) 82 if err != nil { 83 t.Fatalf("Mkdir: %v", err) 84 } 85 86 fses := []pathfs.FileSystem{ 87 pathfs.NewLoopbackFileSystem(wd + "/rw"), 88 NewCachingFileSystem(pathfs.NewLoopbackFileSystem(wd+"/ro"), 0), 89 } 90 ufs, err := NewUnionFs(fses, testOpts) 91 if err != nil { 92 t.Fatalf("NewUnionFs: %v", err) 93 } 94 // We configure timeouts are smaller, so we can check for 95 // UnionFs's cache consistency. 96 opts := &nodefs.Options{ 97 EntryTimeout: entryTTL / 2, 98 AttrTimeout: entryTTL / 2, 99 NegativeTimeout: entryTTL / 2, 100 PortableInodes: true, 101 Debug: testutil.VerboseTest(), 102 LookupKnownChildren: true, 103 } 104 105 pathfs := pathfs.NewPathNodeFs(ufs, 106 &pathfs.PathNodeFsOptions{ClientInodes: true, 107 Debug: opts.Debug, 108 }) 109 state, _, err := nodefs.MountRoot(wd+"/mnt", pathfs.Root(), opts) 110 if err != nil { 111 t.Fatalf("MountNodeFileSystem: %v", err) 112 } 113 go state.Serve() 114 state.WaitMount() 115 116 return wd, func() { 117 err := state.Unmount() 118 if err != nil { 119 return 120 } 121 setRecursiveWritable(t, wd, true) 122 os.RemoveAll(wd) 123 } 124 } 125 126 func readFromFile(t *testing.T, path string) string { 127 b, err := ioutil.ReadFile(path) 128 if err != nil { 129 t.Fatalf("ReadFile: %v", err) 130 } 131 return string(b) 132 } 133 134 func dirNames(t *testing.T, path string) map[string]bool { 135 f, err := os.Open(path) 136 if err != nil { 137 t.Fatalf("Open: %v", err) 138 } 139 140 result := make(map[string]bool) 141 names, err := f.Readdirnames(-1) 142 if err != nil { 143 t.Fatalf("Readdirnames: %v", err) 144 } 145 err = f.Close() 146 if err != nil { 147 t.Fatalf("Close: %v", err) 148 } 149 150 for _, nm := range names { 151 result[nm] = true 152 } 153 return result 154 } 155 156 func checkMapEq(t *testing.T, m1, m2 map[string]bool) { 157 if !mapEq(m1, m2) { 158 msg := fmt.Sprintf("mismatch: got %v != expect %v", m1, m2) 159 panic(msg) 160 } 161 } 162 163 func mapEq(m1, m2 map[string]bool) bool { 164 if len(m1) != len(m2) { 165 return false 166 } 167 168 for k, v := range m1 { 169 val, ok := m2[k] 170 if !ok || val != v { 171 return false 172 } 173 } 174 return true 175 } 176 177 func fileExists(path string) bool { 178 f, err := os.Lstat(path) 179 return err == nil && f != nil 180 } 181 182 func TestUnionFsAutocreateDeletionDir(t *testing.T) { 183 wd, clean := setupUfs(t) 184 defer clean() 185 186 err := os.Remove(wd + "/rw/DELETIONS") 187 if err != nil { 188 t.Fatalf("Remove: %v", err) 189 } 190 191 err = os.Mkdir(wd+"/mnt/dir", 0755) 192 if err != nil { 193 t.Fatalf("Mkdir: %v", err) 194 } 195 196 _, err = ioutil.ReadDir(wd + "/mnt/dir") 197 if err != nil { 198 t.Fatalf("ReadDir: %v", err) 199 } 200 } 201 202 func TestUnionFsSymlink(t *testing.T) { 203 wd, clean := setupUfs(t) 204 defer clean() 205 206 err := os.Symlink("/foobar", wd+"/mnt/link") 207 if err != nil { 208 t.Fatalf("Symlink: %v", err) 209 } 210 211 val, err := os.Readlink(wd + "/mnt/link") 212 if err != nil { 213 t.Fatalf("Readlink: %v", err) 214 } 215 216 if val != "/foobar" { 217 t.Errorf("symlink mismatch: %v", val) 218 } 219 } 220 221 func TestUnionFsSymlinkPromote(t *testing.T) { 222 wd, clean := setupUfs(t) 223 defer clean() 224 225 err := os.Mkdir(wd+"/ro/subdir", 0755) 226 if err != nil { 227 t.Fatalf("Mkdir: %v", err) 228 } 229 230 err = os.Symlink("/foobar", wd+"/mnt/subdir/link") 231 if err != nil { 232 t.Fatalf("Symlink: %v", err) 233 } 234 } 235 236 func TestUnionFsChtimes(t *testing.T) { 237 wd, clean := setupUfs(t) 238 defer clean() 239 240 WriteFile(t, wd+"/ro/file", "a") 241 err := os.Chtimes(wd+"/ro/file", time.Unix(42, 0), time.Unix(43, 0)) 242 if err != nil { 243 t.Fatalf("Chtimes: %v", err) 244 } 245 246 err = os.Chtimes(wd+"/mnt/file", time.Unix(82, 0), time.Unix(83, 0)) 247 if err != nil { 248 t.Fatalf("Chtimes: %v", err) 249 } 250 251 fi, err := os.Lstat(wd + "/mnt/file") 252 attr := &fuse.Attr{} 253 attr.FromStat(fuse.ToStatT(fi)) 254 if attr.Atime != 82 || attr.Mtime != 83 { 255 t.Error("Incorrect timestamp", fi) 256 } 257 } 258 259 func TestUnionFsChmod(t *testing.T) { 260 wd, clean := setupUfs(t) 261 defer clean() 262 263 ro_fn := wd + "/ro/file" 264 m_fn := wd + "/mnt/file" 265 WriteFile(t, ro_fn, "a") 266 err := os.Chmod(m_fn, 00070) 267 if err != nil { 268 t.Fatalf("Chmod: %v", err) 269 } 270 271 fi, err := os.Lstat(m_fn) 272 if err != nil { 273 t.Fatalf("Lstat: %v", err) 274 } 275 if fi.Mode()&07777 != 00270 { 276 t.Errorf("Unexpected mode found: %o", uint32(fi.Mode().Perm())) 277 } 278 _, err = os.Lstat(wd + "/rw/file") 279 if err != nil { 280 t.Errorf("File not promoted") 281 } 282 } 283 284 func TestUnionFsChown(t *testing.T) { 285 wd, clean := setupUfs(t) 286 defer clean() 287 288 ro_fn := wd + "/ro/file" 289 m_fn := wd + "/mnt/file" 290 WriteFile(t, ro_fn, "a") 291 292 err := os.Chown(m_fn, 0, 0) 293 code := fuse.ToStatus(err) 294 if code != fuse.EPERM { 295 t.Error("Unexpected error code", code, err) 296 } 297 } 298 299 func TestUnionFsDelete(t *testing.T) { 300 wd, clean := setupUfs(t) 301 defer clean() 302 303 WriteFile(t, wd+"/ro/file", "a") 304 _, err := os.Lstat(wd + "/mnt/file") 305 if err != nil { 306 t.Fatalf("Lstat: %v", err) 307 } 308 309 err = os.Remove(wd + "/mnt/file") 310 if err != nil { 311 t.Fatalf("Remove: %v", err) 312 } 313 314 _, err = os.Lstat(wd + "/mnt/file") 315 if err == nil { 316 t.Fatal("should have disappeared.") 317 } 318 delPath := wd + "/rw/" + testOpts.DeletionDirName 319 names := dirNames(t, delPath) 320 if len(names) != 1 { 321 t.Fatal("Should have 1 deletion", names) 322 } 323 324 for k := range names { 325 c, err := ioutil.ReadFile(delPath + "/" + k) 326 if err != nil { 327 t.Fatalf("ReadFile: %v", err) 328 } 329 if string(c) != "file" { 330 t.Fatal("content mismatch", string(c)) 331 } 332 } 333 } 334 335 func TestUnionFsBasic(t *testing.T) { 336 wd, clean := setupUfs(t) 337 defer clean() 338 339 WriteFile(t, wd+"/rw/rw", "a") 340 WriteFile(t, wd+"/ro/ro1", "a") 341 WriteFile(t, wd+"/ro/ro2", "b") 342 343 names := dirNames(t, wd+"/mnt") 344 expected := map[string]bool{ 345 "rw": true, "ro1": true, "ro2": true, 346 } 347 checkMapEq(t, names, expected) 348 349 WriteFile(t, wd+"/mnt/new", "new contents") 350 if !fileExists(wd + "/rw/new") { 351 t.Errorf("missing file in rw layer: %s", wd+"/rw/new") 352 } 353 354 contents := readFromFile(t, wd+"/mnt/new") 355 if contents != "new contents" { 356 t.Errorf("read mismatch: '%v'", contents) 357 } 358 WriteFile(t, wd+"/mnt/ro1", "promote me") 359 if !fileExists(wd + "/rw/ro1") { 360 t.Errorf("missing file in rw layer: %s", wd+"/mnt/ro1") 361 } 362 363 err := os.Remove(wd + "/mnt/new") 364 if err != nil { 365 t.Fatalf("Remove: %v", err) 366 } 367 368 names = dirNames(t, wd+"/mnt") 369 checkMapEq(t, names, map[string]bool{ 370 "rw": true, "ro1": true, "ro2": true, 371 }) 372 373 names = dirNames(t, wd+"/rw") 374 checkMapEq(t, names, map[string]bool{ 375 testOpts.DeletionDirName: true, 376 "rw": true, "ro1": true, 377 }) 378 names = dirNames(t, wd+"/rw/"+testOpts.DeletionDirName) 379 if len(names) != 0 { 380 t.Errorf("Expected 0 entry in %v", names) 381 } 382 383 err = os.Remove(wd + "/mnt/ro1") 384 if err != nil { 385 t.Fatalf("Remove: %v", err) 386 } 387 names = dirNames(t, wd+"/mnt") 388 checkMapEq(t, names, map[string]bool{ 389 "rw": true, "ro2": true, 390 }) 391 392 names = dirNames(t, wd+"/rw") 393 checkMapEq(t, names, map[string]bool{ 394 "rw": true, testOpts.DeletionDirName: true, 395 }) 396 397 names = dirNames(t, wd+"/rw/"+testOpts.DeletionDirName) 398 if len(names) != 1 { 399 t.Errorf("Expected 1 entry in %v", names) 400 } 401 } 402 403 func TestUnionFsPromote(t *testing.T) { 404 wd, clean := setupUfs(t) 405 defer clean() 406 407 err := os.Mkdir(wd+"/ro/subdir", 0755) 408 if err != nil { 409 t.Fatalf("Mkdir: %v", err) 410 } 411 WriteFile(t, wd+"/ro/subdir/file", "content") 412 WriteFile(t, wd+"/mnt/subdir/file", "other-content") 413 } 414 415 func TestUnionFsCreate(t *testing.T) { 416 wd, clean := setupUfs(t) 417 defer clean() 418 419 err := os.MkdirAll(wd+"/ro/subdir/sub2", 0755) 420 if err != nil { 421 t.Fatalf("MkdirAll: %v", err) 422 } 423 WriteFile(t, wd+"/mnt/subdir/sub2/file", "other-content") 424 _, err = os.Lstat(wd + "/mnt/subdir/sub2/file") 425 if err != nil { 426 t.Fatalf("Lstat: %v", err) 427 } 428 } 429 430 func TestUnionFsOpenUndeletes(t *testing.T) { 431 wd, clean := setupUfs(t) 432 defer clean() 433 434 WriteFile(t, wd+"/ro/file", "X") 435 err := os.Remove(wd + "/mnt/file") 436 if err != nil { 437 t.Fatalf("Remove: %v", err) 438 } 439 WriteFile(t, wd+"/mnt/file", "X") 440 _, err = os.Lstat(wd + "/mnt/file") 441 if err != nil { 442 t.Fatalf("Lstat: %v", err) 443 } 444 } 445 446 func TestUnionFsMkdir(t *testing.T) { 447 wd, clean := setupUfs(t) 448 defer clean() 449 450 dirname := wd + "/mnt/subdir" 451 err := os.Mkdir(dirname, 0755) 452 if err != nil { 453 t.Fatalf("Mkdir: %v", err) 454 } 455 456 err = os.Remove(dirname) 457 if err != nil { 458 t.Fatalf("Remove: %v", err) 459 } 460 } 461 462 func TestUnionFsMkdirPromote(t *testing.T) { 463 wd, clean := setupUfs(t) 464 defer clean() 465 466 dirname := wd + "/ro/subdir/subdir2" 467 err := os.MkdirAll(dirname, 0755) 468 if err != nil { 469 t.Fatalf("MkdirAll: %v", err) 470 } 471 472 err = os.Mkdir(wd+"/mnt/subdir/subdir2/dir3", 0755) 473 if err != nil { 474 t.Fatalf("Mkdir: %v", err) 475 } 476 fi, _ := os.Lstat(wd + "/rw/subdir/subdir2/dir3") 477 if err != nil { 478 t.Fatalf("Lstat: %v", err) 479 } 480 if fi == nil || !fi.IsDir() { 481 t.Error("is not a directory: ", fi) 482 } 483 } 484 485 func TestUnionFsRmdirMkdir(t *testing.T) { 486 wd, clean := setupUfs(t) 487 defer clean() 488 489 err := os.Mkdir(wd+"/ro/subdir", 0755) 490 if err != nil { 491 t.Fatalf("Mkdir: %v", err) 492 } 493 494 dirname := wd + "/mnt/subdir" 495 err = os.Remove(dirname) 496 if err != nil { 497 t.Fatalf("Remove: %v", err) 498 } 499 500 err = os.Mkdir(dirname, 0755) 501 if err != nil { 502 t.Fatalf("Mkdir: %v", err) 503 } 504 } 505 506 func TestUnionFsRename(t *testing.T) { 507 type Config struct { 508 f1_ro bool 509 f1_rw bool 510 f2_ro bool 511 f2_rw bool 512 } 513 514 configs := make([]Config, 0) 515 for i := 0; i < 16; i++ { 516 c := Config{i&0x1 != 0, i&0x2 != 0, i&0x4 != 0, i&0x8 != 0} 517 if !(c.f1_ro || c.f1_rw) { 518 continue 519 } 520 521 configs = append(configs, c) 522 } 523 524 for i, c := range configs { 525 t.Run(fmt.Sprintf("config %d", i), func(t *testing.T) { 526 wd, clean := setupUfs(t) 527 defer clean() 528 529 if c.f1_ro { 530 WriteFile(t, wd+"/ro/file1", "c1") 531 } 532 if c.f1_rw { 533 WriteFile(t, wd+"/rw/file1", "c2") 534 } 535 if c.f2_ro { 536 WriteFile(t, wd+"/ro/file2", "c3") 537 } 538 if c.f2_rw { 539 WriteFile(t, wd+"/rw/file2", "c4") 540 } 541 542 err := os.Rename(wd+"/mnt/file1", wd+"/mnt/file2") 543 if err != nil { 544 t.Fatalf("Rename: %v", err) 545 } 546 547 _, err = os.Lstat(wd + "/mnt/file1") 548 if err == nil { 549 t.Errorf("Should have lost file1") 550 } 551 _, err = os.Lstat(wd + "/mnt/file2") 552 if err != nil { 553 t.Errorf("Should have gotten file2: %v", err) 554 } 555 err = os.Rename(wd+"/mnt/file2", wd+"/mnt/file1") 556 if err != nil { 557 t.Fatalf("Rename: %v", err) 558 } 559 560 _, err = os.Lstat(wd + "/mnt/file2") 561 if err == nil { 562 t.Errorf("Should have lost file2") 563 } 564 _, err = os.Lstat(wd + "/mnt/file1") 565 if err != nil { 566 t.Errorf("Should have gotten file1: %v", err) 567 } 568 }) 569 } 570 } 571 572 func TestUnionFsRenameDirBasic(t *testing.T) { 573 wd, clean := setupUfs(t) 574 defer clean() 575 576 err := os.MkdirAll(wd+"/ro/dir/subdir", 0755) 577 if err != nil { 578 t.Fatalf("MkdirAll: %v", err) 579 } 580 581 err = os.Rename(wd+"/mnt/dir", wd+"/mnt/renamed") 582 if err != nil { 583 t.Fatalf("Rename: %v", err) 584 } 585 586 if fi, _ := os.Lstat(wd + "/mnt/dir"); fi != nil { 587 t.Fatalf("%s/mnt/dir should have disappeared: %v", wd, fi) 588 } 589 590 if fi, _ := os.Lstat(wd + "/mnt/renamed"); fi == nil || !fi.IsDir() { 591 t.Fatalf("%s/mnt/renamed should be directory: %v", wd, fi) 592 } 593 594 entries, err := ioutil.ReadDir(wd + "/mnt/renamed") 595 if err != nil || len(entries) != 1 || entries[0].Name() != "subdir" { 596 t.Errorf("readdir(%s/mnt/renamed) should have one entry: %v, err %v", wd, entries, err) 597 } 598 599 if err = os.Mkdir(wd+"/mnt/dir", 0755); err != nil { 600 t.Errorf("mkdir should succeed %v", err) 601 } 602 } 603 604 func TestUnionFsRenameDirAllSourcesGone(t *testing.T) { 605 wd, clean := setupUfs(t) 606 defer clean() 607 608 err := os.MkdirAll(wd+"/ro/dir", 0755) 609 if err != nil { 610 t.Fatalf("MkdirAll: %v", err) 611 } 612 613 err = ioutil.WriteFile(wd+"/ro/dir/file.txt", []byte{42}, 0644) 614 if err != nil { 615 t.Fatalf("WriteFile: %v", err) 616 } 617 618 setRecursiveWritable(t, wd+"/ro", false) 619 err = os.Rename(wd+"/mnt/dir", wd+"/mnt/renamed") 620 if err != nil { 621 t.Fatalf("Rename: %v", err) 622 } 623 624 names := dirNames(t, wd+"/rw/"+testOpts.DeletionDirName) 625 if len(names) != 2 { 626 t.Errorf("Expected 2 entries in %v", names) 627 } 628 } 629 630 func TestUnionFsRenameDirWithDeletions(t *testing.T) { 631 wd, clean := setupUfs(t) 632 defer clean() 633 634 err := os.MkdirAll(wd+"/ro/dir/subdir", 0755) 635 if err != nil { 636 t.Fatalf("MkdirAll: %v", err) 637 } 638 639 err = ioutil.WriteFile(wd+"/ro/dir/file.txt", []byte{42}, 0644) 640 if err != nil { 641 t.Fatalf("WriteFile: %v", err) 642 } 643 644 err = ioutil.WriteFile(wd+"/ro/dir/subdir/file.txt", []byte{42}, 0644) 645 if err != nil { 646 t.Fatalf("WriteFile: %v", err) 647 } 648 setRecursiveWritable(t, wd+"/ro", false) 649 650 if fi, _ := os.Lstat(wd + "/mnt/dir/subdir/file.txt"); fi == nil || fi.Mode()&os.ModeType != 0 { 651 t.Fatalf("%s/mnt/dir/subdir/file.txt should be file: %v", wd, fi) 652 } 653 654 err = os.Remove(wd + "/mnt/dir/file.txt") 655 if err != nil { 656 t.Fatalf("Remove: %v", err) 657 } 658 659 err = os.Rename(wd+"/mnt/dir", wd+"/mnt/renamed") 660 if err != nil { 661 t.Fatalf("Rename: %v", err) 662 } 663 664 if fi, _ := os.Lstat(wd + "/mnt/dir/subdir/file.txt"); fi != nil { 665 t.Fatalf("%s/mnt/dir/subdir/file.txt should have disappeared: %v", wd, fi) 666 } 667 668 if fi, _ := os.Lstat(wd + "/mnt/dir"); fi != nil { 669 t.Fatalf("%s/mnt/dir should have disappeared: %v", wd, fi) 670 } 671 672 if fi, _ := os.Lstat(wd + "/mnt/renamed"); fi == nil || !fi.IsDir() { 673 t.Fatalf("%s/mnt/renamed should be directory: %v", wd, fi) 674 } 675 676 if fi, _ := os.Lstat(wd + "/mnt/renamed/file.txt"); fi != nil { 677 t.Fatalf("%s/mnt/renamed/file.txt should have disappeared %#v", wd, fi) 678 } 679 680 if err = os.Mkdir(wd+"/mnt/dir", 0755); err != nil { 681 t.Errorf("mkdir should succeed %v", err) 682 } 683 684 if fi, _ := os.Lstat(wd + "/mnt/dir/subdir"); fi != nil { 685 t.Fatalf("%s/mnt/dir/subdir should have disappeared %#v", wd, fi) 686 } 687 } 688 689 func TestUnionFsRenameSymlink(t *testing.T) { 690 wd, clean := setupUfs(t) 691 defer clean() 692 693 err := os.Symlink("linktarget", wd+"/ro/link") 694 if err != nil { 695 t.Fatalf("Symlink: %v", err) 696 } 697 698 err = os.Rename(wd+"/mnt/link", wd+"/mnt/renamed") 699 if err != nil { 700 t.Fatalf("Rename: %v", err) 701 } 702 703 if fi, _ := os.Lstat(wd + "/mnt/link"); fi != nil { 704 t.Fatalf("%s/mnt/link should have disappeared: %v", wd, fi) 705 } 706 707 if fi, _ := os.Lstat(wd + "/mnt/renamed"); fi == nil || fi.Mode()&os.ModeSymlink == 0 { 708 t.Fatalf("%s/mnt/renamed should be link: %v", wd, fi) 709 } 710 711 if link, err := os.Readlink(wd + "/mnt/renamed"); err != nil || link != "linktarget" { 712 t.Fatalf("readlink(%s/mnt/renamed) should point to 'linktarget': %v, err %v", wd, link, err) 713 } 714 } 715 716 func TestUnionFsWritableDir(t *testing.T) { 717 wd, clean := setupUfs(t) 718 defer clean() 719 720 dirname := wd + "/ro/subdir" 721 err := os.Mkdir(dirname, 0555) 722 if err != nil { 723 t.Fatalf("Mkdir: %v", err) 724 } 725 setRecursiveWritable(t, wd+"/ro", false) 726 727 fi, err := os.Lstat(wd + "/mnt/subdir") 728 if err != nil { 729 t.Fatalf("Lstat: %v", err) 730 } 731 if fi.Mode().Perm()&0222 == 0 { 732 t.Errorf("unexpected permission %o", fi.Mode().Perm()) 733 } 734 } 735 736 func TestUnionFsWriteAccess(t *testing.T) { 737 wd, clean := setupUfs(t) 738 defer clean() 739 740 fn := wd + "/ro/file" 741 // No write perms. 742 err := ioutil.WriteFile(fn, []byte("foo"), 0444) 743 if err != nil { 744 t.Fatalf("WriteFile: %v", err) 745 } 746 setRecursiveWritable(t, wd+"/ro", false) 747 748 err = syscall.Access(wd+"/mnt/file", fuse.W_OK) 749 if err != nil { 750 if err != nil { 751 t.Fatalf("Access: %v", err) 752 } 753 } 754 } 755 756 func TestUnionFsLink(t *testing.T) { 757 wd, clean := setupUfs(t) 758 defer clean() 759 760 content := "blabla" 761 fn := wd + "/ro/file" 762 err := ioutil.WriteFile(fn, []byte(content), 0666) 763 if err != nil { 764 t.Fatalf("WriteFile: %v", err) 765 } 766 setRecursiveWritable(t, wd+"/ro", false) 767 768 err = os.Link(wd+"/mnt/file", wd+"/mnt/linked") 769 if err != nil { 770 t.Fatalf("Link: %v", err) 771 } 772 773 fi2, err := os.Lstat(wd + "/mnt/linked") 774 if err != nil { 775 t.Fatalf("Lstat: %v", err) 776 } 777 778 fi1, err := os.Lstat(wd + "/mnt/file") 779 if err != nil { 780 t.Fatalf("Lstat: %v", err) 781 } 782 783 s1 := fuse.ToStatT(fi1) 784 s2 := fuse.ToStatT(fi2) 785 if s1.Ino != s2.Ino { 786 t.Errorf("inode numbers should be equal for linked files %v, %v", s1.Ino, s2.Ino) 787 } 788 c, err := ioutil.ReadFile(wd + "/mnt/linked") 789 if string(c) != content { 790 t.Errorf("content mismatch got %q want %q", string(c), content) 791 } 792 } 793 794 func TestUnionFsTruncate(t *testing.T) { 795 wd, clean := setupUfs(t) 796 defer clean() 797 798 WriteFile(t, wd+"/ro/file", "hello") 799 setRecursiveWritable(t, wd+"/ro", false) 800 801 os.Truncate(wd+"/mnt/file", 2) 802 content := readFromFile(t, wd+"/mnt/file") 803 if content != "he" { 804 t.Errorf("unexpected content %v", content) 805 } 806 content2 := readFromFile(t, wd+"/rw/file") 807 if content2 != content { 808 t.Errorf("unexpected rw content %v", content2) 809 } 810 } 811 812 func TestUnionFsCopyChmod(t *testing.T) { 813 wd, clean := setupUfs(t) 814 defer clean() 815 816 contents := "hello" 817 fn := wd + "/mnt/y" 818 err := ioutil.WriteFile(fn, []byte(contents), 0644) 819 if err != nil { 820 t.Fatalf("WriteFile(%v): %v", fn, err) 821 } 822 823 err = os.Chmod(fn, 0755) 824 if err != nil { 825 t.Fatalf("Chmod(%v): %v", fn, err) 826 } 827 828 fi, err := os.Lstat(fn) 829 if err != nil { 830 t.Fatalf("Lstat(%v): %v", fn, err) 831 } 832 if fi.Mode()&0111 == 0 { 833 t.Errorf("Lstat(%v): got mode %o, want some +x bit", fn, fi.Mode()) 834 } 835 time.Sleep(entryTTL) 836 fi, err = os.Lstat(fn) 837 if err != nil { 838 t.Fatalf("Lstat(%v) after sleep: %v", fn, err) 839 } 840 if fi.Mode()&0111 == 0 { 841 t.Errorf("Lstat(%v) after sleep: mode %o", fn, fi.Mode()) 842 } 843 } 844 845 func abs(dt int64) int64 { 846 if dt >= 0 { 847 return dt 848 } 849 return -dt 850 } 851 852 func TestUnionFsTruncateTimestamp(t *testing.T) { 853 wd, clean := setupUfs(t) 854 defer clean() 855 856 contents := "hello" 857 fn := wd + "/mnt/y" 858 err := ioutil.WriteFile(fn, []byte(contents), 0644) 859 if err != nil { 860 t.Fatalf("WriteFile(%v): %v", fn, err) 861 } 862 time.Sleep(200 * time.Millisecond) 863 864 truncTs := time.Now() 865 err = os.Truncate(fn, 3) 866 if err != nil { 867 t.Fatalf("Truncate(%v): %v", fn, err) 868 } 869 870 fi, err := os.Lstat(fn) 871 if err != nil { 872 t.Fatalf("Lstat(%v): %v", fn, err) 873 } 874 875 if truncTs.Sub(fi.ModTime()) > 100*time.Millisecond { 876 t.Errorf("after Truncate: got TS %v, want %v", fi.ModTime(), truncTs) 877 } 878 } 879 880 func TestUnionFsRemoveAll(t *testing.T) { 881 wd, clean := setupUfs(t) 882 defer clean() 883 884 err := os.MkdirAll(wd+"/ro/dir/subdir", 0755) 885 if err != nil { 886 t.Fatalf("MkdirAll: %v", err) 887 } 888 889 contents := "hello" 890 fn := wd + "/ro/dir/subdir/y" 891 err = ioutil.WriteFile(fn, []byte(contents), 0644) 892 if err != nil { 893 t.Fatalf("WriteFile: %v", err) 894 } 895 setRecursiveWritable(t, wd+"/ro", false) 896 897 err = os.RemoveAll(wd + "/mnt/dir") 898 if err != nil { 899 t.Error("Should delete all") 900 } 901 902 for _, f := range []string{"dir/subdir/y", "dir/subdir", "dir"} { 903 if fi, _ := os.Lstat(filepath.Join(wd, "mount", f)); fi != nil { 904 t.Errorf("file %s should have disappeared: %v", f, fi) 905 } 906 } 907 908 names, err := Readdirnames(wd + "/rw/DELETIONS") 909 if err != nil { 910 t.Fatalf("Readdirnames: %v", err) 911 } 912 if len(names) != 3 { 913 t.Fatal("unexpected names", names) 914 } 915 } 916 917 func ProgramVersion(bin string) (major, minor int64, err error) { 918 cmd := exec.Command(bin, "--version") 919 buf := &bytes.Buffer{} 920 cmd.Stdout = buf 921 if err := cmd.Run(); err != nil { 922 return 0, 0, err 923 } 924 lines := strings.Split(buf.String(), "\n") 925 if len(lines) < 1 { 926 return 0, 0, fmt.Errorf("no output") 927 } 928 matches := regexp.MustCompile(".* ([0-9]+)\\.([0-9]+)").FindStringSubmatch(lines[0]) 929 930 if matches == nil { 931 return 0, 0, fmt.Errorf("no match for %q", lines[0]) 932 } 933 major, err = strconv.ParseInt(matches[1], 10, 64) 934 if err != nil { 935 return 0, 0, err 936 } 937 minor, err = strconv.ParseInt(matches[2], 10, 64) 938 if err != nil { 939 return 0, 0, err 940 } 941 return major, minor, nil 942 } 943 944 func TestUnionFsRmRf(t *testing.T) { 945 wd, clean := setupUfs(t) 946 defer clean() 947 948 err := os.MkdirAll(wd+"/ro/dir/subdir", 0755) 949 if err != nil { 950 t.Fatalf("MkdirAll: %v", err) 951 } 952 953 contents := "hello" 954 fn := wd + "/ro/dir/subdir/y" 955 err = ioutil.WriteFile(fn, []byte(contents), 0644) 956 if err != nil { 957 t.Fatalf("WriteFile: %v", err) 958 } 959 setRecursiveWritable(t, wd+"/ro", false) 960 961 bin, err := exec.LookPath("rm") 962 if err != nil { 963 t.Fatalf("LookPath: %v", err) 964 } 965 966 maj, min, err := ProgramVersion(bin) 967 if err != nil { 968 t.Logf("ProgramVersion: %v", err) 969 } 970 if maj < 8 { // assuming GNU coreutils. 971 t.Skipf("Skipping test; GNU rm %d.%d is not POSIX compliant.", maj, min) 972 } 973 names, _ := Readdirnames(wd + "/mnt/dir") 974 t.Logf("Contents of %s/mnt/dir: %s", wd, strings.Join(names, ", ")) 975 cmd := exec.Command(bin, "-rf", wd+"/mnt/dir") 976 err = cmd.Run() 977 if err != nil { 978 t.Fatal("rm -rf returned error:", err) 979 } 980 981 for _, f := range []string{"dir/subdir/y", "dir/subdir", "dir"} { 982 if fi, _ := os.Lstat(filepath.Join(wd, "mount", f)); fi != nil { 983 t.Errorf("file %s should have disappeared: %v", f, fi) 984 } 985 } 986 987 names, err = Readdirnames(wd + "/rw/DELETIONS") 988 if err != nil { 989 t.Fatalf("Readdirnames: %v", err) 990 } 991 if len(names) != 3 { 992 t.Fatal("unexpected names", names) 993 } 994 } 995 996 func Readdirnames(dir string) ([]string, error) { 997 f, err := os.Open(dir) 998 if err != nil { 999 return nil, err 1000 } 1001 1002 defer f.Close() 1003 return f.Readdirnames(-1) 1004 } 1005 1006 func TestUnionFsDropDeletionCache(t *testing.T) { 1007 wd, clean := setupUfs(t) 1008 defer clean() 1009 1010 err := ioutil.WriteFile(wd+"/ro/file", []byte("bla"), 0644) 1011 if err != nil { 1012 t.Fatalf("WriteFile: %v", err) 1013 } 1014 setRecursiveWritable(t, wd+"/ro", false) 1015 1016 _, err = os.Lstat(wd + "/mnt/file") 1017 if err != nil { 1018 t.Fatalf("Lstat: %v", err) 1019 } 1020 err = os.Remove(wd + "/mnt/file") 1021 if err != nil { 1022 t.Fatalf("Remove: %v", err) 1023 } 1024 fi, _ := os.Lstat(wd + "/mnt/file") 1025 if fi != nil { 1026 t.Fatal("Lstat() should have failed", fi) 1027 } 1028 1029 names, err := Readdirnames(wd + "/rw/DELETIONS") 1030 if err != nil { 1031 t.Fatalf("Readdirnames: %v", err) 1032 } 1033 if len(names) != 1 { 1034 t.Fatal("unexpected names", names) 1035 } 1036 os.Remove(wd + "/rw/DELETIONS/" + names[0]) 1037 fi, _ = os.Lstat(wd + "/mnt/file") 1038 if fi != nil { 1039 t.Fatal("Lstat() should have failed", fi) 1040 } 1041 1042 // Expire kernel entry. 1043 time.Sleep((6 * entryTTL) / 10) 1044 err = ioutil.WriteFile(wd+"/mnt/.drop_cache", []byte(""), 0644) 1045 if err != nil { 1046 t.Fatalf("WriteFile: %v", err) 1047 } 1048 _, err = os.Lstat(wd + "/mnt/file") 1049 if err != nil { 1050 t.Fatal("Lstat() should have succeeded", err) 1051 } 1052 } 1053 1054 func TestUnionFsDropCache(t *testing.T) { 1055 wd, clean := setupUfs(t) 1056 defer clean() 1057 1058 err := ioutil.WriteFile(wd+"/ro/file", []byte("bla"), 0644) 1059 if err != nil { 1060 t.Fatalf("WriteFile: %v", err) 1061 } 1062 1063 _, err = os.Lstat(wd + "/mnt/.drop_cache") 1064 if err != nil { 1065 t.Fatalf("Lstat: %v", err) 1066 } 1067 1068 names, err := Readdirnames(wd + "/mnt") 1069 if err != nil { 1070 t.Fatalf("Readdirnames: %v", err) 1071 } 1072 if len(names) != 1 || names[0] != "file" { 1073 t.Fatal("unexpected names", names) 1074 } 1075 1076 err = ioutil.WriteFile(wd+"/ro/file2", []byte("blabla"), 0644) 1077 names2, err := Readdirnames(wd + "/mnt") 1078 if err != nil { 1079 t.Fatalf("Readdirnames: %v", err) 1080 } 1081 if len(names2) != len(names) { 1082 t.Fatal("mismatch", names2) 1083 } 1084 1085 err = ioutil.WriteFile(wd+"/mnt/.drop_cache", []byte("does not matter"), 0644) 1086 if err != nil { 1087 t.Fatalf("WriteFile: %v", err) 1088 } 1089 names2, err = Readdirnames(wd + "/mnt") 1090 if len(names2) != 2 { 1091 t.Fatal("mismatch 2", names2) 1092 } 1093 } 1094 1095 type disappearingFS struct { 1096 pathfs.FileSystem 1097 1098 normal pathfs.FileSystem 1099 nop pathfs.FileSystem 1100 visible bool 1101 visibleChan chan bool 1102 } 1103 1104 func (d *disappearingFS) fs() pathfs.FileSystem { 1105 select { 1106 case v := <-d.visibleChan: 1107 d.visible = v 1108 if v { 1109 d.FileSystem = d.normal 1110 } else { 1111 d.FileSystem = d.nop 1112 } 1113 default: 1114 } 1115 return d.FileSystem 1116 } 1117 1118 func (d *disappearingFS) GetAttr(name string, context *fuse.Context) (a *fuse.Attr, s fuse.Status) { 1119 return d.fs().GetAttr(name, context) 1120 } 1121 1122 func (d *disappearingFS) OpenDir(name string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) { 1123 return d.fs().OpenDir(name, context) 1124 } 1125 1126 func newDisappearingFS(fs, nop pathfs.FileSystem) *disappearingFS { 1127 return &disappearingFS{ 1128 visibleChan: make(chan bool, 1), 1129 visible: true, 1130 normal: fs, 1131 nop: nop, 1132 FileSystem: fs, 1133 } 1134 } 1135 1136 func TestUnionFsDisappearing(t *testing.T) { 1137 // This init is like setupUfs, but we want access to the 1138 // writable Fs. 1139 wd := testutil.TempDir() 1140 defer os.RemoveAll(wd) 1141 err := os.Mkdir(wd+"/mnt", 0700) 1142 if err != nil { 1143 t.Fatalf("Mkdir: %v", err) 1144 } 1145 1146 err = os.Mkdir(wd+"/rw", 0700) 1147 if err != nil { 1148 t.Fatalf("Mkdir: %v", err) 1149 } 1150 1151 os.Mkdir(wd+"/ro", 0700) 1152 if err != nil { 1153 t.Fatalf("Mkdir: %v", err) 1154 } 1155 1156 wrFs := newDisappearingFS(pathfs.NewLoopbackFileSystem(wd+"/rw"), 1157 pathfs.NewLoopbackFileSystem("/dev/null")) 1158 var fses []pathfs.FileSystem 1159 fses = append(fses, pathfs.NewLockingFileSystem(wrFs)) 1160 fses = append(fses, pathfs.NewLoopbackFileSystem(wd+"/ro")) 1161 ufs, err := NewUnionFs(fses, testOpts) 1162 if err != nil { 1163 t.Fatalf("NewUnionFs: %v", err) 1164 } 1165 1166 opts := &nodefs.Options{ 1167 EntryTimeout: entryTTL, 1168 AttrTimeout: entryTTL, 1169 NegativeTimeout: entryTTL, 1170 Debug: testutil.VerboseTest(), 1171 LookupKnownChildren: true, 1172 } 1173 1174 nfs := pathfs.NewPathNodeFs(ufs, nil) 1175 state, _, err := nodefs.MountRoot(wd+"/mnt", nfs.Root(), opts) 1176 if err != nil { 1177 t.Fatalf("MountNodeFileSystem: %v", err) 1178 } 1179 defer state.Unmount() 1180 go state.Serve() 1181 state.WaitMount() 1182 1183 err = ioutil.WriteFile(wd+"/ro/file", []byte("blabla"), 0644) 1184 if err != nil { 1185 t.Fatalf("WriteFile: %v", err) 1186 } 1187 setRecursiveWritable(t, wd+"/ro", false) 1188 1189 err = os.Remove(wd + "/mnt/file") 1190 if err != nil { 1191 t.Fatalf("Remove: %v", err) 1192 } 1193 1194 wrFs.visibleChan <- false 1195 time.Sleep((3 * entryTTL) / 2) 1196 1197 _, err = ioutil.ReadDir(wd + "/mnt") 1198 if err == nil { 1199 t.Fatal("Readdir should have failed") 1200 } 1201 1202 err = ioutil.WriteFile(wd+"/mnt/file2", []byte("blabla"), 0644) 1203 if err == nil { 1204 t.Fatal("write should have failed") 1205 } 1206 1207 // Wait for the caches to purge, and then restore. 1208 time.Sleep((3 * entryTTL) / 2) 1209 wrFs.visibleChan <- true 1210 1211 _, err = ioutil.ReadDir(wd + "/mnt") 1212 if err != nil { 1213 t.Fatal("Readdir should succeed", err) 1214 } 1215 err = ioutil.WriteFile(wd+"/mnt/file2", []byte("blabla"), 0644) 1216 if err != nil { 1217 t.Fatal("write should succeed", err) 1218 } 1219 } 1220 1221 func TestUnionFsDeletedGetAttr(t *testing.T) { 1222 wd, clean := setupUfs(t) 1223 defer clean() 1224 1225 err := ioutil.WriteFile(wd+"/ro/file", []byte("blabla"), 0644) 1226 if err != nil { 1227 t.Fatalf("WriteFile: %v", err) 1228 } 1229 setRecursiveWritable(t, wd+"/ro", false) 1230 1231 f, err := os.Open(wd + "/mnt/file") 1232 if err != nil { 1233 t.Fatalf("Open: %v", err) 1234 } 1235 defer f.Close() 1236 1237 err = os.Remove(wd + "/mnt/file") 1238 if err != nil { 1239 t.Fatalf("Remove: %v", err) 1240 } 1241 1242 if fi, err := f.Stat(); err != nil || fi.Mode()&os.ModeType != 0 { 1243 t.Fatalf("stat returned error or non-file: %v %v", err, fi) 1244 } 1245 } 1246 1247 func TestUnionFsDoubleOpen(t *testing.T) { 1248 wd, clean := setupUfs(t) 1249 defer clean() 1250 err := ioutil.WriteFile(wd+"/ro/file", []byte("blablabla"), 0644) 1251 if err != nil { 1252 t.Fatalf("WriteFile: %v", err) 1253 } 1254 setRecursiveWritable(t, wd+"/ro", false) 1255 1256 roFile, err := os.Open(wd + "/mnt/file") 1257 if err != nil { 1258 t.Fatalf("Open: %v", err) 1259 } 1260 defer roFile.Close() 1261 rwFile, err := os.OpenFile(wd+"/mnt/file", os.O_WRONLY|os.O_TRUNC, 0666) 1262 if err != nil { 1263 t.Fatalf("OpenFile: %v", err) 1264 } 1265 defer rwFile.Close() 1266 1267 output, err := ioutil.ReadAll(roFile) 1268 if err != nil { 1269 t.Fatalf("ReadAll: %v", err) 1270 } 1271 if len(output) != 0 { 1272 t.Errorf("After r/w truncation, r/o file should be empty too: %q", string(output)) 1273 } 1274 1275 want := "hello" 1276 _, err = rwFile.Write([]byte(want)) 1277 if err != nil { 1278 t.Fatalf("Write: %v", err) 1279 } 1280 1281 b := make([]byte, 100) 1282 1283 roFile.Seek(0, 0) 1284 n, err := roFile.Read(b) 1285 if err != nil { 1286 t.Fatalf("Read: %v", err) 1287 } 1288 b = b[:n] 1289 1290 if string(b) != "hello" { 1291 t.Errorf("r/w and r/o file are not synchronized: got %q want %q", string(b), want) 1292 } 1293 } 1294 1295 func TestUnionFsStatFs(t *testing.T) { 1296 wd, clean := setupUfs(t) 1297 defer clean() 1298 1299 s1 := syscall.Statfs_t{} 1300 err := syscall.Statfs(wd+"/mnt", &s1) 1301 if err != nil { 1302 t.Fatal("statfs mnt", err) 1303 } 1304 if s1.Bsize == 0 { 1305 t.Fatal("Expect blocksize > 0") 1306 } 1307 } 1308 1309 func TestUnionFsFlushSize(t *testing.T) { 1310 wd, clean := setupUfs(t) 1311 defer clean() 1312 1313 fn := wd + "/mnt/file" 1314 f, err := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE, 0644) 1315 if err != nil { 1316 t.Fatalf("OpenFile: %v", err) 1317 } 1318 fi, err := f.Stat() 1319 if err != nil { 1320 t.Fatalf("Stat: %v", err) 1321 } 1322 1323 n, err := f.Write([]byte("hello")) 1324 if err != nil { 1325 t.Fatalf("Write: %v", err) 1326 } 1327 1328 f.Close() 1329 fi, err = os.Lstat(fn) 1330 if err != nil { 1331 t.Fatalf("Lstat: %v", err) 1332 } 1333 if fi.Size() != int64(n) { 1334 t.Errorf("got %d from Stat().Size, want %d", fi.Size(), n) 1335 } 1336 } 1337 1338 func TestUnionFsFlushRename(t *testing.T) { 1339 wd, clean := setupUfs(t) 1340 defer clean() 1341 1342 err := ioutil.WriteFile(wd+"/mnt/file", []byte("x"), 0644) 1343 1344 fn := wd + "/mnt/tmp" 1345 f, err := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE, 0644) 1346 if err != nil { 1347 t.Fatalf("OpenFile: %v", err) 1348 } 1349 fi, err := f.Stat() 1350 if err != nil { 1351 t.Fatalf("Stat: %v", err) 1352 } 1353 1354 n, err := f.Write([]byte("hello")) 1355 if err != nil { 1356 t.Fatalf("Write: %v", err) 1357 } 1358 f.Close() 1359 1360 dst := wd + "/mnt/file" 1361 err = os.Rename(fn, dst) 1362 if err != nil { 1363 t.Fatalf("Rename: %v", err) 1364 } 1365 1366 fi, err = os.Lstat(dst) 1367 if err != nil { 1368 t.Fatalf("Lstat: %v", err) 1369 } 1370 if fi.Size() != int64(n) { 1371 t.Errorf("got %d from Stat().Size, want %d", fi.Size(), n) 1372 } 1373 } 1374 1375 func TestUnionFsTruncGetAttr(t *testing.T) { 1376 wd, clean := setupUfs(t) 1377 defer clean() 1378 1379 c := []byte("hello") 1380 f, err := os.OpenFile(wd+"/mnt/file", os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644) 1381 if err != nil { 1382 t.Fatalf("OpenFile: %v", err) 1383 } 1384 _, err = f.Write(c) 1385 if err != nil { 1386 t.Fatalf("Write: %v", err) 1387 } 1388 err = f.Close() 1389 if err != nil { 1390 t.Fatalf("Close: %v", err) 1391 } 1392 1393 fi, err := os.Lstat(wd + "/mnt/file") 1394 if fi.Size() != int64(len(c)) { 1395 t.Fatalf("Length mismatch got %d want %d", fi.Size(), len(c)) 1396 } 1397 } 1398 1399 func TestUnionFsPromoteDirTimeStamp(t *testing.T) { 1400 wd, clean := setupUfs(t) 1401 defer clean() 1402 1403 err := os.Mkdir(wd+"/ro/subdir", 0750) 1404 if err != nil { 1405 t.Fatalf("Mkdir: %v", err) 1406 } 1407 err = ioutil.WriteFile(wd+"/ro/subdir/file", []byte("hello"), 0644) 1408 if err != nil { 1409 t.Fatalf("WriteFile: %v", err) 1410 } 1411 setRecursiveWritable(t, wd+"/ro", false) 1412 1413 err = os.Chmod(wd+"/mnt/subdir/file", 0060) 1414 if err != nil { 1415 t.Fatalf("Chmod: %v", err) 1416 } 1417 1418 fRo, err := os.Lstat(wd + "/ro/subdir") 1419 if err != nil { 1420 t.Fatalf("Lstat: %v", err) 1421 } 1422 fRw, err := os.Lstat(wd + "/rw/subdir") 1423 if err != nil { 1424 t.Fatalf("Lstat: %v", err) 1425 } 1426 1427 // TODO - need to update timestamps after promoteDirsTo calls, 1428 // not during. 1429 if false && fRo.ModTime().Equal(fRw.ModTime()) { 1430 t.Errorf("Changed timestamps on promoted subdir: ro %v rw %v", fRo.ModTime(), fRw.ModTime()) 1431 } 1432 1433 if fRo.Mode().Perm()|0200 != fRw.Mode().Perm() { 1434 t.Errorf("Changed mode ro: %v, rw: %v", fRo.Mode(), fRw.Mode()) 1435 } 1436 } 1437 1438 func TestUnionFsCheckHiddenFiles(t *testing.T) { 1439 wd, clean := setupUfs(t) 1440 defer clean() 1441 1442 err := ioutil.WriteFile(wd+"/ro/hidden", []byte("bla"), 0644) 1443 if err != nil { 1444 t.Fatalf("WriteFile: %v", err) 1445 } 1446 err = ioutil.WriteFile(wd+"/ro/not_hidden", []byte("bla"), 0644) 1447 if err != nil { 1448 t.Fatalf("WriteFile: %v", err) 1449 } 1450 setRecursiveWritable(t, wd+"/ro", false) 1451 1452 fi, _ := os.Lstat(wd + "/mnt/hidden") 1453 if fi != nil { 1454 t.Fatal("Lstat() should have failed", fi) 1455 } 1456 _, err = os.Lstat(wd + "/mnt/not_hidden") 1457 if err != nil { 1458 t.Fatalf("Lstat: %v", err) 1459 } 1460 1461 names, err := Readdirnames(wd + "/mnt") 1462 if err != nil { 1463 t.Fatalf("Readdirnames: %v", err) 1464 } 1465 if len(names) != 1 || names[0] != "not_hidden" { 1466 t.Fatal("unexpected names", names) 1467 } 1468 } 1469 1470 func TestUnionFSBarf(t *testing.T) { 1471 wd, clean := setupUfs(t) 1472 defer clean() 1473 1474 if err := os.Mkdir(wd+"/mnt/dir", 0755); err != nil { 1475 t.Fatalf("os.Mkdir: %v", err) 1476 } 1477 if err := os.Mkdir(wd+"/mnt/dir2", 0755); err != nil { 1478 t.Fatalf("os.Mkdir: %v", err) 1479 } 1480 if err := ioutil.WriteFile(wd+"/rw/dir/file", []byte("bla"), 0644); err != nil { 1481 t.Fatalf("WriteFile: %v", err) 1482 } 1483 if _, err := os.Lstat(wd + "/mnt/dir/file"); err != nil { 1484 t.Fatalf("Lstat: %v", err) 1485 } 1486 if err := os.Rename(wd+"/rw/dir/file", wd+"/rw/file"); err != nil { 1487 t.Fatalf("os.Rename: %v", err) 1488 } 1489 if err := os.Rename(wd+"/mnt/file", wd+"/mnt/dir2/file"); err != nil { 1490 t.Fatalf("os.Rename: %v", err) 1491 } 1492 }