1// Copyright 2016 Keybase Inc. All rights reserved. 2// Use of this source code is governed by a BSD 3// license that can be found in the LICENSE file. 4// 5// +build !windows 6 7package libfuse 8 9import ( 10 "bytes" 11 "encoding/json" 12 "fmt" 13 "io" 14 "os" 15 "os/exec" 16 "path" 17 "reflect" 18 "runtime" 19 "strconv" 20 "strings" 21 "syscall" 22 "testing" 23 "time" 24 25 "bazil.org/fuse" 26 "bazil.org/fuse/fs" 27 "bazil.org/fuse/fs/fstestutil" 28 "github.com/keybase/client/go/kbfs/ioutil" 29 "github.com/keybase/client/go/kbfs/libcontext" 30 "github.com/keybase/client/go/kbfs/libfs" 31 "github.com/keybase/client/go/kbfs/libkbfs" 32 "github.com/keybase/client/go/kbfs/test/clocktest" 33 "github.com/keybase/client/go/kbfs/tlf" 34 kbname "github.com/keybase/client/go/kbun" 35 "github.com/keybase/client/go/logger" 36 "github.com/keybase/client/go/protocol/keybase1" 37 "github.com/pkg/errors" 38 "github.com/stretchr/testify/require" 39 "golang.org/x/net/context" 40 "golang.org/x/sys/unix" 41) 42 43func makeFS(ctx context.Context, t testing.TB, config *libkbfs.ConfigLocal) ( 44 *fstestutil.Mount, *FS, func()) { 45 log := logger.NewTestLogger(t) 46 debugLog := log.CloneWithAddedDepth(1) 47 fuse.Debug = MakeFuseDebugFn(debugLog, false /* superVerbose */) 48 49 // TODO duplicates main() in kbfsfuse/main.go too much 50 filesys := &FS{ 51 config: config, 52 log: log, 53 vlog: config.MakeVLogger(log), 54 errLog: log, 55 errVlog: config.MakeVLogger(log), 56 notifications: libfs.NewFSNotifications(log), 57 root: NewRoot(), 58 } 59 filesys.root.private = &FolderList{ 60 fs: filesys, 61 tlfType: tlf.Private, 62 folders: make(map[string]*TLF), 63 } 64 filesys.root.public = &FolderList{ 65 fs: filesys, 66 tlfType: tlf.Public, 67 folders: make(map[string]*TLF), 68 } 69 filesys.root.team = &FolderList{ 70 fs: filesys, 71 tlfType: tlf.SingleTeam, 72 folders: make(map[string]*TLF), 73 } 74 filesys.execAfterDelay = func(d time.Duration, f func()) { 75 time.AfterFunc(d, f) 76 } 77 fn := func(mnt *fstestutil.Mount) fs.FS { 78 filesys.fuse = mnt.Server 79 filesys.conn = mnt.Conn 80 return filesys 81 } 82 options := GetPlatformSpecificMountOptionsForTest() 83 mnt, err := fstestutil.MountedFuncT(t, fn, &fs.Config{ 84 WithContext: func(ctx context.Context, req fuse.Request) context.Context { 85 return filesys.WithContext(ctx) 86 }, 87 }, options...) 88 if err != nil { 89 t.Fatal(err) 90 } 91 // the cancelFn returned will cancel notification processing; the 92 // FUSE serve loop is terminated by unmounting the filesystem 93 ctx = context.WithValue(ctx, libfs.CtxAppIDKey, filesys) 94 ctx, cancelFn := context.WithCancel(ctx) 95 filesys.LaunchNotificationProcessor(ctx) 96 return mnt, filesys, func() { 97 cancelFn() 98 } 99} 100 101type fileInfoCheck func(fi os.FileInfo) error 102 103func mustBeFileWithSize(fi os.FileInfo, size int64) error { 104 if fi.Size() != size { 105 return fmt.Errorf("Bad file size: %d", fi.Size()) 106 } 107 return nil 108} 109 110func mustBeDir(fi os.FileInfo) error { 111 if !fi.IsDir() { 112 return fmt.Errorf("not a directory: %v", fi) 113 } 114 return nil 115} 116 117func checkDirNoTestError( 118 t testing.TB, dir string, want map[string]fileInfoCheck) error { 119 // make a copy of want, to be safe 120 { 121 tmp := make(map[string]fileInfoCheck, len(want)) 122 for k, v := range want { 123 tmp[k] = v 124 } 125 want = tmp 126 } 127 128 fis, err := ioutil.ReadDir(dir) 129 if err != nil { 130 t.Fatal(err) 131 } 132 for _, fi := range fis { 133 if check, ok := want[fi.Name()]; ok { 134 delete(want, fi.Name()) 135 if check != nil { 136 if err := check(fi); err != nil { 137 return fmt.Errorf("check failed: %v: %v", fi.Name(), err) 138 } 139 } 140 continue 141 } 142 return fmt.Errorf("unexpected direntry: %q size=%v mode=%v", 143 fi.Name(), fi.Size(), fi.Mode()) 144 } 145 for filename := range want { 146 return fmt.Errorf("never saw file: %v", filename) 147 } 148 return nil 149} 150 151func checkDir(t testing.TB, dir string, want map[string]fileInfoCheck) { 152 err := checkDirNoTestError(t, dir, want) 153 if err != nil { 154 t.Error(err) 155 } 156} 157 158// timeEqualFuzzy returns whether a is b+-skew. 159func timeEqualFuzzy(a, b time.Time, skew time.Duration) bool { 160 b1 := b.Add(-skew) 161 b2 := b.Add(skew) 162 return !a.Before(b1) && !a.After(b2) 163} 164 165func testCleanupDelayer(ctx context.Context, t *testing.T) { 166 err := libcontext.CleanupCancellationDelayer(ctx) 167 require.NoError(t, err) 168} 169 170func TestStatRoot(t *testing.T) { 171 ctx := libcontext.BackgroundContextWithCancellationDelayer() 172 defer testCleanupDelayer(ctx, t) 173 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 174 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 175 mnt, _, cancelFn := makeFS(ctx, t, config) 176 defer mnt.Close() 177 defer cancelFn() 178 179 fi, err := ioutil.Lstat(mnt.Dir) 180 if err != nil { 181 t.Fatal(err) 182 } 183 if g, e := fi.Mode().String(), `dr-x------`; g != e { 184 t.Errorf("wrong mode for folder: %q != %q", g, e) 185 } 186} 187 188func TestStatPrivate(t *testing.T) { 189 ctx := libcontext.BackgroundContextWithCancellationDelayer() 190 defer testCleanupDelayer(ctx, t) 191 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 192 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 193 mnt, _, cancelFn := makeFS(ctx, t, config) 194 defer mnt.Close() 195 defer cancelFn() 196 197 fi, err := ioutil.Lstat(path.Join(mnt.Dir, PrivateName)) 198 if err != nil { 199 t.Fatal(err) 200 } 201 if g, e := fi.Mode().String(), `dr-x------`; g != e { 202 t.Errorf("wrong mode for folder: %q != %q", g, e) 203 } 204} 205 206func TestStatPublic(t *testing.T) { 207 ctx := libcontext.BackgroundContextWithCancellationDelayer() 208 defer testCleanupDelayer(ctx, t) 209 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 210 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 211 mnt, _, cancelFn := makeFS(ctx, t, config) 212 defer mnt.Close() 213 defer cancelFn() 214 215 fi, err := ioutil.Lstat(path.Join(mnt.Dir, PublicName)) 216 if err != nil { 217 t.Fatal(err) 218 } 219 if g, e := fi.Mode().String(), `dr-x------`; g != e { 220 t.Errorf("wrong mode for folder: %q != %q", g, e) 221 } 222} 223 224func TestStatMyFolder(t *testing.T) { 225 ctx := libcontext.BackgroundContextWithCancellationDelayer() 226 defer testCleanupDelayer(ctx, t) 227 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 228 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 229 mnt, _, cancelFn := makeFS(ctx, t, config) 230 defer mnt.Close() 231 defer cancelFn() 232 233 // Access the tlf once to have the *Dir populated in tlf.go 234 if err := ioutil.Mkdir( 235 path.Join(mnt.Dir, PrivateName, "jdoe", "d"), os.ModeDir); err != nil { 236 t.Fatal(err) 237 } 238 239 fi, err := ioutil.Lstat(path.Join(mnt.Dir, PrivateName, "jdoe")) 240 if err != nil { 241 t.Fatal(err) 242 } 243 if g, e := fi.Mode().String(), `drwx------`; g != e { 244 t.Errorf("wrong mode for folder: %q != %q", g, e) 245 } 246} 247 248func TestStatNonexistentFolder(t *testing.T) { 249 ctx := libcontext.BackgroundContextWithCancellationDelayer() 250 defer testCleanupDelayer(ctx, t) 251 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 252 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 253 mnt, _, cancelFn := makeFS(ctx, t, config) 254 defer mnt.Close() 255 defer cancelFn() 256 257 if _, err := ioutil.Lstat(path.Join(mnt.Dir, PrivateName, "does-not-exist")); !ioutil.IsNotExist(err) { 258 t.Fatalf("expected ENOENT: %v", err) 259 } 260} 261 262func TestStatAlias(t *testing.T) { 263 ctx := libcontext.BackgroundContextWithCancellationDelayer() 264 defer testCleanupDelayer(ctx, t) 265 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 266 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 267 mnt, _, cancelFn := makeFS(ctx, t, config) 268 defer mnt.Close() 269 defer cancelFn() 270 271 p := path.Join(mnt.Dir, PrivateName, "jdoe,jdoe") 272 fi, err := ioutil.Lstat(p) 273 if err != nil { 274 t.Fatal(err) 275 } 276 if g, e := fi.Mode().String(), `Lrwxrwxrwx`; g != e { 277 t.Errorf("wrong mode for alias : %q != %q", g, e) 278 } 279 target, err := os.Readlink(p) 280 if err != nil { 281 t.Fatal(err) 282 } 283 if g, e := target, "jdoe"; g != e { 284 t.Errorf("wrong alias symlink target: %q != %q", g, e) 285 } 286} 287 288// Test that we can determine a normalized alias without any identify 289// calls (regression test for KBFS-531). 290func TestStatAliasCausesNoIdentifies(t *testing.T) { 291 ctx := libcontext.BackgroundContextWithCancellationDelayer() 292 defer testCleanupDelayer(ctx, t) 293 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 294 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 295 296 mnt, _, cancelFn := makeFS(ctx, t, config) 297 defer mnt.Close() 298 defer cancelFn() 299 300 p := path.Join(mnt.Dir, PublicName, "HEAD") 301 // Even though "head" is not a real user in our config, this stat 302 // should succeed because no identify calls should be triggered. 303 fi, err := ioutil.Lstat(p) 304 if err != nil { 305 t.Fatal(err) 306 } 307 if g, e := fi.Mode().String(), `Lrwxrwxrwx`; g != e { 308 t.Errorf("wrong mode for alias : %q != %q", g, e) 309 } 310 target, err := os.Readlink(p) 311 if err != nil { 312 t.Fatal(err) 313 } 314 if g, e := target, "head"; g != e { 315 t.Errorf("wrong alias symlink target: %q != %q", g, e) 316 } 317} 318 319func TestStatInvalidAliasFails(t *testing.T) { 320 ctx := libcontext.BackgroundContextWithCancellationDelayer() 321 defer testCleanupDelayer(ctx, t) 322 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 323 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 324 325 mnt, _, cancelFn := makeFS(ctx, t, config) 326 defer mnt.Close() 327 defer cancelFn() 328 329 p := path.Join(mnt.Dir, PublicName, "HEAD.JPG") 330 // This should fail as HEAD.JPG has the wrong format. 331 _, err := ioutil.Lstat(p) 332 if err == nil { 333 t.Fatal("Lstat of HEAD.JPG didn't return an error!") 334 } 335} 336 337func TestRemoveAlias(t *testing.T) { 338 ctx := libcontext.BackgroundContextWithCancellationDelayer() 339 defer testCleanupDelayer(ctx, t) 340 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 341 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 342 mnt, _, cancelFn := makeFS(ctx, t, config) 343 defer mnt.Close() 344 defer cancelFn() 345 346 p := path.Join(mnt.Dir, PrivateName, "jdoe,jdoe") 347 err := ioutil.Remove(p) 348 if err != nil { 349 t.Fatalf("Removing alias failed: %v", err) 350 } 351} 352 353func TestStatMyPublic(t *testing.T) { 354 ctx := libcontext.BackgroundContextWithCancellationDelayer() 355 defer testCleanupDelayer(ctx, t) 356 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 357 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 358 mnt, _, cancelFn := makeFS(ctx, t, config) 359 defer mnt.Close() 360 defer cancelFn() 361 362 // Access the tlf once to have the *Dir populated in tlf.go 363 if err := ioutil.Mkdir( 364 path.Join(mnt.Dir, PublicName, "jdoe", "d"), os.ModeDir); err != nil { 365 t.Fatal(err) 366 } 367 368 fi, err := ioutil.Lstat(path.Join(mnt.Dir, PublicName, "jdoe")) 369 if err != nil { 370 t.Fatal(err) 371 } 372 if g, e := fi.Mode().String(), `drwx------`; g != e { 373 t.Errorf("wrong mode for folder: %q != %q", g, e) 374 } 375} 376 377func TestReaddirRoot(t *testing.T) { 378 ctx := libcontext.BackgroundContextWithCancellationDelayer() 379 defer testCleanupDelayer(ctx, t) 380 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 381 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 382 mnt, _, cancelFn := makeFS(ctx, t, config) 383 defer mnt.Close() 384 defer cancelFn() 385 386 checkDir(t, mnt.Dir, map[string]fileInfoCheck{ 387 PrivateName: mustBeDir, 388 PublicName: mustBeDir, 389 TeamName: mustBeDir, 390 }) 391} 392 393func TestReaddirPrivate(t *testing.T) { 394 ctx := libcontext.BackgroundContextWithCancellationDelayer() 395 defer testCleanupDelayer(ctx, t) 396 config := libkbfs.MakeTestConfigOrBust(t, "jdoe", "janedoe") 397 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 398 mnt, _, cancelFn := makeFS(ctx, t, config) 399 defer mnt.Close() 400 defer cancelFn() 401 402 { 403 ctx := libcontext.BackgroundContextWithCancellationDelayer() 404 defer testCleanupDelayer(ctx, t) 405 // Force FakeMDServer to have some TlfIDs it can present to us 406 // as favorites. Don't go through VFS to avoid caching causing 407 // false positives. 408 libkbfs.GetRootNodeOrBust(ctx, t, config, "janedoe,jdoe", tlf.Private) 409 libkbfs.GetRootNodeOrBust(ctx, t, config, "janedoe,jdoe", tlf.Public) 410 } 411 412 checkDir(t, path.Join(mnt.Dir, PrivateName), map[string]fileInfoCheck{ 413 "jdoe,janedoe": mustBeDir, 414 "jdoe": mustBeDir, // default home directory 415 }) 416} 417 418func TestReaddirPrivateDeleteAndReaddFavorite(t *testing.T) { 419 ctx := libcontext.BackgroundContextWithCancellationDelayer() 420 defer testCleanupDelayer(ctx, t) 421 config := libkbfs.MakeTestConfigOrBust(t, "jdoe", "janedoe") 422 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 423 mnt, fs, cancelFn := makeFS(ctx, t, config) 424 fs.execAfterDelay = func(d time.Duration, f func()) { 425 // this causes the entry added to fl.recentlyRemoved (in 426 // addToRecentlyRemove) to be removed instantly. this way we can avoid 427 // adding delays in tests. 428 f() 429 } 430 defer mnt.Close() 431 defer cancelFn() 432 433 { 434 ctx := libcontext.BackgroundContextWithCancellationDelayer() 435 defer testCleanupDelayer(ctx, t) 436 // Force FakeMDServer to have some TlfIDs it can present to us 437 // as favorites. Don't go through VFS to avoid caching causing 438 // false positives. 439 libkbfs.GetRootNodeOrBust(ctx, t, config, "janedoe,jdoe", tlf.Private) 440 libkbfs.GetRootNodeOrBust(ctx, t, config, "janedoe,jdoe", tlf.Public) 441 } 442 443 err := ioutil.Remove(path.Join(mnt.Dir, PrivateName, "jdoe,janedoe")) 444 if err != nil { 445 t.Fatalf("Removing favorite failed: %v", err) 446 } 447 448 checkDir(t, path.Join(mnt.Dir, PrivateName), map[string]fileInfoCheck{ 449 "jdoe": mustBeDir, // default home directory 450 }) 451 452 // Re-add the favorite by doing a readdir 453 checkDir(t, path.Join(mnt.Dir, PrivateName, "jdoe,janedoe"), 454 map[string]fileInfoCheck{}) 455 456 checkDir(t, path.Join(mnt.Dir, PrivateName), map[string]fileInfoCheck{ 457 "jdoe,janedoe": mustBeDir, 458 "jdoe": mustBeDir, // default home directory 459 }) 460} 461 462func TestReaddirPublic(t *testing.T) { 463 ctx := libcontext.BackgroundContextWithCancellationDelayer() 464 defer testCleanupDelayer(ctx, t) 465 config := libkbfs.MakeTestConfigOrBust(t, "jdoe", "janedoe") 466 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 467 mnt, _, cancelFn := makeFS(ctx, t, config) 468 defer mnt.Close() 469 defer cancelFn() 470 471 { 472 ctx := libcontext.BackgroundContextWithCancellationDelayer() 473 defer testCleanupDelayer(ctx, t) 474 // Force FakeMDServer to have some TlfIDs it can present to us 475 // as favorites. Don't go through VFS to avoid caching causing 476 // false positives. 477 libkbfs.GetRootNodeOrBust(ctx, t, config, "janedoe,jdoe", tlf.Private) 478 libkbfs.GetRootNodeOrBust(ctx, t, config, "janedoe,jdoe", tlf.Public) 479 } 480 481 checkDir(t, path.Join(mnt.Dir, PublicName), map[string]fileInfoCheck{ 482 "jdoe,janedoe": mustBeDir, 483 "jdoe": mustBeDir, // default personal public directory 484 }) 485} 486 487type kbserviceBrokenIdentify struct { 488 libkbfs.KeybaseService 489} 490 491func (k kbserviceBrokenIdentify) Identify( 492 ctx context.Context, assertion, reason string, 493 _ keybase1.OfflineAvailability) ( 494 kbname.NormalizedUsername, keybase1.UserOrTeamID, error) { 495 return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""), 496 errors.New("Fake identify error") 497} 498 499// Regression test for KBFS-772 on OSX. (There's a bug where ls only 500// respects errors from Open, not from ReadDirAll.) 501func TestReaddirPublicFailedIdentifyViaOSCall(t *testing.T) { 502 ctx := libcontext.BackgroundContextWithCancellationDelayer() 503 defer testCleanupDelayer(ctx, t) 504 config1 := libkbfs.MakeTestConfigOrBust(t, "u1", "u2") 505 defer libkbfs.CheckConfigAndShutdown(ctx, t, config1) 506 mnt1, _, cancelFn1 := makeFS(ctx, t, config1) 507 defer mnt1.Close() 508 defer cancelFn1() 509 510 config2 := libkbfs.ConfigAsUser(config1, "u2") 511 defer libkbfs.CheckConfigAndShutdown(ctx, t, config2) 512 mnt2, _, cancelFn2 := makeFS(ctx, t, config2) 513 defer mnt2.Close() 514 defer cancelFn2() 515 516 // Create a shared folder via u2. 517 p := path.Join(mnt2.Dir, PrivateName, "u1,u2", "mydir") 518 if err := ioutil.Mkdir(p, 0755); err != nil { 519 t.Fatal(err) 520 } 521 522 // Make u1 get failures for every identify call. 523 config1.SetKeybaseService(kbserviceBrokenIdentify{ 524 KeybaseService: config1.KeybaseService(), 525 }) 526 527 // A private non-existing home folder, with write permissions, fails. 528 err := exec.Command("ls", path.Join(mnt1.Dir, PublicName, "u1")).Run() 529 if _, ok := err.(*exec.ExitError); !ok { 530 t.Fatalf("No error as expected on broken user identify: %v", err) 531 } 532 533 // A private existing shared folder, with write permissions, fails. 534 err = exec.Command("ls", path.Join(mnt1.Dir, PrivateName, "u1,u2")).Run() 535 if _, ok := err.(*exec.ExitError); !ok { 536 t.Fatalf("No error as expected on broken user identify: %v", err) 537 } 538 539 // A public, non-existing folder, without write permissions, fails. 540 err = exec.Command("ls", path.Join(mnt1.Dir, PublicName, "u2")).Run() 541 if _, ok := err.(*exec.ExitError); !ok { 542 t.Fatalf("No error as expected on broken user identify: %v", err) 543 } 544} 545 546func TestReaddirMyFolderEmpty(t *testing.T) { 547 ctx := libcontext.BackgroundContextWithCancellationDelayer() 548 defer testCleanupDelayer(ctx, t) 549 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 550 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 551 mnt, _, cancelFn := makeFS(ctx, t, config) 552 defer mnt.Close() 553 defer cancelFn() 554 555 checkDir(t, path.Join(mnt.Dir, PrivateName, "jdoe"), map[string]fileInfoCheck{}) 556} 557 558func syncAll(t *testing.T, tlf string, ty tlf.Type, fs *FS) { 559 // golang doesn't let us sync on a directory handle, so if we need 560 // to sync all without a file, go through libkbfs directly. 561 ctx := libcontext.BackgroundContextWithCancellationDelayer() 562 defer testCleanupDelayer(ctx, t) 563 root := libkbfs.GetRootNodeOrBust(ctx, t, fs.config, tlf, ty) 564 err := fs.config.KBFSOps().SyncAll(ctx, root.GetFolderBranch()) 565 if err != nil { 566 t.Fatalf("Couldn't sync all: %v", err) 567 } 568} 569 570func syncAndClose(t *testing.T, f *os.File) { 571 if f == nil { 572 return 573 } 574 err := f.Sync() 575 if err != nil { 576 t.Fatal(err) 577 } 578 f.Close() 579} 580 581func syncFilename(t *testing.T, name string) { 582 f, err := os.OpenFile(name, os.O_WRONLY, 0644) 583 if err != nil { 584 t.Fatal(err) 585 } 586 syncAndClose(t, f) 587} 588 589func TestReaddirMyFolderWithFiles(t *testing.T) { 590 ctx := libcontext.BackgroundContextWithCancellationDelayer() 591 defer testCleanupDelayer(ctx, t) 592 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 593 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 594 mnt, _, cancelFn := makeFS(ctx, t, config) 595 defer mnt.Close() 596 defer cancelFn() 597 598 files := map[string]fileInfoCheck{ 599 "one": nil, 600 "two": nil, 601 } 602 for filename, check := range files { 603 if check != nil { 604 // only set up the files 605 continue 606 } 607 p := path.Join(mnt.Dir, PrivateName, "jdoe", filename) 608 if err := ioutil.WriteFile( 609 p, []byte("data for "+filename), 0644); err != nil { 610 t.Fatal(err) 611 } 612 syncFilename(t, p) 613 } 614 checkDir(t, path.Join(mnt.Dir, PrivateName, "jdoe"), files) 615} 616 617func testOneCreateThenRead(t *testing.T, p string) { 618 f, err := os.Create(p) 619 if err != nil { 620 t.Fatal(err) 621 } 622 // Call in a closure since `f` is overridden below. 623 defer func() { syncAndClose(t, f) }() 624 const input = "hello, world\n" 625 if _, err := io.WriteString(f, input); err != nil { 626 t.Fatalf("write error: %v", err) 627 } 628 syncAndClose(t, f) 629 f = nil 630 631 buf, err := ioutil.ReadFile(p) 632 if err != nil { 633 t.Fatalf("read error: %v", err) 634 } 635 if g, e := string(buf), input; g != e { 636 t.Errorf("bad file contents: %q != %q", g, e) 637 } 638} 639 640func TestCreateThenRead(t *testing.T) { 641 ctx := libcontext.BackgroundContextWithCancellationDelayer() 642 defer testCleanupDelayer(ctx, t) 643 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 644 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 645 mnt, _, cancelFn := makeFS(ctx, t, config) 646 defer mnt.Close() 647 defer cancelFn() 648 649 p := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile") 650 testOneCreateThenRead(t, p) 651} 652 653// Tests that writing and reading multiple files works, implicitly 654// exercising any block pointer reference counting code (since the 655// initial created files will have identical empty blocks to start 656// with). 657func TestMultipleCreateThenRead(t *testing.T) { 658 ctx := libcontext.BackgroundContextWithCancellationDelayer() 659 defer testCleanupDelayer(ctx, t) 660 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 661 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 662 mnt, _, cancelFn := makeFS(ctx, t, config) 663 defer mnt.Close() 664 defer cancelFn() 665 666 p1 := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile1") 667 testOneCreateThenRead(t, p1) 668 p2 := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile2") 669 testOneCreateThenRead(t, p2) 670} 671 672func TestReadUnflushed(t *testing.T) { 673 ctx := libcontext.BackgroundContextWithCancellationDelayer() 674 defer testCleanupDelayer(ctx, t) 675 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 676 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 677 mnt, _, cancelFn := makeFS(ctx, t, config) 678 defer mnt.Close() 679 defer cancelFn() 680 681 p := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile") 682 f, err := os.Create(p) 683 if err != nil { 684 t.Fatal(err) 685 } 686 defer syncAndClose(t, f) 687 const input = "hello, world\n" 688 if _, err := io.WriteString(f, input); err != nil { 689 t.Fatalf("write error: %v", err) 690 } 691 // explicitly no close here 692 693 buf, err := ioutil.ReadFile(p) 694 if err != nil { 695 t.Fatalf("read error: %v", err) 696 } 697 if g, e := string(buf), input; g != e { 698 t.Errorf("bad file contents: %q != %q", g, e) 699 } 700} 701 702func TestMountAgain(t *testing.T) { 703 ctx := libcontext.BackgroundContextWithCancellationDelayer() 704 defer testCleanupDelayer(ctx, t) 705 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 706 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 707 708 const input = "hello, world\n" 709 const filename = "myfile" 710 func() { 711 mnt, _, cancelFn := makeFS(ctx, t, config) 712 defer mnt.Close() 713 defer cancelFn() 714 715 p := path.Join(mnt.Dir, PrivateName, "jdoe", filename) 716 if err := ioutil.WriteFile(p, []byte(input), 0644); err != nil { 717 t.Fatal(err) 718 } 719 syncFilename(t, p) 720 }() 721 722 func() { 723 mnt, _, cancelFn := makeFS(ctx, t, config) 724 defer mnt.Close() 725 defer cancelFn() 726 p := path.Join(mnt.Dir, PrivateName, "jdoe", filename) 727 buf, err := ioutil.ReadFile(p) 728 if err != nil { 729 t.Fatalf("read error: %v", err) 730 } 731 if g, e := string(buf), input; g != e { 732 t.Errorf("bad file contents: %q != %q", g, e) 733 } 734 }() 735} 736 737func TestCreateExecutable(t *testing.T) { 738 ctx := libcontext.BackgroundContextWithCancellationDelayer() 739 defer testCleanupDelayer(ctx, t) 740 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 741 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 742 mnt, _, cancelFn := makeFS(ctx, t, config) 743 defer mnt.Close() 744 defer cancelFn() 745 746 p := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile") 747 if err := ioutil.WriteFile(p, []byte("fake binary"), 0755); err != nil { 748 t.Fatal(err) 749 } 750 syncFilename(t, p) 751 fi, err := ioutil.Lstat(p) 752 if err != nil { 753 t.Fatal(err) 754 } 755 if g, e := fi.Mode().String(), `-rwx------`; g != e { 756 t.Errorf("wrong mode for executable: %q != %q", g, e) 757 } 758} 759 760func TestMkdir(t *testing.T) { 761 ctx := libcontext.BackgroundContextWithCancellationDelayer() 762 defer testCleanupDelayer(ctx, t) 763 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 764 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 765 mnt, _, cancelFn := makeFS(ctx, t, config) 766 defer mnt.Close() 767 defer cancelFn() 768 769 p := path.Join(mnt.Dir, PrivateName, "jdoe", "mydir") 770 if err := ioutil.Mkdir(p, 0755); err != nil { 771 t.Fatal(err) 772 } 773 fi, err := ioutil.Lstat(p) 774 if err != nil { 775 t.Fatal(err) 776 } 777 if g, e := fi.Mode().String(), `drwx------`; g != e { 778 t.Errorf("wrong mode for subdir: %q != %q", g, e) 779 } 780} 781 782func TestMkdirAndCreateDeep(t *testing.T) { 783 ctx := libcontext.BackgroundContextWithCancellationDelayer() 784 defer testCleanupDelayer(ctx, t) 785 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 786 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 787 const input = "hello, world\n" 788 789 func() { 790 mnt, _, cancelFn := makeFS(ctx, t, config) 791 defer mnt.Close() 792 defer cancelFn() 793 794 one := path.Join(mnt.Dir, PrivateName, "jdoe", "one") 795 if err := ioutil.Mkdir(one, 0755); err != nil { 796 t.Fatal(err) 797 } 798 two := path.Join(one, "two") 799 if err := ioutil.Mkdir(two, 0755); err != nil { 800 t.Fatal(err) 801 } 802 three := path.Join(two, "three") 803 if err := ioutil.WriteFile(three, []byte(input), 0644); err != nil { 804 t.Fatal(err) 805 } 806 syncFilename(t, three) 807 }() 808 809 // unmount to flush cache 810 func() { 811 mnt, _, cancelFn := makeFS(ctx, t, config) 812 defer mnt.Close() 813 defer cancelFn() 814 815 p := path.Join(mnt.Dir, PrivateName, "jdoe", "one", "two", "three") 816 buf, err := ioutil.ReadFile(p) 817 if err != nil { 818 t.Fatalf("read error: %v", err) 819 } 820 if g, e := string(buf), input; g != e { 821 t.Errorf("bad file contents: %q != %q", g, e) 822 } 823 }() 824} 825 826func TestSymlink(t *testing.T) { 827 ctx := libcontext.BackgroundContextWithCancellationDelayer() 828 defer testCleanupDelayer(ctx, t) 829 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 830 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 831 832 func() { 833 mnt, _, cancelFn := makeFS(ctx, t, config) 834 defer mnt.Close() 835 defer cancelFn() 836 837 p := path.Join(mnt.Dir, PrivateName, "jdoe", "mylink") 838 if err := os.Symlink("myfile", p); err != nil { 839 t.Fatal(err) 840 } 841 }() 842 843 // unmount to flush cache 844 func() { 845 mnt, _, cancelFn := makeFS(ctx, t, config) 846 defer mnt.Close() 847 defer cancelFn() 848 849 p := path.Join(mnt.Dir, PrivateName, "jdoe", "mylink") 850 target, err := os.Readlink(p) 851 if err != nil { 852 t.Fatal(err) 853 } 854 if g, e := target, "myfile"; g != e { 855 t.Errorf("bad symlink target: %q != %q", g, e) 856 } 857 }() 858} 859 860func TestRename(t *testing.T) { 861 ctx := libcontext.BackgroundContextWithCancellationDelayer() 862 defer testCleanupDelayer(ctx, t) 863 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 864 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 865 mnt, _, cancelFn := makeFS(ctx, t, config) 866 defer mnt.Close() 867 defer cancelFn() 868 869 p1 := path.Join(mnt.Dir, PrivateName, "jdoe", "old") 870 p2 := path.Join(mnt.Dir, PrivateName, "jdoe", "new") 871 const input = "hello, world\n" 872 if err := ioutil.WriteFile(p1, []byte(input), 0644); err != nil { 873 t.Fatal(err) 874 } 875 syncFilename(t, p1) 876 877 if err := ioutil.Rename(p1, p2); err != nil { 878 t.Fatal(err) 879 } 880 881 checkDir(t, path.Join(mnt.Dir, PrivateName, "jdoe"), map[string]fileInfoCheck{ 882 "new": func(fi os.FileInfo) error { 883 return mustBeFileWithSize(fi, int64(len(input))) 884 }, 885 }) 886 887 buf, err := ioutil.ReadFile(p2) 888 if err != nil { 889 t.Errorf("read error: %v", err) 890 } 891 if g, e := string(buf), input; g != e { 892 t.Errorf("bad file contents: %q != %q", g, e) 893 } 894 895 if _, err := ioutil.ReadFile(p1); !ioutil.IsNotExist(err) { 896 t.Errorf("old name still exists: %v", err) 897 } 898} 899 900func TestRenameOverwrite(t *testing.T) { 901 ctx := libcontext.BackgroundContextWithCancellationDelayer() 902 defer testCleanupDelayer(ctx, t) 903 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 904 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 905 mnt, _, cancelFn := makeFS(ctx, t, config) 906 defer mnt.Close() 907 defer cancelFn() 908 909 p1 := path.Join(mnt.Dir, PrivateName, "jdoe", "old") 910 p2 := path.Join(mnt.Dir, PrivateName, "jdoe", "new") 911 const input = "hello, world\n" 912 if err := ioutil.WriteFile(p1, []byte(input), 0644); err != nil { 913 t.Fatal(err) 914 } 915 syncFilename(t, p1) 916 if err := ioutil.WriteFile(p2, []byte("loser\n"), 0644); err != nil { 917 t.Fatal(err) 918 } 919 syncFilename(t, p2) 920 921 if err := ioutil.Rename(p1, p2); err != nil { 922 t.Fatal(err) 923 } 924 925 checkDir(t, path.Join(mnt.Dir, PrivateName, "jdoe"), map[string]fileInfoCheck{ 926 "new": nil, 927 }) 928 929 buf, err := ioutil.ReadFile(p2) 930 if err != nil { 931 t.Errorf("read error: %v", err) 932 } 933 if g, e := string(buf), input; g != e { 934 t.Errorf("bad file contents: %q != %q", g, e) 935 } 936 937 if _, err := ioutil.ReadFile(p1); !ioutil.IsNotExist(err) { 938 t.Errorf("old name still exists: %v", err) 939 } 940} 941 942func TestRenameCrossDir(t *testing.T) { 943 ctx := libcontext.BackgroundContextWithCancellationDelayer() 944 defer testCleanupDelayer(ctx, t) 945 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 946 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 947 mnt, _, cancelFn := makeFS(ctx, t, config) 948 defer mnt.Close() 949 defer cancelFn() 950 951 if err := ioutil.Mkdir(path.Join(mnt.Dir, PrivateName, "jdoe", "one"), 0755); err != nil { 952 t.Fatal(err) 953 } 954 if err := ioutil.Mkdir(path.Join(mnt.Dir, PrivateName, "jdoe", "two"), 0755); err != nil { 955 t.Fatal(err) 956 } 957 p1 := path.Join(mnt.Dir, PrivateName, "jdoe", "one", "old") 958 p2 := path.Join(mnt.Dir, PrivateName, "jdoe", "two", "new") 959 const input = "hello, world\n" 960 if err := ioutil.WriteFile(p1, []byte(input), 0644); err != nil { 961 t.Fatal(err) 962 } 963 syncFilename(t, p1) 964 965 if err := ioutil.Rename(p1, p2); err != nil { 966 t.Fatal(err) 967 } 968 969 checkDir(t, path.Join(mnt.Dir, PrivateName, "jdoe", "one"), map[string]fileInfoCheck{}) 970 checkDir(t, path.Join(mnt.Dir, PrivateName, "jdoe", "two"), map[string]fileInfoCheck{ 971 "new": nil, 972 }) 973 974 buf, err := ioutil.ReadFile(p2) 975 if err != nil { 976 t.Errorf("read error: %v", err) 977 } 978 if g, e := string(buf), input; g != e { 979 t.Errorf("bad file contents: %q != %q", g, e) 980 } 981 982 if _, err := ioutil.ReadFile(p1); !ioutil.IsNotExist(err) { 983 t.Errorf("old name still exists: %v", err) 984 } 985} 986 987func TestRenameCrossFolder(t *testing.T) { 988 ctx := libcontext.BackgroundContextWithCancellationDelayer() 989 defer testCleanupDelayer(ctx, t) 990 config := libkbfs.MakeTestConfigOrBust(t, "jdoe", "wsmith") 991 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 992 mnt, _, cancelFn := makeFS(ctx, t, config) 993 defer mnt.Close() 994 defer cancelFn() 995 996 p1 := path.Join(mnt.Dir, PrivateName, "jdoe", "old") 997 p2 := path.Join(mnt.Dir, PrivateName, "wsmith,jdoe", "new") 998 const input = "hello, world\n" 999 if err := ioutil.WriteFile(p1, []byte(input), 0644); err != nil { 1000 t.Fatal(err) 1001 } 1002 syncFilename(t, p1) 1003 1004 err := ioutil.Rename(p1, p2) 1005 if err == nil { 1006 t.Fatalf("expected an error from rename: %v", err) 1007 } 1008 lerr, ok := errors.Cause(err).(*os.LinkError) 1009 if !ok { 1010 t.Fatalf("expected a LinkError from rename: %v", err) 1011 } 1012 if g, e := lerr.Op, "rename"; g != e { 1013 t.Errorf("wrong LinkError.Op: %q != %q", g, e) 1014 } 1015 if g, e := lerr.Old, p1; g != e { 1016 t.Errorf("wrong LinkError.Old: %q != %q", g, e) 1017 } 1018 if g, e := lerr.New, p2; g != e { 1019 t.Errorf("wrong LinkError.New: %q != %q", g, e) 1020 } 1021 if g, e := lerr.Err, syscall.EXDEV; g != e { 1022 t.Errorf("expected EXDEV: %T %v", lerr.Err, lerr.Err) 1023 } 1024 1025 checkDir(t, path.Join(mnt.Dir, PrivateName, "jdoe"), map[string]fileInfoCheck{ 1026 "old": nil, 1027 }) 1028 checkDir(t, path.Join(mnt.Dir, PrivateName, "wsmith,jdoe"), map[string]fileInfoCheck{}) 1029 1030 buf, err := ioutil.ReadFile(p1) 1031 if err != nil { 1032 t.Errorf("read error: %v", err) 1033 } 1034 if g, e := string(buf), input; g != e { 1035 t.Errorf("bad file contents: %q != %q", g, e) 1036 } 1037 1038 if _, err := ioutil.ReadFile(p2); !ioutil.IsNotExist(err) { 1039 t.Errorf("new name exists even on error: %v", err) 1040 } 1041} 1042 1043func TestWriteThenRename(t *testing.T) { 1044 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1045 defer testCleanupDelayer(ctx, t) 1046 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 1047 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 1048 mnt, _, cancelFn := makeFS(ctx, t, config) 1049 defer mnt.Close() 1050 defer cancelFn() 1051 1052 p1 := path.Join(mnt.Dir, PrivateName, "jdoe", "old") 1053 p2 := path.Join(mnt.Dir, PrivateName, "jdoe", "new") 1054 1055 f, err := os.Create(p1) 1056 if err != nil { 1057 t.Fatalf("cannot create file: %v", err) 1058 } 1059 defer syncAndClose(t, f) 1060 1061 // write to the file 1062 const input = "hello, world\n" 1063 if _, err := f.Write([]byte(input)); err != nil { 1064 t.Fatalf("cannot write: %v", err) 1065 } 1066 1067 // now rename the file while it's still open 1068 if err := ioutil.Rename(p1, p2); err != nil { 1069 t.Fatal(err) 1070 } 1071 1072 // check that the new path has the right length still 1073 checkDir(t, path.Join(mnt.Dir, PrivateName, "jdoe"), map[string]fileInfoCheck{ 1074 "new": func(fi os.FileInfo) error { 1075 return mustBeFileWithSize(fi, int64(len(input))) 1076 }, 1077 }) 1078 1079 // write again to the same file 1080 const input2 = "goodbye, world\n" 1081 if _, err := f.Write([]byte(input2)); err != nil { 1082 t.Fatalf("cannot write after rename: %v", err) 1083 } 1084 1085 buf, err := ioutil.ReadFile(p2) 1086 if err != nil { 1087 t.Errorf("read error: %v", err) 1088 } 1089 if g, e := string(buf), input+input2; g != e { 1090 t.Errorf("bad file contents: %q != %q", g, e) 1091 } 1092 1093 if _, err := ioutil.ReadFile(p1); !ioutil.IsNotExist(err) { 1094 t.Errorf("old name still exists: %v", err) 1095 } 1096} 1097 1098func TestWriteThenRenameCrossDir(t *testing.T) { 1099 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1100 defer testCleanupDelayer(ctx, t) 1101 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 1102 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 1103 mnt, _, cancelFn := makeFS(ctx, t, config) 1104 defer mnt.Close() 1105 defer cancelFn() 1106 1107 if err := ioutil.Mkdir(path.Join(mnt.Dir, PrivateName, "jdoe", "one"), 0755); err != nil { 1108 t.Fatal(err) 1109 } 1110 if err := ioutil.Mkdir(path.Join(mnt.Dir, PrivateName, "jdoe", "two"), 0755); err != nil { 1111 t.Fatal(err) 1112 } 1113 p1 := path.Join(mnt.Dir, PrivateName, "jdoe", "one", "old") 1114 p2 := path.Join(mnt.Dir, PrivateName, "jdoe", "two", "new") 1115 1116 f, err := os.Create(p1) 1117 if err != nil { 1118 t.Fatalf("cannot create file: %v", err) 1119 } 1120 defer syncAndClose(t, f) 1121 1122 // write to the file 1123 const input = "hello, world\n" 1124 if _, err := f.Write([]byte(input)); err != nil { 1125 t.Fatalf("cannot write: %v", err) 1126 } 1127 1128 // now rename the file while it's still open 1129 if err := ioutil.Rename(p1, p2); err != nil { 1130 t.Fatal(err) 1131 } 1132 1133 // check that the new path has the right length still 1134 checkDir(t, path.Join(mnt.Dir, PrivateName, "jdoe", "two"), map[string]fileInfoCheck{ 1135 "new": func(fi os.FileInfo) error { 1136 return mustBeFileWithSize(fi, int64(len(input))) 1137 }, 1138 }) 1139 1140 // write again to the same file 1141 const input2 = "goodbye, world\n" 1142 if _, err := f.Write([]byte(input2)); err != nil { 1143 t.Fatalf("cannot write after rename: %v", err) 1144 } 1145 1146 buf, err := ioutil.ReadFile(p2) 1147 if err != nil { 1148 t.Errorf("read error: %v", err) 1149 } 1150 if g, e := string(buf), input+input2; g != e { 1151 t.Errorf("bad file contents: %q != %q", g, e) 1152 } 1153 1154 if _, err := ioutil.ReadFile(p1); !ioutil.IsNotExist(err) { 1155 t.Errorf("old name still exists: %v", err) 1156 } 1157} 1158 1159func TestRemoveFile(t *testing.T) { 1160 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1161 defer testCleanupDelayer(ctx, t) 1162 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 1163 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 1164 mnt, _, cancelFn := makeFS(ctx, t, config) 1165 defer mnt.Close() 1166 defer cancelFn() 1167 1168 p := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile") 1169 const input = "hello, world\n" 1170 if err := ioutil.WriteFile(p, []byte(input), 0644); err != nil { 1171 t.Fatal(err) 1172 } 1173 syncFilename(t, p) 1174 1175 if err := ioutil.Remove(p); err != nil { 1176 t.Fatal(err) 1177 } 1178 1179 checkDir(t, path.Join(mnt.Dir, PrivateName, "jdoe"), map[string]fileInfoCheck{}) 1180 1181 if _, err := ioutil.ReadFile(p); !ioutil.IsNotExist(err) { 1182 t.Errorf("file still exists: %v", err) 1183 } 1184} 1185 1186func TestRemoveTLF(t *testing.T) { 1187 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1188 defer testCleanupDelayer(ctx, t) 1189 config := libkbfs.MakeTestConfigOrBust(t, "jdoe", "pikachu") 1190 mnt, _, cancelFn := makeFS(ctx, t, config) 1191 defer mnt.Close() 1192 defer cancelFn() 1193 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 1194 1195 p := path.Join(mnt.Dir, PrivateName, "jdoe,pikachu") 1196 f1, err := os.Create(path.Join(p, "f")) 1197 if err != nil { 1198 t.Fatal(err) 1199 } 1200 syncAndClose(t, f1) 1201 1202 privatePath := path.Join(mnt.Dir, PrivateName) 1203 checks := map[string]fileInfoCheck{ 1204 "jdoe": nil, 1205 } 1206 1207 var lastErr error 1208 for i := 0; i < 10; i++ { 1209 if err := syscall.Rmdir(p); err != nil { 1210 t.Fatal(err) 1211 } 1212 1213 if runtime.GOOS != "darwin" { 1214 checkDir(t, privatePath, checks) 1215 return 1216 } 1217 1218 // On OSX, the OS might decide to look up "f" at exactly the wrong 1219 // time, and reinstate the "jdoe,pikachu". Unfortunately there's 1220 // no good way to prevent this, so for now we just allow it to 1221 // happen and retry until we get what we want. See KBFS-1370. 1222 lastErr = checkDirNoTestError(t, privatePath, checks) 1223 if lastErr == nil { 1224 return 1225 } 1226 1227 // Make sure the test should still be running. 1228 select { 1229 case <-ctx.Done(): 1230 t.Fatal(ctx.Err()) 1231 default: 1232 t.Logf("Retrying TLF removal after error %+v", lastErr) 1233 } 1234 } 1235 if lastErr != nil { 1236 t.Error(lastErr) 1237 } 1238} 1239 1240func TestRemoveDir(t *testing.T) { 1241 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1242 defer testCleanupDelayer(ctx, t) 1243 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 1244 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 1245 mnt, _, cancelFn := makeFS(ctx, t, config) 1246 defer mnt.Close() 1247 defer cancelFn() 1248 1249 p := path.Join(mnt.Dir, PrivateName, "jdoe", "mydir") 1250 if err := ioutil.Mkdir(p, 0755); err != nil { 1251 t.Fatal(err) 1252 } 1253 1254 if err := syscall.Rmdir(p); err != nil { 1255 t.Fatal(err) 1256 } 1257 1258 checkDir(t, path.Join(mnt.Dir, PrivateName, "jdoe"), map[string]fileInfoCheck{}) 1259 1260 if _, err := ioutil.Stat(p); !ioutil.IsNotExist(err) { 1261 t.Errorf("file still exists: %v", err) 1262 } 1263} 1264 1265func TestRemoveDirNotEmpty(t *testing.T) { 1266 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1267 defer testCleanupDelayer(ctx, t) 1268 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 1269 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 1270 mnt, _, cancelFn := makeFS(ctx, t, config) 1271 defer mnt.Close() 1272 defer cancelFn() 1273 1274 p := path.Join(mnt.Dir, PrivateName, "jdoe", "mydir") 1275 if err := ioutil.Mkdir(p, 0755); err != nil { 1276 t.Fatal(err) 1277 } 1278 pFile := path.Join(p, "myfile") 1279 if err := ioutil.WriteFile(pFile, []byte("i'm important"), 0644); err != nil { 1280 t.Fatal(err) 1281 } 1282 syncFilename(t, pFile) 1283 1284 err := syscall.Rmdir(p) 1285 if g, e := err, syscall.ENOTEMPTY; g != e { 1286 t.Fatalf("wrong error from rmdir: %v (%T) != %v (%T)", g, g, e, e) 1287 } 1288 1289 if _, err := ioutil.ReadFile(pFile); err != nil { 1290 t.Errorf("file was lost: %v", err) 1291 } 1292} 1293 1294func TestRemoveFileWhileOpenSetEx(t *testing.T) { 1295 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1296 defer testCleanupDelayer(ctx, t) 1297 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 1298 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 1299 mnt, _, cancelFn := makeFS(ctx, t, config) 1300 defer mnt.Close() 1301 defer cancelFn() 1302 1303 p := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile") 1304 f, err := os.Create(p) 1305 if err != nil { 1306 t.Fatalf("cannot create file: %v", err) 1307 } 1308 // Call in a closure since `f` is overridden below. 1309 defer func() { syncAndClose(t, f) }() 1310 1311 if err := ioutil.Remove(p); err != nil { 1312 t.Fatalf("cannot delete file: %v", err) 1313 } 1314 1315 // this must not resurrect a deleted file 1316 if err := f.Chmod(0755); err != nil { 1317 t.Fatalf("cannot setex: %v", err) 1318 } 1319 1320 // Make sure the mode sticks around even though the file was unlinked. 1321 fi, err := f.Stat() 1322 if err != nil { 1323 t.Fatal(err) 1324 } 1325 if g, e := fi.Mode().String(), `-rwx------`; g != e { 1326 t.Errorf("wrong mode: %q != %q", g, e) 1327 } 1328 syncAndClose(t, f) 1329 f = nil 1330 1331 checkDir(t, path.Join(mnt.Dir, PrivateName, "jdoe"), 1332 map[string]fileInfoCheck{}) 1333 1334 if _, err := ioutil.ReadFile(p); !ioutil.IsNotExist(err) { 1335 t.Errorf("file still exists: %v", err) 1336 } 1337} 1338 1339func TestRemoveFileWhileOpenWritingInTLFRoot(t *testing.T) { 1340 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1341 defer testCleanupDelayer(ctx, t) 1342 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 1343 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 1344 mnt, _, cancelFn := makeFS(ctx, t, config) 1345 defer mnt.Close() 1346 defer cancelFn() 1347 1348 p := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile") 1349 f, err := os.Create(p) 1350 if err != nil { 1351 t.Fatalf("cannot create file: %v", err) 1352 } 1353 // Call in a closure since `f` is overridden below. 1354 defer func() { syncAndClose(t, f) }() 1355 1356 if err := ioutil.Remove(p); err != nil { 1357 t.Fatalf("cannot delete file: %v", err) 1358 } 1359 1360 // this must not resurrect a deleted file 1361 const input = "hello, world\n" 1362 if _, err := f.Write([]byte(input)); err != nil { 1363 t.Fatalf("cannot write: %v", err) 1364 } 1365 syncAndClose(t, f) 1366 f = nil 1367 1368 checkDir(t, path.Join(mnt.Dir, PrivateName, "jdoe"), map[string]fileInfoCheck{}) 1369 1370 if _, err := ioutil.ReadFile(p); !ioutil.IsNotExist(err) { 1371 t.Errorf("file still exists: %v", err) 1372 } 1373} 1374 1375func TestRemoveFileWhileOpenWritingInSubDir(t *testing.T) { 1376 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1377 defer testCleanupDelayer(ctx, t) 1378 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 1379 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 1380 mnt, _, cancelFn := makeFS(ctx, t, config) 1381 defer mnt.Close() 1382 defer cancelFn() 1383 1384 dirPath := path.Join(mnt.Dir, PrivateName, "jdoe", "dir") 1385 if err := os.Mkdir(dirPath, 0700); err != nil { 1386 t.Fatal(err) 1387 } 1388 1389 p := path.Join(dirPath, "myfile") 1390 f, err := os.Create(p) 1391 if err != nil { 1392 t.Fatalf("cannot create file: %v", err) 1393 } 1394 // Call in a closure since `f` is overridden below. 1395 defer func() { syncAndClose(t, f) }() 1396 1397 if err := ioutil.Remove(p); err != nil { 1398 t.Fatalf("cannot delete file: %v", err) 1399 } 1400 1401 // this must not resurrect a deleted file 1402 const input = "hello, world\n" 1403 if _, err := f.Write([]byte(input)); err != nil { 1404 t.Fatalf("cannot write: %v", err) 1405 } 1406 syncAndClose(t, f) 1407 f = nil 1408 1409 checkDir(t, dirPath, map[string]fileInfoCheck{}) 1410 1411 if _, err := ioutil.ReadFile(p); !ioutil.IsNotExist(err) { 1412 t.Errorf("file still exists: %v", err) 1413 } 1414} 1415 1416func TestRenameOverFileWhileOpenWritingInDifferentDir(t *testing.T) { 1417 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1418 defer testCleanupDelayer(ctx, t) 1419 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 1420 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 1421 mnt, _, cancelFn := makeFS(ctx, t, config) 1422 defer mnt.Close() 1423 defer cancelFn() 1424 1425 dirPath := path.Join(mnt.Dir, PrivateName, "jdoe", "dir") 1426 if err := os.Mkdir(dirPath, 0700); err != nil { 1427 t.Fatal(err) 1428 } 1429 1430 p1 := path.Join(dirPath, "myfile") 1431 f1, err := os.Create(p1) 1432 if err != nil { 1433 t.Fatalf("cannot create file: %v", err) 1434 } 1435 // Call in a closure since `f1` is overridden below. 1436 defer func() { syncAndClose(t, f1) }() 1437 1438 p2 := path.Join(mnt.Dir, PrivateName, "jdoe", "mynewfile") 1439 f2, err := os.Create(p2) 1440 if err != nil { 1441 t.Fatalf("cannot create file: %v", err) 1442 } 1443 syncAndClose(t, f2) 1444 1445 if err := os.Rename(p2, p1); err != nil { 1446 t.Fatalf("cannot move file: %v", err) 1447 } 1448 1449 // this must not resurrect content in f2 1450 const input = "hello, world\n" 1451 if _, err := f1.Write([]byte(input)); err != nil { 1452 t.Fatalf("cannot write: %v", err) 1453 } 1454 syncAndClose(t, f1) 1455 f1 = nil 1456 1457 checkDir(t, dirPath, map[string]fileInfoCheck{"myfile": nil}) 1458 1459 content, err := ioutil.ReadFile(p1) 1460 if err != nil { 1461 t.Fatal(err) 1462 } 1463 if len(content) > 0 { 1464 t.Errorf("write to overwritee resulted in content in overwriter") 1465 } 1466} 1467 1468func TestRenameOverFileWhileOpenWritingInSameSubDir(t *testing.T) { 1469 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1470 defer testCleanupDelayer(ctx, t) 1471 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 1472 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 1473 mnt, _, cancelFn := makeFS(ctx, t, config) 1474 defer mnt.Close() 1475 defer cancelFn() 1476 1477 dirPath := path.Join(mnt.Dir, PrivateName, "jdoe", "dir") 1478 if err := os.Mkdir(dirPath, 0700); err != nil { 1479 t.Fatal(err) 1480 } 1481 1482 p1 := path.Join(dirPath, "myfile") 1483 f1, err := os.Create(p1) 1484 if err != nil { 1485 t.Fatalf("cannot create file: %v", err) 1486 } 1487 // Call in a closure since `f1` is overridden below. 1488 defer func() { syncAndClose(t, f1) }() 1489 1490 p2 := path.Join(dirPath, "mynewfile") 1491 f2, err := os.Create(p2) 1492 if err != nil { 1493 t.Fatalf("cannot create file: %v", err) 1494 } 1495 syncAndClose(t, f2) 1496 1497 if err := os.Rename(p2, p1); err != nil { 1498 t.Fatalf("cannot move file: %v", err) 1499 } 1500 1501 // this must not resurrect content in f2 1502 const input = "hello, world\n" 1503 if _, err := f1.Write([]byte(input)); err != nil { 1504 t.Fatalf("cannot write: %v", err) 1505 } 1506 syncAndClose(t, f1) 1507 f1 = nil 1508 1509 checkDir(t, dirPath, map[string]fileInfoCheck{"myfile": nil}) 1510 1511 content, err := ioutil.ReadFile(p1) 1512 if err != nil { 1513 t.Fatal(err) 1514 } 1515 if len(content) > 0 { 1516 t.Errorf("write to overwritee resulted in content in overwriter") 1517 } 1518} 1519 1520func TestRemoveFileWhileOpenReading(t *testing.T) { 1521 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1522 defer testCleanupDelayer(ctx, t) 1523 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 1524 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 1525 mnt, _, cancelFn := makeFS(ctx, t, config) 1526 defer mnt.Close() 1527 defer cancelFn() 1528 1529 p := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile") 1530 const input = "hello, world\n" 1531 if err := ioutil.WriteFile(p, []byte(input), 0644); err != nil { 1532 t.Fatal(err) 1533 } 1534 syncFilename(t, p) 1535 1536 f, err := os.Open(p) 1537 if err != nil { 1538 t.Fatalf("cannot open file: %v", err) 1539 } 1540 // Call in a closure since `f` is overridden below. 1541 defer func() { syncAndClose(t, f) }() 1542 1543 if err := ioutil.Remove(p); err != nil { 1544 t.Fatalf("cannot delete file: %v", err) 1545 } 1546 1547 buf, err := ioutil.ReadAll(f) 1548 if err != nil { 1549 t.Fatalf("cannot read unlinked file: %v", err) 1550 } 1551 if g, e := string(buf), input; g != e { 1552 t.Errorf("read wrong content: %q != %q", g, e) 1553 } 1554 1555 syncAndClose(t, f) 1556 f = nil 1557 1558 checkDir(t, path.Join(mnt.Dir, PrivateName, "jdoe"), map[string]fileInfoCheck{}) 1559 1560 if _, err := ioutil.ReadFile(p); !ioutil.IsNotExist(err) { 1561 t.Errorf("file still exists: %v", err) 1562 } 1563} 1564 1565func TestRemoveFileWhileOpenReadingAcrossMounts(t *testing.T) { 1566 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1567 defer testCleanupDelayer(ctx, t) 1568 config1 := libkbfs.MakeTestConfigOrBust(t, "user1", 1569 "user2") 1570 defer libkbfs.CheckConfigAndShutdown(ctx, t, config1) 1571 mnt1, fs1, cancelFn1 := makeFS(ctx, t, config1) 1572 defer mnt1.Close() 1573 defer cancelFn1() 1574 1575 config2 := libkbfs.ConfigAsUser(config1, "user2") 1576 defer libkbfs.CheckConfigAndShutdown(ctx, t, config2) 1577 mnt2, fs2, cancelFn2 := makeFS(ctx, t, config2) 1578 defer mnt2.Close() 1579 defer cancelFn2() 1580 1581 if !mnt2.Conn.Protocol().HasInvalidate() { 1582 t.Skip("Old FUSE protocol") 1583 } 1584 1585 p1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "myfile") 1586 const input = "hello, world\n" 1587 if err := ioutil.WriteFile(p1, []byte(input), 0644); err != nil { 1588 t.Fatal(err) 1589 } 1590 syncFilename(t, p1) 1591 1592 f, err := os.Open(p1) 1593 if err != nil { 1594 t.Fatalf("cannot open file: %v", err) 1595 } 1596 // Call in a closure since `f` is overridden below. 1597 defer func() { syncAndClose(t, f) }() 1598 1599 syncFolderToServer(t, "user1,user2", fs2) 1600 1601 p2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", "myfile") 1602 if err := ioutil.Remove(p2); err != nil { 1603 t.Fatalf("cannot delete file: %v", err) 1604 } 1605 syncAll(t, "user1,user2", tlf.Private, fs2) 1606 1607 syncFolderToServer(t, "user1,user2", fs1) 1608 1609 buf, err := ioutil.ReadAll(f) 1610 if err != nil { 1611 t.Fatalf("cannot read unlinked file: %v", err) 1612 } 1613 if g, e := string(buf), input; g != e { 1614 t.Errorf("read wrong content: %q != %q", g, e) 1615 } 1616 1617 syncAndClose(t, f) 1618 f = nil 1619 1620 checkDir(t, path.Join(mnt1.Dir, PrivateName, "user1,user2"), 1621 map[string]fileInfoCheck{}) 1622 1623 if _, err := ioutil.ReadFile(p1); !ioutil.IsNotExist(err) { 1624 t.Errorf("file still exists: %v", err) 1625 } 1626} 1627 1628func TestRenameOverFileWhileOpenReadingAcrossMounts(t *testing.T) { 1629 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1630 defer testCleanupDelayer(ctx, t) 1631 config1 := libkbfs.MakeTestConfigOrBust(t, "user1", 1632 "user2") 1633 defer libkbfs.CheckConfigAndShutdown(ctx, t, config1) 1634 mnt1, fs1, cancelFn1 := makeFS(ctx, t, config1) 1635 defer mnt1.Close() 1636 defer cancelFn1() 1637 1638 config2 := libkbfs.ConfigAsUser(config1, "user2") 1639 defer libkbfs.CheckConfigAndShutdown(ctx, t, config2) 1640 mnt2, fs2, cancelFn2 := makeFS(ctx, t, config2) 1641 defer mnt2.Close() 1642 defer cancelFn2() 1643 1644 if !mnt2.Conn.Protocol().HasInvalidate() { 1645 t.Skip("Old FUSE protocol") 1646 } 1647 1648 p1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "myfile") 1649 const input = "hello, world\n" 1650 if err := ioutil.WriteFile(p1, []byte(input), 0644); err != nil { 1651 t.Fatal(err) 1652 } 1653 syncFilename(t, p1) 1654 1655 p1Other := path.Join(mnt1.Dir, PrivateName, "user1,user2", "other") 1656 const inputOther = "hello, other\n" 1657 if err := ioutil.WriteFile(p1Other, []byte(inputOther), 0644); err != nil { 1658 t.Fatal(err) 1659 } 1660 syncFilename(t, p1Other) 1661 1662 f, err := os.Open(p1) 1663 if err != nil { 1664 t.Fatalf("cannot open file: %v", err) 1665 } 1666 // Call in a closure since `f` is overridden below. 1667 defer func() { syncAndClose(t, f) }() 1668 1669 syncFolderToServer(t, "user1,user2", fs2) 1670 1671 p2Other := path.Join(mnt2.Dir, PrivateName, "user1,user2", "other") 1672 p2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", "myfile") 1673 if err := ioutil.Rename(p2Other, p2); err != nil { 1674 t.Fatalf("cannot rename file: %v", err) 1675 } 1676 syncAll(t, "user1,user2", tlf.Private, fs2) 1677 1678 syncFolderToServer(t, "user1,user2", fs1) 1679 1680 buf, err := ioutil.ReadAll(f) 1681 if err != nil { 1682 t.Fatalf("cannot read unlinked file: %v", err) 1683 } 1684 if g, e := string(buf), input; g != e { 1685 t.Errorf("read wrong content: %q != %q", g, e) 1686 } 1687 1688 syncAndClose(t, f) 1689 f = nil 1690 1691 checkDir(t, path.Join(mnt1.Dir, PrivateName, "user1,user2"), 1692 map[string]fileInfoCheck{ 1693 "myfile": nil, 1694 }) 1695 1696 if _, err := ioutil.ReadFile(p1Other); !ioutil.IsNotExist(err) { 1697 t.Errorf("other file still exists: %v", err) 1698 } 1699 1700 buf, err = ioutil.ReadFile(p1) 1701 if err != nil { 1702 t.Errorf("read error: %v", err) 1703 } 1704 if g, e := string(buf), inputOther; g != e { 1705 t.Errorf("bad file contents: %q != %q", g, e) 1706 } 1707} 1708 1709func TestTruncateGrow(t *testing.T) { 1710 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1711 defer testCleanupDelayer(ctx, t) 1712 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 1713 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 1714 mnt, _, cancelFn := makeFS(ctx, t, config) 1715 defer mnt.Close() 1716 defer cancelFn() 1717 1718 p := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile") 1719 const input = "hello, world\n" 1720 if err := ioutil.WriteFile(p, []byte(input), 0644); err != nil { 1721 t.Fatal(err) 1722 } 1723 syncFilename(t, p) 1724 1725 const newSize = 100 1726 if err := os.Truncate(p, newSize); err != nil { 1727 t.Fatal(err) 1728 } 1729 syncFilename(t, p) 1730 1731 fi, err := ioutil.Lstat(p) 1732 if err != nil { 1733 t.Fatal(err) 1734 } 1735 if g, e := fi.Size(), int64(newSize); g != e { 1736 t.Errorf("wrong size: %v != %v", g, e) 1737 } 1738 1739 buf, err := ioutil.ReadFile(p) 1740 if err != nil { 1741 t.Fatalf("cannot read unlinked file: %v", err) 1742 } 1743 if g, e := string(buf), input+strings.Repeat("\x00", newSize-len(input)); g != e { 1744 t.Errorf("read wrong content: %q != %q", g, e) 1745 } 1746} 1747 1748func TestTruncateShrink(t *testing.T) { 1749 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1750 defer testCleanupDelayer(ctx, t) 1751 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 1752 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 1753 mnt, _, cancelFn := makeFS(ctx, t, config) 1754 defer mnt.Close() 1755 defer cancelFn() 1756 1757 p := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile") 1758 const input = "hello, world\n" 1759 if err := ioutil.WriteFile(p, []byte(input), 0644); err != nil { 1760 t.Fatal(err) 1761 } 1762 syncFilename(t, p) 1763 1764 const newSize = 4 1765 if err := os.Truncate(p, newSize); err != nil { 1766 t.Fatal(err) 1767 } 1768 syncFilename(t, p) 1769 1770 fi, err := ioutil.Lstat(p) 1771 if err != nil { 1772 t.Fatal(err) 1773 } 1774 if g, e := fi.Size(), int64(newSize); g != e { 1775 t.Errorf("wrong size: %v != %v", g, e) 1776 } 1777 1778 buf, err := ioutil.ReadFile(p) 1779 if err != nil { 1780 t.Fatalf("cannot read unlinked file: %v", err) 1781 } 1782 if g, e := string(buf), input[:newSize]; g != e { 1783 t.Errorf("read wrong content: %q != %q", g, e) 1784 } 1785} 1786 1787func TestChmodExec(t *testing.T) { 1788 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1789 defer testCleanupDelayer(ctx, t) 1790 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 1791 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 1792 mnt, _, cancelFn := makeFS(ctx, t, config) 1793 defer mnt.Close() 1794 defer cancelFn() 1795 1796 p := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile") 1797 const input = "hello, world\n" 1798 if err := ioutil.WriteFile(p, []byte(input), 0644); err != nil { 1799 t.Fatal(err) 1800 } 1801 syncFilename(t, p) 1802 1803 if err := os.Chmod(p, 0744); err != nil { 1804 t.Fatal(err) 1805 } 1806 1807 fi, err := ioutil.Lstat(p) 1808 if err != nil { 1809 t.Fatal(err) 1810 } 1811 if g, e := fi.Mode().String(), `-rwx------`; g != e { 1812 t.Errorf("wrong mode: %q != %q", g, e) 1813 } 1814} 1815 1816func TestChmodNonExec(t *testing.T) { 1817 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1818 defer testCleanupDelayer(ctx, t) 1819 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 1820 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 1821 mnt, _, cancelFn := makeFS(ctx, t, config) 1822 defer mnt.Close() 1823 defer cancelFn() 1824 1825 p := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile") 1826 const input = "hello, world\n" 1827 if err := ioutil.WriteFile(p, []byte(input), 0755); err != nil { 1828 t.Fatal(err) 1829 } 1830 syncFilename(t, p) 1831 1832 if err := os.Chmod(p, 0655); err != nil { 1833 t.Fatal(err) 1834 } 1835 1836 fi, err := ioutil.Lstat(p) 1837 if err != nil { 1838 t.Fatal(err) 1839 } 1840 if g, e := fi.Mode().String(), `-rw-------`; g != e { 1841 t.Errorf("wrong mode: %q != %q", g, e) 1842 } 1843} 1844 1845func TestChownFileIgnored(t *testing.T) { 1846 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1847 defer testCleanupDelayer(ctx, t) 1848 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 1849 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 1850 mnt, _, cancelFn := makeFS(ctx, t, config) 1851 defer mnt.Close() 1852 defer cancelFn() 1853 1854 p := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile") 1855 const input = "hello, world\n" 1856 if err := ioutil.WriteFile(p, []byte(input), 0755); err != nil { 1857 t.Fatal(err) 1858 } 1859 syncFilename(t, p) 1860 1861 fi, err := ioutil.Lstat(p) 1862 if err != nil { 1863 t.Fatal(err) 1864 } 1865 oldOwner := int(fi.Sys().(*syscall.Stat_t).Uid) 1866 1867 if err := os.Chown(p, oldOwner+1, oldOwner+1); err != nil { 1868 t.Fatalf("Expecting the file chown to get swallowed silently, "+ 1869 "but got: %v", err) 1870 } 1871 1872 newFi, err := ioutil.Lstat(p) 1873 if err != nil { 1874 t.Fatal(err) 1875 } 1876 newOwner := int(newFi.Sys().(*syscall.Stat_t).Uid) 1877 if oldOwner != newOwner { 1878 t.Fatalf("Owner changed unexpectedly to %d after a chown", newOwner) 1879 } 1880} 1881 1882func TestChmodDirIgnored(t *testing.T) { 1883 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1884 defer testCleanupDelayer(ctx, t) 1885 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 1886 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 1887 mnt, _, cancelFn := makeFS(ctx, t, config) 1888 defer mnt.Close() 1889 defer cancelFn() 1890 1891 p := path.Join(mnt.Dir, PrivateName, "jdoe", "mydir") 1892 if err := ioutil.Mkdir(p, 0755); err != nil { 1893 t.Fatal(err) 1894 } 1895 1896 if err := os.Chmod(p, 0655); err != nil { 1897 t.Fatalf("Expecting the dir chmod to get swallowed silently, "+ 1898 "but got: %v", err) 1899 } 1900} 1901 1902func TestChownDirIgnored(t *testing.T) { 1903 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1904 defer testCleanupDelayer(ctx, t) 1905 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 1906 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 1907 mnt, _, cancelFn := makeFS(ctx, t, config) 1908 defer mnt.Close() 1909 defer cancelFn() 1910 1911 p := path.Join(mnt.Dir, PrivateName, "jdoe", "mydir") 1912 if err := ioutil.Mkdir(p, 0755); err != nil { 1913 t.Fatal(err) 1914 } 1915 1916 fi, err := ioutil.Lstat(p) 1917 if err != nil { 1918 t.Fatal(err) 1919 } 1920 oldOwner := int(fi.Sys().(*syscall.Stat_t).Uid) 1921 1922 if err := os.Chown(p, 1, 1); err != nil { 1923 t.Fatalf("Expecting the dir chown to get swallowed silently, "+ 1924 "but got: %v", err) 1925 } 1926 1927 newFi, err := ioutil.Lstat(p) 1928 if err != nil { 1929 t.Fatal(err) 1930 } 1931 newOwner := int(newFi.Sys().(*syscall.Stat_t).Uid) 1932 if oldOwner != newOwner { 1933 t.Fatalf("Owner changed unexpectedly to %d after a chown", newOwner) 1934 } 1935} 1936 1937func TestSetattrFileMtime(t *testing.T) { 1938 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1939 defer testCleanupDelayer(ctx, t) 1940 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 1941 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 1942 mnt, _, cancelFn := makeFS(ctx, t, config) 1943 defer mnt.Close() 1944 defer cancelFn() 1945 1946 p := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile") 1947 const input = "hello, world\n" 1948 if err := ioutil.WriteFile(p, []byte(input), 0644); err != nil { 1949 t.Fatal(err) 1950 } 1951 syncFilename(t, p) 1952 1953 mtime := time.Date(2015, 1, 2, 3, 4, 5, 6, time.Local) 1954 // KBFS does not respect atime (which is ok), but we need to give 1955 // something to the syscall. 1956 atime := time.Date(2015, 7, 8, 9, 10, 11, 12, time.Local) 1957 if err := os.Chtimes(p, atime, mtime); err != nil { 1958 t.Fatal(err) 1959 } 1960 1961 fi, err := ioutil.Lstat(p) 1962 if err != nil { 1963 t.Fatal(err) 1964 } 1965 if g, e := fi.ModTime(), mtime; !libfs.TimeEqual(g, e) { 1966 t.Errorf("wrong mtime: %v !~= %v", g, e) 1967 } 1968} 1969 1970func TestSetattrFileMtimeAfterWrite(t *testing.T) { 1971 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1972 defer testCleanupDelayer(ctx, t) 1973 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 1974 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 1975 mnt, _, cancelFn := makeFS(ctx, t, config) 1976 defer mnt.Close() 1977 defer cancelFn() 1978 1979 p := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile") 1980 const input = "hello, world\n" 1981 if err := ioutil.WriteFile(p, []byte(input), 0644); err != nil { 1982 t.Fatal(err) 1983 } 1984 syncFilename(t, p) 1985 1986 const input2 = "second round of content" 1987 { 1988 ctx := libcontext.BackgroundContextWithCancellationDelayer() 1989 defer testCleanupDelayer(ctx, t) 1990 1991 jdoe := libkbfs.GetRootNodeOrBust(ctx, t, config, "jdoe", tlf.Private) 1992 1993 ops := config.KBFSOps() 1994 myfile, _, err := ops.Lookup(ctx, jdoe, jdoe.ChildName("myfile")) 1995 if err != nil { 1996 t.Fatal(err) 1997 } 1998 if err := ops.Write(ctx, myfile, []byte(input2), 0); err != nil { 1999 t.Fatal(err) 2000 } 2001 // Don't sync 2002 } 2003 2004 mtime := time.Date(2015, 1, 2, 3, 4, 5, 6, time.Local) 2005 // KBFS does not respect atime (which is ok), but we need to give 2006 // something to the syscall. 2007 atime := time.Date(2015, 7, 8, 9, 10, 11, 12, time.Local) 2008 if err := os.Chtimes(p, atime, mtime); err != nil { 2009 t.Fatal(err) 2010 } 2011 2012 fi, err := ioutil.Lstat(p) 2013 if err != nil { 2014 t.Fatal(err) 2015 } 2016 if g, e := fi.ModTime(), mtime; !libfs.TimeEqual(g, e) { 2017 t.Errorf("wrong mtime: %v !~= %v", g, e) 2018 } 2019 syncFilename(t, p) 2020} 2021 2022func TestSetattrFileMtimeNow(t *testing.T) { 2023 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2024 defer testCleanupDelayer(ctx, t) 2025 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 2026 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 2027 mnt, _, cancelFn := makeFS(ctx, t, config) 2028 defer mnt.Close() 2029 defer cancelFn() 2030 2031 p := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile") 2032 const input = "hello, world\n" 2033 if err := ioutil.WriteFile(p, []byte(input), 0644); err != nil { 2034 t.Fatal(err) 2035 } 2036 syncFilename(t, p) 2037 2038 mtime := time.Date(2015, 1, 2, 3, 4, 5, 6, time.Local) 2039 // KBFS does not respect atime (which is ok), but we need to give 2040 // something to the syscall. 2041 atime := time.Date(2015, 7, 8, 9, 10, 11, 12, time.Local) 2042 if err := os.Chtimes(p, atime, mtime); err != nil { 2043 t.Fatal(err) 2044 } 2045 2046 // cause mtime to be set to now 2047 if err := unix.Utimes(p, nil); err != nil { 2048 t.Fatalf("touch failed: %v", err) 2049 } 2050 now := time.Now() 2051 2052 fi, err := ioutil.Lstat(p) 2053 if err != nil { 2054 t.Fatal(err) 2055 } 2056 if g, o := fi.ModTime(), mtime; !g.After(o) { 2057 t.Errorf("mtime did not progress: %v <= %v", g, o) 2058 } 2059 if g, e := fi.ModTime(), now; !timeEqualFuzzy(g, e, 1*time.Second) { 2060 t.Errorf("mtime is wrong: %v !~= %v", g, e) 2061 } 2062} 2063 2064func TestSetattrDirMtime(t *testing.T) { 2065 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2066 defer testCleanupDelayer(ctx, t) 2067 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 2068 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 2069 mnt, _, cancelFn := makeFS(ctx, t, config) 2070 defer mnt.Close() 2071 defer cancelFn() 2072 2073 p := path.Join(mnt.Dir, PrivateName, "jdoe", "mydir") 2074 if err := ioutil.Mkdir(p, 0755); err != nil { 2075 t.Fatal(err) 2076 } 2077 2078 mtime := time.Date(2015, 1, 2, 3, 4, 5, 6, time.Local) 2079 // KBFS does not respect atime (which is ok), but we need to give 2080 // something to the syscall. 2081 atime := time.Date(2015, 7, 8, 9, 10, 11, 12, time.Local) 2082 if err := os.Chtimes(p, atime, mtime); err != nil { 2083 t.Fatal(err) 2084 } 2085 2086 fi, err := ioutil.Lstat(p) 2087 if err != nil { 2088 t.Fatal(err) 2089 } 2090 if g, e := fi.ModTime(), mtime; !libfs.TimeEqual(g, e) { 2091 t.Errorf("wrong mtime: %v !~= %v", g, e) 2092 } 2093} 2094 2095func TestSetattrDirMtimeNow(t *testing.T) { 2096 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2097 defer testCleanupDelayer(ctx, t) 2098 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 2099 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 2100 mnt, _, cancelFn := makeFS(ctx, t, config) 2101 defer mnt.Close() 2102 defer cancelFn() 2103 2104 p := path.Join(mnt.Dir, PrivateName, "jdoe", "mydir") 2105 if err := ioutil.Mkdir(p, 0755); err != nil { 2106 t.Fatal(err) 2107 } 2108 2109 mtime := time.Date(2015, 1, 2, 3, 4, 5, 6, time.Local) 2110 // KBFS does not respect atime (which is ok), but we need to give 2111 // something to the syscall. 2112 atime := time.Date(2015, 7, 8, 9, 10, 11, 12, time.Local) 2113 if err := os.Chtimes(p, atime, mtime); err != nil { 2114 t.Fatal(err) 2115 } 2116 2117 // cause mtime to be set to now 2118 if err := unix.Utimes(p, nil); err != nil { 2119 t.Fatalf("touch failed: %v", err) 2120 } 2121 now := time.Now() 2122 2123 fi, err := ioutil.Lstat(p) 2124 if err != nil { 2125 t.Fatal(err) 2126 } 2127 if g, o := fi.ModTime(), mtime; !g.After(o) { 2128 t.Errorf("mtime did not progress: %v <= %v", g, o) 2129 } 2130 if g, e := fi.ModTime(), now; !timeEqualFuzzy(g, e, 1*time.Second) { 2131 t.Errorf("mtime is wrong: %v !~= %v", g, e) 2132 } 2133} 2134 2135func TestFsync(t *testing.T) { 2136 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2137 defer testCleanupDelayer(ctx, t) 2138 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 2139 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 2140 mnt, _, cancelFn := makeFS(ctx, t, config) 2141 defer mnt.Close() 2142 defer cancelFn() 2143 2144 p := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile") 2145 f, err := os.Create(p) 2146 if err != nil { 2147 t.Fatal(err) 2148 } 2149 // Call in a closure since `f` is overridden below. 2150 defer func() { syncAndClose(t, f) }() 2151 const input = "hello, world\n" 2152 if _, err := io.WriteString(f, input); err != nil { 2153 t.Fatalf("write error: %v", err) 2154 } 2155 if err := f.Sync(); err != nil { 2156 t.Fatalf("fsync error: %v", err) 2157 } 2158 if err := f.Close(); err != nil { 2159 t.Fatalf("close error: %v", err) 2160 } 2161 f = nil 2162} 2163 2164func TestReaddirMyPublic(t *testing.T) { 2165 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2166 defer testCleanupDelayer(ctx, t) 2167 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 2168 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 2169 mnt, _, cancelFn := makeFS(ctx, t, config) 2170 defer mnt.Close() 2171 defer cancelFn() 2172 2173 files := map[string]fileInfoCheck{ 2174 "one": nil, 2175 "two": nil, 2176 } 2177 for filename := range files { 2178 p := path.Join(mnt.Dir, PublicName, "jdoe", filename) 2179 if err := ioutil.WriteFile( 2180 p, []byte("data for "+filename), 0644); err != nil { 2181 t.Fatal(err) 2182 } 2183 syncFilename(t, p) 2184 } 2185 2186 checkDir(t, path.Join(mnt.Dir, PublicName, "jdoe"), files) 2187} 2188 2189func TestReaddirOtherFolderAsReader(t *testing.T) { 2190 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2191 defer testCleanupDelayer(ctx, t) 2192 config := libkbfs.MakeTestConfigOrBust(t, "jdoe", "wsmith") 2193 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 2194 func() { 2195 mnt, _, cancelFn := makeFS(ctx, t, config) 2196 defer mnt.Close() 2197 defer cancelFn() 2198 2199 // cause the folder to exist 2200 p := path.Join(mnt.Dir, PrivateName, "jdoe#wsmith", "myfile") 2201 if err := ioutil.WriteFile( 2202 p, []byte("data for myfile"), 0644); err != nil { 2203 t.Fatal(err) 2204 } 2205 syncFilename(t, p) 2206 }() 2207 2208 c2 := libkbfs.ConfigAsUser(config, "wsmith") 2209 defer libkbfs.CheckConfigAndShutdown(ctx, t, c2) 2210 mnt, _, cancelFn := makeFS(ctx, t, c2) 2211 defer mnt.Close() 2212 defer cancelFn() 2213 2214 checkDir(t, path.Join(mnt.Dir, PrivateName, "jdoe#wsmith"), map[string]fileInfoCheck{ 2215 "myfile": nil, 2216 }) 2217} 2218 2219func TestReaddirMissingOtherFolderAsReader(t *testing.T) { 2220 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2221 defer testCleanupDelayer(ctx, t) 2222 config := libkbfs.MakeTestConfigOrBust(t, "jdoe", "wsmith") 2223 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 2224 c2 := libkbfs.ConfigAsUser(config, "wsmith") 2225 defer libkbfs.CheckConfigAndShutdown(ctx, t, c2) 2226 mnt, _, cancelFn := makeFS(ctx, t, c2) 2227 defer mnt.Close() 2228 defer cancelFn() 2229 2230 // Check that folder that doesn't exist yet looks empty 2231 checkDir(t, path.Join(mnt.Dir, PrivateName, "jdoe#wsmith"), 2232 map[string]fileInfoCheck{}) 2233} 2234 2235func TestLookupMissingOtherFolderAsReader(t *testing.T) { 2236 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2237 defer testCleanupDelayer(ctx, t) 2238 config := libkbfs.MakeTestConfigOrBust(t, "jdoe", "wsmith") 2239 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 2240 c2 := libkbfs.ConfigAsUser(config, "wsmith") 2241 defer libkbfs.CheckConfigAndShutdown(ctx, t, c2) 2242 mnt, _, cancelFn := makeFS(ctx, t, c2) 2243 defer mnt.Close() 2244 defer cancelFn() 2245 2246 p := path.Join(mnt.Dir, PrivateName, "jdoe#wsmith", "foo") 2247 if _, err := ioutil.Stat(p); !ioutil.IsNotExist(err) { 2248 t.Errorf("Expected ENOENT, but got: %v", err) 2249 } 2250} 2251 2252func TestStatOtherFolder(t *testing.T) { 2253 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2254 defer testCleanupDelayer(ctx, t) 2255 config := libkbfs.MakeTestConfigOrBust(t, "jdoe", "wsmith") 2256 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 2257 func() { 2258 mnt, _, cancelFn := makeFS(ctx, t, config) 2259 defer mnt.Close() 2260 defer cancelFn() 2261 2262 // cause the folder to exist 2263 p := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile") 2264 if err := ioutil.WriteFile( 2265 p, []byte("data for myfile"), 0644); err != nil { 2266 t.Fatal(err) 2267 } 2268 syncFilename(t, p) 2269 }() 2270 2271 c2 := libkbfs.ConfigAsUser(config, "wsmith") 2272 defer libkbfs.CheckConfigAndShutdown(ctx, t, c2) 2273 mnt, _, cancelFn := makeFS(ctx, t, c2) 2274 defer mnt.Close() 2275 defer cancelFn() 2276 2277 switch _, err := ioutil.Lstat(path.Join(mnt.Dir, PrivateName, "jdoe")); err := errors.Cause(err).(type) { 2278 case *os.PathError: 2279 if g, e := err.Err, syscall.EACCES; g != e { 2280 t.Fatalf("wrong error: %v != %v", g, e) 2281 } 2282 default: 2283 t.Fatalf("expected a PathError, got %T: %v", err, err) 2284 } 2285} 2286 2287func TestStatOtherFolderFirstUse(t *testing.T) { 2288 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2289 defer testCleanupDelayer(ctx, t) 2290 // This triggers a different error than with the warmup. 2291 config := libkbfs.MakeTestConfigOrBust(t, "jdoe", "wsmith") 2292 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 2293 2294 c2 := libkbfs.ConfigAsUser(config, "wsmith") 2295 defer libkbfs.CheckConfigAndShutdown(ctx, t, c2) 2296 mnt, _, cancelFn := makeFS(ctx, t, c2) 2297 defer mnt.Close() 2298 defer cancelFn() 2299 2300 switch _, err := ioutil.Lstat(path.Join(mnt.Dir, PrivateName, "jdoe")); err := errors.Cause(err).(type) { 2301 case *os.PathError: 2302 if g, e := err.Err, syscall.EACCES; g != e { 2303 t.Fatalf("wrong error: %v != %v", g, e) 2304 } 2305 default: 2306 t.Fatalf("expected a PathError, got %T: %v", err, err) 2307 } 2308} 2309 2310func TestStatOtherFolderPublic(t *testing.T) { 2311 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2312 defer testCleanupDelayer(ctx, t) 2313 config := libkbfs.MakeTestConfigOrBust(t, "jdoe", "wsmith") 2314 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 2315 func() { 2316 mnt, _, cancelFn := makeFS(ctx, t, config) 2317 defer mnt.Close() 2318 defer cancelFn() 2319 2320 // cause the folder to exist 2321 p := path.Join(mnt.Dir, PublicName, "jdoe", "myfile") 2322 if err := ioutil.WriteFile( 2323 p, []byte("data for myfile"), 0644); err != nil { 2324 t.Fatal(err) 2325 } 2326 syncFilename(t, p) 2327 }() 2328 2329 c2 := libkbfs.ConfigAsUser(config, "wsmith") 2330 defer libkbfs.CheckConfigAndShutdown(ctx, t, c2) 2331 mnt, _, cancelFn := makeFS(ctx, t, c2) 2332 defer mnt.Close() 2333 defer cancelFn() 2334 2335 fi, err := ioutil.Lstat(path.Join(mnt.Dir, PublicName, "jdoe")) 2336 if err != nil { 2337 t.Fatal(err) 2338 } 2339 // TODO figure out right modes, note owner is the person running 2340 // fuse, not the person owning the folder 2341 if g, e := fi.Mode().String(), `dr-x------`; g != e { 2342 t.Errorf("wrong mode for folder: %q != %q", g, e) 2343 } 2344} 2345 2346func TestReadPublicFile(t *testing.T) { 2347 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2348 defer testCleanupDelayer(ctx, t) 2349 config := libkbfs.MakeTestConfigOrBust(t, "jdoe", "wsmith") 2350 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 2351 const input = "hello, world\n" 2352 func() { 2353 mnt, _, cancelFn := makeFS(ctx, t, config) 2354 defer mnt.Close() 2355 defer cancelFn() 2356 2357 // cause the folder to exist 2358 p := path.Join(mnt.Dir, PublicName, "jdoe", "myfile") 2359 if err := ioutil.WriteFile(p, []byte(input), 0644); err != nil { 2360 t.Fatal(err) 2361 } 2362 syncFilename(t, p) 2363 }() 2364 2365 c2 := libkbfs.ConfigAsUser(config, "wsmith") 2366 defer libkbfs.CheckConfigAndShutdown(ctx, t, c2) 2367 mnt, _, cancelFn := makeFS(ctx, t, c2) 2368 defer mnt.Close() 2369 defer cancelFn() 2370 2371 buf, err := ioutil.ReadFile(path.Join(mnt.Dir, PublicName, "jdoe", "myfile")) 2372 if err != nil { 2373 t.Fatal(err) 2374 } 2375 if g, e := string(buf), input; g != e { 2376 t.Errorf("bad file contents: %q != %q", g, e) 2377 } 2378} 2379 2380func TestReaddirOtherFolderPublicAsAnyone(t *testing.T) { 2381 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2382 defer testCleanupDelayer(ctx, t) 2383 config := libkbfs.MakeTestConfigOrBust(t, "jdoe", "wsmith") 2384 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 2385 func() { 2386 mnt, _, cancelFn := makeFS(ctx, t, config) 2387 defer mnt.Close() 2388 defer cancelFn() 2389 2390 // cause the folder to exist 2391 p := path.Join(mnt.Dir, PublicName, "jdoe", "myfile") 2392 if err := ioutil.WriteFile( 2393 p, []byte("data for myfile"), 0644); err != nil { 2394 t.Fatal(err) 2395 } 2396 syncFilename(t, p) 2397 }() 2398 2399 c2 := libkbfs.ConfigAsUser(config, "wsmith") 2400 defer libkbfs.CheckConfigAndShutdown(ctx, t, c2) 2401 mnt, _, cancelFn := makeFS(ctx, t, c2) 2402 defer mnt.Close() 2403 defer cancelFn() 2404 2405 checkDir(t, path.Join(mnt.Dir, PublicName, "jdoe"), map[string]fileInfoCheck{ 2406 "myfile": nil, 2407 }) 2408} 2409 2410func TestReaddirMissingFolderPublicAsAnyone(t *testing.T) { 2411 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2412 defer testCleanupDelayer(ctx, t) 2413 config := libkbfs.MakeTestConfigOrBust(t, "jdoe", "wsmith") 2414 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 2415 c2 := libkbfs.ConfigAsUser(config, "wsmith") 2416 defer libkbfs.CheckConfigAndShutdown(ctx, t, c2) 2417 mnt, _, cancelFn := makeFS(ctx, t, c2) 2418 defer mnt.Close() 2419 defer cancelFn() 2420 2421 // Make sure a public folder, not yet created by its writer, looks empty. 2422 checkDir(t, path.Join(mnt.Dir, PublicName, "jdoe"), 2423 map[string]fileInfoCheck{}) 2424} 2425 2426func TestReaddirOtherFolderAsAnyone(t *testing.T) { 2427 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2428 defer testCleanupDelayer(ctx, t) 2429 config := libkbfs.MakeTestConfigOrBust(t, "jdoe", "wsmith") 2430 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 2431 func() { 2432 mnt, _, cancelFn := makeFS(ctx, t, config) 2433 defer mnt.Close() 2434 defer cancelFn() 2435 2436 // cause the folder to exist 2437 p := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile") 2438 if err := ioutil.WriteFile( 2439 p, []byte("data for myfile"), 0644); err != nil { 2440 t.Fatal(err) 2441 } 2442 syncFilename(t, p) 2443 }() 2444 2445 c2 := libkbfs.ConfigAsUser(config, "wsmith") 2446 defer libkbfs.CheckConfigAndShutdown(ctx, t, c2) 2447 mnt, _, cancelFn := makeFS(ctx, t, c2) 2448 defer mnt.Close() 2449 defer cancelFn() 2450 2451 switch _, err := ioutil.ReadDir(path.Join(mnt.Dir, PrivateName, "jdoe")); err := errors.Cause(err).(type) { 2452 case *os.PathError: 2453 if g, e := err.Err, syscall.EACCES; g != e { 2454 t.Fatalf("wrong error: %v != %v", g, e) 2455 } 2456 default: 2457 t.Fatalf("expected a PathError, got %T: %v", err, err) 2458 } 2459} 2460 2461func syncFolderToServerHelper(t *testing.T, tlf string, ty tlf.Type, fs *FS) { 2462 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2463 defer testCleanupDelayer(ctx, t) 2464 root := libkbfs.GetRootNodeOrBust(ctx, t, fs.config, tlf, ty) 2465 err := fs.config.KBFSOps().SyncFromServer(ctx, 2466 root.GetFolderBranch(), nil) 2467 if err != nil { 2468 t.Fatalf("Couldn't sync from server: %v", err) 2469 } 2470 fs.NotificationGroupWait() 2471} 2472 2473func syncFolderToServer(t *testing.T, name string, fs *FS) { 2474 syncFolderToServerHelper(t, name, tlf.Private, fs) 2475} 2476 2477func syncPublicFolderToServer(t *testing.T, name string, fs *FS) { 2478 syncFolderToServerHelper(t, name, tlf.Public, fs) 2479} 2480 2481func TestInvalidateDataOnWrite(t *testing.T) { 2482 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2483 defer testCleanupDelayer(ctx, t) 2484 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 2485 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 2486 mnt1, _, cancelFn1 := makeFS(ctx, t, config) 2487 defer mnt1.Close() 2488 defer cancelFn1() 2489 config2 := libkbfs.ConfigAsUser(config, "jdoe") 2490 defer libkbfs.CheckConfigAndShutdown(ctx, t, config2) 2491 mnt2, fs2, cancelFn2 := makeFS(ctx, t, config2) 2492 defer mnt2.Close() 2493 defer cancelFn2() 2494 2495 if !mnt2.Conn.Protocol().HasInvalidate() { 2496 t.Skip("Old FUSE protocol") 2497 } 2498 2499 const input1 = "input round one" 2500 p := path.Join(mnt1.Dir, PrivateName, "jdoe", "myfile") 2501 if err := ioutil.WriteFile(p, []byte(input1), 0644); err != nil { 2502 t.Fatal(err) 2503 } 2504 syncFilename(t, p) 2505 2506 syncFolderToServer(t, "jdoe", fs2) 2507 2508 f, err := os.Open(path.Join(mnt2.Dir, PrivateName, "jdoe", "myfile")) 2509 if err != nil { 2510 t.Fatal(err) 2511 } 2512 defer syncAndClose(t, f) 2513 2514 { 2515 buf := make([]byte, 4096) 2516 n, err := f.ReadAt(buf, 0) 2517 if err != nil && err != io.EOF { 2518 t.Fatal(err) 2519 } 2520 if g, e := string(buf[:n]), input1; g != e { 2521 t.Errorf("wrong content: %q != %q", g, e) 2522 } 2523 } 2524 2525 const input2 = "second round of content" 2526 if err := ioutil.WriteFile(p, []byte(input2), 0644); err != nil { 2527 t.Fatal(err) 2528 } 2529 syncFilename(t, p) 2530 2531 syncFolderToServer(t, "jdoe", fs2) 2532 2533 { 2534 buf := make([]byte, 4096) 2535 n, err := f.ReadAt(buf, 0) 2536 if err != nil && err != io.EOF { 2537 t.Fatal(err) 2538 } 2539 if g, e := string(buf[:n]), input2; g != e { 2540 t.Errorf("wrong content: %q != %q", g, e) 2541 } 2542 } 2543} 2544 2545func TestInvalidatePublicDataOnWrite(t *testing.T) { 2546 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2547 defer testCleanupDelayer(ctx, t) 2548 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 2549 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 2550 mnt1, _, cancelFn1 := makeFS(ctx, t, config) 2551 defer mnt1.Close() 2552 defer cancelFn1() 2553 config2 := libkbfs.ConfigAsUser(config, "jdoe") 2554 defer libkbfs.CheckConfigAndShutdown(ctx, t, config2) 2555 mnt2, fs2, cancelFn2 := makeFS(ctx, t, config2) 2556 defer mnt2.Close() 2557 defer cancelFn2() 2558 2559 if !mnt2.Conn.Protocol().HasInvalidate() { 2560 t.Skip("Old FUSE protocol") 2561 } 2562 2563 const input1 = "input round one" 2564 p := path.Join(mnt1.Dir, PublicName, "jdoe", "myfile") 2565 if err := ioutil.WriteFile(p, []byte(input1), 0644); err != nil { 2566 t.Fatal(err) 2567 } 2568 syncFilename(t, p) 2569 2570 syncPublicFolderToServer(t, "jdoe", fs2) 2571 2572 f, err := os.Open(path.Join(mnt2.Dir, PublicName, "jdoe", "myfile")) 2573 if err != nil { 2574 t.Fatal(err) 2575 } 2576 defer syncAndClose(t, f) 2577 2578 { 2579 buf := make([]byte, 4096) 2580 n, err := f.ReadAt(buf, 0) 2581 if err != nil && err != io.EOF { 2582 t.Fatal(err) 2583 } 2584 if g, e := string(buf[:n]), input1; g != e { 2585 t.Errorf("wrong content: %q != %q", g, e) 2586 } 2587 } 2588 2589 const input2 = "second round of content" 2590 if err := ioutil.WriteFile(p, []byte(input2), 0644); err != nil { 2591 t.Fatal(err) 2592 } 2593 syncFilename(t, p) 2594 2595 syncPublicFolderToServer(t, "jdoe", fs2) 2596 2597 { 2598 buf := make([]byte, 4096) 2599 n, err := f.ReadAt(buf, 0) 2600 if err != nil && err != io.EOF { 2601 t.Fatal(err) 2602 } 2603 if g, e := string(buf[:n]), input2; g != e { 2604 t.Errorf("wrong content: %q != %q", g, e) 2605 } 2606 } 2607} 2608 2609func TestInvalidateDataOnTruncate(t *testing.T) { 2610 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2611 defer testCleanupDelayer(ctx, t) 2612 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 2613 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 2614 mnt1, _, cancelFn1 := makeFS(ctx, t, config) 2615 defer mnt1.Close() 2616 defer cancelFn1() 2617 config2 := libkbfs.ConfigAsUser(config, "jdoe") 2618 defer libkbfs.CheckConfigAndShutdown(ctx, t, config2) 2619 mnt2, fs2, cancelFn2 := makeFS(ctx, t, config2) 2620 defer mnt2.Close() 2621 defer cancelFn2() 2622 2623 if !mnt2.Conn.Protocol().HasInvalidate() { 2624 t.Skip("Old FUSE protocol") 2625 } 2626 2627 const input1 = "input round one" 2628 p := path.Join(mnt1.Dir, PrivateName, "jdoe", "myfile") 2629 if err := ioutil.WriteFile(p, []byte(input1), 0644); err != nil { 2630 t.Fatal(err) 2631 } 2632 syncFilename(t, p) 2633 2634 syncFolderToServer(t, "jdoe", fs2) 2635 2636 f, err := os.Open(path.Join(mnt2.Dir, PrivateName, "jdoe", "myfile")) 2637 if err != nil { 2638 t.Fatal(err) 2639 } 2640 defer syncAndClose(t, f) 2641 2642 { 2643 buf := make([]byte, 4096) 2644 n, err := f.ReadAt(buf, 0) 2645 if err != nil && err != io.EOF { 2646 t.Fatal(err) 2647 } 2648 if g, e := string(buf[:n]), input1; g != e { 2649 t.Errorf("wrong content: %q != %q", g, e) 2650 } 2651 } 2652 2653 const newSize = 3 2654 if err := os.Truncate(p, newSize); err != nil { 2655 t.Fatal(err) 2656 } 2657 syncFilename(t, p) 2658 2659 syncFolderToServer(t, "jdoe", fs2) 2660 2661 { 2662 buf := make([]byte, 4096) 2663 n, err := f.ReadAt(buf, 0) 2664 if err != nil && err != io.EOF { 2665 t.Fatal(err) 2666 } 2667 if g, e := string(buf[:n]), input1[:newSize]; g != e { 2668 t.Errorf("wrong content: %q != %q", g, e) 2669 } 2670 } 2671} 2672 2673func TestInvalidateDataOnLocalWrite(t *testing.T) { 2674 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2675 defer testCleanupDelayer(ctx, t) 2676 config := libkbfs.MakeTestConfigOrBust(t, "jdoe", "wsmith") 2677 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 2678 mnt, fs, cancelFn := makeFS(ctx, t, config) 2679 defer mnt.Close() 2680 defer cancelFn() 2681 2682 if !mnt.Conn.Protocol().HasInvalidate() { 2683 t.Skip("Old FUSE protocol") 2684 } 2685 2686 const input1 = "input round one" 2687 p := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile") 2688 if err := ioutil.WriteFile(p, []byte(input1), 0644); err != nil { 2689 t.Fatal(err) 2690 } 2691 syncFilename(t, p) 2692 2693 f, err := os.Open(path.Join(mnt.Dir, PrivateName, "jdoe", "myfile")) 2694 if err != nil { 2695 t.Fatal(err) 2696 } 2697 defer syncAndClose(t, f) 2698 2699 { 2700 buf := make([]byte, 4096) 2701 n, err := f.ReadAt(buf, 0) 2702 if err != nil && err != io.EOF { 2703 t.Fatal(err) 2704 } 2705 if g, e := string(buf[:n]), input1; g != e { 2706 t.Errorf("wrong content: %q != %q", g, e) 2707 } 2708 } 2709 2710 const input2 = "second round of content" 2711 { 2712 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2713 defer testCleanupDelayer(ctx, t) 2714 2715 jdoe := libkbfs.GetRootNodeOrBust(ctx, t, config, "jdoe", tlf.Private) 2716 ops := config.KBFSOps() 2717 myfile, _, err := ops.Lookup(ctx, jdoe, jdoe.ChildName("myfile")) 2718 if err != nil { 2719 t.Fatal(err) 2720 } 2721 if err := ops.Write(ctx, myfile, []byte(input2), 0); err != nil { 2722 t.Fatal(err) 2723 } 2724 } 2725 2726 // The Write above is a local change, and thus we can just do a 2727 // local wait without syncing to the server. 2728 fs.NotificationGroupWait() 2729 2730 { 2731 buf := make([]byte, 4096) 2732 n, err := f.ReadAt(buf, 0) 2733 if err != nil && err != io.EOF { 2734 t.Fatal(err) 2735 } 2736 if g, e := string(buf[:n]), input2; g != e { 2737 t.Errorf("wrong content: %q != %q", g, e) 2738 } 2739 } 2740} 2741 2742func TestInvalidateEntryOnDelete(t *testing.T) { 2743 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2744 defer testCleanupDelayer(ctx, t) 2745 config := libkbfs.MakeTestConfigOrBust(t, "jdoe", "wsmith") 2746 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 2747 mnt1, _, cancelFn1 := makeFS(ctx, t, config) 2748 defer mnt1.Close() 2749 defer cancelFn1() 2750 mnt2, fs2, cancelFn2 := makeFS(ctx, t, config) 2751 defer mnt2.Close() 2752 defer cancelFn2() 2753 2754 if !mnt2.Conn.Protocol().HasInvalidate() { 2755 t.Skip("Old FUSE protocol") 2756 } 2757 2758 const input1 = "input round one" 2759 p := path.Join(mnt1.Dir, PrivateName, "jdoe", "myfile") 2760 if err := ioutil.WriteFile(p, []byte(input1), 0644); err != nil { 2761 t.Fatal(err) 2762 } 2763 syncFilename(t, p) 2764 2765 syncFolderToServer(t, "jdoe", fs2) 2766 2767 buf, err := ioutil.ReadFile(path.Join(mnt2.Dir, PrivateName, "jdoe", "myfile")) 2768 if err != nil { 2769 t.Fatal(err) 2770 } 2771 if g, e := string(buf), input1; g != e { 2772 t.Errorf("wrong content: %q != %q", g, e) 2773 } 2774 2775 if err := ioutil.Remove(path.Join(mnt1.Dir, PrivateName, "jdoe", "myfile")); err != nil { 2776 t.Fatal(err) 2777 } 2778 2779 syncFolderToServer(t, "jdoe", fs2) 2780 2781 if buf, err := ioutil.ReadFile(path.Join(mnt2.Dir, PrivateName, "jdoe", "myfile")); !ioutil.IsNotExist(err) { 2782 t.Fatalf("expected ENOENT: %v: %q", err, buf) 2783 } 2784} 2785 2786func testForErrorText(t *testing.T, path string, expectedErr error, 2787 fileType string) { 2788 buf, err := ioutil.ReadFile(path) 2789 if err != nil { 2790 t.Fatalf("Bad error reading %s error file: %v", path, err) 2791 } 2792 2793 var errors []libfs.JSONReportedError 2794 err = json.Unmarshal(buf, &errors) 2795 if err != nil { 2796 t.Fatalf("Couldn't unmarshal error file: %v. Full contents: %s", 2797 err, string(buf)) 2798 } 2799 2800 found := false 2801 for _, e := range errors { 2802 if e.Error == expectedErr.Error() { 2803 found = true 2804 break 2805 } 2806 } 2807 2808 if !found { 2809 t.Errorf("%s error file did not contain the error %s. "+ 2810 "Full contents: %s", fileType, expectedErr, buf) 2811 } 2812} 2813 2814func TestErrorFile(t *testing.T) { 2815 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2816 defer testCleanupDelayer(ctx, t) 2817 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 2818 config.SetReporter(libkbfs.NewReporterSimple(config.Clock(), 0)) 2819 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 2820 mnt, _, cancelFn := makeFS(ctx, t, config) 2821 defer mnt.Close() 2822 defer cancelFn() 2823 2824 libfs.AddRootWrapper(config) 2825 2826 // cause an error by stating a non-existent user 2827 _, err := ioutil.Lstat(path.Join(mnt.Dir, PrivateName, "janedoe")) 2828 if err == nil { 2829 t.Fatal("Stat of non-existent user worked!") 2830 } 2831 2832 // Make sure the root error file reads as expected 2833 expectedErr := fuse.ENOENT 2834 2835 // test both the root error file and one in a directory 2836 testForErrorText(t, path.Join(mnt.Dir, libfs.ErrorFileName), 2837 expectedErr, "root") 2838 testForErrorText(t, path.Join(mnt.Dir, PublicName, libfs.ErrorFileName), 2839 expectedErr, "root") 2840 testForErrorText(t, path.Join(mnt.Dir, PrivateName, libfs.ErrorFileName), 2841 expectedErr, "root") 2842 2843 // Create public and private jdoe TLFs. 2844 const b = "hello world" 2845 p := path.Join(mnt.Dir, PublicName, "jdoe", "myfile") 2846 if err := ioutil.WriteFile(p, []byte(b), 0644); err != nil { 2847 t.Fatal(err) 2848 } 2849 syncFilename(t, p) 2850 p = path.Join(mnt.Dir, PrivateName, "jdoe", "myfile") 2851 if err := ioutil.WriteFile(p, []byte(b), 0644); err != nil { 2852 t.Fatal(err) 2853 } 2854 syncFilename(t, p) 2855 2856 testForErrorText( 2857 t, path.Join(mnt.Dir, PublicName, "jdoe", libfs.ErrorFileName), 2858 expectedErr, "dir") 2859 testForErrorText( 2860 t, path.Join(mnt.Dir, PrivateName, "jdoe", libfs.ErrorFileName), 2861 expectedErr, "dir") 2862} 2863 2864func TestInvalidateAcrossMounts(t *testing.T) { 2865 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2866 defer testCleanupDelayer(ctx, t) 2867 config1 := libkbfs.MakeTestConfigOrBust(t, "user1", 2868 "user2") 2869 defer libkbfs.CheckConfigAndShutdown(ctx, t, config1) 2870 mnt1, fs1, cancelFn1 := makeFS(ctx, t, config1) 2871 defer mnt1.Close() 2872 defer cancelFn1() 2873 2874 config2 := libkbfs.ConfigAsUser(config1, "user2") 2875 defer libkbfs.CheckConfigAndShutdown(ctx, t, config2) 2876 mnt2, fs2, cancelFn2 := makeFS(ctx, t, config2) 2877 defer mnt2.Close() 2878 defer cancelFn2() 2879 2880 if !mnt2.Conn.Protocol().HasInvalidate() { 2881 t.Skip("Old FUSE protocol") 2882 } 2883 2884 // user 1 writes one file to root and one to a sub directory 2885 const input1 = "input round one" 2886 myfile1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "myfile") 2887 if err := ioutil.WriteFile(myfile1, []byte(input1), 0644); err != nil { 2888 t.Fatal(err) 2889 } 2890 syncFilename(t, myfile1) 2891 mydir1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "mydir") 2892 if err := ioutil.Mkdir(mydir1, 0755); err != nil { 2893 t.Fatal(err) 2894 } 2895 mydira1 := path.Join(mydir1, "a") 2896 if err := ioutil.WriteFile(mydira1, []byte(input1), 0644); err != nil { 2897 t.Fatal(err) 2898 } 2899 syncFilename(t, mydira1) 2900 2901 syncFolderToServer(t, "user1,user2", fs2) 2902 2903 myfile2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", "myfile") 2904 buf, err := ioutil.ReadFile(myfile2) 2905 if err != nil { 2906 t.Fatal(err) 2907 } 2908 if g, e := string(buf), input1; g != e { 2909 t.Errorf("wrong content: %q != %q", g, e) 2910 } 2911 2912 mydir2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", "mydir") 2913 mydira2 := path.Join(mydir2, "a") 2914 buf, err = ioutil.ReadFile(mydira2) 2915 if err != nil { 2916 t.Fatal(err) 2917 } 2918 if g, e := string(buf), input1; g != e { 2919 t.Errorf("wrong content: %q != %q", g, e) 2920 } 2921 2922 // now remove the first file, and rename the second 2923 if err := ioutil.Remove(myfile1); err != nil { 2924 t.Fatal(err) 2925 } 2926 mydirb1 := path.Join(mydir1, "b") 2927 if err := ioutil.Rename(mydira1, mydirb1); err != nil { 2928 t.Fatal(err) 2929 } 2930 syncAll(t, "user1,user2", tlf.Private, fs1) 2931 2932 syncFolderToServer(t, "user1,user2", fs2) 2933 2934 // check everything from user 2's perspective 2935 if buf, err := ioutil.ReadFile(myfile2); !ioutil.IsNotExist(err) { 2936 t.Fatalf("expected ENOENT: %v: %q", err, buf) 2937 } 2938 if buf, err := ioutil.ReadFile(mydira2); !ioutil.IsNotExist(err) { 2939 t.Fatalf("expected ENOENT: %v: %q", err, buf) 2940 } 2941 2942 checkDir(t, mydir2, map[string]fileInfoCheck{ 2943 "b": func(fi os.FileInfo) error { 2944 return mustBeFileWithSize(fi, int64(len(input1))) 2945 }, 2946 }) 2947 2948 mydirb2 := path.Join(mydir2, "b") 2949 buf, err = ioutil.ReadFile(mydirb2) 2950 if err != nil { 2951 t.Fatal(err) 2952 } 2953 if g, e := string(buf), input1; g != e { 2954 t.Errorf("wrong content: %q != %q", g, e) 2955 } 2956} 2957 2958func TestInvalidateAppendAcrossMounts(t *testing.T) { 2959 ctx := libcontext.BackgroundContextWithCancellationDelayer() 2960 defer testCleanupDelayer(ctx, t) 2961 config1 := libkbfs.MakeTestConfigOrBust(t, "user1", 2962 "user2") 2963 defer libkbfs.CheckConfigAndShutdown(ctx, t, config1) 2964 mnt1, _, cancelFn1 := makeFS(ctx, t, config1) 2965 defer mnt1.Close() 2966 defer cancelFn1() 2967 2968 config2 := libkbfs.ConfigAsUser(config1, "user2") 2969 defer libkbfs.CheckConfigAndShutdown(ctx, t, config2) 2970 mnt2, fs2, cancelFn2 := makeFS(ctx, t, config2) 2971 defer mnt2.Close() 2972 defer cancelFn2() 2973 2974 if !mnt2.Conn.Protocol().HasInvalidate() { 2975 t.Skip("Old FUSE protocol") 2976 } 2977 2978 // user 1 writes one file to root and one to a sub directory 2979 const input1 = "input round one" 2980 myfile1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "myfile") 2981 if err := ioutil.WriteFile(myfile1, []byte(input1), 0644); err != nil { 2982 t.Fatal(err) 2983 } 2984 syncFilename(t, myfile1) 2985 syncFolderToServer(t, "user1,user2", fs2) 2986 myfile2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", "myfile") 2987 buf, err := ioutil.ReadFile(myfile2) 2988 if err != nil { 2989 t.Fatal(err) 2990 } 2991 if g, e := string(buf), input1; g != e { 2992 t.Errorf("wrong content: %q != %q", g, e) 2993 } 2994 2995 // user 1 append using libkbfs, to ensure that it doesn't flush 2996 // the whole page. 2997 const input2 = "input round two" 2998 { 2999 ctx := libcontext.BackgroundContextWithCancellationDelayer() 3000 defer testCleanupDelayer(ctx, t) 3001 3002 jdoe := libkbfs.GetRootNodeOrBust(ctx, t, config1, "user1,user2", tlf.Private) 3003 3004 ops := config1.KBFSOps() 3005 myfile, _, err := ops.Lookup(ctx, jdoe, jdoe.ChildName("myfile")) 3006 if err != nil { 3007 t.Fatal(err) 3008 } 3009 if err := ops.Write( 3010 ctx, myfile, []byte(input2), int64(len(input1))); err != nil { 3011 t.Fatal(err) 3012 } 3013 if err := ops.SyncAll(ctx, myfile.GetFolderBranch()); err != nil { 3014 t.Fatal(err) 3015 } 3016 } 3017 3018 syncFolderToServer(t, "user1,user2", fs2) 3019 3020 // check everything from user 2's perspective 3021 buf, err = ioutil.ReadFile(myfile2) 3022 if err != nil { 3023 t.Fatal(err) 3024 } 3025 if g, e := string(buf), input1+input2; g != e { 3026 t.Errorf("wrong content: %q != %q", g, e) 3027 } 3028} 3029 3030func TestInvalidateRenameToUncachedDir(t *testing.T) { 3031 ctx := libcontext.BackgroundContextWithCancellationDelayer() 3032 defer testCleanupDelayer(ctx, t) 3033 config1 := libkbfs.MakeTestConfigOrBust(t, "user1", 3034 "user2") 3035 defer libkbfs.CheckConfigAndShutdown(ctx, t, config1) 3036 mnt1, fs1, cancelFn1 := makeFS(ctx, t, config1) 3037 defer mnt1.Close() 3038 defer cancelFn1() 3039 3040 config2 := libkbfs.ConfigAsUser(config1, "user2") 3041 defer libkbfs.CheckConfigAndShutdown(ctx, t, config2) 3042 mnt2, fs2, cancelFn2 := makeFS(ctx, t, config2) 3043 defer mnt2.Close() 3044 defer cancelFn2() 3045 3046 if !mnt2.Conn.Protocol().HasInvalidate() { 3047 t.Skip("Old FUSE protocol") 3048 } 3049 3050 // user 1 writes one file to root and one to a sub directory 3051 const input1 = "input round one" 3052 myfile1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "myfile") 3053 if err := ioutil.WriteFile(myfile1, []byte(input1), 0644); err != nil { 3054 t.Fatal(err) 3055 } 3056 syncFilename(t, myfile1) 3057 mydir1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "mydir") 3058 if err := ioutil.Mkdir(mydir1, 0755); err != nil { 3059 t.Fatal(err) 3060 } 3061 mydirfile1 := path.Join(mydir1, "myfile") 3062 3063 syncFolderToServer(t, "user1,user2", fs2) 3064 myfile2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", "myfile") 3065 f, err := os.OpenFile(myfile2, os.O_RDWR, 0644) 3066 if err != nil { 3067 t.Fatal(err) 3068 } 3069 // Call in a closure since `f` is overridden below. 3070 defer func() { syncAndClose(t, f) }() 3071 3072 { 3073 buf := make([]byte, 4096) 3074 n, err := f.ReadAt(buf, 0) 3075 if err != nil && err != io.EOF { 3076 t.Fatal(err) 3077 } 3078 if g, e := string(buf[:n]), input1; g != e { 3079 t.Errorf("wrong content: %q != %q", g, e) 3080 } 3081 } 3082 3083 // now rename the second into a directory that user 2 hasn't seen 3084 if err := ioutil.Rename(myfile1, mydirfile1); err != nil { 3085 t.Fatal(err) 3086 } 3087 syncAll(t, "user1,user2", tlf.Private, fs1) 3088 3089 syncFolderToServer(t, "user1,user2", fs2) 3090 3091 // user 2 should be able to write to its open file, and user 1 3092 // will see the change 3093 const input2 = "input round two" 3094 { 3095 n, err := f.WriteAt([]byte(input2), 0) 3096 if err != nil || n != len(input2) { 3097 t.Fatal(err) 3098 } 3099 } 3100 syncAndClose(t, f) 3101 f = nil 3102 3103 syncFolderToServer(t, "user1,user2", fs1) 3104 3105 buf, err := ioutil.ReadFile(mydirfile1) 3106 if err != nil { 3107 t.Fatal(err) 3108 } 3109 if g, e := string(buf), input2; g != e { 3110 t.Errorf("wrong content: %q != %q", g, e) 3111 } 3112} 3113 3114func TestStatusFile(t *testing.T) { 3115 ctx := libcontext.BackgroundContextWithCancellationDelayer() 3116 defer testCleanupDelayer(ctx, t) 3117 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 3118 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 3119 mnt, _, cancelFn := makeFS(ctx, t, config) 3120 defer mnt.Close() 3121 defer cancelFn() 3122 3123 libfs.AddRootWrapper(config) 3124 3125 jdoe := libkbfs.GetRootNodeOrBust(ctx, t, config, "jdoe", tlf.Public) 3126 mydir := path.Join(mnt.Dir, PublicName, "jdoe", "mydir") 3127 err := ioutil.Mkdir(mydir, 0755) 3128 require.NoError(t, err) 3129 3130 ops := config.KBFSOps() 3131 status, _, err := ops.FolderStatus(ctx, jdoe.GetFolderBranch()) 3132 require.NoError(t, err) 3133 3134 checkStatus := func(dir string) { 3135 // Simply make sure the status in the file matches what we'd 3136 // expect. Checking the exact content should be left for tests 3137 // within libkbfs. 3138 buf, err := ioutil.ReadFile(path.Join(dir, libfs.StatusFileName)) 3139 require.NoError(t, err) 3140 3141 var bufStatus libkbfs.FolderBranchStatus 3142 err = json.Unmarshal(buf, &bufStatus) 3143 require.NoError(t, err) 3144 3145 // Use a fuzzy check on the timestamps, since it could include 3146 // monotonic clock stuff. 3147 require.True(t, timeEqualFuzzy( 3148 status.LocalTimestamp, bufStatus.LocalTimestamp, time.Millisecond)) 3149 status.LocalTimestamp = bufStatus.LocalTimestamp 3150 3151 // It's safe to compare the path slices with DeepEqual since 3152 // they will all be null for this test (nothing is dirtied). 3153 require.True(t, reflect.DeepEqual(status, bufStatus)) 3154 } 3155 checkStatus(path.Join(mnt.Dir, PublicName, "jdoe")) 3156 checkStatus(mydir) 3157} 3158 3159// TODO: remove once we have automatic conflict resolution tests 3160func TestUnstageFile(t *testing.T) { 3161 ctx := libcontext.BackgroundContextWithCancellationDelayer() 3162 defer testCleanupDelayer(ctx, t) 3163 config1 := libkbfs.MakeTestConfigOrBust(t, "user1", 3164 "user2") 3165 defer libkbfs.CheckConfigAndShutdown(ctx, t, config1) 3166 mnt1, fs1, cancelFn1 := makeFS(ctx, t, config1) 3167 defer mnt1.Close() 3168 defer cancelFn1() 3169 3170 config2 := libkbfs.ConfigAsUser(config1, "user2") 3171 defer libkbfs.CheckConfigAndShutdown(ctx, t, config2) 3172 mnt2, fs2, cancelFn2 := makeFS(ctx, t, config2) 3173 defer mnt2.Close() 3174 defer cancelFn2() 3175 3176 if !mnt2.Conn.Protocol().HasInvalidate() { 3177 t.Skip("Old FUSE protocol") 3178 } 3179 3180 // both users read the root dir first 3181 myroot1 := path.Join(mnt1.Dir, PrivateName, "user1,user2") 3182 myroot2 := path.Join(mnt2.Dir, PrivateName, "user1,user2") 3183 checkDir(t, myroot1, map[string]fileInfoCheck{}) 3184 checkDir(t, myroot2, map[string]fileInfoCheck{}) 3185 3186 // turn updates off for user 2 3187 rootNode2 := libkbfs.GetRootNodeOrBust(ctx, t, config2, "user1,user2", tlf.Private) 3188 _, err := libkbfs.DisableUpdatesForTesting(config2, 3189 rootNode2.GetFolderBranch()) 3190 if err != nil { 3191 t.Fatalf("Couldn't pause user 2 updates") 3192 } 3193 err = libkbfs.DisableCRForTesting(config2, rootNode2.GetFolderBranch()) 3194 if err != nil { 3195 t.Fatalf("Couldn't disable user 2 CR") 3196 } 3197 3198 // user1 writes a file and makes a few directories 3199 const input1 = "input round one" 3200 myfile1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "myfile") 3201 if err := ioutil.WriteFile(myfile1, []byte(input1), 0644); err != nil { 3202 t.Fatal(err) 3203 } 3204 syncFilename(t, myfile1) 3205 mydir1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "mydir") 3206 if err := ioutil.Mkdir(mydir1, 0755); err != nil { 3207 t.Fatal(err) 3208 } 3209 mysubdir1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "mydir", 3210 "mysubdir") 3211 if err := ioutil.Mkdir(mysubdir1, 0755); err != nil { 3212 t.Fatal(err) 3213 } 3214 syncAll(t, "user1,user2", tlf.Private, fs1) 3215 3216 // user2 does similar 3217 const input2 = "input round two" 3218 myfile2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", "myfile") 3219 if err := ioutil.WriteFile(myfile2, []byte(input2), 0644); err != nil { 3220 t.Fatal(err) 3221 } 3222 syncFilename(t, myfile2) 3223 mydir2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", "mydir") 3224 if err := ioutil.Mkdir(mydir2, 0755); err != nil { 3225 t.Fatal(err) 3226 } 3227 myothersubdir2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", "mydir", 3228 "myothersubdir") 3229 if err := ioutil.Mkdir(myothersubdir2, 0755); err != nil { 3230 t.Fatal(err) 3231 } 3232 syncAll(t, "user1,user2", tlf.Private, fs2) 3233 3234 // verify that they don't see each other's files 3235 checkDir(t, mydir1, map[string]fileInfoCheck{ 3236 "mysubdir": mustBeDir, 3237 }) 3238 checkDir(t, mydir2, map[string]fileInfoCheck{ 3239 "myothersubdir": mustBeDir, 3240 }) 3241 3242 // now unstage user 2 and they should see the same stuff 3243 unstageFile2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", 3244 libfs.UnstageFileName) 3245 if err := ioutil.WriteFile(unstageFile2, []byte{1}, 0222); err != nil { 3246 t.Fatal(err) 3247 } 3248 3249 syncFolderToServer(t, "user1,user2", fs2) 3250 3251 // They should see identical folders now 3252 checkDir(t, mydir1, map[string]fileInfoCheck{ 3253 "mysubdir": mustBeDir, 3254 }) 3255 checkDir(t, mydir2, map[string]fileInfoCheck{ 3256 "mysubdir": mustBeDir, 3257 }) 3258 3259 buf, err := ioutil.ReadFile(myfile1) 3260 if err != nil { 3261 t.Fatal(err) 3262 } 3263 if g, e := string(buf), input1; g != e { 3264 t.Errorf("wrong content: %q != %q", g, e) 3265 } 3266 buf, err = ioutil.ReadFile(myfile2) 3267 if err != nil { 3268 t.Fatal(err) 3269 } 3270 if g, e := string(buf), input1; g != e { 3271 t.Errorf("wrong content: %q != %q", g, e) 3272 } 3273} 3274 3275func TestSimpleCRNoConflict(t *testing.T) { 3276 ctx := libcontext.BackgroundContextWithCancellationDelayer() 3277 defer testCleanupDelayer(ctx, t) 3278 config1 := libkbfs.MakeTestConfigOrBust(t, "user1", 3279 "user2") 3280 mnt1, fs1, cancelFn1 := makeFS(ctx, t, config1) 3281 defer mnt1.Close() 3282 defer cancelFn1() 3283 defer libkbfs.CheckConfigAndShutdown(ctx, t, config1) 3284 3285 config2 := libkbfs.ConfigAsUser(config1, "user2") 3286 mnt2, fs2, cancelFn2 := makeFS(ctx, t, config2) 3287 defer mnt2.Close() 3288 defer cancelFn2() 3289 defer libkbfs.CheckConfigAndShutdown(ctx, t, config2) 3290 3291 if !mnt2.Conn.Protocol().HasInvalidate() { 3292 t.Skip("Old FUSE protocol") 3293 } 3294 3295 root1 := path.Join(mnt1.Dir, PrivateName, "user1,user2") 3296 root2 := path.Join(mnt2.Dir, PrivateName, "user1,user2") 3297 // Please create TLF here first 3298 d1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "D") 3299 d2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", "E") 3300 if err := ioutil.Mkdir(d1, 0755); err != nil { 3301 t.Fatal("Mkdir failed") 3302 } 3303 syncAll(t, "user1,user2", tlf.Private, fs1) 3304 syncFolderToServer(t, "user1,user2", fs2) 3305 if err := ioutil.Mkdir(d2, 0755); err != nil { 3306 t.Fatal("Mkdir failed") 3307 } 3308 syncAll(t, "user1,user2", tlf.Private, fs2) 3309 syncFolderToServer(t, "user1,user2", fs1) 3310 3311 // disable updates for user 2 3312 disableUpdatesFile := path.Join(mnt2.Dir, PrivateName, "user1,user2", 3313 libfs.DisableUpdatesFileName) 3314 if err := ioutil.WriteFile(disableUpdatesFile, 3315 []byte("off"), 0644); err != nil { 3316 t.Fatal(err) 3317 } 3318 3319 // user1 writes a file and makes a few directories 3320 const input1 = "input round one" 3321 file1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "file1") 3322 if err := ioutil.WriteFile(file1, []byte(input1), 0644); err != nil { 3323 t.Fatal(err) 3324 } 3325 syncFilename(t, file1) 3326 dir1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "dir") 3327 if err := ioutil.Mkdir(dir1, 0755); err != nil { 3328 t.Fatal(err) 3329 } 3330 subdir1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "dir", "subdir1") 3331 if err := ioutil.Mkdir(subdir1, 0755); err != nil { 3332 t.Fatal(err) 3333 } 3334 syncAll(t, "user1,user2", tlf.Private, fs1) 3335 3336 // user2 does similar 3337 const input2 = "input round two two two" 3338 file2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", "file2") 3339 if err := ioutil.WriteFile(file2, []byte(input2), 0644); err != nil { 3340 t.Fatal(err) 3341 } 3342 syncFilename(t, file2) 3343 dir2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", "dir") 3344 if err := ioutil.Mkdir(dir2, 0755); err != nil { 3345 t.Fatal(err) 3346 } 3347 subdir2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", "dir", "subdir2") 3348 if err := ioutil.Mkdir(subdir2, 0755); err != nil { 3349 t.Fatal(err) 3350 } 3351 syncAll(t, "user1,user2", tlf.Private, fs2) 3352 3353 // verify that they don't see each other's files 3354 checkDir(t, root1, map[string]fileInfoCheck{ 3355 "file1": func(fi os.FileInfo) error { 3356 return mustBeFileWithSize(fi, int64(len(input1))) 3357 }, 3358 "dir": mustBeDir, 3359 "D": mustBeDir, 3360 "E": mustBeDir, 3361 }) 3362 checkDir(t, dir1, map[string]fileInfoCheck{ 3363 "subdir1": mustBeDir, 3364 }) 3365 checkDir(t, root2, map[string]fileInfoCheck{ 3366 "file2": func(fi os.FileInfo) error { 3367 return mustBeFileWithSize(fi, int64(len(input2))) 3368 }, 3369 "dir": mustBeDir, 3370 "D": mustBeDir, 3371 "E": mustBeDir, 3372 }) 3373 checkDir(t, dir2, map[string]fileInfoCheck{ 3374 "subdir2": mustBeDir, 3375 }) 3376 3377 // now re-enable user 2 updates and CR, and the merge should happen 3378 enableUpdatesFile := path.Join(mnt2.Dir, PrivateName, "user1,user2", 3379 libfs.EnableUpdatesFileName) 3380 if err := ioutil.WriteFile(enableUpdatesFile, 3381 []byte("on"), 0644); err != nil { 3382 t.Fatal(err) 3383 } 3384 3385 syncFolderToServer(t, "user1,user2", fs2) 3386 syncFolderToServer(t, "user1,user2", fs1) 3387 3388 // They should see identical folders now (conflict-free merge) 3389 checkDir(t, root1, map[string]fileInfoCheck{ 3390 "file1": func(fi os.FileInfo) error { 3391 return mustBeFileWithSize(fi, int64(len(input1))) 3392 }, 3393 "file2": func(fi os.FileInfo) error { 3394 return mustBeFileWithSize(fi, int64(len(input2))) 3395 }, 3396 "dir": mustBeDir, 3397 "D": mustBeDir, 3398 "E": mustBeDir, 3399 }) 3400 checkDir(t, dir1, map[string]fileInfoCheck{ 3401 "subdir1": mustBeDir, 3402 "subdir2": mustBeDir, 3403 }) 3404 checkDir(t, root2, map[string]fileInfoCheck{ 3405 "file1": func(fi os.FileInfo) error { 3406 return mustBeFileWithSize(fi, int64(len(input1))) 3407 }, 3408 "file2": func(fi os.FileInfo) error { 3409 return mustBeFileWithSize(fi, int64(len(input2))) 3410 }, 3411 "dir": mustBeDir, 3412 "D": mustBeDir, 3413 "E": mustBeDir, 3414 }) 3415 checkDir(t, dir2, map[string]fileInfoCheck{ 3416 "subdir1": mustBeDir, 3417 "subdir2": mustBeDir, 3418 }) 3419 3420 buf, err := ioutil.ReadFile(file1) 3421 if err != nil { 3422 t.Fatal(err) 3423 } 3424 if g, e := string(buf), input1; g != e { 3425 t.Errorf("wrong content: %q != %q", g, e) 3426 } 3427 file2u1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "file2") 3428 buf, err = ioutil.ReadFile(file2u1) 3429 if err != nil { 3430 t.Fatal(err) 3431 } 3432 if g, e := string(buf), input2; g != e { 3433 t.Errorf("wrong content: %q != %q", g, e) 3434 } 3435 3436 file1u2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", "file1") 3437 buf, err = ioutil.ReadFile(file1u2) 3438 if err != nil { 3439 t.Fatal(err) 3440 } 3441 if g, e := string(buf), input1; g != e { 3442 t.Errorf("wrong content: %q != %q", g, e) 3443 } 3444 buf, err = ioutil.ReadFile(file2) 3445 if err != nil { 3446 t.Fatal(err) 3447 } 3448 if g, e := string(buf), input2; g != e { 3449 t.Errorf("wrong content: %q != %q", g, e) 3450 } 3451} 3452 3453func TestSimpleCRConflictOnOpenFiles(t *testing.T) { 3454 ctx := libcontext.BackgroundContextWithCancellationDelayer() 3455 defer testCleanupDelayer(ctx, t) 3456 config1 := libkbfs.MakeTestConfigOrBust(t, "user1", 3457 "user2") 3458 mnt1, fs1, cancelFn1 := makeFS(ctx, t, config1) 3459 defer mnt1.Close() 3460 defer cancelFn1() 3461 defer libkbfs.CheckConfigAndShutdown(ctx, t, config1) 3462 3463 config2 := libkbfs.ConfigAsUser(config1, "user2") 3464 mnt2, fs2, cancelFn2 := makeFS(ctx, t, config2) 3465 defer mnt2.Close() 3466 defer cancelFn2() 3467 defer libkbfs.CheckConfigAndShutdown(ctx, t, config2) 3468 3469 if !mnt2.Conn.Protocol().HasInvalidate() { 3470 t.Skip("Old FUSE protocol") 3471 } 3472 3473 now := time.Now() 3474 var clock clocktest.TestClock 3475 clock.Set(now) 3476 config2.SetClock(&clock) 3477 3478 root1 := path.Join(mnt1.Dir, PrivateName, "user1,user2") 3479 root2 := path.Join(mnt2.Dir, PrivateName, "user1,user2") 3480 3481 // both users should mutate the dir first 3482 d1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "D") 3483 d2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", "E") 3484 if err := ioutil.Mkdir(d1, 0755); err != nil { 3485 t.Fatal("Mkdir failed") 3486 } 3487 syncFolderToServer(t, "user1,user2", fs2) 3488 if err := ioutil.Mkdir(d2, 0755); err != nil { 3489 t.Fatal("Mkdir failed") 3490 } 3491 syncFolderToServer(t, "user1,user2", fs1) 3492 3493 // disable updates for user 2 3494 disableUpdatesFile := path.Join(mnt2.Dir, PrivateName, "user1,user2", 3495 libfs.DisableUpdatesFileName) 3496 if err := ioutil.WriteFile(disableUpdatesFile, 3497 []byte("off"), 0644); err != nil { 3498 t.Fatal(err) 3499 } 3500 3501 // user1 creates and writes a file 3502 file1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "f") 3503 f1, err := os.Create(file1) 3504 if err != nil { 3505 t.Fatal(err) 3506 } 3507 defer syncAndClose(t, f1) 3508 3509 const input1 = "hello" 3510 { 3511 n, err := f1.WriteAt([]byte(input1), 0) 3512 if err != nil || n != len(input1) { 3513 t.Fatal(err) 3514 } 3515 if err := f1.Sync(); err != nil { 3516 t.Fatal(err) 3517 } 3518 } 3519 3520 // user2 creates and writes a file 3521 file2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", "f") 3522 f2, err := os.Create(file2) 3523 if err != nil { 3524 t.Fatal(err) 3525 } 3526 defer syncAndClose(t, f2) 3527 3528 const input2 = "ohell" 3529 { 3530 n, err := f2.WriteAt([]byte(input2), 0) 3531 if err != nil || n != len(input2) { 3532 t.Fatal(err) 3533 } 3534 if err := f2.Sync(); err != nil { 3535 t.Fatal(err) 3536 } 3537 } 3538 3539 // now re-enable user 2 updates and CR, and the merge should happen 3540 enableUpdatesFile := path.Join(mnt2.Dir, PrivateName, "user1,user2", 3541 libfs.EnableUpdatesFileName) 3542 if err := ioutil.WriteFile(enableUpdatesFile, 3543 []byte("on"), 0644); err != nil { 3544 t.Fatal(err) 3545 } 3546 3547 syncFolderToServer(t, "user1,user2", fs2) 3548 syncFolderToServer(t, "user1,user2", fs1) 3549 3550 // They should both be able to read their past writes. 3551 { 3552 buf := make([]byte, len(input1)) 3553 n, err := f1.ReadAt(buf, 0) 3554 if err != nil || n != len(input1) { 3555 t.Fatal(err) 3556 } 3557 if g, e := string(buf), input1; g != e { 3558 t.Errorf("Unexpected read on f2: %s vs %s", g, e) 3559 } 3560 } 3561 { 3562 buf := make([]byte, len(input2)) 3563 n, err := f2.ReadAt(buf, 0) 3564 if err != nil || n != len(input2) { 3565 t.Fatal(err) 3566 } 3567 if g, e := string(buf), input2; g != e { 3568 t.Errorf("Unexpected read on f2: %s vs %s", g, e) 3569 } 3570 } 3571 3572 // They should see the conflict. 3573 cre := libkbfs.WriterDeviceDateConflictRenamer{} 3574 checkDir(t, root1, map[string]fileInfoCheck{ 3575 "f": func(fi os.FileInfo) error { 3576 return mustBeFileWithSize(fi, int64(len(input1))) 3577 }, 3578 cre.ConflictRenameHelper(now, "user2", "dev1", "f"): func(fi os.FileInfo) error { 3579 return mustBeFileWithSize(fi, int64(len(input2))) 3580 }, 3581 "D": mustBeDir, 3582 "E": mustBeDir, 3583 }) 3584 checkDir(t, root2, map[string]fileInfoCheck{ 3585 "f": func(fi os.FileInfo) error { 3586 return mustBeFileWithSize(fi, int64(len(input1))) 3587 }, 3588 cre.ConflictRenameHelper(now, "user2", "dev1", "f"): func(fi os.FileInfo) error { 3589 return mustBeFileWithSize(fi, int64(len(input2))) 3590 }, 3591 "D": mustBeDir, 3592 "E": mustBeDir, 3593 }) 3594 3595 input3 := " world" 3596 { 3597 n, err := f1.WriteAt([]byte(input3), int64(len(input1))) 3598 if err != nil || n != len(input3) { 3599 t.Fatal(err) 3600 } 3601 if err := f1.Sync(); err != nil { 3602 t.Fatal(err) 3603 } 3604 } 3605 3606 syncFolderToServer(t, "user1,user2", fs2) 3607 3608 input4 := " dlrow" 3609 { 3610 n, err := f2.WriteAt([]byte(input4), int64(len(input2))) 3611 if err != nil || n != len(input4) { 3612 t.Fatal(err) 3613 } 3614 if err := f2.Sync(); err != nil { 3615 t.Fatal(err) 3616 } 3617 } 3618 3619 syncFolderToServer(t, "user1,user2", fs1) 3620 3621 buf, err := ioutil.ReadFile(file1) 3622 if err != nil { 3623 t.Fatal(err) 3624 } 3625 if g, e := string(buf), input1+input3; g != e { 3626 t.Errorf("wrong content: %q != %q", g, e) 3627 } 3628 buf, err = ioutil.ReadFile(file2) 3629 if err != nil { 3630 t.Fatal(err) 3631 } 3632 if g, e := string(buf), input1+input3; g != e { 3633 t.Errorf("wrong content: %q != %q", g, e) 3634 } 3635 3636 filec1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", cre.ConflictRenameHelper(now, "user2", "dev1", "f")) 3637 filec2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", cre.ConflictRenameHelper(now, "user2", "dev1", "f")) 3638 buf, err = ioutil.ReadFile(filec1) 3639 if err != nil { 3640 t.Fatal(err) 3641 } 3642 if g, e := string(buf), input2+input4; g != e { 3643 t.Errorf("wrong content: %q != %q", g, e) 3644 } 3645 buf, err = ioutil.ReadFile(filec2) 3646 if err != nil { 3647 t.Fatal(err) 3648 } 3649 if g, e := string(buf), input2+input4; g != e { 3650 t.Errorf("wrong content: %q != %q", g, e) 3651 } 3652} 3653 3654func TestSimpleCRConflictOnOpenMergedFile(t *testing.T) { 3655 ctx := libcontext.BackgroundContextWithCancellationDelayer() 3656 defer testCleanupDelayer(ctx, t) 3657 config1 := libkbfs.MakeTestConfigOrBust(t, "user1", 3658 "user2") 3659 mnt1, fs1, cancelFn1 := makeFS(ctx, t, config1) 3660 defer mnt1.Close() 3661 defer cancelFn1() 3662 defer libkbfs.CheckConfigAndShutdown(ctx, t, config1) 3663 3664 config2 := libkbfs.ConfigAsUser(config1, "user2") 3665 mnt2, fs2, cancelFn2 := makeFS(ctx, t, config2) 3666 defer mnt2.Close() 3667 defer cancelFn2() 3668 defer libkbfs.CheckConfigAndShutdown(ctx, t, config2) 3669 3670 if !mnt2.Conn.Protocol().HasInvalidate() { 3671 t.Skip("Old FUSE protocol") 3672 } 3673 3674 now := time.Now() 3675 var clock clocktest.TestClock 3676 clock.Set(now) 3677 config2.SetClock(&clock) 3678 3679 root1 := path.Join(mnt1.Dir, PrivateName, "user1,user2") 3680 root2 := path.Join(mnt2.Dir, PrivateName, "user1,user2") 3681 // both users should mutate the dir first 3682 d1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "D") 3683 d2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", "E") 3684 if err := ioutil.Mkdir(d1, 0755); err != nil { 3685 t.Fatal("Mkdir failed") 3686 } 3687 syncFolderToServer(t, "user1,user2", fs2) 3688 if err := ioutil.Mkdir(d2, 0755); err != nil { 3689 t.Fatal("Mkdir failed") 3690 } 3691 syncFolderToServer(t, "user1,user2", fs1) 3692 3693 // disable updates for user 2 3694 disableUpdatesFile := path.Join(mnt2.Dir, PrivateName, "user1,user2", 3695 libfs.DisableUpdatesFileName) 3696 if err := ioutil.WriteFile(disableUpdatesFile, 3697 []byte("off"), 0644); err != nil { 3698 t.Fatal(err) 3699 } 3700 3701 // user1 creates and writes a file 3702 file1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "f") 3703 f1, err := os.Create(file1) 3704 if err != nil { 3705 t.Fatal(err) 3706 } 3707 defer syncAndClose(t, f1) 3708 3709 const input1 = "hello" 3710 { 3711 n, err := f1.WriteAt([]byte(input1), 0) 3712 if err != nil || n != len(input1) { 3713 t.Fatal(err) 3714 } 3715 if err := f1.Sync(); err != nil { 3716 t.Fatal(err) 3717 } 3718 } 3719 3720 // user2 creates a directory and writes a file to it 3721 dir2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", "f") 3722 if err := ioutil.Mkdir(dir2, 0755); err != nil { 3723 t.Fatal(err) 3724 } 3725 file2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", "f", "foo") 3726 f2, err := os.Create(file2) 3727 if err != nil { 3728 t.Fatal(err) 3729 } 3730 defer syncAndClose(t, f2) 3731 3732 const input2 = "ohell" 3733 { 3734 n, err := f2.WriteAt([]byte(input2), 0) 3735 if err != nil || n != len(input2) { 3736 t.Fatal(err) 3737 } 3738 if err := f2.Sync(); err != nil { 3739 t.Fatal(err) 3740 } 3741 } 3742 3743 // now re-enable user 2 updates and CR, and the merge should happen 3744 enableUpdatesFile := path.Join(mnt2.Dir, PrivateName, "user1,user2", 3745 libfs.EnableUpdatesFileName) 3746 if err := ioutil.WriteFile(enableUpdatesFile, 3747 []byte("on"), 0644); err != nil { 3748 t.Fatal(err) 3749 } 3750 3751 syncFolderToServer(t, "user1,user2", fs2) 3752 syncFolderToServer(t, "user1,user2", fs1) 3753 3754 // They should both be able to read their past writes. 3755 { 3756 buf := make([]byte, len(input1)) 3757 n, err := f1.ReadAt(buf, 0) 3758 if err != nil || n != len(input1) { 3759 t.Fatal(err) 3760 } 3761 if g, e := string(buf), input1; g != e { 3762 t.Errorf("Unexpected read on f2: %s vs %s", g, e) 3763 } 3764 } 3765 { 3766 buf := make([]byte, len(input2)) 3767 n, err := f2.ReadAt(buf, 0) 3768 if err != nil || n != len(input2) { 3769 t.Fatal(err) 3770 } 3771 if g, e := string(buf), input2; g != e { 3772 t.Errorf("Unexpected read on f2: %s vs %s", g, e) 3773 } 3774 } 3775 3776 // They should see the conflict. 3777 cre := libkbfs.WriterDeviceDateConflictRenamer{} 3778 fcr := cre.ConflictRenameHelper(now, "user1", "dev1", "f") 3779 checkDir(t, root1, map[string]fileInfoCheck{ 3780 fcr: func(fi os.FileInfo) error { 3781 return mustBeFileWithSize(fi, int64(len(input1))) 3782 }, 3783 "f": mustBeDir, 3784 "D": mustBeDir, 3785 "E": mustBeDir, 3786 }) 3787 checkDir(t, root2, map[string]fileInfoCheck{ 3788 fcr: func(fi os.FileInfo) error { 3789 return mustBeFileWithSize(fi, int64(len(input1))) 3790 }, 3791 "f": mustBeDir, 3792 "D": mustBeDir, 3793 "E": mustBeDir, 3794 }) 3795 3796 input3 := " world" 3797 { 3798 n, err := f1.WriteAt([]byte(input3), int64(len(input1))) 3799 if err != nil || n != len(input3) { 3800 t.Fatal(err) 3801 } 3802 if err := f1.Sync(); err != nil { 3803 t.Fatal(err) 3804 } 3805 } 3806 3807 syncFolderToServer(t, "user1,user2", fs2) 3808 3809 input4 := " dlrow" 3810 { 3811 n, err := f2.WriteAt([]byte(input4), int64(len(input2))) 3812 if err != nil || n != len(input4) { 3813 t.Fatal(err) 3814 } 3815 if err := f2.Sync(); err != nil { 3816 t.Fatal(err) 3817 } 3818 } 3819 3820 syncFolderToServer(t, "user1,user2", fs1) 3821 3822 file2u1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "f", "foo") 3823 buf, err := ioutil.ReadFile(file2u1) 3824 if err != nil { 3825 t.Fatal(err) 3826 } 3827 if g, e := string(buf), input2+input4; g != e { 3828 t.Errorf("wrong content: %q != %q", g, e) 3829 } 3830 buf, err = ioutil.ReadFile(file2) 3831 if err != nil { 3832 t.Fatal(err) 3833 } 3834 if g, e := string(buf), input2+input4; g != e { 3835 t.Errorf("wrong content: %q != %q", g, e) 3836 } 3837 3838 filec1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", fcr) 3839 filec2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", fcr) 3840 buf, err = ioutil.ReadFile(filec1) 3841 if err != nil { 3842 t.Fatal(err) 3843 } 3844 if g, e := string(buf), input1+input3; g != e { 3845 t.Errorf("wrong content: %q != %q", g, e) 3846 } 3847 buf, err = ioutil.ReadFile(filec2) 3848 if err != nil { 3849 t.Fatal(err) 3850 } 3851 if g, e := string(buf), input1+input3; g != e { 3852 t.Errorf("wrong content: %q != %q", g, e) 3853 } 3854} 3855 3856func TestKbfsFileInfo(t *testing.T) { 3857 ctx := libcontext.BackgroundContextWithCancellationDelayer() 3858 defer testCleanupDelayer(ctx, t) 3859 config1 := libkbfs.MakeTestConfigOrBust(t, "user1", "user2") 3860 mnt1, _, cancelFn1 := makeFS(ctx, t, config1) 3861 defer mnt1.Close() 3862 defer cancelFn1() 3863 defer libkbfs.CheckConfigAndShutdown(ctx, t, config1) 3864 3865 config2 := libkbfs.ConfigAsUser(config1, "user2") 3866 mnt2, fs2, cancelFn2 := makeFS(ctx, t, config2) 3867 defer mnt2.Close() 3868 defer cancelFn2() 3869 defer libkbfs.CheckConfigAndShutdown(ctx, t, config2) 3870 3871 // Turn off the prefetcher to avoid races when reading the file info file. 3872 ch := config2.BlockOps().TogglePrefetcher(false) 3873 select { 3874 case <-ch: 3875 case <-ctx.Done(): 3876 t.Fatal(ctx.Err()) 3877 } 3878 3879 mydir1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "mydir") 3880 if err := ioutil.Mkdir(mydir1, 0755); err != nil { 3881 t.Fatal(err) 3882 } 3883 myfile1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "mydir", "myfile") 3884 if err := ioutil.WriteFile(myfile1, []byte("foo"), 0644); err != nil { 3885 t.Fatal(err) 3886 } 3887 syncFilename(t, myfile1) 3888 syncFolderToServer(t, "user1,user2", fs2) 3889 fi2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", "mydir", libfs.FileInfoPrefix+"myfile") 3890 bs, err := ioutil.ReadFile(fi2) 3891 if err != nil { 3892 t.Fatal(err) 3893 } 3894 var dst libkbfs.NodeMetadata 3895 err = json.Unmarshal(bs, &dst) 3896 if err != nil { 3897 t.Fatal(err) 3898 } 3899 if dst.LastWriterUnverified != kbname.NormalizedUsername("user1") { 3900 t.Fatalf("Expected user1, %v raw %X", dst, bs) 3901 } 3902} 3903 3904func TestDirSyncAll(t *testing.T) { 3905 ctx := libcontext.BackgroundContextWithCancellationDelayer() 3906 defer testCleanupDelayer(ctx, t) 3907 config1 := libkbfs.MakeTestConfigOrBust(t, "user1", "user2") 3908 mnt1, _, cancelFn1 := makeFS(ctx, t, config1) 3909 defer mnt1.Close() 3910 defer cancelFn1() 3911 defer libkbfs.CheckConfigAndShutdown(ctx, t, config1) 3912 3913 config2 := libkbfs.ConfigAsUser(config1, "user2") 3914 mnt2, fs2, cancelFn2 := makeFS(ctx, t, config2) 3915 defer mnt2.Close() 3916 defer cancelFn2() 3917 defer libkbfs.CheckConfigAndShutdown(ctx, t, config2) 3918 3919 mydir1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "mydir") 3920 if err := ioutil.Mkdir(mydir1, 0755); err != nil { 3921 t.Fatal(err) 3922 } 3923 myfile1 := path.Join(mnt1.Dir, PrivateName, "user1,user2", "mydir", "myfile") 3924 data := []byte("foo") 3925 if err := ioutil.WriteFile(myfile1, data, 0644); err != nil { 3926 t.Fatal(err) 3927 } 3928 3929 d, err := os.Open(mydir1) 3930 if err != nil { 3931 t.Fatal(err) 3932 } 3933 defer d.Close() 3934 err = d.Sync() 3935 if err != nil { 3936 t.Fatal(err) 3937 } 3938 3939 syncFolderToServer(t, "user1,user2", fs2) 3940 myfile2 := path.Join(mnt2.Dir, PrivateName, "user1,user2", "mydir", "myfile") 3941 gotData, err := ioutil.ReadFile(myfile2) 3942 if err != nil { 3943 t.Fatal(err) 3944 } 3945 if !bytes.Equal(data, gotData) { 3946 t.Fatalf("Expected=%v, got=%v", data, gotData) 3947 } 3948} 3949 3950// Regression test for KBFS-2853. 3951func TestInodes(t *testing.T) { 3952 ctx := libcontext.BackgroundContextWithCancellationDelayer() 3953 defer testCleanupDelayer(ctx, t) 3954 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 3955 mnt, _, cancelFn := makeFS(ctx, t, config) 3956 defer mnt.Close() 3957 defer cancelFn() 3958 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 3959 3960 p := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile") 3961 if err := ioutil.WriteFile(p, []byte("fake binary"), 0755); err != nil { 3962 t.Fatal(err) 3963 } 3964 syncFilename(t, p) 3965 3966 getInode := func(p string) uint64 { 3967 fi, err := ioutil.Lstat(p) 3968 if err != nil { 3969 t.Fatal(err) 3970 } 3971 stat, ok := fi.Sys().(*syscall.Stat_t) 3972 if !ok { 3973 t.Fatalf("Not a syscall.Stat_t") 3974 } 3975 return stat.Ino 3976 } 3977 inode := getInode(p) 3978 3979 t.Log("Rename file and make sure inode hasn't changed.") 3980 p2 := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile2") 3981 if err := ioutil.Rename(p, p2); err != nil { 3982 t.Fatal(err) 3983 } 3984 syncFilename(t, p2) 3985 3986 inode2 := getInode(p2) 3987 if inode != inode2 { 3988 t.Fatalf("Inode changed after rename: %d vs %d", inode, inode2) 3989 } 3990 3991 t.Log("A new file with the previous name should get a new inode") 3992 3993 if err := ioutil.WriteFile(p, []byte("more fake data"), 0755); err != nil { 3994 t.Fatal(err) 3995 } 3996 syncFilename(t, p) 3997 3998 inode3 := getInode(p) 3999 if inode == inode3 { 4000 t.Fatal("New and old files have the same inode") 4001 } 4002} 4003 4004func TestHardLinkNotSupported(t *testing.T) { 4005 ctx := libcontext.BackgroundContextWithCancellationDelayer() 4006 defer testCleanupDelayer(ctx, t) 4007 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 4008 mnt, _, cancelFn := makeFS(ctx, t, config) 4009 defer mnt.Close() 4010 defer cancelFn() 4011 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 4012 4013 checkLinkErr := func(old, new string, checkPermErr bool) { 4014 err := os.Link(old, new) 4015 linkErr, ok := errors.Cause(err).(*os.LinkError) 4016 require.True(t, ok) 4017 if checkPermErr && runtime.GOOS == "darwin" { 4018 // On macOS, in directories without the write bit set like 4019 // /keybase and /keybase/private, the `Link` call gets an 4020 // `EPERM` error back from the `Access()` Fuse request, 4021 // and never even tries calling `Link()`. 4022 require.Equal(t, syscall.EPERM, linkErr.Err) 4023 } else { 4024 require.Equal(t, syscall.ENOTSUP, linkErr.Err) 4025 } 4026 } 4027 4028 t.Log("Test hardlink in root of TLF") 4029 old := path.Join(mnt.Dir, PrivateName, "jdoe", "myfile") 4030 err := ioutil.WriteFile(old, []byte("hello"), 0755) 4031 require.NoError(t, err) 4032 syncFilename(t, old) 4033 new := path.Join(mnt.Dir, PrivateName, "jdoe", "hardlink") 4034 checkLinkErr(old, new, false) 4035 4036 t.Log("Test hardlink in subdir of TLF") 4037 mydir := path.Join(mnt.Dir, PrivateName, "jdoe", "mydir") 4038 err = ioutil.Mkdir(mydir, 0755) 4039 require.NoError(t, err) 4040 old2 := path.Join(mydir, "myfile") 4041 err = ioutil.WriteFile(old2, []byte("hello"), 0755) 4042 require.NoError(t, err) 4043 syncFilename(t, old2) 4044 new2 := path.Join(mydir, "hardlink") 4045 checkLinkErr(old2, new2, false) 4046 4047 t.Log("Test hardlink in folder list") 4048 old3 := path.Join(mnt.Dir, PrivateName, ".kbfs_status") 4049 new3 := path.Join(mnt.Dir, PrivateName, "hardlink") 4050 checkLinkErr(old3, new3, true) 4051 4052 t.Log("Test hardlink in root") 4053 old4 := path.Join(mnt.Dir, ".kbfs_status") 4054 new4 := path.Join(mnt.Dir, "hardlink") 4055 checkLinkErr(old4, new4, true) 4056} 4057 4058func TestOpenFileCount(t *testing.T) { 4059 ctx := libcontext.BackgroundContextWithCancellationDelayer() 4060 defer testCleanupDelayer(ctx, t) 4061 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 4062 mnt, _, cancelFn := makeFS(ctx, t, config) 4063 defer mnt.Close() 4064 defer cancelFn() 4065 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 4066 4067 p := path.Join(mnt.Dir, libfs.OpenFileCountFileName) 4068 checkCount := func(expected int64) { 4069 f, err := os.Open(p) 4070 require.NoError(t, err) 4071 defer f.Close() 4072 4073 b, err := ioutil.ReadAll(f) 4074 require.NoError(t, err) 4075 4076 i, err := strconv.ParseInt(string(b), 10, 64) 4077 require.NoError(t, err) 4078 require.Equal(t, expected, i) 4079 } 4080 4081 checkCount(0) 4082 4083 _, err := ioutil.Lstat(path.Join(mnt.Dir, PrivateName)) 4084 require.NoError(t, err) 4085 checkCount(1) 4086 4087 _, err = ioutil.Lstat(path.Join(mnt.Dir, PublicName)) 4088 require.NoError(t, err) 4089 checkCount(2) 4090 4091 _, err = ioutil.Lstat(path.Join(mnt.Dir, PrivateName)) 4092 require.NoError(t, err) 4093 checkCount(2) 4094 4095 err = ioutil.Mkdir( 4096 path.Join(mnt.Dir, PrivateName, "jdoe", "d"), os.ModeDir) 4097 require.NoError(t, err) 4098 checkCount(4) 4099} 4100 4101func TestUpdateHistoryFile(t *testing.T) { 4102 ctx := libcontext.BackgroundContextWithCancellationDelayer() 4103 defer testCleanupDelayer(ctx, t) 4104 config := libkbfs.MakeTestConfigOrBust(t, "jdoe") 4105 mnt, _, cancelFn := makeFS(ctx, t, config) 4106 defer mnt.Close() 4107 defer cancelFn() 4108 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 4109 4110 libfs.AddRootWrapper(config) 4111 4112 t.Log("Make several revisions") 4113 p := path.Join(mnt.Dir, PrivateName, "jdoe") 4114 for i := 0; i < 10; i++ { 4115 file := path.Join(p, fmt.Sprintf("foo-%d", i)) 4116 f, err := os.Create(file) 4117 require.NoError(t, err) 4118 syncAndClose(t, f) 4119 } 4120 4121 t.Log("Read a revision range") 4122 histPrefix := path.Join(p, libfs.UpdateHistoryFileName) 4123 fRange, err := os.Open(histPrefix + ".3-5") 4124 require.NoError(t, err) 4125 defer fRange.Close() 4126 b, err := ioutil.ReadAll(fRange) 4127 require.NoError(t, err) 4128 var histRange libkbfs.TLFUpdateHistory 4129 err = json.Unmarshal(b, &histRange) 4130 require.NoError(t, err) 4131 require.Len(t, histRange.Updates, 3) 4132 4133 t.Log("Read a single revision") 4134 fSingle, err := os.Open(histPrefix + ".7") 4135 require.NoError(t, err) 4136 defer fSingle.Close() 4137 b, err = ioutil.ReadAll(fSingle) 4138 require.NoError(t, err) 4139 var histSingle libkbfs.TLFUpdateHistory 4140 err = json.Unmarshal(b, &histSingle) 4141 require.NoError(t, err) 4142 require.Len(t, histSingle.Updates, 1) 4143 4144 t.Log("Read the entire history") 4145 fAll, err := os.Open(histPrefix) 4146 require.NoError(t, err) 4147 defer fAll.Close() 4148 b, err = ioutil.ReadAll(fAll) 4149 require.NoError(t, err) 4150 var histAll libkbfs.TLFUpdateHistory 4151 err = json.Unmarshal(b, &histAll) 4152 require.NoError(t, err) 4153 require.Len(t, histAll.Updates, 11) 4154} 4155