github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/libkbfs/kbfs_cr_test.go (about) 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 package libkbfs 6 7 import ( 8 "fmt" 9 "os" 10 "sync" 11 "testing" 12 "time" 13 14 "github.com/keybase/client/go/kbfs/data" 15 "github.com/keybase/client/go/kbfs/ioutil" 16 "github.com/keybase/client/go/kbfs/kbfsmd" 17 "github.com/keybase/client/go/kbfs/libcontext" 18 "github.com/keybase/client/go/kbfs/test/clocktest" 19 "github.com/keybase/client/go/kbfs/tlf" 20 "github.com/keybase/client/go/kbfs/tlfhandle" 21 kbname "github.com/keybase/client/go/kbun" 22 "github.com/stretchr/testify/assert" 23 "github.com/stretchr/testify/require" 24 "golang.org/x/net/context" 25 ) 26 27 func readAndCompareData(ctx context.Context, t *testing.T, config Config, 28 name string, expectedData []byte, user kbname.NormalizedUsername) { 29 rootNode := GetRootNodeOrBust(ctx, t, config, name, tlf.Private) 30 31 kbfsOps := config.KBFSOps() 32 fileNode, _, err := kbfsOps.Lookup(ctx, rootNode, testPPS("a")) 33 require.NoError(t, err) 34 data := make([]byte, 1) 35 _, err = kbfsOps.Read(ctx, fileNode, data, 0) 36 require.NoError(t, err) 37 assert.Equal(t, expectedData[0], data[0]) 38 } 39 40 type testCRObserver struct { 41 c chan<- struct{} 42 changes []NodeChange 43 } 44 45 func (t *testCRObserver) LocalChange(ctx context.Context, node Node, 46 write WriteRange) { 47 // ignore 48 } 49 50 func (t *testCRObserver) BatchChanges(ctx context.Context, 51 changes []NodeChange, _ []NodeID) { 52 t.changes = append(t.changes, changes...) 53 if len(changes) > 0 { 54 t.c <- struct{}{} 55 } 56 } 57 58 func (t *testCRObserver) TlfHandleChange(ctx context.Context, 59 newHandle *tlfhandle.Handle) { 60 } 61 62 func checkStatus(ctx context.Context, t *testing.T, kbfsOps KBFSOps, 63 staged bool, headWriter kbname.NormalizedUsername, dirtyPaths []string, fb data.FolderBranch, 64 prefix string) { 65 status, _, err := kbfsOps.FolderStatus(ctx, fb) 66 require.NoError(t, err) 67 assert.Equal(t, status.Staged, staged) 68 assert.Equal(t, status.HeadWriter, headWriter) 69 checkStringSlices(t, dirtyPaths, status.DirtyPaths) 70 } 71 72 func TestBasicMDUpdate(t *testing.T) { 73 // simulate two users 74 var userName1, userName2 kbname.NormalizedUsername = "u1", "u2" 75 config1, _, ctx, cancel := kbfsOpsConcurInit(t, userName1, userName2) 76 defer kbfsConcurTestShutdown(ctx, t, config1, cancel) 77 78 config2 := ConfigAsUser(config1, userName2) 79 defer CheckConfigAndShutdown(ctx, t, config2) 80 81 name := userName1.String() + "," + userName2.String() 82 83 rootNode1 := GetRootNodeOrBust(ctx, t, config1, name, tlf.Private) 84 rootNode2 := GetRootNodeOrBust(ctx, t, config2, name, tlf.Private) 85 86 kbfsOps2 := config2.KBFSOps() 87 _, statusChan, err := kbfsOps2.FolderStatus(ctx, rootNode2.GetFolderBranch()) 88 require.NoError(t, err) 89 90 // user 1 creates a file 91 kbfsOps1 := config1.KBFSOps() 92 _, _, err = kbfsOps1.CreateFile(ctx, rootNode1, testPPS("a"), false, NoExcl) 93 require.NoError(t, err) 94 err = kbfsOps1.SyncAll(ctx, rootNode1.GetFolderBranch()) 95 require.NoError(t, err) 96 97 err = kbfsOps2.SyncFromServer(ctx, 98 rootNode2.GetFolderBranch(), nil) 99 require.NoError(t, err) 100 101 entries, err := kbfsOps2.GetDirChildren(ctx, rootNode2) 102 require.NoError(t, err) 103 require.Equal(t, 1, len(entries)) 104 _, ok := entries[rootNode2.ChildName("a")] 105 require.True(t, ok) 106 107 // The status should have fired as well (though in this case the 108 // writer is the same as before) 109 <-statusChan 110 checkStatus(ctx, t, kbfsOps1, false, userName1, nil, 111 rootNode1.GetFolderBranch(), "Node 1") 112 checkStatus(ctx, t, kbfsOps2, false, userName1, nil, 113 rootNode2.GetFolderBranch(), "Node 2") 114 } 115 116 func testMultipleMDUpdates(t *testing.T, unembedChanges bool) { 117 // simulate two users 118 var userName1, userName2 kbname.NormalizedUsername = "u1", "u2" 119 config1, _, ctx, cancel := kbfsOpsConcurInit(t, userName1, userName2) 120 defer kbfsConcurTestShutdown(ctx, t, config1, cancel) 121 122 config2 := ConfigAsUser(config1, userName2) 123 defer CheckConfigAndShutdown(ctx, t, config2) 124 125 if unembedChanges { 126 bss1, ok1 := config1.BlockSplitter().(*data.BlockSplitterSimple) 127 require.True(t, ok1) 128 bss2, ok2 := config2.BlockSplitter().(*data.BlockSplitterSimple) 129 require.True(t, ok2) 130 bss1.SetBlockChangeEmbedMaxSizeForTesting(3) 131 bss2.SetBlockChangeEmbedMaxSizeForTesting(3) 132 } 133 134 name := userName1.String() + "," + userName2.String() 135 136 rootNode1 := GetRootNodeOrBust(ctx, t, config1, name, tlf.Private) 137 138 kbfsOps1 := config1.KBFSOps() 139 // user 1 creates a file 140 _, _, err := kbfsOps1.CreateFile( 141 ctx, rootNode1, testPPS("a"), false, NoExcl) 142 require.NoError(t, err) 143 err = kbfsOps1.SyncAll(ctx, rootNode1.GetFolderBranch()) 144 require.NoError(t, err) 145 146 // user 2 looks up the directory (and sees the file) 147 rootNode2 := GetRootNodeOrBust(ctx, t, config2, name, tlf.Private) 148 149 // now user 1 renames the old file, and creates a new one 150 err = kbfsOps1.Rename(ctx, rootNode1, testPPS("a"), rootNode1, testPPS("b")) 151 require.NoError(t, err) 152 _, _, err = kbfsOps1.CreateFile(ctx, rootNode1, testPPS("c"), false, NoExcl) 153 require.NoError(t, err) 154 err = kbfsOps1.SyncAll(ctx, rootNode1.GetFolderBranch()) 155 require.NoError(t, err) 156 157 kbfsOps2 := config2.KBFSOps() 158 err = kbfsOps2.SyncFromServer(ctx, 159 rootNode2.GetFolderBranch(), nil) 160 require.NoError(t, err) 161 162 entries, err := kbfsOps2.GetDirChildren(ctx, rootNode2) 163 require.NoError(t, err) 164 require.Equal(t, 2, len(entries)) 165 _, ok := entries[rootNode2.ChildName("b")] 166 require.True(t, ok) 167 _, ok = entries[rootNode2.ChildName("c")] 168 require.True(t, ok) 169 } 170 171 func TestMultipleMDUpdates(t *testing.T) { 172 testMultipleMDUpdates(t, false) 173 } 174 175 func TestMultipleMDUpdatesUnembedChanges(t *testing.T) { 176 testMultipleMDUpdates(t, true) 177 } 178 179 func TestGetTLFCryptKeysWhileUnmergedAfterRestart(t *testing.T) { 180 tempdir, err := ioutil.TempDir(os.TempDir(), "journal_for_gettlfcryptkeys") 181 require.NoError(t, err) 182 defer func() { 183 err := ioutil.RemoveAll(tempdir) 184 assert.NoError(t, err) 185 }() 186 187 var userName1, userName2 kbname.NormalizedUsername = "u1", "u2" 188 config1, _, ctx, cancel := kbfsOpsConcurInit(t, userName1, userName2) 189 defer kbfsConcurTestShutdown(ctx, t, config1, cancel) 190 191 // enable journaling to see patrick's error 192 err = config1.EnableDiskLimiter(tempdir) 193 require.NoError(t, err) 194 err = config1.EnableJournaling( 195 ctx, tempdir, TLFJournalBackgroundWorkEnabled) 196 require.NoError(t, err) 197 jManager, err := GetJournalManager(config1) 198 require.NoError(t, err) 199 jManager.onBranchChange = nil 200 jManager.onMDFlush = nil 201 err = jManager.EnableAuto(ctx) 202 require.NoError(t, err) 203 204 config2 := ConfigAsUser(config1, userName2) 205 defer CheckConfigAndShutdown(ctx, t, config2) 206 name := userName1.String() + "," + userName2.String() 207 208 // user1 creates a file in a shared dir 209 rootNode1 := GetRootNodeOrBust(ctx, t, config1, name, tlf.Private) 210 211 kbfsOps1 := config1.KBFSOps() 212 fileNode1, _, err := kbfsOps1.CreateFile( 213 ctx, rootNode1, testPPS("a"), false, NoExcl) 214 require.NoError(t, err) 215 err = kbfsOps1.SyncAll(ctx, rootNode1.GetFolderBranch()) 216 require.NoError(t, err) 217 218 _, err = DisableUpdatesForTesting(config1, rootNode1.GetFolderBranch()) 219 require.NoError(t, err) 220 err = DisableCRForTesting(config1, rootNode1.GetFolderBranch()) 221 require.NoError(t, err) 222 223 // Wait for "a" to flush to the server. 224 err = jManager.Wait(ctx, rootNode1.GetFolderBranch().Tlf) 225 require.NoError(t, err) 226 227 // then user2 write to the file 228 rootNode2 := GetRootNodeOrBust(ctx, t, config2, name, tlf.Private) 229 230 kbfsOps2 := config2.KBFSOps() 231 fileNode2, _, err := kbfsOps2.Lookup(ctx, rootNode2, testPPS("a")) 232 require.NoError(t, err) 233 data2 := []byte{2} 234 err = kbfsOps2.Write(ctx, fileNode2, data2, 0) 235 require.NoError(t, err) 236 checkStatus(ctx, t, kbfsOps2, false, userName1, []string{"u1,u2/a"}, 237 rootNode2.GetFolderBranch(), "Node 2 (after write)") 238 err = kbfsOps2.SyncAll(ctx, fileNode2.GetFolderBranch()) 239 require.NoError(t, err) 240 241 // Now when user 1 tries to write to file 1 and sync, it will 242 // become unmerged. 243 data1 := []byte{1} 244 err = kbfsOps1.Write(ctx, fileNode1, data1, 0) 245 require.NoError(t, err) 246 // sync the file from u1 so that we get a clean exit state 247 err = kbfsOps1.SyncAll(ctx, fileNode1.GetFolderBranch()) 248 require.NoError(t, err) 249 250 // Wait for the conflict to be detected. 251 err = jManager.Wait(ctx, rootNode1.GetFolderBranch().Tlf) 252 require.NoError(t, err) 253 254 // now re-login u1 255 config1B := ConfigAsUser(config1, userName1) 256 err = config1B.EnableDiskLimiter(tempdir) 257 require.NoError(t, err) 258 defer CheckConfigAndShutdown(ctx, t, config1B) 259 err = config1B.EnableJournaling( 260 ctx, tempdir, TLFJournalBackgroundWorkEnabled) 261 require.NoError(t, err) 262 jManager, err = GetJournalManager(config1B) 263 require.NoError(t, err) 264 jManager.onBranchChange = nil 265 jManager.onMDFlush = nil 266 267 err = DisableCRForTesting(config1B, rootNode1.GetFolderBranch()) 268 require.NoError(t, err) 269 270 tlfHandle, err := tlfhandle.ParseHandle( 271 ctx, config1B.KBPKI(), config1B.MDOps(), nil, name, tlf.Private) 272 require.NoError(t, err) 273 274 _, _, err = config1B.KBFSOps().GetTLFCryptKeys(ctx, tlfHandle) 275 require.NoError(t, err) 276 } 277 278 // Tests that, in the face of a conflict, a user will commit its 279 // changes to a private branch, which will persist after restart (and 280 // the other user will be unaffected). 281 func TestUnmergedAfterRestart(t *testing.T) { 282 // simulate two users 283 var userName1, userName2 kbname.NormalizedUsername = "u1", "u2" 284 config1, _, ctx, cancel := kbfsOpsConcurInit(t, userName1, userName2) 285 defer kbfsConcurTestShutdown(ctx, t, config1, cancel) 286 287 config2 := ConfigAsUser(config1, userName2) 288 defer CheckConfigAndShutdown(ctx, t, config2) 289 290 name := userName1.String() + "," + userName2.String() 291 292 // user1 creates a file in a shared dir 293 rootNode1 := GetRootNodeOrBust(ctx, t, config1, name, tlf.Private) 294 295 kbfsOps1 := config1.KBFSOps() 296 fileNode1, _, err := kbfsOps1.CreateFile( 297 ctx, rootNode1, testPPS("a"), false, NoExcl) 298 require.NoError(t, err) 299 err = kbfsOps1.SyncAll(ctx, rootNode1.GetFolderBranch()) 300 require.NoError(t, err) 301 302 _, err = DisableUpdatesForTesting(config1, rootNode1.GetFolderBranch()) 303 require.NoError(t, err) 304 err = DisableCRForTesting(config1, rootNode1.GetFolderBranch()) 305 require.NoError(t, err) 306 307 // then user2 write to the file 308 rootNode2 := GetRootNodeOrBust(ctx, t, config2, name, tlf.Private) 309 310 kbfsOps2 := config2.KBFSOps() 311 fileNode2, _, err := kbfsOps2.Lookup(ctx, rootNode2, testPPS("a")) 312 require.NoError(t, err) 313 data2 := []byte{2} 314 err = kbfsOps2.Write(ctx, fileNode2, data2, 0) 315 require.NoError(t, err) 316 checkStatus(ctx, t, kbfsOps2, false, userName1, []string{"u1,u2/a"}, 317 rootNode2.GetFolderBranch(), "Node 2 (after write)") 318 err = kbfsOps2.SyncAll(ctx, fileNode2.GetFolderBranch()) 319 require.NoError(t, err) 320 321 // Now when user 1 tries to write to file 1 and sync, it will 322 // become unmerged. Because this happens in the same goroutine as 323 // the above Sync, we can be sure that the updater on client 1 324 // hasn't yet seen the MD update, and so its Sync will present a 325 // conflict. 326 data1 := []byte{1} 327 err = kbfsOps1.Write(ctx, fileNode1, data1, 0) 328 require.NoError(t, err) 329 checkStatus(ctx, t, kbfsOps1, false, userName1, []string{"u1,u2/a"}, 330 rootNode1.GetFolderBranch(), "Node 1 (after write)") 331 err = kbfsOps1.SyncAll(ctx, fileNode1.GetFolderBranch()) 332 require.NoError(t, err) 333 334 checkStatus(ctx, t, kbfsOps1, true, userName1, nil, 335 rootNode1.GetFolderBranch(), "Node 1") 336 checkStatus(ctx, t, kbfsOps2, false, userName2, nil, 337 rootNode2.GetFolderBranch(), "Node 2") 338 339 // now re-login the users, and make sure 1 can see the changes, 340 // but 2 can't 341 config1B := ConfigAsUser(config1, userName1) 342 defer CheckConfigAndShutdown(ctx, t, config1B) 343 config2B := ConfigAsUser(config1, userName2) 344 defer CheckConfigAndShutdown(ctx, t, config2B) 345 346 err = DisableCRForTesting(config1B, rootNode1.GetFolderBranch()) 347 require.NoError(t, err) 348 349 // Keep the config1B node in memory, so it doesn't get garbage 350 // collected (preventing notifications) 351 rootNode1B := GetRootNodeOrBust(ctx, t, config1B, name, tlf.Private) 352 353 kbfsOps1B := config1B.KBFSOps() 354 fileNode1B, _, err := kbfsOps1B.Lookup(ctx, rootNode1B, testPPS("a")) 355 require.NoError(t, err) 356 357 readAndCompareData(ctx, t, config1B, name, data1, userName1) 358 readAndCompareData(ctx, t, config2B, name, data2, userName2) 359 360 checkStatus(ctx, t, config1B.KBFSOps(), true, userName1, nil, 361 fileNode1B.GetFolderBranch(), "Node 1") 362 checkStatus(ctx, t, config2B.KBFSOps(), false, userName2, nil, 363 rootNode2.GetFolderBranch(), "Node 2") 364 365 // register as a listener before the unstaging happens 366 c := make(chan struct{}, 2) 367 cro := &testCRObserver{c, nil} 368 err = config1B.Notifier().RegisterForChanges( 369 []data.FolderBranch{rootNode1B.GetFolderBranch()}, cro) 370 require.NoError(t, err) 371 372 ops1B := getOps(config1B, fileNode1B.GetFolderBranch().Tlf) 373 ops2B := getOps(config2B, fileNode1B.GetFolderBranch().Tlf) 374 lState := makeFBOLockState() 375 require.Equal(t, ops1B.getLatestMergedRevision(lState), ops2B.getCurrMDRevision(lState)) 376 377 // Unstage user 1's changes, and make sure everyone is back in 378 // sync. TODO: remove this once we have automatic conflict 379 // resolution. 380 err = config1B.KBFSOps().UnstageForTesting(ctx, 381 rootNode1B.GetFolderBranch()) 382 require.NoError(t, err) 383 384 // we should have had at least two updates, one for the unstaging and one 385 // for the fast-forward 386 select { 387 case <-c: 388 default: 389 t.Fatal("No update!") 390 } 391 select { 392 case <-c: 393 default: 394 t.Fatal("No 2nd update!") 395 } 396 // make sure we see two sync op changes, on the same node 397 assert.Equal(t, 2, len(cro.changes)) 398 var n Node 399 for _, change := range cro.changes { 400 if n == nil { 401 n = change.Node 402 } else { 403 assert.Equal(t, n.GetID(), change.Node.GetID()) 404 } 405 } 406 407 err = config1B.KBFSOps().SyncFromServer( 408 ctx, fileNode1B.GetFolderBranch(), nil) 409 require.NoError(t, err) 410 err = config2B.KBFSOps(). 411 SyncFromServer(ctx, 412 rootNode2.GetFolderBranch(), nil) 413 require.NoError(t, err) 414 415 readAndCompareData(ctx, t, config1B, name, data2, userName2) 416 readAndCompareData(ctx, t, config2B, name, data2, userName2) 417 checkStatus(ctx, t, config1B.KBFSOps(), false, userName1, nil, 418 rootNode1.GetFolderBranch(), "Node 1 (after unstage)") 419 checkStatus(ctx, t, config2B.KBFSOps(), false, userName1, nil, 420 rootNode2.GetFolderBranch(), "Node 2 (after unstage)") 421 } 422 423 // Tests that multiple users can write to the same file sequentially 424 // without any problems. 425 func TestMultiUserWrite(t *testing.T) { 426 // simulate two users 427 var userName1, userName2 kbname.NormalizedUsername = "u1", "u2" 428 config1, _, ctx, cancel := kbfsOpsConcurInit(t, userName1, userName2) 429 defer kbfsConcurTestShutdown(ctx, t, config1, cancel) 430 431 config2 := ConfigAsUser(config1, userName2) 432 defer CheckConfigAndShutdown(ctx, t, config2) 433 434 name := userName1.String() + "," + userName2.String() 435 436 // user1 creates a file in a shared dir 437 rootNode1 := GetRootNodeOrBust(ctx, t, config1, name, tlf.Private) 438 439 kbfsOps1 := config1.KBFSOps() 440 _, _, err := kbfsOps1.CreateFile( 441 ctx, rootNode1, testPPS("a"), false, NoExcl) 442 require.NoError(t, err) 443 err = kbfsOps1.SyncAll(ctx, rootNode1.GetFolderBranch()) 444 require.NoError(t, err) 445 446 // then user2 write to the file 447 rootNode2 := GetRootNodeOrBust(ctx, t, config2, name, tlf.Private) 448 449 kbfsOps2 := config2.KBFSOps() 450 fileNode2, _, err := kbfsOps2.Lookup(ctx, rootNode2, testPPS("a")) 451 require.NoError(t, err) 452 453 data2 := []byte{2} 454 err = kbfsOps2.Write(ctx, fileNode2, data2, 0) 455 require.NoError(t, err) 456 // Write twice to make sure that multiple write operations within 457 // a sync work when the writer is changing. 458 err = kbfsOps2.Write(ctx, fileNode2, data2, 0) 459 require.NoError(t, err) 460 err = kbfsOps2.SyncAll(ctx, fileNode2.GetFolderBranch()) 461 require.NoError(t, err) 462 readAndCompareData(ctx, t, config2, name, data2, userName2) 463 464 // A second write by the same user 465 data3 := []byte{3} 466 err = kbfsOps2.Write(ctx, fileNode2, data3, 0) 467 require.NoError(t, err) 468 err = kbfsOps2.SyncAll(ctx, fileNode2.GetFolderBranch()) 469 require.NoError(t, err) 470 471 readAndCompareData(ctx, t, config2, name, data3, userName2) 472 473 err = kbfsOps1.SyncFromServer(ctx, 474 rootNode1.GetFolderBranch(), nil) 475 require.NoError(t, err) 476 readAndCompareData(ctx, t, config1, name, data3, userName2) 477 } 478 479 func testBasicCRNoConflict(t *testing.T, unembedChanges bool) { 480 // simulate two users 481 var userName1, userName2 kbname.NormalizedUsername = "u1", "u2" 482 config1, _, ctx, cancel := kbfsOpsConcurInit(t, userName1, userName2) 483 defer kbfsConcurTestShutdown(ctx, t, config1, cancel) 484 485 config2 := ConfigAsUser(config1, userName2) 486 defer CheckConfigAndShutdown(ctx, t, config2) 487 488 if unembedChanges { 489 bss1, ok1 := config1.BlockSplitter().(*data.BlockSplitterSimple) 490 require.True(t, ok1) 491 bss2, ok2 := config2.BlockSplitter().(*data.BlockSplitterSimple) 492 require.True(t, ok2) 493 // 128 seems to be a good size that works on both 386 and x64 494 // platforms. 495 bss1.SetBlockChangeEmbedMaxSizeForTesting(128) 496 bss2.SetBlockChangeEmbedMaxSizeForTesting(128) 497 } 498 499 name := userName1.String() + "," + userName2.String() 500 501 // user1 creates a file in a shared dir 502 rootNode1 := GetRootNodeOrBust(ctx, t, config1, name, tlf.Private) 503 504 kbfsOps1 := config1.KBFSOps() 505 _, _, err := kbfsOps1.CreateFile( 506 ctx, rootNode1, testPPS("a"), false, NoExcl) 507 require.NoError(t, err) 508 err = kbfsOps1.SyncAll(ctx, rootNode1.GetFolderBranch()) 509 require.NoError(t, err) 510 511 // look it up on user2 512 rootNode2 := GetRootNodeOrBust(ctx, t, config2, name, tlf.Private) 513 514 kbfsOps2 := config2.KBFSOps() 515 _, _, err = kbfsOps2.Lookup(ctx, rootNode2, testPPS("a")) 516 require.NoError(t, err) 517 518 // disable updates on user 2 519 c, err := DisableUpdatesForTesting(config2, rootNode2.GetFolderBranch()) 520 require.NoError(t, err) 521 err = DisableCRForTesting(config2, rootNode2.GetFolderBranch()) 522 require.NoError(t, err) 523 524 // User 1 makes a new file 525 _, _, err = kbfsOps1.CreateFile( 526 ctx, rootNode1, testPPS("b"), false, NoExcl) 527 require.NoError(t, err) 528 err = kbfsOps1.SyncAll(ctx, rootNode1.GetFolderBranch()) 529 require.NoError(t, err) 530 531 // User 2 makes a new different file 532 _, _, err = kbfsOps2.CreateFile( 533 ctx, rootNode2, testPPS("c"), false, NoExcl) 534 require.NoError(t, err) 535 err = kbfsOps2.SyncAll(ctx, rootNode2.GetFolderBranch()) 536 require.NoError(t, err) 537 538 // re-enable updates, and wait for CR to complete 539 c <- struct{}{} 540 err = RestartCRForTesting( 541 libcontext.BackgroundContextWithCancellationDelayer(), config2, 542 rootNode2.GetFolderBranch()) 543 require.NoError(t, err) 544 err = kbfsOps2.SyncFromServer(ctx, 545 rootNode2.GetFolderBranch(), nil) 546 require.NoError(t, err) 547 548 err = kbfsOps1.SyncFromServer(ctx, 549 rootNode1.GetFolderBranch(), nil) 550 require.NoError(t, err) 551 552 // Make sure they both see the same set of children 553 expectedChildren := []string{"a", "b", "c"} 554 children1, err := kbfsOps1.GetDirChildren(ctx, rootNode1) 555 require.NoError(t, err) 556 557 children2, err := kbfsOps2.GetDirChildren(ctx, rootNode2) 558 require.NoError(t, err) 559 560 assert.Equal(t, len(expectedChildren), len(children1)) 561 562 for _, child := range expectedChildren { 563 _, ok := children1[rootNode1.ChildName(child)] 564 assert.True(t, ok) 565 } 566 567 require.Equal(t, children1, children2) 568 569 if unembedChanges { 570 // Make sure the MD has an unembedded change block. 571 md, err := config1.MDOps().GetForTLF(ctx, 572 rootNode1.GetFolderBranch().Tlf, nil) 573 require.NoError(t, err) 574 require.NotEqual(t, data.ZeroPtr, md.data.cachedChanges.Info.BlockPointer) 575 } 576 } 577 578 // Tests that two users can make independent writes while forked, and 579 // conflict resolution will merge them correctly. 580 func TestBasicCRNoConflict(t *testing.T) { 581 testBasicCRNoConflict(t, false) 582 } 583 584 // Tests same as above, with unembedded block changes 585 func TestBasicCRNoConflictWithUnembeddedBlockChanges(t *testing.T) { 586 testBasicCRNoConflict(t, true) 587 } 588 589 type registerForUpdateRecord struct { 590 id tlf.ID 591 currHead kbfsmd.Revision 592 } 593 594 type mdServerLocalRecordingRegisterForUpdate struct { 595 mdServerLocal 596 ch chan<- registerForUpdateRecord 597 } 598 599 // newMDServerLocalRecordingRegisterForUpdate returns a wrapper of 600 // MDServerLocal that records RegisterforUpdate calls. 601 func newMDServerLocalRecordingRegisterForUpdate(mdServerRaw mdServerLocal) ( 602 mdServer mdServerLocalRecordingRegisterForUpdate, 603 records <-chan registerForUpdateRecord) { 604 ch := make(chan registerForUpdateRecord, 8) 605 ret := mdServerLocalRecordingRegisterForUpdate{mdServerRaw, ch} 606 return ret, ch 607 } 608 609 func (md mdServerLocalRecordingRegisterForUpdate) RegisterForUpdate( 610 ctx context.Context, 611 id tlf.ID, currHead kbfsmd.Revision) (<-chan error, error) { 612 md.ch <- registerForUpdateRecord{id: id, currHead: currHead} 613 return md.mdServerLocal.RegisterForUpdate(ctx, id, currHead) 614 } 615 616 func TestCRFileConflictWithMoreUpdatesFromOneUser(t *testing.T) { 617 // simulate two users 618 var userName1, userName2 kbname.NormalizedUsername = "u1", "u2" 619 config1, _, ctx, cancel := kbfsOpsConcurInit(t, userName1, userName2) 620 defer kbfsConcurTestShutdown(ctx, t, config1, cancel) 621 622 config2 := ConfigAsUser(config1, userName2) 623 mdServ, chForMdServer2 := newMDServerLocalRecordingRegisterForUpdate( 624 config2.MDServer().(mdServerLocal)) 625 config2.SetMDServer(mdServ) 626 defer CheckConfigAndShutdown(ctx, t, config2) 627 628 name := userName1.String() + "," + userName2.String() 629 630 // user1 creates a file in a shared dir 631 rootNode1 := GetRootNodeOrBust(ctx, t, config1, name, tlf.Private) 632 633 kbfsOps1 := config1.KBFSOps() 634 dirA1, _, err := kbfsOps1.CreateDir(ctx, rootNode1, testPPS("a")) 635 require.NoError(t, err) 636 fileB1, _, err := kbfsOps1.CreateFile(ctx, dirA1, testPPS("b"), false, NoExcl) 637 require.NoError(t, err) 638 err = kbfsOps1.SyncAll(ctx, rootNode1.GetFolderBranch()) 639 require.NoError(t, err) 640 641 // look it up on user2 642 rootNode2 := GetRootNodeOrBust(ctx, t, config2, name, tlf.Private) 643 644 kbfsOps2 := config2.KBFSOps() 645 dirA2, _, err := kbfsOps2.Lookup(ctx, rootNode2, testPPS("a")) 646 require.NoError(t, err) 647 fileB2, _, err := kbfsOps2.Lookup(ctx, dirA2, testPPS("b")) 648 require.NoError(t, err) 649 650 // disable updates on user 2 651 chForEnablingUpdates, err := DisableUpdatesForTesting( 652 config2, rootNode2.GetFolderBranch()) 653 require.NoError(t, err) 654 err = DisableCRForTesting(config2, rootNode2.GetFolderBranch()) 655 require.NoError(t, err) 656 657 // User 1 writes the file 658 data := []byte{1, 2, 3, 4, 5} 659 err = kbfsOps1.Write(ctx, fileB1, data, 0) 660 require.NoError(t, err) 661 err = kbfsOps1.SyncAll(ctx, fileB1.GetFolderBranch()) 662 require.NoError(t, err) 663 664 // User 2 makes a few changes in the file 665 for i := byte(0); i < 4; i++ { 666 // This makes sure the unmerged head of user 2 is ahead of (has large 667 // revision than) the merged master branch, so we can test that we properly 668 // fetch updates when unmerged revision number is greater than merged 669 // revision number (regression in KBFS-1206). 670 671 data = []byte{1, 2, 3, 4, i} 672 err = kbfsOps2.Write(ctx, fileB2, data, 0) 673 require.NoError(t, err) 674 675 err = kbfsOps2.SyncAll(ctx, fileB2.GetFolderBranch()) 676 require.NoError(t, err) 677 } 678 679 chForEnablingUpdates <- struct{}{} 680 681 equal := false 682 683 // check for at most 4 times. This should be sufficiently long for client get 684 // latest merged revision and register with that 685 for i := 0; i < 4; i++ { 686 record := <-chForMdServer2 687 mergedRev, err := mdServ.getCurrentMergedHeadRevision( 688 ctx, rootNode2.GetFolderBranch().Tlf) 689 require.NoError(t, err) 690 if record.currHead == mergedRev { 691 equal = true 692 break 693 } 694 } 695 696 require.True(t, equal) 697 } 698 699 // Tests that two users can make independent writes while forked, and 700 // conflict resolution will merge them correctly. 701 func TestBasicCRFileConflict(t *testing.T) { 702 // simulate two users 703 var userName1, userName2 kbname.NormalizedUsername = "u1", "u2" 704 config1, _, ctx, cancel := kbfsOpsConcurInit(t, userName1, userName2) 705 defer kbfsConcurTestShutdown(ctx, t, config1, cancel) 706 707 config2 := ConfigAsUser(config1, userName2) 708 defer CheckConfigAndShutdown(ctx, t, config2) 709 710 clock, now := clocktest.NewTestClockAndTimeNow() 711 config2.SetClock(clock) 712 713 name := userName1.String() + "," + userName2.String() 714 715 // user1 creates a file in a shared dir 716 rootNode1 := GetRootNodeOrBust(ctx, t, config1, name, tlf.Private) 717 718 kbfsOps1 := config1.KBFSOps() 719 dirA1, _, err := kbfsOps1.CreateDir(ctx, rootNode1, testPPS("a")) 720 require.NoError(t, err) 721 fileB1, _, err := kbfsOps1.CreateFile( 722 ctx, dirA1, testPPS("b"), false, NoExcl) 723 require.NoError(t, err) 724 err = kbfsOps1.SyncAll(ctx, rootNode1.GetFolderBranch()) 725 require.NoError(t, err) 726 727 // look it up on user2 728 rootNode2 := GetRootNodeOrBust(ctx, t, config2, name, tlf.Private) 729 730 kbfsOps2 := config2.KBFSOps() 731 dirA2, _, err := kbfsOps2.Lookup(ctx, rootNode2, testPPS("a")) 732 require.NoError(t, err) 733 fileB2, _, err := kbfsOps2.Lookup(ctx, dirA2, testPPS("b")) 734 require.NoError(t, err) 735 736 // disable updates on user 2 737 c, err := DisableUpdatesForTesting(config2, rootNode2.GetFolderBranch()) 738 require.NoError(t, err) 739 err = DisableCRForTesting(config2, rootNode2.GetFolderBranch()) 740 require.NoError(t, err) 741 742 // User 1 writes the file 743 data1 := []byte{1, 2, 3, 4, 5} 744 err = kbfsOps1.Write(ctx, fileB1, data1, 0) 745 require.NoError(t, err) 746 err = kbfsOps1.SyncAll(ctx, fileB1.GetFolderBranch()) 747 require.NoError(t, err) 748 749 // User 2 makes a new different file 750 data2 := []byte{5, 4, 3, 2, 1} 751 err = kbfsOps2.Write(ctx, fileB2, data2, 0) 752 require.NoError(t, err) 753 err = kbfsOps2.SyncAll(ctx, fileB2.GetFolderBranch()) 754 require.NoError(t, err) 755 756 // re-enable updates, and wait for CR to complete 757 c <- struct{}{} 758 err = RestartCRForTesting( 759 libcontext.BackgroundContextWithCancellationDelayer(), config2, 760 rootNode2.GetFolderBranch()) 761 require.NoError(t, err) 762 err = kbfsOps2.SyncFromServer(ctx, 763 rootNode2.GetFolderBranch(), nil) 764 require.NoError(t, err) 765 766 err = kbfsOps1.SyncFromServer(ctx, 767 rootNode1.GetFolderBranch(), nil) 768 require.NoError(t, err) 769 770 cre := WriterDeviceDateConflictRenamer{} 771 // Make sure they both see the same set of children 772 expectedChildren := []string{ 773 "b", 774 cre.ConflictRenameHelper(now, "u2", "dev1", "b"), 775 } 776 children1, err := kbfsOps1.GetDirChildren(ctx, dirA1) 777 require.NoError(t, err) 778 779 children2, err := kbfsOps2.GetDirChildren(ctx, dirA2) 780 require.NoError(t, err) 781 782 assert.Equal(t, len(expectedChildren), len(children1)) 783 784 for _, child := range expectedChildren { 785 _, ok := children1[dirA1.ChildName(child)] 786 assert.True(t, ok) 787 } 788 789 require.Equal(t, children1, children2) 790 } 791 792 // Tests that if CR fails enough times it will stop trying, 793 // and that we can move the conflicts out of the way. 794 func TestBasicCRFailureAndFixing(t *testing.T) { 795 tempdir, err := ioutil.TempDir(os.TempDir(), "journal_for_fail_fix") 796 defer os.RemoveAll(tempdir) 797 798 // simulate two users 799 var userName1, userName2 kbname.NormalizedUsername = "u1", "u2" 800 config1, _, ctx, cancel := kbfsOpsConcurInit(t, userName1, userName2) 801 defer kbfsConcurTestShutdown(ctx, t, config1, cancel) 802 803 config2 := ConfigAsUser(config1, userName2) 804 defer CheckConfigAndShutdown(ctx, t, config2) 805 806 // Enable journaling on user 2 807 require.NoError(t, err) 808 err = config2.EnableDiskLimiter(tempdir) 809 require.NoError(t, err) 810 err = config2.EnableJournaling(ctx, tempdir, 811 TLFJournalBackgroundWorkEnabled) 812 require.NoError(t, err) 813 jManager, err := GetJournalManager(config2) 814 require.NoError(t, err) 815 err = jManager.EnableAuto(ctx) 816 require.NoError(t, err) 817 818 clock, now := clocktest.NewTestClockAndTimeNow() 819 config2.SetClock(clock) 820 821 name := userName1.String() + "," + userName2.String() 822 823 t.Log("User 1 creates a file a/b.") 824 rootNode1 := GetRootNodeOrBust(ctx, t, config1, name, tlf.Private) 825 826 kbfsOps1 := config1.KBFSOps() 827 dirA1, _, err := kbfsOps1.CreateDir(ctx, rootNode1, testPPS("a")) 828 require.NoError(t, err) 829 fileB1, _, err := kbfsOps1.CreateFile( 830 ctx, dirA1, testPPS("b"), false, NoExcl) 831 require.NoError(t, err) 832 err = kbfsOps1.SyncAll(ctx, rootNode1.GetFolderBranch()) 833 require.NoError(t, err) 834 835 t.Log("User 2 looks up file a/b.") 836 rootNode2 := GetRootNodeOrBust(ctx, t, config2, name, tlf.Private) 837 838 kbfsOps2 := config2.KBFSOps() 839 dirA2, _, err := kbfsOps2.Lookup(ctx, rootNode2, testPPS("a")) 840 require.NoError(t, err) 841 fileB2, _, err := kbfsOps2.Lookup(ctx, dirA2, testPPS("b")) 842 require.NoError(t, err) 843 844 err = SetCRFailureForTesting(ctx, config2, rootNode2.GetFolderBranch(), 845 alwaysFailCR) 846 require.NoError(t, err) 847 848 t.Log("Disable updates on user 2.") 849 c, err := DisableUpdatesForTesting(config2, rootNode2.GetFolderBranch()) 850 require.NoError(t, err) 851 err = DisableCRForTesting(config2, rootNode2.GetFolderBranch()) 852 require.NoError(t, err) 853 854 t.Log("User 1 writes to file a/b.") 855 data1 := []byte{1, 2, 3, 4, 5} 856 err = kbfsOps1.Write(ctx, fileB1, data1, 0) 857 require.NoError(t, err) 858 err = kbfsOps1.SyncAll(ctx, fileB1.GetFolderBranch()) 859 require.NoError(t, err) 860 861 t.Log("User 2 writes to file a/b without having heard user 1's update.") 862 data2 := []byte{5, 4, 3, 2, 1} 863 err = kbfsOps2.Write(ctx, fileB2, data2, 0) 864 require.NoError(t, err) 865 err = kbfsOps2.SyncAll(ctx, fileB2.GetFolderBranch()) 866 require.NoError(t, err) 867 868 t.Log("Reenable updates and wait for CR to fail.") 869 c <- struct{}{} 870 err = RestartCRForTesting( 871 libcontext.BackgroundContextWithCancellationDelayer(), config2, 872 rootNode2.GetFolderBranch()) 873 require.NoError(t, err) 874 875 t.Log("Try to SyncFromServer on user 2.") 876 err = kbfsOps2.SyncFromServer(ctx, 877 rootNode2.GetFolderBranch(), nil) 878 require.Equal(t, &ErrStillStagedAfterCR{}, err) 879 880 ops, ok := config2.KBFSOps().(*KBFSOpsStandard) 881 require.True(t, ok) 882 fbo := ops.getOpsNoAdd(ctx, rootNode2.GetFolderBranch()) 883 884 t.Log("Write a bunch more files as user 2, creating more conflicts.") 885 for i := 0; i < maxConflictResolutionAttempts; i++ { 886 fileName := fmt.Sprintf("file%d", i) 887 newFile, _, err := kbfsOps2.CreateFile( 888 ctx, dirA2, testPPS(fileName), false, NoExcl) 889 require.NoError(t, err, "Loop %d", i) 890 err = kbfsOps2.SyncAll(ctx, newFile.GetFolderBranch()) 891 require.NoError(t, err, "Loop %d", i) 892 err = fbo.cr.Wait(ctx) 893 require.NoError(t, err, "Loop %d", i) 894 } 895 896 t.Log("Check that there is conflict state in the CR DB.") 897 crdb := config2.GetConflictResolutionDB() 898 crData, err := crdb.Get(fbo.id().Bytes(), nil) 899 require.NoError(t, err) 900 require.NotZero(t, len(crData)) 901 902 t.Log("Clear the conflict state and re-enable CR.") 903 err = fbo.clearConflictView(ctx) 904 require.NoError(t, err) 905 906 err = SetCRFailureForTesting(ctx, config2, rootNode2.GetFolderBranch(), 907 doNotAlwaysFailCR) 908 require.NoError(t, err) 909 910 t.Log("Trigger CR and wait for it to resolve.") 911 dirA2, _, err = kbfsOps2.Lookup(ctx, rootNode2, testPPS("a")) 912 require.NoError(t, err) 913 _, _, err = kbfsOps2.CreateFile( 914 ctx, dirA2, testPPS("newFile"), false, NoExcl) 915 require.NoError(t, err) 916 917 err = kbfsOps2.SyncAll(ctx, dirA2.GetFolderBranch()) 918 require.NoError(t, err) 919 err = fbo.cr.Wait(ctx) 920 require.NoError(t, err) 921 922 t.Log("Verify that the conflict is resolved.") 923 err = kbfsOps2.SyncFromServer(ctx, 924 rootNode2.GetFolderBranch(), nil) 925 require.NoError(t, err) 926 927 err = kbfsOps1.SyncFromServer(ctx, 928 rootNode1.GetFolderBranch(), nil) 929 require.NoError(t, err) 930 931 t.Log("Check that the directories match on the 2 users.") 932 933 children1, err := kbfsOps1.GetDirChildren(ctx, dirA1) 934 require.NoError(t, err) 935 936 children2, err := kbfsOps2.GetDirChildren(ctx, dirA2) 937 require.NoError(t, err) 938 939 require.Equal(t, children2, children1) 940 941 t.Log("Verify we can access the conflict folder through another handle") 942 dateStr := now.UTC().Format("2006-01-02") 943 h, err := tlfhandle.ParseHandle( 944 ctx, config2.KBPKI(), config2.MDOps(), nil, 945 name+" (local conflicted copy "+dateStr+")", tlf.Private) 946 require.NoError(t, err) 947 b, ok := data.MakeConflictBranchName(h) 948 require.True(t, ok) 949 950 rootNodeConflict, _, err := kbfsOps2.GetRootNode(ctx, h, b) 951 require.NoError(t, err) 952 dirAConflict, _, err := kbfsOps2.Lookup(ctx, rootNodeConflict, testPPS("a")) 953 require.NoError(t, err) 954 fileBConflict, _, err := kbfsOps2.Lookup(ctx, dirAConflict, testPPS("b")) 955 require.NoError(t, err) 956 957 gotData2 := make([]byte, len(data2)) 958 _, err = kbfsOps2.Read(ctx, fileBConflict, gotData2, 0) 959 require.NoError(t, err) 960 require.Equal(t, data2, gotData2) 961 } 962 963 // Tests that two users can create the same file simultaneously, and 964 // the unmerged user can write to it, and they will be merged into a 965 // single file. 966 func TestBasicCRFileCreateUnmergedWriteConflict(t *testing.T) { 967 // simulate two users 968 var userName1, userName2 kbname.NormalizedUsername = "u1", "u2" 969 config1, _, ctx, cancel := kbfsOpsConcurInit(t, userName1, userName2) 970 defer kbfsConcurTestShutdown(ctx, t, config1, cancel) 971 972 config2 := ConfigAsUser(config1, userName2) 973 defer CheckConfigAndShutdown(ctx, t, config2) 974 975 config2.SetClock(clocktest.NewTestClockNow()) 976 977 name := userName1.String() + "," + userName2.String() 978 979 // user1 creates a file in a shared dir 980 rootNode1 := GetRootNodeOrBust(ctx, t, config1, name, tlf.Private) 981 982 kbfsOps1 := config1.KBFSOps() 983 dirA1, _, err := kbfsOps1.CreateDir(ctx, rootNode1, testPPS("a")) 984 require.NoError(t, err) 985 err = kbfsOps1.SyncAll(ctx, rootNode1.GetFolderBranch()) 986 require.NoError(t, err) 987 988 // look it up on user2 989 rootNode2 := GetRootNodeOrBust(ctx, t, config2, name, tlf.Private) 990 991 kbfsOps2 := config2.KBFSOps() 992 dirA2, _, err := kbfsOps2.Lookup(ctx, rootNode2, testPPS("a")) 993 require.NoError(t, err) 994 // disable updates and CR on user 2 995 c, err := DisableUpdatesForTesting(config2, rootNode2.GetFolderBranch()) 996 require.NoError(t, err) 997 err = DisableCRForTesting(config2, rootNode2.GetFolderBranch()) 998 require.NoError(t, err) 999 1000 // User 1 creates a file 1001 _, _, err = kbfsOps1.CreateFile(ctx, dirA1, testPPS("b"), false, NoExcl) 1002 require.NoError(t, err) 1003 err = kbfsOps1.SyncAll(ctx, rootNode1.GetFolderBranch()) 1004 require.NoError(t, err) 1005 1006 // User 2 creates the same file, and writes to it. 1007 fileB2, _, err := kbfsOps2.CreateFile( 1008 ctx, dirA2, testPPS("b"), false, NoExcl) 1009 require.NoError(t, err) 1010 err = kbfsOps2.SyncAll(ctx, rootNode2.GetFolderBranch()) 1011 require.NoError(t, err) 1012 data2 := []byte{5, 4, 3, 2, 1} 1013 err = kbfsOps2.Write(ctx, fileB2, data2, 0) 1014 require.NoError(t, err) 1015 err = kbfsOps2.SyncAll(ctx, fileB2.GetFolderBranch()) 1016 require.NoError(t, err) 1017 1018 // re-enable updates, and wait for CR to complete 1019 c <- struct{}{} 1020 err = RestartCRForTesting( 1021 libcontext.BackgroundContextWithCancellationDelayer(), config2, 1022 rootNode2.GetFolderBranch()) 1023 require.NoError(t, err) 1024 err = kbfsOps2.SyncFromServer(ctx, 1025 rootNode2.GetFolderBranch(), nil) 1026 require.NoError(t, err) 1027 1028 err = kbfsOps1.SyncFromServer(ctx, 1029 rootNode1.GetFolderBranch(), nil) 1030 require.NoError(t, err) 1031 1032 // Make sure they both see the same set of children 1033 expectedChildren := []string{ 1034 "b", 1035 } 1036 children1, err := kbfsOps1.GetDirChildren(ctx, dirA1) 1037 require.NoError(t, err) 1038 1039 children2, err := kbfsOps2.GetDirChildren(ctx, dirA2) 1040 require.NoError(t, err) 1041 1042 assert.Equal(t, len(expectedChildren), len(children1)) 1043 1044 for _, child := range expectedChildren { 1045 _, ok := children1[dirA1.ChildName(child)] 1046 assert.True(t, ok) 1047 } 1048 1049 require.Equal(t, children1, children2) 1050 } 1051 1052 // Test that two conflict resolutions work correctly. 1053 func TestCRDouble(t *testing.T) { 1054 // simulate two users 1055 var userName1, userName2 kbname.NormalizedUsername = "u1", "u2" 1056 config1, _, ctx, cancel := kbfsOpsConcurInit(t, userName1, userName2) 1057 defer kbfsConcurTestShutdown(ctx, t, config1, cancel) 1058 config1.MDServer().DisableRekeyUpdatesForTesting() 1059 1060 config2 := ConfigAsUser(config1, userName2) 1061 defer CheckConfigAndShutdown(ctx, t, config2) 1062 _, err := config2.KBPKI().GetCurrentSession(context.Background()) 1063 require.NoError(t, err) 1064 config2.MDServer().DisableRekeyUpdatesForTesting() 1065 1066 config2.SetClock(clocktest.NewTestClockNow()) 1067 name := userName1.String() + "," + userName2.String() 1068 1069 // create and write to a file 1070 rootNode := GetRootNodeOrBust(ctx, t, config1, name, tlf.Private) 1071 kbfsOps1 := config1.KBFSOps() 1072 _, _, err = kbfsOps1.CreateFile(ctx, rootNode, testPPS("a"), false, NoExcl) 1073 require.NoError(t, err) 1074 err = kbfsOps1.SyncAll(ctx, rootNode.GetFolderBranch()) 1075 require.NoError(t, err) 1076 1077 // look it up on user2 1078 rootNode2 := GetRootNodeOrBust(ctx, t, config2, name, tlf.Private) 1079 1080 kbfsOps2 := config2.KBFSOps() 1081 _, _, err = kbfsOps2.Lookup(ctx, rootNode2, testPPS("a")) 1082 require.NoError(t, err) 1083 // disable updates and CR on user 2 1084 c, err := DisableUpdatesForTesting(config2, rootNode2.GetFolderBranch()) 1085 require.NoError(t, err) 1086 err = DisableCRForTesting(config2, rootNode2.GetFolderBranch()) 1087 require.NoError(t, err) 1088 1089 // User 1 creates a new file to start a conflict. 1090 _, _, err = kbfsOps1.CreateFile(ctx, rootNode, testPPS("b"), false, NoExcl) 1091 require.NoError(t, err) 1092 err = kbfsOps1.SyncAll(ctx, rootNode.GetFolderBranch()) 1093 require.NoError(t, err) 1094 1095 // User 2 makes a couple revisions 1096 fileNodeC, _, err := kbfsOps2.CreateFile( 1097 ctx, rootNode2, testPPS("c"), false, NoExcl) 1098 require.NoError(t, err) 1099 err = kbfsOps2.SyncAll(ctx, rootNode2.GetFolderBranch()) 1100 require.NoError(t, err) 1101 err = kbfsOps2.Write(ctx, fileNodeC, []byte{0}, 0) 1102 require.NoError(t, err) 1103 1104 var wg sync.WaitGroup 1105 syncCtx, cancel := context.WithCancel(ctx) 1106 1107 // Cancel this revision after the Put happens, to force the 1108 // background block manager to try to clean up. 1109 onSyncStalledCh, syncUnstallCh, syncCtx := StallMDOp( 1110 syncCtx, config2, StallableMDAfterPutUnmerged, 1) 1111 1112 wg.Add(1) 1113 go func() { 1114 defer wg.Done() 1115 err = kbfsOps2.SyncAll(syncCtx, fileNodeC.GetFolderBranch()) 1116 // Even though internally folderBranchOps ignores the 1117 // cancellation error when putting on an unmerged branch, the 1118 // wrapper function *might* still return it. 1119 if err != nil { 1120 assert.Equal(t, context.Canceled, err) 1121 } 1122 }() 1123 <-onSyncStalledCh 1124 cancel() 1125 close(syncUnstallCh) 1126 wg.Wait() 1127 1128 // Sync for real to clear out the dirty files. 1129 err = kbfsOps2.SyncAll(ctx, fileNodeC.GetFolderBranch()) 1130 require.NoError(t, err) 1131 1132 // Do one CR. 1133 c <- struct{}{} 1134 err = RestartCRForTesting( 1135 libcontext.BackgroundContextWithCancellationDelayer(), config2, 1136 rootNode2.GetFolderBranch()) 1137 require.NoError(t, err) 1138 err = kbfsOps2.SyncFromServer(ctx, 1139 rootNode2.GetFolderBranch(), nil) 1140 require.NoError(t, err) 1141 1142 // A few merged revisions 1143 _, _, err = kbfsOps2.CreateFile(ctx, rootNode2, testPPS("e"), false, NoExcl) 1144 require.NoError(t, err) 1145 err = kbfsOps2.SyncAll(ctx, rootNode2.GetFolderBranch()) 1146 require.NoError(t, err) 1147 _, _, err = kbfsOps2.CreateFile(ctx, rootNode2, testPPS("f"), false, NoExcl) 1148 require.NoError(t, err) 1149 err = kbfsOps2.SyncAll(ctx, rootNode2.GetFolderBranch()) 1150 require.NoError(t, err) 1151 1152 ops := getOps(config2, rootNode.GetFolderBranch().Tlf) 1153 // Wait for the processor to try to delete the failed revision 1154 // (which pulls the unmerged MD ops back into the cache). 1155 err = ops.fbm.waitForArchives(ctx) 1156 require.NoError(t, err) 1157 err = ops.fbm.waitForDeletingBlocks(ctx) 1158 require.NoError(t, err) 1159 1160 // Sync user 1, then start another round of CR. 1161 err = kbfsOps1.SyncFromServer(ctx, 1162 rootNode2.GetFolderBranch(), nil) 1163 require.NoError(t, err) 1164 // disable updates and CR on user 2 1165 c, err = DisableUpdatesForTesting(config2, rootNode2.GetFolderBranch()) 1166 require.NoError(t, err) 1167 err = DisableCRForTesting(config2, rootNode2.GetFolderBranch()) 1168 require.NoError(t, err) 1169 _, _, err = kbfsOps1.CreateFile(ctx, rootNode, testPPS("g"), false, NoExcl) 1170 require.NoError(t, err) 1171 err = kbfsOps1.SyncAll(ctx, rootNode.GetFolderBranch()) 1172 require.NoError(t, err) 1173 1174 // User 2 makes a couple unmerged revisions 1175 _, _, err = kbfsOps2.CreateFile(ctx, rootNode2, testPPS("h"), false, NoExcl) 1176 require.NoError(t, err) 1177 err = kbfsOps2.SyncAll(ctx, rootNode2.GetFolderBranch()) 1178 require.NoError(t, err) 1179 _, _, err = kbfsOps2.CreateFile(ctx, rootNode2, testPPS("i"), false, NoExcl) 1180 require.NoError(t, err) 1181 err = kbfsOps2.SyncAll(ctx, rootNode2.GetFolderBranch()) 1182 require.NoError(t, err) 1183 1184 // Do a second CR. 1185 c <- struct{}{} 1186 err = RestartCRForTesting( 1187 libcontext.BackgroundContextWithCancellationDelayer(), config2, 1188 rootNode2.GetFolderBranch()) 1189 require.NoError(t, err) 1190 err = kbfsOps2.SyncFromServer(ctx, 1191 rootNode2.GetFolderBranch(), nil) 1192 require.NoError(t, err) 1193 } 1194 1195 // Tests that two users can make independent writes while forked, and 1196 // conflict resolution will merge them correctly and the rekey bit is 1197 // preserved until rekey. 1198 func TestBasicCRFileConflictWithRekey(t *testing.T) { 1199 // simulate two users 1200 var userName1, userName2 kbname.NormalizedUsername = "u1", "u2" 1201 config1, _, ctx, cancel := kbfsOpsConcurInit(t, userName1, userName2) 1202 defer kbfsConcurTestShutdown(ctx, t, config1, cancel) 1203 config1.MDServer().DisableRekeyUpdatesForTesting() 1204 1205 config2 := ConfigAsUser(config1, userName2) 1206 defer CheckConfigAndShutdown(ctx, t, config2) 1207 session2, err := config2.KBPKI().GetCurrentSession(context.Background()) 1208 require.NoError(t, err) 1209 config2.MDServer().DisableRekeyUpdatesForTesting() 1210 1211 clock, now := clocktest.NewTestClockAndTimeNow() 1212 config2.SetClock(clock) 1213 name := userName1.String() + "," + userName2.String() 1214 1215 // user1 creates a file in a shared dir 1216 rootNode1 := GetRootNodeOrBust(ctx, t, config1, name, tlf.Private) 1217 1218 kbfsOps1 := config1.KBFSOps() 1219 dirA1, _, err := kbfsOps1.CreateDir(ctx, rootNode1, testPPS("a")) 1220 require.NoError(t, err) 1221 fileB1, _, err := kbfsOps1.CreateFile( 1222 ctx, dirA1, testPPS("b"), false, NoExcl) 1223 require.NoError(t, err) 1224 err = kbfsOps1.SyncAll(ctx, rootNode1.GetFolderBranch()) 1225 require.NoError(t, err) 1226 1227 // look it up on user2 1228 rootNode2 := GetRootNodeOrBust(ctx, t, config2, name, tlf.Private) 1229 1230 kbfsOps2 := config2.KBFSOps() 1231 dirA2, _, err := kbfsOps2.Lookup(ctx, rootNode2, testPPS("a")) 1232 require.NoError(t, err) 1233 fileB2, _, err := kbfsOps2.Lookup(ctx, dirA2, testPPS("b")) 1234 require.NoError(t, err) 1235 1236 config2Dev2 := ConfigAsUser(config1, userName2) 1237 // we don't check the config because this device can't read all of 1238 // the md blocks. 1239 defer func() { _ = config2Dev2.Shutdown(ctx) }() 1240 config2Dev2.MDServer().DisableRekeyUpdatesForTesting() 1241 1242 // Now give u2 a new device. The configs don't share a Keybase 1243 // Daemon so we have to do it in all places. 1244 AddDeviceForLocalUserOrBust(t, config1, session2.UID) 1245 AddDeviceForLocalUserOrBust(t, config2, session2.UID) 1246 devIndex := AddDeviceForLocalUserOrBust(t, config2Dev2, session2.UID) 1247 SwitchDeviceForLocalUserOrBust(t, config2Dev2, devIndex) 1248 1249 // user2 device 2 should be unable to read the data now since its device 1250 // wasn't registered when the folder was originally created. 1251 _, err = GetRootNodeForTest(ctx, config2Dev2, name, tlf.Private) 1252 require.IsType(t, NeedSelfRekeyError{}, err) 1253 1254 // User 2 syncs 1255 err = kbfsOps2.SyncFromServer(ctx, 1256 rootNode2.GetFolderBranch(), nil) 1257 require.NoError(t, err) 1258 1259 // disable updates on user2 1260 c, err := DisableUpdatesForTesting(config2, rootNode2.GetFolderBranch()) 1261 require.NoError(t, err) 1262 err = DisableCRForTesting(config2, rootNode2.GetFolderBranch()) 1263 require.NoError(t, err) 1264 1265 // User 1 writes the file 1266 data1 := []byte{1, 2, 3, 4, 5} 1267 err = kbfsOps1.Write(ctx, fileB1, data1, 0) 1268 require.NoError(t, err) 1269 err = kbfsOps1.SyncAll(ctx, fileB1.GetFolderBranch()) 1270 require.NoError(t, err) 1271 1272 // User 2 dev 2 should set the rekey bit 1273 kbfsOps2Dev2 := config2Dev2.KBFSOps() 1274 _, _ = RequestRekeyAndWaitForOneFinishEvent(ctx, 1275 kbfsOps2Dev2, rootNode2.GetFolderBranch().Tlf) 1276 1277 // User 1 syncs 1278 err = kbfsOps1.SyncFromServer(ctx, 1279 rootNode1.GetFolderBranch(), nil) 1280 require.NoError(t, err) 1281 1282 // User 2 makes a new different file 1283 data2 := []byte{5, 4, 3, 2, 1} 1284 err = kbfsOps2.Write(ctx, fileB2, data2, 0) 1285 require.NoError(t, err) 1286 err = kbfsOps2.SyncAll(ctx, fileB2.GetFolderBranch()) 1287 require.NoError(t, err) 1288 1289 // re-enable updates, and wait for CR to complete. 1290 // this should also cause a rekey of the folder. 1291 c <- struct{}{} 1292 err = RestartCRForTesting( 1293 libcontext.BackgroundContextWithCancellationDelayer(), config2, 1294 rootNode2.GetFolderBranch()) 1295 require.NoError(t, err) 1296 err = kbfsOps2.SyncFromServer(ctx, 1297 rootNode2.GetFolderBranch(), nil) 1298 require.NoError(t, err) 1299 // wait for the rekey to happen 1300 _, _ = RequestRekeyAndWaitForOneFinishEvent(ctx, 1301 config2.KBFSOps(), rootNode2.GetFolderBranch().Tlf) 1302 1303 err = kbfsOps1.SyncFromServer(ctx, 1304 rootNode1.GetFolderBranch(), nil) 1305 require.NoError(t, err) 1306 1307 // Look it up on user 2 dev 2 after syncing. 1308 err = kbfsOps2Dev2.SyncFromServer(ctx, 1309 rootNode2.GetFolderBranch(), nil) 1310 require.NoError(t, err) 1311 rootNode2Dev2 := GetRootNodeOrBust(ctx, t, config2Dev2, name, tlf.Private) 1312 dirA2Dev2, _, err := kbfsOps2Dev2.Lookup(ctx, rootNode2Dev2, testPPS("a")) 1313 require.NoError(t, err) 1314 1315 cre := WriterDeviceDateConflictRenamer{} 1316 // Make sure they all see the same set of children 1317 expectedChildren := []string{ 1318 "b", 1319 cre.ConflictRenameHelper(now, "u2", "dev1", "b"), 1320 } 1321 children1, err := kbfsOps1.GetDirChildren(ctx, dirA1) 1322 require.NoError(t, err) 1323 1324 children2, err := kbfsOps2.GetDirChildren(ctx, dirA2) 1325 require.NoError(t, err) 1326 1327 children2Dev2, err := kbfsOps2Dev2.GetDirChildren(ctx, dirA2Dev2) 1328 require.NoError(t, err) 1329 1330 assert.Equal(t, len(expectedChildren), len(children1)) 1331 1332 for _, child := range expectedChildren { 1333 _, ok := children1[dirA1.ChildName(child)] 1334 assert.True(t, ok) 1335 } 1336 1337 require.Equal(t, children1, children2) 1338 require.Equal(t, children2, children2Dev2) 1339 } 1340 1341 // Same as above, except the "winner" is the rekey request, and the 1342 // "loser" is the file write. Regression test for KBFS-773. 1343 func TestBasicCRFileConflictWithMergedRekey(t *testing.T) { 1344 // simulate two users 1345 var userName1, userName2 kbname.NormalizedUsername = "u1", "u2" 1346 config1, _, ctx, cancel := kbfsOpsConcurInit(t, userName1, userName2) 1347 defer kbfsConcurTestShutdown(ctx, t, config1, cancel) 1348 config1.MDServer().DisableRekeyUpdatesForTesting() 1349 1350 config2 := ConfigAsUser(config1, userName2) 1351 defer CheckConfigAndShutdown(ctx, t, config2) 1352 session2, err := config2.KBPKI().GetCurrentSession(context.Background()) 1353 require.NoError(t, err) 1354 config2.MDServer().DisableRekeyUpdatesForTesting() 1355 1356 config2.SetClock(clocktest.NewTestClockNow()) 1357 name := userName1.String() + "," + userName2.String() 1358 1359 // user1 creates a file in a shared dir 1360 rootNode1 := GetRootNodeOrBust(ctx, t, config1, name, tlf.Private) 1361 1362 kbfsOps1 := config1.KBFSOps() 1363 dirA1, _, err := kbfsOps1.CreateDir(ctx, rootNode1, testPPS("a")) 1364 require.NoError(t, err) 1365 fileB1, _, err := kbfsOps1.CreateFile( 1366 ctx, dirA1, testPPS("b"), false, NoExcl) 1367 require.NoError(t, err) 1368 err = kbfsOps1.SyncAll(ctx, rootNode1.GetFolderBranch()) 1369 require.NoError(t, err) 1370 1371 // look it up on user2 1372 rootNode2 := GetRootNodeOrBust(ctx, t, config2, name, tlf.Private) 1373 1374 kbfsOps2 := config2.KBFSOps() 1375 dirA2, _, err := kbfsOps2.Lookup(ctx, rootNode2, testPPS("a")) 1376 require.NoError(t, err) 1377 1378 config2Dev2 := ConfigAsUser(config1, userName2) 1379 // we don't check the config because this device can't read all of 1380 // the md blocks. 1381 defer func() { _ = config2Dev2.Shutdown(ctx) }() 1382 config2Dev2.MDServer().DisableRekeyUpdatesForTesting() 1383 1384 // Now give u2 a new device. The configs don't share a Keybase 1385 // Daemon so we have to do it in all places. 1386 AddDeviceForLocalUserOrBust(t, config1, session2.UID) 1387 AddDeviceForLocalUserOrBust(t, config2, session2.UID) 1388 devIndex := AddDeviceForLocalUserOrBust(t, config2Dev2, session2.UID) 1389 SwitchDeviceForLocalUserOrBust(t, config2Dev2, devIndex) 1390 1391 // user2 device 2 should be unable to read the data now since its device 1392 // wasn't registered when the folder was originally created. 1393 _, err = GetRootNodeForTest(ctx, config2Dev2, name, tlf.Private) 1394 require.IsType(t, NeedSelfRekeyError{}, err) 1395 1396 // User 2 syncs 1397 err = kbfsOps2.SyncFromServer(ctx, 1398 rootNode2.GetFolderBranch(), nil) 1399 require.NoError(t, err) 1400 1401 // disable updates on user1 1402 c, err := DisableUpdatesForTesting(config1, rootNode2.GetFolderBranch()) 1403 require.NoError(t, err) 1404 err = DisableCRForTesting(config1, rootNode2.GetFolderBranch()) 1405 require.NoError(t, err) 1406 1407 // User 2 dev 2 should set the rekey bit 1408 kbfsOps2Dev2 := config2Dev2.KBFSOps() 1409 _, _ = RequestRekeyAndWaitForOneFinishEvent(ctx, 1410 kbfsOps2Dev2, rootNode2.GetFolderBranch().Tlf) 1411 1412 // User 1 writes the file 1413 data1 := []byte{1, 2, 3, 4, 5} 1414 err = kbfsOps1.Write(ctx, fileB1, data1, 0) 1415 require.NoError(t, err) 1416 err = kbfsOps1.SyncAll(ctx, fileB1.GetFolderBranch()) 1417 require.NoError(t, err) 1418 1419 // re-enable updates, and wait for CR to complete. 1420 // this should also cause a rekey of the folder. 1421 c <- struct{}{} 1422 err = RestartCRForTesting( 1423 libcontext.BackgroundContextWithCancellationDelayer(), config1, 1424 rootNode2.GetFolderBranch()) 1425 require.NoError(t, err) 1426 err = kbfsOps1.SyncFromServer(ctx, 1427 rootNode1.GetFolderBranch(), nil) 1428 require.NoError(t, err) 1429 // wait for the rekey to happen 1430 _, _ = RequestRekeyAndWaitForOneFinishEvent(ctx, 1431 config1.KBFSOps(), rootNode1.GetFolderBranch().Tlf) 1432 1433 err = kbfsOps1.SyncFromServer(ctx, 1434 rootNode1.GetFolderBranch(), nil) 1435 require.NoError(t, err) 1436 1437 err = kbfsOps2.SyncFromServer(ctx, 1438 rootNode2.GetFolderBranch(), nil) 1439 require.NoError(t, err) 1440 1441 // Look it up on user 2 dev 2 after syncing. 1442 err = kbfsOps2Dev2.SyncFromServer(ctx, 1443 rootNode2.GetFolderBranch(), nil) 1444 require.NoError(t, err) 1445 rootNode2Dev2 := GetRootNodeOrBust(ctx, t, config2Dev2, name, tlf.Private) 1446 dirA2Dev2, _, err := kbfsOps2Dev2.Lookup(ctx, rootNode2Dev2, testPPS("a")) 1447 require.NoError(t, err) 1448 1449 // Make sure they all see the same set of children 1450 expectedChildren := []string{ 1451 "b", 1452 } 1453 children1, err := kbfsOps1.GetDirChildren(ctx, dirA1) 1454 require.NoError(t, err) 1455 1456 children2, err := kbfsOps2.GetDirChildren(ctx, dirA2) 1457 require.NoError(t, err) 1458 1459 children2Dev2, err := kbfsOps2Dev2.GetDirChildren(ctx, dirA2Dev2) 1460 require.NoError(t, err) 1461 1462 assert.Equal(t, len(expectedChildren), len(children1)) 1463 1464 for _, child := range expectedChildren { 1465 _, ok := children1[dirA1.ChildName(child)] 1466 assert.True(t, ok) 1467 } 1468 1469 require.Equal(t, children1, children2) 1470 require.Equal(t, children2, children2Dev2) 1471 } 1472 1473 // Test that, when writing multiple blocks in parallel under conflict 1474 // resolution, one error will cancel the remaining puts and the block 1475 // server will be consistent. 1476 func TestCRSyncParallelBlocksErrorCleanup(t *testing.T) { 1477 t.Skip("Broken due to KBFS-1193") 1478 1479 // simulate two users 1480 var userName1, userName2 kbname.NormalizedUsername = "u1", "u2" 1481 config1, _, ctx, cancel := kbfsOpsConcurInit(t, userName1, userName2) 1482 defer kbfsConcurTestShutdown(ctx, t, config1, cancel) 1483 config1.MDServer().DisableRekeyUpdatesForTesting() 1484 1485 config2 := ConfigAsUser(config1, userName2) 1486 defer CheckConfigAndShutdown(ctx, t, config2) 1487 _, err := config2.KBPKI().GetCurrentSession(context.Background()) 1488 require.NoError(t, err) 1489 config2.MDServer().DisableRekeyUpdatesForTesting() 1490 1491 config2.SetClock(clocktest.NewTestClockNow()) 1492 name := userName1.String() + "," + userName2.String() 1493 1494 // Make the blocks small, with multiple levels of indirection, but 1495 // make the unembedded size large, so we don't create thousands of 1496 // unembedded block change blocks. 1497 blockSize := int64(5) 1498 bsplit, err := data.NewBlockSplitterSimpleExact(blockSize, 2, 100*1024) 1499 require.NoError(t, err) 1500 config1.SetBlockSplitter(bsplit) 1501 1502 // create and write to a file 1503 rootNode := GetRootNodeOrBust(ctx, t, config1, name, tlf.Private) 1504 kbfsOps1 := config1.KBFSOps() 1505 _, _, err = kbfsOps1.CreateFile(ctx, rootNode, testPPS("a"), false, NoExcl) 1506 require.NoError(t, err) 1507 err = kbfsOps1.SyncAll(ctx, rootNode.GetFolderBranch()) 1508 require.NoError(t, err) 1509 1510 // look it up on user2 1511 rootNode2 := GetRootNodeOrBust(ctx, t, config2, name, tlf.Private) 1512 1513 kbfsOps2 := config2.KBFSOps() 1514 _, _, err = kbfsOps2.Lookup(ctx, rootNode2, testPPS("a")) 1515 require.NoError(t, err) 1516 // disable updates and CR on user 2 1517 c, err := DisableUpdatesForTesting(config2, rootNode2.GetFolderBranch()) 1518 require.NoError(t, err) 1519 err = DisableCRForTesting(config2, rootNode2.GetFolderBranch()) 1520 require.NoError(t, err) 1521 1522 // User 1 creates a new file to start a conflict. 1523 _, _, err = kbfsOps1.CreateFile(ctx, rootNode, testPPS("b"), false, NoExcl) 1524 require.NoError(t, err) 1525 err = kbfsOps1.SyncAll(ctx, rootNode.GetFolderBranch()) 1526 require.NoError(t, err) 1527 1528 // User 2 does one successful operation to create the first unmerged MD. 1529 fileNodeB, _, err := kbfsOps2.CreateFile( 1530 ctx, rootNode2, testPPS("b"), false, NoExcl) 1531 require.NoError(t, err) 1532 err = kbfsOps2.SyncAll(ctx, rootNode2.GetFolderBranch()) 1533 require.NoError(t, err) 1534 1535 // User 2 writes some data 1536 fileBlocks := int64(maxParallelBlockPuts + 5) 1537 var data []byte 1538 for i := int64(0); i < blockSize*fileBlocks; i++ { 1539 data = append(data, byte(i)) 1540 } 1541 err = kbfsOps2.Write(ctx, fileNodeB, data, 0) 1542 require.NoError(t, err) 1543 1544 // Start the sync and wait for it to stall. 1545 var wg sync.WaitGroup 1546 wg.Add(1) 1547 syncCtx, cancel := context.WithCancel( 1548 libcontext.BackgroundContextWithCancellationDelayer()) 1549 defer func() { 1550 err := libcontext.CleanupCancellationDelayer(syncCtx) 1551 require.NoError(t, err) 1552 }() 1553 1554 // Now user 2 makes a big write where most of the blocks get canceled. 1555 // We only need to know the first time we stall. 1556 onSyncStalledCh, syncUnstallCh, syncCtx := StallBlockOp( 1557 syncCtx, config2, StallableBlockPut, 2) 1558 1559 var syncErr error 1560 go func() { 1561 defer wg.Done() 1562 1563 syncErr = kbfsOps2.SyncAll(syncCtx, fileNodeB.GetFolderBranch()) 1564 }() 1565 // Wait for 2 of the blocks and let them go 1566 <-onSyncStalledCh 1567 <-onSyncStalledCh 1568 syncUnstallCh <- struct{}{} 1569 syncUnstallCh <- struct{}{} 1570 1571 // Wait for the rest of the puts (this indicates that the first 1572 // two succeeded correctly and two more were sent to replace them) 1573 for i := 0; i < maxParallelBlockPuts; i++ { 1574 <-onSyncStalledCh 1575 } 1576 // Cancel so all other block puts fail 1577 cancel() 1578 close(syncUnstallCh) 1579 wg.Wait() 1580 1581 require.Equal(t, context.Canceled, syncErr) 1582 1583 // Get the mdWriterLock to be sure the sync has exited (since the 1584 // cleanup logic happens in a background goroutine) 1585 ops := getOps(config2, rootNode2.GetFolderBranch().Tlf) 1586 lState := makeFBOLockState() 1587 ops.mdWriterLock.Lock(lState) 1588 ops.mdWriterLock.Unlock(lState) 1589 1590 // The state checker will make sure those blocks from 1591 // the failed sync get cleaned up. 1592 1593 for i := int64(0); i < blockSize*fileBlocks; i++ { 1594 data[i] = byte(i + 10) 1595 } 1596 err = kbfsOps2.Write(ctx, fileNodeB, data, 0) 1597 require.NoError(t, err) 1598 err = kbfsOps2.SyncAll(ctx, fileNodeB.GetFolderBranch()) 1599 require.NoError(t, err) 1600 1601 c <- struct{}{} 1602 err = RestartCRForTesting( 1603 libcontext.BackgroundContextWithCancellationDelayer(), config2, 1604 rootNode2.GetFolderBranch()) 1605 require.NoError(t, err) 1606 err = kbfsOps2.SyncFromServer(ctx, 1607 rootNode2.GetFolderBranch(), nil) 1608 require.NoError(t, err) 1609 } 1610 1611 // Test that a resolution can be canceled right before the Put due to 1612 // another operation, and then the second resolution includes both 1613 // unmerged operations. Regression test for KBFS-1133. 1614 func TestCRCanceledAfterNewOperation(t *testing.T) { 1615 // simulate two users 1616 var userName1, userName2 kbname.NormalizedUsername = "u1", "u2" 1617 config1, _, ctx, cancel := kbfsOpsConcurInit(t, userName1, userName2) 1618 defer kbfsConcurTestShutdown(ctx, t, config1, cancel) 1619 config1.MDServer().DisableRekeyUpdatesForTesting() 1620 1621 config2 := ConfigAsUser(config1, userName2) 1622 defer CheckConfigAndShutdown(ctx, t, config2) 1623 _, err := config2.KBPKI().GetCurrentSession(context.Background()) 1624 require.NoError(t, err) 1625 config2.MDServer().DisableRekeyUpdatesForTesting() 1626 1627 clock, now := clocktest.NewTestClockAndTimeNow() 1628 config2.SetClock(clock) 1629 name := userName1.String() + "," + userName2.String() 1630 1631 // create and write to a file 1632 rootNode := GetRootNodeOrBust(ctx, t, config1, name, tlf.Private) 1633 kbfsOps1 := config1.KBFSOps() 1634 aNode1, _, err := kbfsOps1.CreateFile( 1635 ctx, rootNode, testPPS("a"), false, NoExcl) 1636 require.NoError(t, err) 1637 data := []byte{1, 2, 3, 4, 5} 1638 err = kbfsOps1.Write(ctx, aNode1, data, 0) 1639 require.NoError(t, err) 1640 err = kbfsOps1.SyncAll(ctx, aNode1.GetFolderBranch()) 1641 require.NoError(t, err) 1642 1643 // look it up on user2 1644 rootNode2 := GetRootNodeOrBust(ctx, t, config2, name, tlf.Private) 1645 1646 kbfsOps2 := config2.KBFSOps() 1647 aNode2, _, err := kbfsOps2.Lookup(ctx, rootNode2, testPPS("a")) 1648 require.NoError(t, err) 1649 // disable updates and CR on user 2 1650 c, err := DisableUpdatesForTesting(config2, rootNode2.GetFolderBranch()) 1651 require.NoError(t, err) 1652 err = DisableCRForTesting(config2, rootNode2.GetFolderBranch()) 1653 require.NoError(t, err) 1654 1655 // User 1 truncates file a. 1656 err = kbfsOps1.Truncate(ctx, aNode1, 0) 1657 require.NoError(t, err) 1658 err = kbfsOps1.SyncAll(ctx, aNode1.GetFolderBranch()) 1659 require.NoError(t, err) 1660 1661 // User 2 writes to the file, creating a conflict. 1662 data2 := []byte{5, 4, 3, 2, 1} 1663 err = kbfsOps2.Write(ctx, aNode2, data2, 0) 1664 require.NoError(t, err) 1665 err = kbfsOps2.SyncAll(ctx, aNode2.GetFolderBranch()) 1666 require.NoError(t, err) 1667 1668 onPutStalledCh, putUnstallCh, putCtx := 1669 StallMDOp(context.Background(), config2, StallableMDResolveBranch, 1) 1670 1671 var wg sync.WaitGroup 1672 putCtx, cancel2 := context.WithCancel(putCtx) 1673 wg.Add(1) 1674 go func() { 1675 defer wg.Done() 1676 1677 c <- struct{}{} 1678 // Make sure the CR gets done with a context we can use for 1679 // stalling. 1680 err = RestartCRForTesting(putCtx, config2, 1681 rootNode2.GetFolderBranch()) 1682 assert.NoError(t, err) 1683 err = kbfsOps2.SyncFromServer(putCtx, 1684 rootNode2.GetFolderBranch(), nil) 1685 assert.Error(t, err) 1686 }() 1687 <-onPutStalledCh 1688 cancel2() 1689 close(putUnstallCh) 1690 wg.Wait() 1691 1692 // Disable again 1693 c, err = DisableUpdatesForTesting(config2, rootNode2.GetFolderBranch()) 1694 require.NoError(t, err) 1695 err = DisableCRForTesting(config2, rootNode2.GetFolderBranch()) 1696 require.NoError(t, err) 1697 1698 // Do a second operation and complete the resolution. 1699 _, _, err = kbfsOps2.CreateFile(ctx, rootNode2, testPPS("b"), false, NoExcl) 1700 require.NoError(t, err) 1701 err = kbfsOps2.SyncAll(ctx, rootNode2.GetFolderBranch()) 1702 require.NoError(t, err) 1703 c <- struct{}{} 1704 err = RestartCRForTesting( 1705 libcontext.BackgroundContextWithCancellationDelayer(), config2, 1706 rootNode2.GetFolderBranch()) 1707 require.NoError(t, err) 1708 err = kbfsOps2.SyncFromServer(ctx, 1709 rootNode2.GetFolderBranch(), nil) 1710 require.NoError(t, err) 1711 1712 // Now there should be a conflict file containing data2. 1713 cre := WriterDeviceDateConflictRenamer{} 1714 // Make sure they both see the same set of children 1715 expectedChildren := []string{ 1716 "a", 1717 cre.ConflictRenameHelper(now, "u2", "dev1", "a"), 1718 "b", 1719 } 1720 children2, err := kbfsOps2.GetDirChildren(ctx, rootNode2) 1721 require.NoError(t, err) 1722 assert.Equal(t, len(expectedChildren), len(children2)) 1723 for _, child := range expectedChildren { 1724 _, ok := children2[rootNode2.ChildName(child)] 1725 assert.True(t, ok) 1726 } 1727 } 1728 1729 // Tests that if a user gets /too/ unmerged, they will have their 1730 // unmerged writes blocked. 1731 func TestBasicCRBlockUnmergedWrites(t *testing.T) { 1732 // simulate two users 1733 var userName1, userName2 kbname.NormalizedUsername = "u1", "u2" 1734 config1, _, ctx, cancel := kbfsOpsConcurInit(t, userName1, userName2) 1735 defer kbfsConcurTestShutdown(ctx, t, config1, cancel) 1736 1737 config2 := ConfigAsUser(config1, userName2) 1738 defer CheckConfigAndShutdown(ctx, t, config2) 1739 1740 name := userName1.String() + "," + userName2.String() 1741 1742 // user1 creates a file in a shared dir 1743 rootNode1 := GetRootNodeOrBust(ctx, t, config1, name, tlf.Private) 1744 1745 kbfsOps1 := config1.KBFSOps() 1746 dirA1, _, err := kbfsOps1.CreateDir(ctx, rootNode1, testPPS("a")) 1747 require.NoError(t, err) 1748 _, _, err = kbfsOps1.CreateFile(ctx, dirA1, testPPS("b"), false, NoExcl) 1749 require.NoError(t, err) 1750 err = kbfsOps1.SyncAll(ctx, rootNode1.GetFolderBranch()) 1751 require.NoError(t, err) 1752 1753 // look it up on user2 1754 rootNode2 := GetRootNodeOrBust(ctx, t, config2, name, tlf.Private) 1755 1756 kbfsOps2 := config2.KBFSOps() 1757 ops2 := getOps(config2, rootNode2.GetFolderBranch().Tlf) 1758 ops2.cr.maxRevsThreshold = 2 1759 dirA2, _, err := kbfsOps2.Lookup(ctx, rootNode2, testPPS("a")) 1760 require.NoError(t, err) 1761 _, _, err = kbfsOps2.Lookup(ctx, dirA2, testPPS("b")) 1762 require.NoError(t, err) 1763 1764 // disable updates on user 2 1765 c, err := DisableUpdatesForTesting(config2, rootNode2.GetFolderBranch()) 1766 require.NoError(t, err) 1767 err = DisableCRForTesting(config2, rootNode2.GetFolderBranch()) 1768 require.NoError(t, err) 1769 1770 // One write for user 1 1771 _, _, err = kbfsOps1.CreateFile(ctx, dirA1, testPPS("c"), false, NoExcl) 1772 require.NoError(t, err) 1773 err = kbfsOps1.SyncAll(ctx, dirA1.GetFolderBranch()) 1774 require.NoError(t, err) 1775 1776 // Two writes for user 2 1777 _, _, err = kbfsOps2.CreateFile(ctx, dirA2, testPPS("d"), false, NoExcl) 1778 require.NoError(t, err) 1779 err = kbfsOps2.SyncAll(ctx, rootNode2.GetFolderBranch()) 1780 require.NoError(t, err) 1781 _, _, err = kbfsOps2.CreateFile(ctx, dirA2, testPPS("e"), false, NoExcl) 1782 require.NoError(t, err) 1783 err = kbfsOps2.SyncAll(ctx, rootNode2.GetFolderBranch()) 1784 require.NoError(t, err) 1785 1786 // Start CR, but cancel it before it completes, which should lead 1787 // to it locking next time (since it has seen how many revisions 1788 // are outstanding). 1789 onPutStalledCh, putUnstallCh, putCtx := 1790 StallMDOp(context.Background(), config2, StallableMDResolveBranch, 1) 1791 1792 var wg sync.WaitGroup 1793 firstPutCtx, cancel := context.WithCancel(putCtx) 1794 wg.Add(1) 1795 go func() { 1796 defer wg.Done() 1797 1798 // Make sure the CR gets done with a context we can use for 1799 // stalling. 1800 err = RestartCRForTesting(firstPutCtx, config2, 1801 rootNode2.GetFolderBranch()) 1802 if !assert.NoError(t, err) { 1803 return 1804 } 1805 err = kbfsOps2.SyncFromServer(firstPutCtx, 1806 rootNode2.GetFolderBranch(), nil) 1807 if !assert.Error(t, err) { 1808 return 1809 } 1810 err = DisableCRForTesting(config2, rootNode2.GetFolderBranch()) 1811 if !assert.NoError(t, err) { 1812 return 1813 } 1814 }() 1815 <-onPutStalledCh 1816 cancel() 1817 putUnstallCh <- struct{}{} 1818 wg.Wait() 1819 1820 // Pretend that CR was canceled by another write. 1821 _, _, err = kbfsOps2.CreateFile(ctx, dirA2, testPPS("f"), false, NoExcl) 1822 require.NoError(t, err) 1823 err = kbfsOps2.SyncAll(ctx, rootNode2.GetFolderBranch()) 1824 require.NoError(t, err) 1825 1826 // Now restart CR, and make sure it blocks all writes. 1827 wg.Add(1) 1828 go func() { 1829 defer wg.Done() 1830 1831 // Make sure the CR gets done with a context we can use for 1832 // stalling. 1833 err = RestartCRForTesting(putCtx, config2, 1834 rootNode2.GetFolderBranch()) 1835 if !assert.NoError(t, err) { 1836 return 1837 } 1838 }() 1839 <-onPutStalledCh 1840 c <- struct{}{} 1841 1842 // Now try to write again 1843 writeErrCh := make(chan error, 1) 1844 go func() { 1845 _, _, err := kbfsOps2.CreateFile( 1846 ctx, dirA2, testPPS("g"), false, NoExcl) 1847 assert.NoError(t, err) 1848 err = kbfsOps2.SyncAll(ctx, rootNode2.GetFolderBranch()) 1849 require.NoError(t, err) 1850 writeErrCh <- err 1851 }() 1852 1853 // For now, assume we're blocked if the write doesn't go through 1854 // in 20ms. TODO: instruct mdWriterLock to know for sure how many 1855 // goroutines it's blocking? 1856 timer := time.After(20 * time.Millisecond) 1857 select { 1858 case <-writeErrCh: 1859 t.Fatalf("Write finished without blocking") 1860 case <-timer: 1861 } 1862 1863 // Finish the CR. 1864 close(putUnstallCh) 1865 wg.Wait() 1866 1867 // Now the write can finish 1868 err = <-writeErrCh 1869 require.NoError(t, err) 1870 } 1871 1872 // Test that an umerged put can be canceled, and the conflict 1873 // resolution will fix the resulting weird state. 1874 func TestUnmergedPutAfterCanceledUnmergedPut(t *testing.T) { 1875 // simulate two users 1876 var userName1, userName2 kbname.NormalizedUsername = "u1", "u2" 1877 config1, _, ctx, cancel := kbfsOpsConcurInit(t, userName1, userName2) 1878 defer kbfsConcurTestShutdown(ctx, t, config1, cancel) 1879 config1.MDServer().DisableRekeyUpdatesForTesting() 1880 1881 config2 := ConfigAsUser(config1, userName2) 1882 defer CheckConfigAndShutdown(ctx, t, config2) 1883 _, err := config2.KBPKI().GetCurrentSession(context.Background()) 1884 require.NoError(t, err) 1885 config2.MDServer().DisableRekeyUpdatesForTesting() 1886 1887 name := userName1.String() + "," + userName2.String() 1888 1889 // create and write to a file 1890 rootNode := GetRootNodeOrBust(ctx, t, config1, name, tlf.Private) 1891 kbfsOps1 := config1.KBFSOps() 1892 aNode1, _, err := kbfsOps1.CreateFile( 1893 ctx, rootNode, testPPS("a"), false, NoExcl) 1894 require.NoError(t, err) 1895 data := []byte{1, 2, 3, 4, 5} 1896 err = kbfsOps1.Write(ctx, aNode1, data, 0) 1897 require.NoError(t, err) 1898 err = kbfsOps1.SyncAll(ctx, aNode1.GetFolderBranch()) 1899 require.NoError(t, err) 1900 1901 // look it up on user2 1902 rootNode2 := GetRootNodeOrBust(ctx, t, config2, name, tlf.Private) 1903 1904 kbfsOps2 := config2.KBFSOps() 1905 _, _, err = kbfsOps2.Lookup(ctx, rootNode2, testPPS("a")) 1906 require.NoError(t, err) 1907 // disable updates and CR on user 2 1908 c, err := DisableUpdatesForTesting(config2, rootNode2.GetFolderBranch()) 1909 require.NoError(t, err) 1910 err = DisableCRForTesting(config2, rootNode2.GetFolderBranch()) 1911 require.NoError(t, err) 1912 1913 // User 1 truncates file a. 1914 err = kbfsOps1.Truncate(ctx, aNode1, 0) 1915 require.NoError(t, err) 1916 err = kbfsOps1.SyncAll(ctx, aNode1.GetFolderBranch()) 1917 require.NoError(t, err) 1918 1919 // User 2 creates a file to start a conflict branch. 1920 _, _, err = kbfsOps2.CreateFile(ctx, rootNode2, testPPS("b"), false, NoExcl) 1921 require.NoError(t, err) 1922 err = kbfsOps2.SyncAll(ctx, rootNode2.GetFolderBranch()) 1923 require.NoError(t, err) 1924 1925 onPutStalledCh, putUnstallCh, putCtx := 1926 StallMDOp(ctx, config2, StallableMDPutUnmerged, 1) 1927 1928 var wg sync.WaitGroup 1929 putCtx, cancel2 := context.WithCancel(putCtx) 1930 wg.Add(1) 1931 go func() { 1932 defer wg.Done() 1933 _, _, err = kbfsOps2.CreateFile( 1934 putCtx, rootNode2, testPPS("c"), false, NoExcl) 1935 require.NoError(t, err) 1936 err = kbfsOps2.SyncAll(putCtx, rootNode2.GetFolderBranch()) 1937 // Even though internally folderBranchOps ignores the 1938 // cancellation error when putting on an unmerged branch, the 1939 // wrapper function *might* still return it. 1940 if err != nil { 1941 assert.Equal(t, context.Canceled, err) 1942 } 1943 1944 }() 1945 <-onPutStalledCh 1946 cancel2() 1947 close(putUnstallCh) 1948 wg.Wait() 1949 1950 // At this point, the local unmerged head doesn't match the 1951 // server's unmerged head, but CR will fix it up. 1952 1953 c <- struct{}{} 1954 err = RestartCRForTesting( 1955 libcontext.BackgroundContextWithCancellationDelayer(), config2, 1956 rootNode2.GetFolderBranch()) 1957 require.NoError(t, err) 1958 err = kbfsOps2.SyncFromServer(ctx, 1959 rootNode2.GetFolderBranch(), nil) 1960 require.NoError(t, err) 1961 1962 // Make sure they both see the same set of children. 1963 expectedChildren := []string{ 1964 "a", 1965 "b", 1966 "c", 1967 } 1968 children2, err := kbfsOps2.GetDirChildren(ctx, rootNode2) 1969 require.NoError(t, err) 1970 assert.Equal(t, len(expectedChildren), len(children2)) 1971 for _, child := range expectedChildren { 1972 _, ok := children2[rootNode2.ChildName(child)] 1973 assert.True(t, ok) 1974 } 1975 } 1976 1977 func TestForceStuckConflict(t *testing.T) { 1978 tempdir, err := ioutil.TempDir(os.TempDir(), "journal_for_stuck_cr") 1979 defer os.RemoveAll(tempdir) 1980 require.NoError(t, err) 1981 1982 var u1 kbname.NormalizedUsername = "u1" 1983 config, _, ctx, cancel := kbfsOpsConcurInit(t, u1) 1984 defer kbfsConcurTestShutdown(ctx, t, config, cancel) 1985 1986 t.Log("Enable journaling") 1987 err = config.EnableDiskLimiter(tempdir) 1988 require.NoError(t, err) 1989 err = config.EnableJournaling( 1990 ctx, tempdir, TLFJournalBackgroundWorkEnabled) 1991 require.NoError(t, err) 1992 jManager, err := GetJournalManager(config) 1993 require.NoError(t, err) 1994 err = jManager.EnableAuto(ctx) 1995 require.NoError(t, err) 1996 1997 name := "u1" 1998 h, err := tlfhandle.ParseHandle( 1999 ctx, config.KBPKI(), config.MDOps(), nil, name, tlf.Private) 2000 require.NoError(t, err) 2001 kbfsOps := config.KBFSOps() 2002 2003 t.Log("Initialize the TLF") 2004 rootNode, _, err := kbfsOps.GetOrCreateRootNode(ctx, h, data.MasterBranch) 2005 require.NoError(t, err) 2006 _, _, err = kbfsOps.CreateDir(ctx, rootNode, testPPS("a")) 2007 require.NoError(t, err) 2008 err = kbfsOps.SyncAll(ctx, rootNode.GetFolderBranch()) 2009 require.NoError(t, err) 2010 2011 t.Log("Force a conflict") 2012 tlfID := rootNode.GetFolderBranch().Tlf 2013 err = kbfsOps.ForceStuckConflictForTesting(ctx, tlfID) 2014 require.NoError(t, err) 2015 2016 t.Log("Ensure conflict files are there") 2017 children, err := kbfsOps.GetDirChildren(ctx, rootNode) 2018 require.NoError(t, err) 2019 require.Len(t, children, 1+(maxConflictResolutionAttempts+1)) 2020 2021 t.Log("Ensure uploads can't be canceled") 2022 err = kbfsOps.CancelUploads(ctx, rootNode.GetFolderBranch()) 2023 require.Error(t, err) 2024 2025 t.Log("Clear conflict view") 2026 err = kbfsOps.ClearConflictView(ctx, tlfID) 2027 require.NoError(t, err) 2028 2029 t.Log("Ensure conflict files are gone") 2030 children, err = kbfsOps.GetDirChildren(ctx, rootNode) 2031 require.NoError(t, err) 2032 require.Len(t, children, 1) 2033 } 2034 2035 // Tests that if clearing a CR conflict can fast-forward if needed. 2036 func TestBasicCRFailureClearAndFastForward(t *testing.T) { 2037 t.Skip() 2038 tempdir, err := ioutil.TempDir(os.TempDir(), "journal_for_fail_fix") 2039 defer os.RemoveAll(tempdir) 2040 2041 // simulate two users 2042 var userName1, userName2 kbname.NormalizedUsername = "u1", "u2" 2043 config1, _, ctx, cancel := kbfsOpsConcurInit(t, userName1, userName2) 2044 defer kbfsConcurTestShutdown(ctx, t, config1, cancel) 2045 2046 config2 := ConfigAsUser(config1, userName2) 2047 defer CheckConfigAndShutdown(ctx, t, config2) 2048 2049 // Enable journaling on user 2 2050 require.NoError(t, err) 2051 err = config2.EnableDiskLimiter(tempdir) 2052 require.NoError(t, err) 2053 err = config2.EnableJournaling(ctx, tempdir, 2054 TLFJournalBackgroundWorkEnabled) 2055 require.NoError(t, err) 2056 jManager, err := GetJournalManager(config2) 2057 require.NoError(t, err) 2058 err = jManager.EnableAuto(ctx) 2059 require.NoError(t, err) 2060 2061 name := userName1.String() + "," + userName2.String() 2062 2063 t.Log("User 1 creates a file a/b.") 2064 rootNode1 := GetRootNodeOrBust(ctx, t, config1, name, tlf.Private) 2065 2066 kbfsOps1 := config1.KBFSOps() 2067 dirA1, _, err := kbfsOps1.CreateDir(ctx, rootNode1, testPPS("a")) 2068 require.NoError(t, err) 2069 fileB1, _, err := kbfsOps1.CreateFile( 2070 ctx, dirA1, testPPS("b"), false, NoExcl) 2071 require.NoError(t, err) 2072 err = kbfsOps1.SyncAll(ctx, rootNode1.GetFolderBranch()) 2073 require.NoError(t, err) 2074 2075 t.Log("User 2 looks up the file node.") 2076 rootNode2 := GetRootNodeOrBust(ctx, t, config2, name, tlf.Private) 2077 kbfsOps2 := config2.KBFSOps() 2078 dirA2, _, err := kbfsOps2.Lookup(ctx, rootNode2, testPPS("a")) 2079 require.NoError(t, err) 2080 fileB2, _, err := kbfsOps2.Lookup(ctx, dirA2, testPPS("b")) 2081 require.NoError(t, err) 2082 2083 t.Log("Force a conflict") 2084 tlfID := rootNode2.GetFolderBranch().Tlf 2085 err = kbfsOps2.ForceStuckConflictForTesting(ctx, tlfID) 2086 require.NoError(t, err) 2087 2088 t.Log("User 1 updates mod time on a/b.") 2089 2090 mtime := time.Now() 2091 for i := 0; i < fastForwardRevThresh+2; i++ { 2092 mtime = mtime.Add(1 * time.Minute) 2093 err = kbfsOps1.SetMtime(ctx, fileB1, &mtime) 2094 require.NoError(t, err) 2095 err = kbfsOps1.SyncAll(ctx, fileB1.GetFolderBranch()) 2096 require.NoError(t, err) 2097 } 2098 2099 t.Log("Ensure only conflict files are in the conflict view") 2100 children, err := kbfsOps2.GetDirChildren(ctx, rootNode2) 2101 require.NoError(t, err) 2102 require.Len(t, children, 1+(maxConflictResolutionAttempts+1)) 2103 2104 // Expect updates for each of the unmerged revisions, plus exactly 2105 // one for the fast forward (but not more). However, if the 2106 // conflict file nodes haven't been garbage collected yet, we 2107 // might get 3x that (one batch for each sync op, one for each 2108 // remove op, and one for each resolution op), so allow some 2109 // wiggle room. This still isn't big enough to hold all of the 2110 // changes we'd get in a non-fast-forward though. 2111 numUpdatesExpected := 3*(maxConflictResolutionAttempts+1) + 1 2112 c := make(chan struct{}, numUpdatesExpected) 2113 cro := &testCRObserver{c, nil} 2114 err = config2.Notifier().RegisterForChanges( 2115 []data.FolderBranch{rootNode2.GetFolderBranch()}, cro) 2116 require.NoError(t, err) 2117 2118 t.Log("Clear the conflict state and re-enable CR.") 2119 err = kbfsOps2.ClearConflictView(ctx, tlfID) 2120 require.NoError(t, err) 2121 2122 t.Log("Ensure conflict files are gone") 2123 children, err = kbfsOps2.GetDirChildren(ctx, rootNode2) 2124 require.NoError(t, err) 2125 require.Len(t, children, 1) 2126 2127 ei, err := kbfsOps2.Stat(ctx, fileB2) 2128 require.NoError(t, err) 2129 require.Equal(t, mtime.UnixNano(), ei.Mtime) 2130 2131 err = kbfsOps2.SyncFromServer(ctx, rootNode2.GetFolderBranch(), nil) 2132 require.NoError(t, err) 2133 }