Exemple #1
0
// CurrentSession uses the global session to find the session.  If
// the user isn't logged in, it returns ErrNoSession.
func (h *SessionHandler) CurrentSession(_ context.Context, sessionID int) (keybase1.Session, error) {
	var s keybase1.Session
	var token string
	var username libkb.NormalizedUsername
	var uid keybase1.UID
	var deviceSubkey libkb.GenericKey
	var err error

	aerr := h.G().LoginState().Account(func(a *libkb.Account) {
		uid, username, token, deviceSubkey, err = a.UserInfo()
	}, "Service - SessionHandler - UserInfo")
	if aerr != nil {
		return s, aerr
	}
	if err != nil {
		if _, ok := err.(libkb.LoginRequiredError); ok {
			return s, ErrNoSession
		}
		return s, err
	}

	s.Uid = uid
	s.Username = username.String()
	s.Token = token
	s.DeviceSubkeyKid = deviceSubkey.GetKID()

	return s, nil
}
Exemple #2
0
func TestBasicMDUpdate(t *testing.T) {
	// simulate two users
	var userName1, userName2 libkb.NormalizedUsername = "******", "u2"
	config1, _, ctx := kbfsOpsConcurInit(t, userName1, userName2)
	defer CheckConfigAndShutdown(t, config1)

	config2 := ConfigAsUser(config1.(*ConfigLocal), userName2)
	defer CheckConfigAndShutdown(t, config2)

	name := userName1.String() + "," + userName2.String()

	kbfsOps1 := config1.KBFSOps()
	rootNode1, _, err :=
		kbfsOps1.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Couldn't create folder: %v", err)
	}
	kbfsOps2 := config2.KBFSOps()
	rootNode2, _, err :=
		kbfsOps2.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Couldn't get root: %v", err)
	}

	_, statusChan, err := kbfsOps2.Status(ctx, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't get status")
	}

	// user 1 creates a file
	_, _, err = kbfsOps1.CreateFile(ctx, rootNode1, "a", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	err = kbfsOps2.SyncFromServer(ctx, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	entries, err := kbfsOps2.GetDirChildren(ctx, rootNode2)
	if err != nil {
		t.Fatalf("User 2 couldn't see the root dir: %v", err)
	}
	if len(entries) != 1 {
		t.Fatalf("User 2 sees wrong number of entries in root dir: %d vs 1",
			len(entries))
	}
	if _, ok := entries["a"]; !ok {
		t.Fatalf("User 2 doesn't see file a")
	}

	// The status should have fired as well (though in this case the
	// writer is the same as before)
	<-statusChan
	checkStatus(t, ctx, kbfsOps1, false, userName1, nil,
		rootNode1.GetFolderBranch(), "Node 1")
	checkStatus(t, ctx, kbfsOps2, false, userName1, nil,
		rootNode2.GetFolderBranch(), "Node 2")
}
// Same as TestCRMergedChainsSimple, but the two users make changes in
// different, unrelated subdirectories, forcing the resolver to use
// mostly original block pointers when constructing the merged path.
func TestCRMergedChainsDifferentDirectories(t *testing.T) {
	var userName1, userName2 libkb.NormalizedUsername = "******", "u2"
	config1, uid1, ctx := kbfsOpsConcurInit(t, userName1, userName2)
	defer CheckConfigAndShutdown(t, config1)

	config2 := ConfigAsUser(config1.(*ConfigLocal), userName2)
	defer CheckConfigAndShutdown(t, config2)
	_, uid2, err := config2.KBPKI().GetCurrentUserInfo(ctx)
	if err != nil {
		t.Fatal(err)
	}

	name := userName1.String() + "," + userName2.String()

	configs := make(map[keybase1.UID]Config)
	configs[uid1] = config1
	configs[uid2] = config2
	nodesA := testCRSharedFolderForUsers(t, name, uid1, configs, []string{"dirA"})
	dirA1 := nodesA[uid1]
	nodesB := testCRSharedFolderForUsers(t, name, uid1, configs, []string{"dirB"})
	dirB1 := nodesB[uid1]
	dirB2 := nodesB[uid2]
	fb := dirA1.GetFolderBranch()

	// pause user 2
	_, err = DisableUpdatesForTesting(config2, fb)
	if err != nil {
		t.Fatalf("Can't disable updates for user 2: %v", err)
	}

	// user1 makes a file in dir A
	_, _, err = config1.KBFSOps().CreateFile(ctx, dirA1, "file1", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	cr1 := testCRGetCROrBust(t, config1, fb)
	cr2 := testCRGetCROrBust(t, config2, fb)
	cr2.Shutdown()

	// user2 makes a file in dir B
	_, _, err = config2.KBFSOps().CreateFile(ctx, dirB2, "file2", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	// Now step through conflict resolution manually for user 2
	mergedPaths := make(map[BlockPointer]path)
	expectedUnmergedPath := cr2.fbo.nodeCache.PathFromNode(dirB2)
	mergedPath := cr1.fbo.nodeCache.PathFromNode(dirB1)
	mergedPaths[expectedUnmergedPath.tailPointer()] = mergedPath
	expectedActions := map[BlockPointer]crActionList{
		mergedPath.tailPointer(): {&copyUnmergedEntryAction{
			"file2", "file2", "", false, false, DirEntry{}, nil}},
	}
	testCRCheckPathsAndActions(t, cr2, []path{expectedUnmergedPath},
		mergedPaths, nil, expectedActions)
}
// Tests that conflict resolution detects and can fix rename cycles.
func TestCRMergedChainsRenameCycleSimple(t *testing.T) {
	var userName1, userName2 libkb.NormalizedUsername = "******", "u2"
	config1, uid1, ctx := kbfsOpsConcurInit(t, userName1, userName2)
	defer CheckConfigAndShutdown(t, config1)

	config2 := ConfigAsUser(config1.(*ConfigLocal), userName2)
	defer CheckConfigAndShutdown(t, config2)
	_, uid2, err := config2.KBPKI().GetCurrentUserInfo(ctx)
	if err != nil {
		t.Fatal(err)
	}

	name := userName1.String() + "," + userName2.String()

	configs := make(map[keybase1.UID]Config)
	configs[uid1] = config1
	configs[uid2] = config2
	nodesRoot := testCRSharedFolderForUsers(t, name, uid1, configs, []string{"root"})
	dirRoot1 := nodesRoot[uid1]
	dirRoot2 := nodesRoot[uid2]
	fb := dirRoot1.GetFolderBranch()

	nodesA := testCRSharedFolderForUsers(t, name, uid1, configs,
		[]string{"root", "dirA"})
	dirA1 := nodesA[uid1]

	nodesB := testCRSharedFolderForUsers(t, name, uid1, configs,
		[]string{"root", "dirB"})
	dirB1 := nodesB[uid1]
	dirB2 := nodesB[uid2]

	cr1 := testCRGetCROrBust(t, config1, fb)
	cr2 := testCRGetCROrBust(t, config2, fb)
	cr2.Shutdown()

	dirRootPtr := cr2.fbo.nodeCache.PathFromNode(dirRoot2).tailPointer()

	// pause user 2
	_, err = DisableUpdatesForTesting(config2, fb)
	if err != nil {
		t.Fatalf("Can't disable updates for user 2: %v", err)
	}

	// user1 moves dirB into dirA
	err = config1.KBFSOps().Rename(ctx, dirRoot1, "dirB", dirA1, "dirB")
	if err != nil {
		t.Fatalf("Couldn't make dir: %v", err)
	}

	// user2 moves dirA into dirB
	err = config2.KBFSOps().Rename(ctx, dirRoot2, "dirA", dirB2, "dirA")
	if err != nil {
		t.Fatalf("Couldn't make dir: %v", err)
	}

	// Now step through conflict resolution manually for user 2

	mergedPaths := make(map[BlockPointer]path)

	// root
	unmergedPathRoot := cr2.fbo.nodeCache.PathFromNode(dirRoot2)
	mergedPathRoot := cr1.fbo.nodeCache.PathFromNode(dirRoot1)
	mergedPaths[unmergedPathRoot.tailPointer()] = mergedPathRoot
	unmergedPathB := cr2.fbo.nodeCache.PathFromNode(dirB2)
	mergedPathB := cr1.fbo.nodeCache.PathFromNode(dirB1)
	mergedPaths[unmergedPathB.tailPointer()] = mergedPathB

	ro := newRmOp("dirA", dirRootPtr)
	ro.Dir.Ref = unmergedPathRoot.tailPointer()
	ro.dropThis = true
	ro.setWriterInfo(writerInfo{name: "u2"})
	ro.setFinalPath(unmergedPathRoot)
	expectedActions := map[BlockPointer]crActionList{
		mergedPathRoot.tailPointer(): {&dropUnmergedAction{ro}},
		mergedPathB.tailPointer(): {&copyUnmergedEntryAction{
			"dirA", "dirA", "./../", false, false, DirEntry{}, nil}},
	}

	testCRCheckPathsAndActions(t, cr2, []path{unmergedPathRoot, unmergedPathB},
		mergedPaths, nil, expectedActions)
}
func TestRekeyQueueBasic(t *testing.T) {
	var u1, u2, u3, u4 libkb.NormalizedUsername = "******", "u2", "u3", "u4"
	config1, _, ctx := kbfsOpsConcurInit(t, u1, u2, u3, u4)
	defer config1.Shutdown()

	config2 := ConfigAsUser(config1.(*ConfigLocal), u2)
	defer config2.Shutdown()
	_, uid2, err := config2.KBPKI().GetCurrentUserInfo(context.Background())
	if err != nil {
		t.Fatal(err)
	}

	config3 := ConfigAsUser(config1.(*ConfigLocal), u3)
	defer config3.Shutdown()
	_, _, err = config3.KBPKI().GetCurrentUserInfo(context.Background())
	if err != nil {
		t.Fatal(err)
	}

	config4 := ConfigAsUser(config1.(*ConfigLocal), u4)
	defer config4.Shutdown()
	_, _, err = config4.KBPKI().GetCurrentUserInfo(context.Background())
	if err != nil {
		t.Fatal(err)
	}

	kbfsOps1 := config1.KBFSOps()
	var names []string

	// Create a few shared folders
	for i := 0; i < 3; i++ {
		writers := []string{u1.String(), u2.String()}
		if i > 0 {
			writers = append(writers, u3.String())
		}
		if i > 1 {
			writers = append(writers, u4.String())
		}
		name := strings.Join(writers, ",")
		names = append(names, name)
		// user 1 creates the directory
		rootNode1 := GetRootNodeOrBust(t, config1, name, false)
		// user 1 creates a file
		_, _, err = kbfsOps1.CreateFile(ctx, rootNode1, "a", false)
		if err != nil {
			t.Fatalf("Couldn't create file: %v", err)
		}
	}

	// Create a new device for user 2
	config2Dev2 := ConfigAsUser(config1.(*ConfigLocal), u2)
	defer config2Dev2.Shutdown()
	AddDeviceForLocalUserOrBust(t, config1, uid2)
	AddDeviceForLocalUserOrBust(t, config2, uid2)
	devIndex := AddDeviceForLocalUserOrBust(t, config2Dev2, uid2)
	SwitchDeviceForLocalUserOrBust(t, config2Dev2, devIndex)

	// user 2 should be unable to read the data now since its device
	// wasn't registered when the folder was originally created.
	for _, name := range names {
		_, err := GetRootNodeForTest(config2Dev2, name, false)
		if _, ok := err.(NeedSelfRekeyError); !ok {
			t.Fatalf("Got unexpected error when reading with new key: %v", err)
		}
	}

	var rekeyChannels []<-chan error

	// now user 1 should rekey via its rekey worker
	for _, name := range names {
		rootNode1 := GetRootNodeOrBust(t, config1, name, false)
		// queue it for rekey
		c := config1.RekeyQueue().Enqueue(rootNode1.GetFolderBranch().Tlf)
		rekeyChannels = append(rekeyChannels, c)
	}

	// listen for all of the rekey results
	for _, c := range rekeyChannels {
		if err := <-c; err != nil {
			t.Fatal(err)
		}
	}

	// user 2's new device should be able to read now
	for _, name := range names {
		_ = GetRootNodeOrBust(t, config2Dev2, name, false)
	}
}
Exemple #6
0
// Two devices conflict when revoking a 3rd device.
// Test that after this both can still read the latest version of the folder.
func TestKeyManagerRekeyAddAndRevokeDeviceWithConflict(t *testing.T) {
	var u1, u2 libkb.NormalizedUsername = "******", "u2"
	config1, _, ctx := kbfsOpsConcurInit(t, u1, u2)
	defer CheckConfigAndShutdown(t, config1)

	config2 := ConfigAsUser(config1.(*ConfigLocal), u2)
	defer CheckConfigAndShutdown(t, config2)
	uid2, err := config2.KBPKI().GetCurrentUID(context.Background())
	if err != nil {
		t.Fatal(err)
	}

	// create a shared folder
	name := u1.String() + "," + u2.String()

	kbfsOps1 := config1.KBFSOps()
	rootNode1, _, err :=
		kbfsOps1.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Couldn't create folder: %v", err)
	}

	// user 1 creates a file
	_, _, err = kbfsOps1.CreateFile(ctx, rootNode1, "a", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	config2Dev2 := ConfigAsUser(config1.(*ConfigLocal), u2)
	defer CheckConfigAndShutdown(t, config2Dev2)

	// give user 2 a new device
	AddDeviceForLocalUserOrBust(t, config1, uid2)
	AddDeviceForLocalUserOrBust(t, config2, uid2)
	devIndex := AddDeviceForLocalUserOrBust(t, config2Dev2, uid2)
	SwitchDeviceForLocalUserOrBust(t, config2Dev2, devIndex)

	// user 2 should be unable to read the data now since its device
	// wasn't registered when the folder was originally created.
	kbfsOps2Dev2 := config2Dev2.KBFSOps()
	root2Dev2, _, err :=
		kbfsOps2Dev2.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if _, ok := err.(ReadAccessError); !ok {
		t.Fatalf("Got unexpected error when reading with new key: %v", err)
	}

	// now user 1 should rekey
	err = kbfsOps1.Rekey(ctx, rootNode1.GetFolderBranch().Tlf)
	if err != nil {
		t.Fatalf("Couldn't rekey: %v", err)
	}

	// this device should be able to read now
	root2Dev2, _, err =
		kbfsOps2Dev2.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Got unexpected error after rekey: %v", err)
	}

	// Now revoke the original user 2 device
	RevokeDeviceForLocalUserOrBust(t, config1, uid2, 0)
	RevokeDeviceForLocalUserOrBust(t, config2Dev2, uid2, 0)

	// disable updates on user 1
	c, err := DisableUpdatesForTesting(config1, rootNode1.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't disable updates: %v", err)
	}

	// rekey again but with user 2 device 2
	err = kbfsOps2Dev2.Rekey(ctx, root2Dev2.GetFolderBranch().Tlf)
	if err != nil {
		t.Fatalf("Couldn't rekey: %v", err)
	}

	// have user 1 also try to rekey but fail due to conflict
	err = kbfsOps1.Rekey(ctx, rootNode1.GetFolderBranch().Tlf)
	if _, isConflict := err.(MDServerErrorConflictRevision); !isConflict {
		t.Fatalf("Expected failure due to conflict")
	}

	// device 1 re-enables updates
	c <- struct{}{}
	err = kbfsOps1.SyncFromServer(ctx, rootNode1.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	err = kbfsOps2Dev2.SyncFromServer(ctx, root2Dev2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	// force re-encryption of the root dir
	_, _, err = kbfsOps2Dev2.CreateFile(ctx, root2Dev2, "b", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	// device 1 should still work
	err = kbfsOps1.SyncFromServer(ctx, rootNode1.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	rootNode1, _, err =
		kbfsOps1.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Got unexpected error after rekey: %v", err)
	}

	children, err := kbfsOps1.GetDirChildren(ctx, rootNode1)
	if _, ok := children["b"]; !ok {
		t.Fatalf("Device 1 couldn't see the new dir entry")
	}
}
Exemple #7
0
// Tests that multiple users can write to the same file sequentially
// without any problems.
func TestMultiUserWrite(t *testing.T) {
	// simulate two users
	var userName1, userName2 libkb.NormalizedUsername = "******", "u2"
	config1, _, ctx := kbfsOpsConcurInit(t, userName1, userName2)
	defer CheckConfigAndShutdown(t, config1)

	config2 := ConfigAsUser(config1.(*ConfigLocal), userName2)
	defer CheckConfigAndShutdown(t, config2)

	name := userName1.String() + "," + userName2.String()

	// user1 creates a file in a shared dir
	rootNode1 := GetRootNodeOrBust(t, config1, name, false)

	kbfsOps1 := config1.KBFSOps()
	_, _, err := kbfsOps1.CreateFile(ctx, rootNode1, "a", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	// then user2 write to the file
	rootNode2 := GetRootNodeOrBust(t, config2, name, false)

	kbfsOps2 := config2.KBFSOps()
	fileNode2, _, err := kbfsOps2.Lookup(ctx, rootNode2, "a")
	if err != nil {
		t.Fatalf("Couldn't lookup file: %v", err)
	}

	data2 := []byte{2}
	err = kbfsOps2.Write(ctx, fileNode2, data2, 0)
	if err != nil {
		t.Fatalf("Couldn't write file: %v", err)
	}
	// Write twice to make sure that multiple write operations within
	// a sync work when the writer is changing.
	err = kbfsOps2.Write(ctx, fileNode2, data2, 0)
	if err != nil {
		t.Fatalf("Couldn't write file: %v", err)
	}
	err = kbfsOps2.Sync(ctx, fileNode2)
	if err != nil {
		t.Fatalf("Couldn't sync file: %v", err)
	}
	readAndCompareData(t, config2, ctx, name, data2, userName2)

	// A second write by the same user
	data3 := []byte{3}
	err = kbfsOps2.Write(ctx, fileNode2, data3, 0)
	if err != nil {
		t.Fatalf("Couldn't write file: %v", err)
	}
	err = kbfsOps2.Sync(ctx, fileNode2)
	if err != nil {
		t.Fatalf("Couldn't sync file: %v", err)
	}

	readAndCompareData(t, config2, ctx, name, data3, userName2)

	err = kbfsOps1.SyncFromServerForTesting(ctx, rootNode1.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}
	readAndCompareData(t, config1, ctx, name, data3, userName2)
}
// Test that quota reclamation works for a simple case where the user
// does a few updates, then lets quota reclamation run, and we make
// sure that all historical blocks have been deleted.
func TestQuotaReclamationSimple(t *testing.T) {
	var userName libkb.NormalizedUsername = "******"
	config, _, ctx := kbfsOpsInitNoMocks(t, userName)
	defer CheckConfigAndShutdown(t, config)

	clock, now := newTestClockAndTimeNow()
	config.SetClock(clock)

	rootNode := GetRootNodeOrBust(t, config, userName.String(), false)
	kbfsOps := config.KBFSOps()
	_, _, err := kbfsOps.CreateDir(ctx, rootNode, "a")
	if err != nil {
		t.Fatalf("Couldn't create dir: %v", err)
	}
	err = kbfsOps.RemoveDir(ctx, rootNode, "a")
	if err != nil {
		t.Fatalf("Couldn't remove dir: %v", err)
	}

	// Wait for outstanding archives
	err = kbfsOps.SyncFromServerForTesting(ctx, rootNode.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	// Make sure no blocks are deleted before there's a new-enough update.
	bserverLocal, ok := config.BlockServer().(*BlockServerLocal)
	if !ok {
		t.Fatalf("Bad block server")
	}
	preQR1Blocks, err := bserverLocal.getAll(rootNode.GetFolderBranch().Tlf)
	if err != nil {
		t.Fatalf("Couldn't get blocks: %v", err)
	}

	ops := kbfsOps.(*KBFSOpsStandard).getOpsByNode(ctx, rootNode)
	ops.fbm.forceQuotaReclamation()
	err = ops.fbm.waitForQuotaReclamations(ctx)
	if err != nil {
		t.Fatalf("Couldn't wait for QR: %v", err)
	}

	postQR1Blocks, err := bserverLocal.getAll(rootNode.GetFolderBranch().Tlf)
	if err != nil {
		t.Fatalf("Couldn't get blocks: %v", err)
	}

	if !reflect.DeepEqual(preQR1Blocks, postQR1Blocks) {
		t.Fatalf("Blocks deleted too early (%v vs %v)!",
			preQR1Blocks, postQR1Blocks)
	}

	// Increase the time and make a new revision, but don't run quota
	// reclamation yet.
	clock.Set(now.Add(2 * config.QuotaReclamationMinUnrefAge()))
	_, _, err = kbfsOps.CreateDir(ctx, rootNode, "b")
	if err != nil {
		t.Fatalf("Couldn't create dir: %v", err)
	}

	preQR2Blocks, err := bserverLocal.getAll(rootNode.GetFolderBranch().Tlf)
	if err != nil {
		t.Fatalf("Couldn't get blocks: %v", err)
	}

	ops.fbm.forceQuotaReclamation()
	err = ops.fbm.waitForQuotaReclamations(ctx)
	if err != nil {
		t.Fatalf("Couldn't wait for QR: %v", err)
	}

	postQR2Blocks, err := bserverLocal.getAll(rootNode.GetFolderBranch().Tlf)
	if err != nil {
		t.Fatalf("Couldn't get blocks: %v", err)
	}

	if pre, post := totalBlockRefs(preQR2Blocks),
		totalBlockRefs(postQR2Blocks); post >= pre {
		t.Errorf("Blocks didn't shrink after reclamation: pre: %d, post %d",
			pre, post)
	}
}
// Test that a single quota reclamation run doesn't try to reclaim too
// much quota at once.
func TestQuotaReclamationIncrementalReclamation(t *testing.T) {
	var userName libkb.NormalizedUsername = "******"
	config, _, ctx := kbfsOpsInitNoMocks(t, userName)
	defer CheckConfigAndShutdown(t, config)

	now := time.Now()
	var clock TestClock
	clock.Set(now)
	config.SetClock(&clock)

	rootNode := GetRootNodeOrBust(t, config, userName.String(), false)
	// Do a bunch of operations.
	kbfsOps := config.KBFSOps()
	for i := 0; i < numPointersPerGCThreshold; i++ {
		_, _, err := kbfsOps.CreateDir(ctx, rootNode, "a")
		if err != nil {
			t.Fatalf("Couldn't create dir: %v", err)
		}
		err = kbfsOps.RemoveDir(ctx, rootNode, "a")
		if err != nil {
			t.Fatalf("Couldn't remove dir: %v", err)
		}
	}

	// Increase the time, and make sure that there is still more than
	// one block in the history
	clock.Set(now.Add(2 * config.QuotaReclamationMinUnrefAge()))

	// Run it.
	ops := kbfsOps.(*KBFSOpsStandard).getOpsByNode(ctx, rootNode)
	ops.fbm.forceQuotaReclamation()
	err := ops.fbm.waitForQuotaReclamations(ctx)
	if err != nil {
		t.Fatalf("Couldn't wait for QR: %v", err)
	}

	bserverLocal, ok := config.BlockServer().(*BlockServerLocal)
	if !ok {
		t.Fatalf("Bad block server")
	}
	blocks, err := bserverLocal.getAll(rootNode.GetFolderBranch().Tlf)
	if err != nil {
		t.Fatalf("Couldn't get blocks: %v", err)
	}

	b := totalBlockRefs(blocks)
	if b <= 1 {
		t.Errorf("Too many blocks left after first QR: %d", b)
	}

	// Now let it run to completion
	for b > 1 {
		ops.fbm.forceQuotaReclamation()
		err = ops.fbm.waitForQuotaReclamations(ctx)
		if err != nil {
			t.Fatalf("Couldn't wait for QR: %v", err)
		}

		blocks, err := bserverLocal.getAll(rootNode.GetFolderBranch().Tlf)
		if err != nil {
			t.Fatalf("Couldn't get blocks: %v", err)
		}
		oldB := b
		b = totalBlockRefs(blocks)
		if b >= oldB {
			t.Fatalf("Blocks didn't shrink after reclamation: %d vs. %d",
				b, oldB)
		}
	}
}
Exemple #10
0
// Tests that two users can make independent writes while forked, and
// conflict resolution will merge them correctly and the rekey bit is
// preserved until rekey.
func TestBasicCRFileConflictWithRekey(t *testing.T) {
	// simulate two users
	var userName1, userName2 libkb.NormalizedUsername = "******", "u2"
	config1, _, ctx := kbfsOpsConcurInit(t, userName1, userName2)
	defer CheckConfigAndShutdown(t, config1)
	config1.MDServer().DisableRekeyUpdatesForTesting()

	config2 := ConfigAsUser(config1.(*ConfigLocal), userName2)
	defer CheckConfigAndShutdown(t, config2)
	uid2, err := config2.KBPKI().GetCurrentUID(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	config2.MDServer().DisableRekeyUpdatesForTesting()

	now := time.Now()
	config2.SetClock(&TestClock{now})
	name := userName1.String() + "," + userName2.String()

	// user1 creates a file in a shared dir
	kbfsOps1 := config1.KBFSOps()
	rootNode1, _, err :=
		kbfsOps1.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Couldn't create folder: %v", err)
	}
	dirA1, _, err := kbfsOps1.CreateDir(ctx, rootNode1, "a")
	if err != nil {
		t.Fatalf("Couldn't create dir: %v", err)
	}
	fileB1, _, err := kbfsOps1.CreateFile(ctx, dirA1, "b", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	// look it up on user2
	kbfsOps2 := config2.KBFSOps()
	rootNode2, _, err :=
		kbfsOps2.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Couldn't create folder: %v", err)
	}
	dirA2, _, err := kbfsOps2.Lookup(ctx, rootNode2, "a")
	if err != nil {
		t.Fatalf("Couldn't lookup dir: %v", err)
	}
	fileB2, _, err := kbfsOps2.Lookup(ctx, dirA2, "b")
	if err != nil {
		t.Fatalf("Couldn't lookup file: %v", err)
	}

	config2Dev2 := ConfigAsUser(config1.(*ConfigLocal), userName2)
	// we don't check the config because this device can't read all of the md blocks.
	defer config2Dev2.Shutdown()
	config2Dev2.MDServer().DisableRekeyUpdatesForTesting()

	// Now give u2 a new device.  The configs don't share a Keybase
	// Daemon so we have to do it in all places.
	AddDeviceForLocalUserOrBust(t, config1, uid2)
	AddDeviceForLocalUserOrBust(t, config2, uid2)
	devIndex := AddDeviceForLocalUserOrBust(t, config2Dev2, uid2)
	SwitchDeviceForLocalUserOrBust(t, config2Dev2, devIndex)

	// user2 device 2 should be unable to read the data now since its device
	// wasn't registered when the folder was originally created.
	kbfsOps2Dev2 := config2Dev2.KBFSOps()
	_, _, err =
		kbfsOps2Dev2.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if _, ok := err.(ReadAccessError); !ok {
		t.Fatalf("Got unexpected error when reading with new key: %v", err)
	}

	// User 2 syncs
	err = kbfsOps2.SyncFromServer(ctx, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	// disable updates on user2
	c, err := DisableUpdatesForTesting(config2, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't disable updates: %v", err)
	}

	// User 1 writes the file
	data1 := []byte{1, 2, 3, 4, 5}
	err = kbfsOps1.Write(ctx, fileB1, data1, 0)
	if err != nil {
		t.Fatalf("Couldn't write file: %v", err)
	}
	err = kbfsOps1.Sync(ctx, fileB1)
	if err != nil {
		t.Fatalf("Couldn't sync file: %v", err)
	}

	// User 2 dev 2 should set the rekey bit
	err = kbfsOps2Dev2.Rekey(ctx, rootNode2.GetFolderBranch().Tlf)
	if err != nil {
		t.Fatalf("Couldn't set rekey bit: %v", err)
	}

	// User 1 syncs
	err = kbfsOps1.SyncFromServer(ctx, rootNode1.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	// User 2 makes a new different file
	data2 := []byte{5, 4, 3, 2, 1}
	err = kbfsOps2.Write(ctx, fileB2, data2, 0)
	if err != nil {
		t.Fatalf("Couldn't write file: %v", err)
	}
	err = kbfsOps2.Sync(ctx, fileB2)
	if err != nil {
		t.Fatalf("Couldn't sync file: %v", err)
	}

	// re-enable updates, and wait for CR to complete.
	// this should also cause a rekey of the folder.
	c <- struct{}{}
	err = kbfsOps2.SyncFromServer(ctx, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}
	// wait for the rekey to happen
	waitForRekey(t, config2, rootNode2.GetFolderBranch().Tlf)

	err = kbfsOps1.SyncFromServer(ctx, rootNode1.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	err = kbfsOps2Dev2.SyncFromServer(ctx, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	// look it up on user 2 dev 2
	rootNode2Dev2, _, err :=
		kbfsOps2Dev2.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Couldn't create folder: %v", err)
	}
	dirA2Dev2, _, err := kbfsOps2Dev2.Lookup(ctx, rootNode2Dev2, "a")
	if err != nil {
		t.Fatalf("Couldn't lookup dir: %v", err)
	}

	// Make sure they all see the same set of children
	expectedChildren := []string{
		"b",
		"b.conflict.u2." + now.Format(time.RFC3339Nano),
	}
	children1, err := kbfsOps1.GetDirChildren(ctx, dirA1)
	if err != nil {
		t.Fatalf("Couldn't get children: %v", err)
	}

	children2, err := kbfsOps2.GetDirChildren(ctx, dirA2)
	if err != nil {
		t.Fatalf("Couldn't get children: %v", err)
	}

	children2Dev2, err := kbfsOps2Dev2.GetDirChildren(ctx, dirA2Dev2)
	if err != nil {
		t.Fatalf("Couldn't get children: %v", err)
	}

	if g, e := len(children1), len(expectedChildren); g != e {
		t.Errorf("Wrong number of children: %d vs %d", g, e)
	}

	for _, child := range expectedChildren {
		if _, ok := children1[child]; !ok {
			t.Errorf("Couldn't find child %s", child)
		}
	}

	if !reflect.DeepEqual(children1, children2) {
		t.Fatalf("Users 1 and 2 see different children: %v vs %v",
			children1, children2)
	}

	if !reflect.DeepEqual(children2, children2Dev2) {
		t.Fatalf("User 2 device 1 and 2 see different children: %v vs %v",
			children2, children2Dev2)
	}
}
Exemple #11
0
// Tests that multiple users can write to the same file sequentially
// without any problems.
func TestMultiUserWrite(t *testing.T) {
	// simulate two users
	var userName1, userName2 libkb.NormalizedUsername = "******", "u2"
	config1, _, ctx := kbfsOpsConcurInit(t, userName1, userName2)
	defer CheckConfigAndShutdown(t, config1)

	config2 := ConfigAsUser(config1.(*ConfigLocal), userName2)
	defer CheckConfigAndShutdown(t, config2)

	name := userName1.String() + "," + userName2.String()

	// user1 creates a file in a shared dir
	kbfsOps1 := config1.KBFSOps()
	rootNode1, _, err :=
		kbfsOps1.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Errorf("Couldn't create folder: %v", err)
	}
	_, _, err = kbfsOps1.CreateFile(ctx, rootNode1, "a", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	// then user2 write to the file
	kbfsOps2 := config2.KBFSOps()
	rootNode2, _, err :=
		kbfsOps2.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Errorf("Couldn't create folder: %v", err)
	}
	fileNode2, _, err := kbfsOps2.Lookup(ctx, rootNode2, "a")
	if err != nil {
		t.Fatalf("Couldn't lookup file: %v", err)
	}

	data2 := []byte{2}
	err = kbfsOps2.Write(ctx, fileNode2, data2, 0)
	if err != nil {
		t.Fatalf("Couldn't write file: %v", err)
	}

	// The writer should be user 2, even before the Sync
	ops := kbfsOps2.(*KBFSOpsStandard).getOpsByNode(fileNode2)
	de, err := ops.statEntry(ctx, fileNode2)
	if err != nil {
		t.Fatalf("Couldn't lookup file: %v", err)
	}

	uid2, err := config2.KBPKI().GetCurrentUID(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	if de.GetWriter() != uid2 {
		t.Errorf("After user 2's first write, Writer is wrong: %v",
			de.GetWriter())
	}

	err = kbfsOps2.Sync(ctx, fileNode2)
	if err != nil {
		t.Fatalf("Couldn't sync file: %v", err)
	}
	data3 := []byte{3}
	err = kbfsOps2.Write(ctx, fileNode2, data3, 0)
	if err != nil {
		t.Fatalf("Couldn't write file: %v", err)
	}
	err = kbfsOps2.Sync(ctx, fileNode2)
	if err != nil {
		t.Fatalf("Couldn't sync file: %v", err)
	}

	readAndCompareData(t, config2, ctx, name, data3, userName2)
}
Exemple #12
0
func testMultipleMDUpdates(t *testing.T, unembedChanges bool) {
	// simulate two users
	var userName1, userName2 libkb.NormalizedUsername = "******", "u2"
	config1, _, ctx := kbfsOpsConcurInit(t, userName1, userName2)
	defer CheckConfigAndShutdown(t, config1)

	config2 := ConfigAsUser(config1.(*ConfigLocal), userName2)
	defer CheckConfigAndShutdown(t, config2)

	if unembedChanges {
		bss1, ok1 := config1.BlockSplitter().(*BlockSplitterSimple)
		bss2, ok2 := config2.BlockSplitter().(*BlockSplitterSimple)
		if !ok1 || !ok2 {
			t.Fatalf("Couldn't convert BlockSplitters!")
		}
		bss1.blockChangeEmbedMaxSize = 3
		bss2.blockChangeEmbedMaxSize = 3
	}

	name := userName1.String() + "," + userName2.String()

	kbfsOps1 := config1.KBFSOps()
	rootNode1, _, err :=
		kbfsOps1.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Couldn't create folder: %v", err)
	}
	// user 1 creates a file
	_, _, err = kbfsOps1.CreateFile(ctx, rootNode1, "a", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	// user 2 looks up the directory (and sees the file)
	kbfsOps2 := config2.KBFSOps()
	rootNode2, _, err :=
		kbfsOps2.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Errorf("Couldn't get root: %v", err)
	}

	// now user 1 renames the old file, and creates a new one
	err = kbfsOps1.Rename(ctx, rootNode1, "a", rootNode1, "b")
	if err != nil {
		t.Fatalf("Couldn't rename file: %v", err)
	}
	_, _, err = kbfsOps1.CreateFile(ctx, rootNode1, "c", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	err = kbfsOps2.SyncFromServer(ctx, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	entries, err := kbfsOps2.GetDirChildren(ctx, rootNode2)
	if err != nil {
		t.Fatalf("User 2 couldn't see the root dir: %v", err)
	}
	if len(entries) != 2 {
		t.Fatalf("User 2 sees wrong number of entries in root dir: %d vs 2",
			len(entries))
	}
	if _, ok := entries["b"]; !ok {
		t.Fatalf("User 2 doesn't see file b")
	}
	if _, ok := entries["c"]; !ok {
		t.Fatalf("User 2 doesn't see file c")
	}
}
// A mix of the above TestCRMergedChains* tests, with various other
// types of operations thrown in the mix (like u2 deleting unrelated
// directories, etc).
func TestCRMergedChainsComplex(t *testing.T) {
	var userName1, userName2 libkb.NormalizedUsername = "******", "u2"
	config1, uid1, ctx := kbfsOpsConcurInit(t, userName1, userName2)
	defer CheckConfigAndShutdown(t, config1)

	config2 := ConfigAsUser(config1.(*ConfigLocal), userName2)
	defer CheckConfigAndShutdown(t, config2)
	_, uid2, err := config2.KBPKI().GetCurrentUserInfo(ctx)
	if err != nil {
		t.Fatal(err)
	}

	// Setup:
	// /dirA/dirB/dirC
	// /dirA/dirB/dirD/file5
	// /dirE/dirF
	// /dirG/dirH

	name := userName1.String() + "," + userName2.String()

	configs := make(map[keybase1.UID]Config)
	configs[uid1] = config1
	configs[uid2] = config2
	nodesA := testCRSharedFolderForUsers(t, name, uid1, configs, []string{"dirA"})
	dirA1 := nodesA[uid1]
	dirA2 := nodesA[uid2]
	fb := dirA1.GetFolderBranch()

	cr1 := testCRGetCROrBust(t, config1, fb)
	cr2 := testCRGetCROrBust(t, config2, fb)
	cr2.Shutdown()

	nodesB := testCRSharedFolderForUsers(t, name, uid1, configs,
		[]string{"dirA", "dirB"})
	dirB1 := nodesB[uid1]
	dirB2 := nodesB[uid2]
	nodesC := testCRSharedFolderForUsers(t, name, uid1, configs,
		[]string{"dirA", "dirB", "dirC"})
	dirC2 := nodesC[uid2]
	nodesD := testCRSharedFolderForUsers(t, name, uid1, configs,
		[]string{"dirA", "dirB", "dirD"})
	dirD1 := nodesD[uid1]
	dirD2 := nodesD[uid2]
	nodesE := testCRSharedFolderForUsers(t, name, uid1, configs, []string{"dirE"})
	dirE1 := nodesE[uid1]
	nodesF := testCRSharedFolderForUsers(t, name, uid1, configs,
		[]string{"dirE", "dirF"})
	dirF2 := nodesF[uid2]
	dirEPtr := cr2.fbo.nodeCache.PathFromNode(dirE1).tailPointer()
	dirFPtr := cr2.fbo.nodeCache.PathFromNode(dirF2).tailPointer()
	nodesG := testCRSharedFolderForUsers(t, name, uid1, configs, []string{"dirG"})
	dirG1 := nodesG[uid1]
	nodesH := testCRSharedFolderForUsers(t, name, uid1, configs,
		[]string{"dirG", "dirH"})
	dirH1 := nodesH[uid1]
	dirH2 := nodesH[uid2]
	dirHPtr := cr1.fbo.nodeCache.PathFromNode(dirH1).tailPointer()

	_, _, err = config1.KBFSOps().CreateFile(ctx, dirD1, "file5", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}
	config2.KBFSOps().SyncFromServerForTesting(ctx, fb)

	// pause user 2
	_, err = DisableUpdatesForTesting(config2, fb)
	if err != nil {
		t.Fatalf("Can't disable updates for user 2: %v", err)
	}

	// user 1:
	// touch /dirA/file1
	// rm -rf /dirE/dirF
	// mv /dirG/dirH /dirA/dirI
	//
	// user 2:
	// mkdir /dirA/dirJ
	// touch /dirA/dirJ/file2
	// touch /dirE/dirF/file3
	// touch /dirA/dirB/dirC/file4
	// mv /dirA/dirB/dirC/file4 /dirG/dirH/file4
	// rm /dirA/dirB/dirD/file5
	// rm -rf /dirA/dirB/dirD

	// user 1:
	_, _, err = config1.KBFSOps().CreateFile(ctx, dirA1, "file1", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}
	err = config1.KBFSOps().RemoveDir(ctx, dirE1, "dirF")
	if err != nil {
		t.Fatalf("Couldn't remove dir: %v", err)
	}
	err = config1.KBFSOps().Rename(ctx, dirG1, "dirH", dirA1, "dirI")
	if err != nil {
		t.Fatalf("Couldn't remove dir: %v", err)
	}

	// user2
	dirJ2, _, err := config2.KBFSOps().CreateDir(ctx, dirA2, "dirJ")
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}
	_, _, err = config2.KBFSOps().CreateFile(ctx, dirJ2, "file2", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}
	_, _, err = config2.KBFSOps().CreateFile(ctx, dirF2, "file3", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}
	_, _, err = config2.KBFSOps().CreateFile(ctx, dirC2, "file4", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}
	err = config2.KBFSOps().Rename(ctx, dirC2, "file4", dirH2, "file4")
	if err != nil {
		t.Fatalf("Couldn't remove dir: %v", err)
	}
	err = config2.KBFSOps().RemoveEntry(ctx, dirD2, "file5")
	if err != nil {
		t.Fatalf("Couldn't remove dir: %v", err)
	}
	err = config2.KBFSOps().RemoveDir(ctx, dirB2, "dirD")
	if err != nil {
		t.Fatalf("Couldn't remove dir: %v", err)
	}

	// Now step through conflict resolution manually for user 2

	uPathA2 := cr2.fbo.nodeCache.PathFromNode(dirA2)
	uPathF2 := cr2.fbo.nodeCache.PathFromNode(dirF2)
	// no expected unmerged path for dirJ or dirC
	uPathH2 := cr2.fbo.nodeCache.PathFromNode(dirH2)
	// no expected unmerged path for dirD
	uPathB2 := cr2.fbo.nodeCache.PathFromNode(dirB2)

	mergedPaths := make(map[BlockPointer]path)
	// Both users updated A
	mergedPathA := cr1.fbo.nodeCache.PathFromNode(dirA1)
	mergedPaths[uPathA2.tailPointer()] = mergedPathA
	// user 1 deleted dirF, so reconstruct
	mergedPathF := cr1.fbo.nodeCache.PathFromNode(dirE1)
	mergedPathF.path = append(mergedPathF.path, pathNode{
		BlockPointer: dirFPtr,
		Name:         "dirF",
	})
	mergedPaths[uPathF2.tailPointer()] = mergedPathF
	// dirH from user 2 is /dirA/dirI for user 1
	mergedPathH := cr1.fbo.nodeCache.PathFromNode(dirA1)
	mergedPathH.path = append(mergedPathH.path, pathNode{
		BlockPointer: dirHPtr,
		Name:         "dirI",
	})
	mergedPaths[uPathH2.tailPointer()] = mergedPathH
	// dirB wasn't touched by user 1
	mergedPathB := cr1.fbo.nodeCache.PathFromNode(dirB1)
	mergedPaths[uPathB2.tailPointer()] = mergedPathB

	coF := newCreateOp("dirF", dirEPtr, Dir)

	mergedPathE := cr1.fbo.nodeCache.PathFromNode(dirE1)
	expectedActions := map[BlockPointer]crActionList{
		mergedPathA.tailPointer(): {&copyUnmergedEntryAction{
			"dirJ", "dirJ", "", false, false, DirEntry{}, nil}},
		mergedPathE.tailPointer(): {&copyUnmergedEntryAction{
			"dirF", "dirF", "", false, false, DirEntry{}, nil}},
		mergedPathF.tailPointer(): {&copyUnmergedEntryAction{
			"file3", "file3", "", false, false, DirEntry{}, nil}},
		mergedPathH.tailPointer(): {&copyUnmergedEntryAction{
			"file4", "file4", "", false, false, DirEntry{}, nil}},
		mergedPathB.tailPointer(): {&rmMergedEntryAction{"dirD"}},
	}
	// `rm file5` doesn't get an action because the parent directory
	// was deleted in the unmerged branch.

	testCRCheckPathsAndActions(t, cr2, []path{uPathA2, uPathF2, uPathH2,
		uPathB2}, mergedPaths, []*createOp{coF}, expectedActions)
}
// Same as TestCRMergedChainsSimple, but u1 actually deletes some of
// the subdirectories used by u2, forcing the resolver to generate
// some recreateOps.
func TestCRMergedChainsDeletedDirectories(t *testing.T) {
	var userName1, userName2 libkb.NormalizedUsername = "******", "u2"
	config1, uid1, ctx := kbfsOpsConcurInit(t, userName1, userName2)
	defer CheckConfigAndShutdown(t, config1)

	config2 := ConfigAsUser(config1.(*ConfigLocal), userName2)
	defer CheckConfigAndShutdown(t, config2)
	_, uid2, err := config2.KBPKI().GetCurrentUserInfo(ctx)
	if err != nil {
		t.Fatal(err)
	}

	name := userName1.String() + "," + userName2.String()

	configs := make(map[keybase1.UID]Config)
	configs[uid1] = config1
	configs[uid2] = config2
	nodesA := testCRSharedFolderForUsers(t, name, uid1, configs, []string{"dirA"})
	dirA1 := nodesA[uid1]
	fb := dirA1.GetFolderBranch()

	cr1 := testCRGetCROrBust(t, config1, fb)
	cr2 := testCRGetCROrBust(t, config2, fb)
	cr2.Shutdown()

	nodesB := testCRSharedFolderForUsers(t, name, uid1, configs,
		[]string{"dirA", "dirB"})
	dirB1 := nodesB[uid1]
	nodesC := testCRSharedFolderForUsers(t, name, uid1, configs,
		[]string{"dirA", "dirB", "dirC"})
	dirC2 := nodesC[uid2]
	dirAPtr := cr1.fbo.nodeCache.PathFromNode(dirA1).tailPointer()
	dirBPtr := cr1.fbo.nodeCache.PathFromNode(dirB1).tailPointer()
	dirCPtr := cr2.fbo.nodeCache.PathFromNode(dirC2).tailPointer()

	// pause user 2
	_, err = DisableUpdatesForTesting(config2, fb)
	if err != nil {
		t.Fatalf("Can't disable updates for user 2: %v", err)
	}

	// user1 deletes dirB and dirC
	err = config1.KBFSOps().RemoveDir(ctx, dirB1, "dirC")
	if err != nil {
		t.Fatalf("Couldn't remove dir: %v", err)
	}
	err = config1.KBFSOps().RemoveDir(ctx, dirA1, "dirB")
	if err != nil {
		t.Fatalf("Couldn't remove dir: %v", err)
	}

	// user2 makes a file in dir C
	_, _, err = config2.KBFSOps().CreateFile(ctx, dirC2, "file2", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	// Now step through conflict resolution manually for user 2

	expectedUnmergedPath := cr2.fbo.nodeCache.PathFromNode(dirC2)
	// The merged path will consist of the latest root and dirA
	// components, plus the original blockpointers of the deleted
	// nodes.
	mergedPaths := make(map[BlockPointer]path)
	mergedPath := cr1.fbo.nodeCache.PathFromNode(dirA1)
	mergedPath.path = append(mergedPath.path, pathNode{
		BlockPointer: dirBPtr,
		Name:         "dirB",
	})
	mergedPath.path = append(mergedPath.path, pathNode{
		BlockPointer: dirCPtr,
		Name:         "dirC",
	})
	mergedPaths[expectedUnmergedPath.tailPointer()] = mergedPath

	coB := newCreateOp("dirB", dirAPtr, Dir)
	coC := newCreateOp("dirC", dirBPtr, Dir)

	dirAPtr1 := cr1.fbo.nodeCache.PathFromNode(dirA1).tailPointer()
	expectedActions := map[BlockPointer]crActionList{
		dirCPtr: {&copyUnmergedEntryAction{"file2", "file2", "",
			false, false, DirEntry{}, nil}},
		dirBPtr: {&copyUnmergedEntryAction{"dirC", "dirC", "", false, false,
			DirEntry{}, nil}},
		dirAPtr1: {&copyUnmergedEntryAction{"dirB", "dirB", "", false, false,
			DirEntry{}, nil}},
	}

	testCRCheckPathsAndActions(t, cr2, []path{expectedUnmergedPath},
		mergedPaths, []*createOp{coB, coC}, expectedActions)
}
// Test that actions get executed properly in the case of two
// simultaneous writes to the same file.
func TestCRDoActionsWriteConflict(t *testing.T) {
	var userName1, userName2 libkb.NormalizedUsername = "******", "u2"
	config1, uid1, ctx := kbfsOpsConcurInit(t, userName1, userName2)
	defer CheckConfigAndShutdown(t, config1)

	config2 := ConfigAsUser(config1.(*ConfigLocal), userName2)
	defer CheckConfigAndShutdown(t, config2)
	_, uid2, err := config2.KBPKI().GetCurrentUserInfo(ctx)
	if err != nil {
		t.Fatal(err)
	}

	clock, now := newTestClockAndTimeNow()
	config2.SetClock(clock)

	name := userName1.String() + "," + userName2.String()

	configs := make(map[keybase1.UID]Config)
	configs[uid1] = config1
	configs[uid2] = config2
	nodes := testCRSharedFolderForUsers(t, name, uid1, configs, []string{"dir"})
	dir1 := nodes[uid1]
	dir2 := nodes[uid2]
	fb := dir1.GetFolderBranch()

	// user1 makes a file
	file1, _, err := config1.KBFSOps().CreateFile(ctx, dir1, "file", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	// user2 lookup
	err = config2.KBFSOps().SyncFromServerForTesting(ctx, fb)
	if err != nil {
		t.Fatalf("Couldn't sync user 2")
	}
	file2, _, err := config2.KBFSOps().Lookup(ctx, dir2, "file")
	if err != nil {
		t.Fatalf("Couldn't lookup file: %v", err)
	}

	// pause user 2
	_, err = DisableUpdatesForTesting(config2, fb)
	if err != nil {
		t.Fatalf("Can't disable updates for user 2: %v", err)
	}

	cr1 := testCRGetCROrBust(t, config1, fb)
	cr2 := testCRGetCROrBust(t, config2, fb)
	cr2.Shutdown()

	// user1 writes the file
	err = config1.KBFSOps().Write(ctx, file1, []byte{1, 2, 3}, 0)
	if err != nil {
		t.Fatalf("Couldn't write file: %v", err)
	}
	err = config1.KBFSOps().Sync(ctx, file1)
	if err != nil {
		t.Fatalf("Couldn't sync file: %v", err)
	}

	// user2 writes the file
	unmergedData := []byte{4, 5, 6}
	err = config2.KBFSOps().Write(ctx, file2, unmergedData, 0)
	if err != nil {
		t.Fatalf("Couldn't write file: %v", err)
	}
	err = config2.KBFSOps().Sync(ctx, file2)
	if err != nil {
		t.Fatalf("Couldn't sync file: %v", err)
	}

	lState := makeFBOLockState()

	// Now run through conflict resolution manually for user2.
	unmergedChains, mergedChains, unmergedPaths, mergedPaths,
		recreateOps, _, _, err := cr2.buildChainsAndPaths(ctx, lState)
	if err != nil {
		t.Fatalf("Couldn't build chains and paths: %v", err)
	}

	actionMap, _, err := cr2.computeActions(ctx, unmergedChains, mergedChains,
		mergedPaths, recreateOps)
	if err != nil {
		t.Fatalf("Couldn't compute actions: %v", err)
	}

	lbc := make(localBcache)
	newFileBlocks := make(fileBlockMap)
	err = cr2.doActions(ctx, lState, unmergedChains, mergedChains,
		unmergedPaths, mergedPaths, actionMap, lbc, newFileBlocks)
	if err != nil {
		t.Fatalf("Couldn't do actions: %v", err)
	}

	// Does the merged block contain the two files?
	mergedRootPath := cr1.fbo.nodeCache.PathFromNode(dir1)
	cre := WriterDeviceDateConflictRenamer{}
	mergedName := cre.ConflictRenameHelper(now, "u2", "dev1", "file")
	if len(newFileBlocks) != 1 {
		t.Errorf("Unexpected new file blocks!")
	}
	if blocks, ok := newFileBlocks[mergedRootPath.tailPointer()]; !ok {
		t.Errorf("No blocks for dir merged ptr: %v",
			mergedRootPath.tailPointer())
	} else if len(blocks) != 1 {
		t.Errorf("Unexpected number of blocks")
	} else if fblock, ok := blocks[mergedName]; !ok {
		t.Errorf("No block for name %s", mergedName)
	} else if fblock.IsInd {
		t.Errorf("Unexpected indirect block")
	} else if g, e := fblock.Contents, unmergedData; !reflect.DeepEqual(g, e) {
		t.Errorf("Unexpected block contents: %v vs %v", g, e)
	}

	// NOTE: the action doesn't actually create the entry, so this
	// test can only check that newFileBlocks looks correct.
}
// Test that actions get executed properly in the simple case of two
// files being created simultaneously in the same directory.
func TestCRDoActionsSimple(t *testing.T) {
	var userName1, userName2 libkb.NormalizedUsername = "******", "u2"
	config1, uid1, ctx := kbfsOpsConcurInit(t, userName1, userName2)
	defer CheckConfigAndShutdown(t, config1)

	config2 := ConfigAsUser(config1.(*ConfigLocal), userName2)
	defer CheckConfigAndShutdown(t, config2)
	_, uid2, err := config2.KBPKI().GetCurrentUserInfo(ctx)
	if err != nil {
		t.Fatal(err)
	}

	name := userName1.String() + "," + userName2.String()

	configs := make(map[keybase1.UID]Config)
	configs[uid1] = config1
	configs[uid2] = config2
	nodes := testCRSharedFolderForUsers(t, name, uid1, configs, []string{"dir"})
	dir1 := nodes[uid1]
	dir2 := nodes[uid2]
	fb := dir1.GetFolderBranch()

	// pause user 2
	_, err = DisableUpdatesForTesting(config2, fb)
	if err != nil {
		t.Fatalf("Can't disable updates for user 2: %v", err)
	}

	// user1 makes a file
	_, _, err = config1.KBFSOps().CreateFile(ctx, dir1, "file1", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	cr1 := testCRGetCROrBust(t, config1, fb)
	cr2 := testCRGetCROrBust(t, config2, fb)
	cr2.Shutdown()

	// user2 makes a file (causes a conflict, and goes unstaged)
	_, _, err = config2.KBFSOps().CreateFile(ctx, dir2, "file2", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	lState := makeFBOLockState()

	// Now run through conflict resolution manually for user2.
	unmergedChains, mergedChains, unmergedPaths, mergedPaths,
		recreateOps, _, _, err := cr2.buildChainsAndPaths(ctx, lState)
	if err != nil {
		t.Fatalf("Couldn't build chains and paths: %v", err)
	}

	actionMap, _, err := cr2.computeActions(ctx, unmergedChains, mergedChains,
		mergedPaths, recreateOps)
	if err != nil {
		t.Fatalf("Couldn't compute actions: %v", err)
	}

	lbc := make(localBcache)
	newFileBlocks := make(fileBlockMap)
	err = cr2.doActions(ctx, lState, unmergedChains, mergedChains,
		unmergedPaths, mergedPaths, actionMap, lbc, newFileBlocks)
	if err != nil {
		t.Fatalf("Couldn't do actions: %v", err)
	}

	// Does the merged block contain both entries?
	mergedRootPath := cr1.fbo.nodeCache.PathFromNode(dir1)
	block1, ok := lbc[mergedRootPath.tailPointer()]
	if !ok {
		t.Fatalf("Couldn't find merged block at path %s", mergedRootPath)
	}
	if g, e := len(block1.Children), 2; g != e {
		t.Errorf("Unexpected number of children: %d vs %d", g, e)
	}
	for _, file := range []string{"file1", "file2"} {
		if _, ok := block1.Children[file]; !ok {
			t.Errorf("Couldn't find entry in merged children: %s", file)
		}
	}
	if len(newFileBlocks) != 0 {
		t.Errorf("Unexpected new file blocks!")
	}
}
Exemple #17
0
// Test that two conflict resolutions work correctly.
func TestCRDouble(t *testing.T) {
	// simulate two users
	var userName1, userName2 libkb.NormalizedUsername = "******", "u2"
	config1, _, ctx := kbfsOpsConcurInit(t, userName1, userName2)
	defer CheckConfigAndShutdown(t, config1)
	config1.MDServer().DisableRekeyUpdatesForTesting()

	config2 := ConfigAsUser(config1.(*ConfigLocal), userName2)
	defer CheckConfigAndShutdown(t, config2)
	_, _, err := config2.KBPKI().GetCurrentUserInfo(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	config2.MDServer().DisableRekeyUpdatesForTesting()

	config2.SetClock(newTestClockNow())
	name := userName1.String() + "," + userName2.String()

	// create and write to a file
	rootNode := GetRootNodeOrBust(t, config1, name, false)
	kbfsOps1 := config1.KBFSOps()
	_, _, err = kbfsOps1.CreateFile(ctx, rootNode, "a", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	// look it up on user2
	rootNode2 := GetRootNodeOrBust(t, config2, name, false)

	kbfsOps2 := config2.KBFSOps()
	_, _, err = kbfsOps2.Lookup(ctx, rootNode2, "a")
	if err != nil {
		t.Fatalf("Couldn't lookup dir: %v", err)
	}
	// disable updates and CR on user 2
	c, err := DisableUpdatesForTesting(config2, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't disable updates: %v", err)
	}
	err = DisableCRForTesting(config2, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't disable updates: %v", err)
	}

	// User 1 creates a new file to start a conflict.
	_, _, err = kbfsOps1.CreateFile(ctx, rootNode, "b", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	// User 2 makes a couple revisions
	fileNodeC, _, err := kbfsOps2.CreateFile(ctx, rootNode2, "c", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}
	err = kbfsOps2.Write(ctx, fileNodeC, []byte{0}, 0)
	if err != nil {
		t.Fatalf("Couldn't sync file: %v", err)
	}

	// Cancel this revision after the Put happens, to force the
	// background block manager to try to clean up.
	onSyncStalledCh := make(chan struct{}, 1)
	syncUnstallCh := make(chan struct{})
	stallKey := "requestName"
	syncValue := "sync"
	config2.SetMDOps(&stallingMDOps{
		stallOpName: "PutUnmerged",
		stallKey:    stallKey,
		stallMap: map[interface{}]staller{
			syncValue: staller{
				stalled: onSyncStalledCh,
				unstall: syncUnstallCh,
			},
		},
		delegate: config2.MDOps(),
	})
	var wg sync.WaitGroup
	syncCtx, cancel := context.WithCancel(ctx)
	wg.Add(1)
	go func() {
		defer wg.Done()

		syncCtx = context.WithValue(syncCtx, stallKey, syncValue)
		err = kbfsOps2.Sync(syncCtx, fileNodeC)
		if err != context.Canceled {
			t.Fatalf("Bad sync error, expected canceled: %v", err)
		}
	}()
	<-onSyncStalledCh
	cancel()
	close(syncUnstallCh)
	wg.Wait()

	// Sync for real to clear out the dirty files.
	err = kbfsOps2.Sync(ctx, fileNodeC)
	if err != nil {
		t.Fatalf("Couldn't sync: %v", err)
	}

	// Do one CR.
	c <- struct{}{}
	err = RestartCRForTesting(config2, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't disable updates: %v", err)
	}
	err = kbfsOps2.SyncFromServerForTesting(ctx, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	// A few merged revisions
	_, _, err = kbfsOps2.CreateFile(ctx, rootNode2, "e", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}
	_, _, err = kbfsOps2.CreateFile(ctx, rootNode2, "f", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	ops := getOps(config2, rootNode.GetFolderBranch().Tlf)
	// Wait for the processor to try to delete the failed revision
	// (which pulls the unmerged MD ops back into the cache).
	ops.fbm.waitForArchives(ctx)

	// Sync user 1, then start another round of CR.
	err = kbfsOps1.SyncFromServerForTesting(ctx, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}
	// disable updates and CR on user 2
	c, err = DisableUpdatesForTesting(config2, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't disable updates: %v", err)
	}
	err = DisableCRForTesting(config2, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't disable updates: %v", err)
	}
	_, _, err = kbfsOps1.CreateFile(ctx, rootNode, "g", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	// User 2 makes a couple unmerged revisions
	_, _, err = kbfsOps2.CreateFile(ctx, rootNode2, "h", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}
	_, _, err = kbfsOps2.CreateFile(ctx, rootNode2, "i", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	// Do a second CR.
	c <- struct{}{}
	err = RestartCRForTesting(config2, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't disable updates: %v", err)
	}
	err = kbfsOps2.SyncFromServerForTesting(ctx, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}
}
Exemple #18
0
// Tests that two users can make independent writes while forked, and
// conflict resolution will merge them correctly.
func TestBasicCRNoConflict(t *testing.T) {
	// simulate two users
	var userName1, userName2 libkb.NormalizedUsername = "******", "u2"
	config1, _, ctx := kbfsOpsConcurInit(t, userName1, userName2)
	defer CheckConfigAndShutdown(t, config1)

	config2 := ConfigAsUser(config1.(*ConfigLocal), userName2)
	defer CheckConfigAndShutdown(t, config2)

	name := userName1.String() + "," + userName2.String()

	// user1 creates a file in a shared dir
	rootNode1 := GetRootNodeOrBust(t, config1, name, false)

	kbfsOps1 := config1.KBFSOps()
	_, _, err := kbfsOps1.CreateFile(ctx, rootNode1, "a", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	// look it up on user2
	rootNode2 := GetRootNodeOrBust(t, config2, name, false)

	kbfsOps2 := config2.KBFSOps()
	_, _, err = kbfsOps2.Lookup(ctx, rootNode2, "a")
	if err != nil {
		t.Fatalf("Couldn't lookup file: %v", err)
	}

	// disable updates on user 2
	c, err := DisableUpdatesForTesting(config2, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't disable updates: %v", err)
	}
	err = DisableCRForTesting(config2, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't disable updates: %v", err)
	}

	// User 1 makes a new file
	_, _, err = kbfsOps1.CreateFile(ctx, rootNode1, "b", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	// User 2 makes a new different file
	_, _, err = kbfsOps2.CreateFile(ctx, rootNode2, "c", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	// re-enable updates, and wait for CR to complete
	c <- struct{}{}
	err = RestartCRForTesting(config2, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't disable updates: %v", err)
	}
	err = kbfsOps2.SyncFromServerForTesting(ctx, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	err = kbfsOps1.SyncFromServerForTesting(ctx, rootNode1.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	// Make sure they both see the same set of children
	expectedChildren := []string{"a", "b", "c"}
	children1, err := kbfsOps1.GetDirChildren(ctx, rootNode1)
	if err != nil {
		t.Fatalf("Couldn't get children: %v", err)
	}

	children2, err := kbfsOps2.GetDirChildren(ctx, rootNode2)
	if err != nil {
		t.Fatalf("Couldn't get children: %v", err)
	}

	if g, e := len(children1), len(expectedChildren); g != e {
		t.Errorf("Wrong number of children: %d vs %d", g, e)
	}

	for _, child := range expectedChildren {
		if _, ok := children1[child]; !ok {
			t.Errorf("Couldn't find child %s", child)
		}
	}

	if !reflect.DeepEqual(children1, children2) {
		t.Fatalf("Users 1 and 2 see different children: %v vs %v",
			children1, children2)
	}
}
Exemple #19
0
// Tests that, in the face of a conflict, a user will commit its
// changes to a private branch, which will persist after restart (and
// the other user will be unaffected).
func TestUnmergedAfterRestart(t *testing.T) {
	// simulate two users
	var userName1, userName2 libkb.NormalizedUsername = "******", "u2"
	config1, _, ctx := kbfsOpsConcurInit(t, userName1, userName2)
	defer CheckConfigAndShutdown(t, config1)

	config2 := ConfigAsUser(config1.(*ConfigLocal), userName2)
	defer CheckConfigAndShutdown(t, config2)

	name := userName1.String() + "," + userName2.String()

	// user1 creates a file in a shared dir
	kbfsOps1 := config1.KBFSOps()
	rootNode1, _, err :=
		kbfsOps1.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Couldn't create folder: %v", err)
	}
	fileNode1, _, err := kbfsOps1.CreateFile(ctx, rootNode1, "a", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	// then user2 write to the file
	kbfsOps2 := config2.KBFSOps()
	rootNode2, _, err :=
		kbfsOps2.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Couldn't create folder: %v", err)
	}
	fileNode2, _, err := kbfsOps2.Lookup(ctx, rootNode2, "a")
	if err != nil {
		t.Fatalf("Couldn't lookup file: %v", err)
	}
	data2 := []byte{2}
	err = kbfsOps2.Write(ctx, fileNode2, data2, 0)
	if err != nil {
		t.Fatalf("Couldn't write file: %v", err)
	}
	checkStatus(t, ctx, kbfsOps2, false, userName1, []string{"u1,u2/a"},
		rootNode2.GetFolderBranch(), "Node 2 (after write)")
	err = kbfsOps2.Sync(ctx, fileNode2)
	if err != nil {
		t.Fatalf("Couldn't sync file: %v", err)
	}

	DisableCRForTesting(config1, rootNode1.GetFolderBranch())

	// Now when user 1 tries to write to file 1 and sync, it will
	// become unmerged.  Because this happens in the same goroutine as
	// the above Sync, we can be sure that the updater on client 1
	// hasn't yet seen the MD update, and so its Sync will present a
	// conflict.
	data1 := []byte{1}
	err = kbfsOps1.Write(ctx, fileNode1, data1, 0)
	if err != nil {
		t.Fatalf("Couldn't write file: %v", err)
	}
	checkStatus(t, ctx, kbfsOps1, false, userName1, []string{"u1,u2/a"},
		rootNode1.GetFolderBranch(), "Node 1 (after write)")
	err = kbfsOps1.Sync(ctx, fileNode1)
	if err != nil {
		t.Fatalf("Couldn't sync file: %v", err)
	}

	checkStatus(t, ctx, kbfsOps1, true, userName1, nil,
		rootNode1.GetFolderBranch(), "Node 1")
	checkStatus(t, ctx, kbfsOps2, false, userName2, nil,
		rootNode2.GetFolderBranch(), "Node 2")

	// now re-login the users, and make sure 1 can see the changes,
	// but 2 can't
	config1B := ConfigAsUser(config1.(*ConfigLocal), userName1)
	defer CheckConfigAndShutdown(t, config1B)
	config2B := ConfigAsUser(config1.(*ConfigLocal), userName2)
	defer CheckConfigAndShutdown(t, config2B)

	DisableCRForTesting(config1B, rootNode1.GetFolderBranch())

	readAndCompareData(t, config1B, ctx, name, data1, userName1)
	readAndCompareData(t, config2B, ctx, name, data2, userName2)

	checkStatus(t, ctx, config1B.KBFSOps(), true, userName1, nil,
		rootNode1.GetFolderBranch(), "Node 1")
	checkStatus(t, ctx, config2B.KBFSOps(), false, userName2, nil,
		rootNode2.GetFolderBranch(), "Node 2")

	// register as a listener before the unstaging happens
	c := make(chan struct{}, 2)
	cro := &testCRObserver{c, nil}
	config1B.Notifier().RegisterForChanges(
		[]FolderBranch{rootNode1.GetFolderBranch()}, cro)

	// Unstage user 1's changes, and make sure everyone is back in
	// sync.  TODO: remove this once we have automatic conflict
	// resolution.
	err = config1B.KBFSOps().UnstageForTesting(ctx, rootNode1.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't unstage: %v", err)
	}

	// we should have had two updates, one for the unstaging and one
	// for the fast-forward
	<-c
	<-c
	// make sure we see two sync op changes, on the same node
	if len(cro.changes) != 2 {
		t.Errorf("Unexpected number of changes: %d", len(cro.changes))
	}
	var n Node
	for _, change := range cro.changes {
		if n == nil {
			n = change.Node
		} else if n.GetID() != change.Node.GetID() {
			t.Errorf("Changes involve different nodes, %v vs %v\n",
				n.GetID(), change.Node.GetID())
		}
	}

	if err := config1B.KBFSOps().
		SyncFromServer(ctx, rootNode1.GetFolderBranch()); err != nil {
		t.Fatal("Couldn't sync user 1 from server")
	}
	if err := config2B.KBFSOps().
		SyncFromServer(ctx, rootNode2.GetFolderBranch()); err != nil {
		t.Fatal("Couldn't sync user 2 from server")
	}

	readAndCompareData(t, config1B, ctx, name, data2, userName2)
	readAndCompareData(t, config2B, ctx, name, data2, userName2)
	checkStatus(t, ctx, config1B.KBFSOps(), false, userName1, nil,
		rootNode1.GetFolderBranch(), "Node 1 (after unstage)")
	checkStatus(t, ctx, config2B.KBFSOps(), false, userName1, nil,
		rootNode2.GetFolderBranch(), "Node 2 (after unstage)")
}
// Tests that conflict resolution detects and renames conflicts.
func TestCRMergedChainsConflictSimple(t *testing.T) {
	var userName1, userName2 libkb.NormalizedUsername = "******", "u2"
	config1, uid1, ctx := kbfsOpsConcurInit(t, userName1, userName2)
	defer CheckConfigAndShutdown(t, config1)

	config2 := ConfigAsUser(config1.(*ConfigLocal), userName2)
	defer CheckConfigAndShutdown(t, config2)
	_, uid2, err := config2.KBPKI().GetCurrentUserInfo(ctx)
	if err != nil {
		t.Fatal(err)
	}

	clock, now := newTestClockAndTimeNow()
	config2.SetClock(clock)

	name := userName1.String() + "," + userName2.String()

	configs := make(map[keybase1.UID]Config)
	configs[uid1] = config1
	configs[uid2] = config2
	nodesRoot := testCRSharedFolderForUsers(t, name, uid1, configs, []string{"root"})
	dirRoot1 := nodesRoot[uid1]
	dirRoot2 := nodesRoot[uid2]
	fb := dirRoot1.GetFolderBranch()

	cr1 := testCRGetCROrBust(t, config1, fb)
	cr2 := testCRGetCROrBust(t, config2, fb)
	cr2.Shutdown()

	// pause user 2
	_, err = DisableUpdatesForTesting(config2, fb)
	if err != nil {
		t.Fatalf("Can't disable updates for user 2: %v", err)
	}

	// user1 creates file1
	_, _, err = config1.KBFSOps().CreateFile(ctx, dirRoot1, "file1", false)
	if err != nil {
		t.Fatalf("Couldn't make file: %v", err)
	}

	// user2 also create file1, but makes it executable
	_, _, err = config2.KBFSOps().CreateFile(ctx, dirRoot2, "file1", true)
	if err != nil {
		t.Fatalf("Couldn't make dir: %v", err)
	}

	// Now step through conflict resolution manually for user 2
	mergedPaths := make(map[BlockPointer]path)

	// root
	unmergedPathRoot := cr2.fbo.nodeCache.PathFromNode(dirRoot2)
	mergedPathRoot := cr1.fbo.nodeCache.PathFromNode(dirRoot1)
	mergedPaths[unmergedPathRoot.tailPointer()] = mergedPathRoot

	cre := WriterDeviceDateConflictRenamer{}
	expectedActions := map[BlockPointer]crActionList{
		mergedPathRoot.tailPointer(): {&renameUnmergedAction{
			"file1",
			cre.ConflictRenameHelper(now, "u2", "dev1", "file1"),
			"", zeroPtr, zeroPtr}},
	}

	testCRCheckPathsAndActions(t, cr2, []path{unmergedPathRoot},
		mergedPaths, nil, expectedActions)
}
Exemple #21
0
// Tests that two users can make independent writes while forked, and
// conflict resolution will merge them correctly.
func TestBasicCRFileConflict(t *testing.T) {
	// simulate two users
	var userName1, userName2 libkb.NormalizedUsername = "******", "u2"
	config1, _, ctx := kbfsOpsConcurInit(t, userName1, userName2)
	defer CheckConfigAndShutdown(t, config1)

	config2 := ConfigAsUser(config1.(*ConfigLocal), userName2)
	defer CheckConfigAndShutdown(t, config2)

	now := time.Now()
	config2.SetClock(&TestClock{now})

	name := userName1.String() + "," + userName2.String()

	// user1 creates a file in a shared dir
	kbfsOps1 := config1.KBFSOps()
	rootNode1, _, err :=
		kbfsOps1.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Couldn't create folder: %v", err)
	}
	dirA1, _, err := kbfsOps1.CreateDir(ctx, rootNode1, "a")
	if err != nil {
		t.Fatalf("Couldn't create dir: %v", err)
	}
	fileB1, _, err := kbfsOps1.CreateFile(ctx, dirA1, "b", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	// look it up on user2
	kbfsOps2 := config2.KBFSOps()
	rootNode2, _, err :=
		kbfsOps2.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Couldn't create folder: %v", err)
	}
	dirA2, _, err := kbfsOps2.Lookup(ctx, rootNode2, "a")
	if err != nil {
		t.Fatalf("Couldn't lookup dir: %v", err)
	}
	fileB2, _, err := kbfsOps2.Lookup(ctx, dirA2, "b")
	if err != nil {
		t.Fatalf("Couldn't lookup file: %v", err)
	}

	// disable updates on user 2
	c, err := DisableUpdatesForTesting(config2, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't disable updates: %v", err)
	}

	// User 1 writes the file
	data1 := []byte{1, 2, 3, 4, 5}
	err = kbfsOps1.Write(ctx, fileB1, data1, 0)
	if err != nil {
		t.Fatalf("Couldn't write file: %v", err)
	}
	err = kbfsOps1.Sync(ctx, fileB1)
	if err != nil {
		t.Fatalf("Couldn't sync file: %v", err)
	}

	// User 2 makes a new different file
	data2 := []byte{5, 4, 3, 2, 1}
	err = kbfsOps2.Write(ctx, fileB2, data2, 0)
	if err != nil {
		t.Fatalf("Couldn't write file: %v", err)
	}
	err = kbfsOps2.Sync(ctx, fileB2)
	if err != nil {
		t.Fatalf("Couldn't sync file: %v", err)
	}

	// re-enable updates, and wait for CR to complete
	c <- struct{}{}
	err = kbfsOps2.SyncFromServer(ctx, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	err = kbfsOps1.SyncFromServer(ctx, rootNode1.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	// Make sure they both see the same set of children
	expectedChildren := []string{
		"b",
		"b.conflict.u2." + now.Format(time.RFC3339Nano),
	}
	children1, err := kbfsOps1.GetDirChildren(ctx, dirA1)
	if err != nil {
		t.Fatalf("Couldn't get children: %v", err)
	}

	children2, err := kbfsOps2.GetDirChildren(ctx, dirA2)
	if err != nil {
		t.Fatalf("Couldn't get children: %v", err)
	}

	if g, e := len(children1), len(expectedChildren); g != e {
		t.Errorf("Wrong number of children: %d vs %d", g, e)
	}

	for _, child := range expectedChildren {
		if _, ok := children1[child]; !ok {
			t.Errorf("Couldn't find child %s", child)
		}
	}

	if !reflect.DeepEqual(children1, children2) {
		t.Fatalf("Users 1 and 2 see different children: %v vs %v",
			children1, children2)
	}
}
// Tests that conflict resolution detects and renames conflicts.
func TestCRMergedChainsConflictFileCollapse(t *testing.T) {
	var userName1, userName2 libkb.NormalizedUsername = "******", "u2"
	config1, uid1, ctx := kbfsOpsConcurInit(t, userName1, userName2)
	defer CheckConfigAndShutdown(t, config1)

	config2 := ConfigAsUser(config1.(*ConfigLocal), userName2)
	defer CheckConfigAndShutdown(t, config2)
	_, uid2, err := config2.KBPKI().GetCurrentUserInfo(ctx)
	if err != nil {
		t.Fatal(err)
	}

	clock, now := newTestClockAndTimeNow()
	config2.SetClock(clock)

	name := userName1.String() + "," + userName2.String()

	configs := make(map[keybase1.UID]Config)
	configs[uid1] = config1
	configs[uid2] = config2
	nodesRoot := testCRSharedFolderForUsers(t, name, uid1, configs, []string{"root"})
	dirRoot1 := nodesRoot[uid1]
	dirRoot2 := nodesRoot[uid2]
	fb := dirRoot1.GetFolderBranch()

	cr1 := testCRGetCROrBust(t, config1, fb)
	cr2 := testCRGetCROrBust(t, config2, fb)
	cr2.Shutdown()

	// user1 creates file
	_, _, err = config1.KBFSOps().CreateFile(ctx, dirRoot1, "file", false)
	if err != nil {
		t.Fatalf("Couldn't make file: %v", err)
	}

	// user2 lookup
	err = config2.KBFSOps().SyncFromServerForTesting(ctx, fb)
	if err != nil {
		t.Fatalf("Couldn't sync user 2")
	}
	file2, _, err := config2.KBFSOps().Lookup(ctx, dirRoot2, "file")
	if err != nil {
		t.Fatalf("Couldn't lookup file: %v", err)
	}

	filePtr := cr2.fbo.nodeCache.PathFromNode(file2).tailPointer()
	dirRootPtr := cr2.fbo.nodeCache.PathFromNode(dirRoot2).tailPointer()

	// pause user 2
	_, err = DisableUpdatesForTesting(config2, fb)
	if err != nil {
		t.Fatalf("Can't disable updates for user 2: %v", err)
	}

	// user1 deletes the file and creates another
	err = config1.KBFSOps().RemoveEntry(ctx, dirRoot1, "file")
	if err != nil {
		t.Fatalf("Couldn't remove file: %v", err)
	}
	_, _, err = config1.KBFSOps().CreateFile(ctx, dirRoot1, "file", false)
	if err != nil {
		t.Fatalf("Couldn't re-make file: %v", err)
	}

	// user2 updates the file attribute and writes to
	err = config2.KBFSOps().SetEx(ctx, file2, true)
	if err != nil {
		t.Fatalf("Couldn't set ex: %v", err)
	}
	err = config2.KBFSOps().Write(ctx, file2, []byte{1, 2, 3}, 0)
	if err != nil {
		t.Fatalf("Couldn't write: %v", err)
	}

	// Now step through conflict resolution manually for user 2
	mergedPaths := make(map[BlockPointer]path)

	// file (needs to be recreated)
	unmergedPathFile := cr2.fbo.nodeCache.PathFromNode(file2)
	mergedPathFile := cr1.fbo.nodeCache.PathFromNode(dirRoot1)
	mergedPathFile.path = append(mergedPathFile.path, pathNode{
		BlockPointer: filePtr,
		Name:         "file",
	})
	mergedPaths[unmergedPathFile.tailPointer()] = mergedPathFile

	coFile := newCreateOp("file", dirRootPtr, Exec)

	cre := WriterDeviceDateConflictRenamer{}
	mergedPathRoot := cr1.fbo.nodeCache.PathFromNode(dirRoot1)
	// Both unmerged actions should collapse into just one rename operation
	expectedActions := map[BlockPointer]crActionList{
		mergedPathRoot.tailPointer(): {&renameUnmergedAction{
			"file",
			cre.ConflictRenameHelper(now, "u2", "dev1", "file"),
			"", zeroPtr, zeroPtr}},
	}

	testCRCheckPathsAndActions(t, cr2, []path{unmergedPathFile},
		mergedPaths, []*createOp{coFile}, expectedActions)
}
Exemple #23
0
func TestKeyManagerRekeyAddAndRevokeDevice(t *testing.T) {
	var u1, u2 libkb.NormalizedUsername = "******", "u2"
	config1, _, ctx := kbfsOpsConcurInit(t, u1, u2)
	defer CheckConfigAndShutdown(t, config1)

	config2 := ConfigAsUser(config1.(*ConfigLocal), u2)
	defer CheckConfigAndShutdown(t, config2)
	uid2, err := config2.KBPKI().GetCurrentUID(context.Background())
	if err != nil {
		t.Fatal(err)
	}

	// Create a shared folder
	name := u1.String() + "," + u2.String()

	kbfsOps1 := config1.KBFSOps()
	rootNode1, _, err :=
		kbfsOps1.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Couldn't create folder: %v", err)
	}

	// user 1 creates a file
	_, _, err = kbfsOps1.CreateFile(ctx, rootNode1, "a", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	config2Dev2 := ConfigAsUser(config1.(*ConfigLocal), u2)
	defer CheckConfigAndShutdown(t, config2Dev2)

	// Now give u2 a new device.  The configs don't share a Keybase
	// Daemon so we have to do it in all places.
	AddDeviceForLocalUserOrBust(t, config1, uid2)
	AddDeviceForLocalUserOrBust(t, config2, uid2)
	devIndex := AddDeviceForLocalUserOrBust(t, config2Dev2, uid2)
	SwitchDeviceForLocalUserOrBust(t, config2Dev2, devIndex)

	// user 2 should be unable to read the data now since its device
	// wasn't registered when the folder was originally created.
	kbfsOps2Dev2 := config2Dev2.KBFSOps()
	_, _, err =
		kbfsOps2Dev2.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if _, ok := err.(ReadAccessError); !ok {
		t.Fatalf("Got unexpected error when reading with new key: %v", err)
	}

	// now user 1 should rekey
	err = kbfsOps1.Rekey(ctx, rootNode1.GetFolderBranch().Tlf)
	if err != nil {
		t.Fatalf("Couldn't rekey: %v", err)
	}

	// this device should be able to read now
	root2Dev2, _, err :=
		kbfsOps2Dev2.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Got unexpected error after rekey: %v", err)
	}

	// add a third device for user 2
	config2Dev3 := ConfigAsUser(config1.(*ConfigLocal), u2)
	defer CheckConfigAndShutdown(t, config2Dev3)
	AddDeviceForLocalUserOrBust(t, config1, uid2)
	AddDeviceForLocalUserOrBust(t, config2, uid2)
	AddDeviceForLocalUserOrBust(t, config2Dev2, uid2)
	devIndex = AddDeviceForLocalUserOrBust(t, config2Dev3, uid2)
	SwitchDeviceForLocalUserOrBust(t, config2Dev3, devIndex)

	// Now revoke the original user 2 device
	RevokeDeviceForLocalUserOrBust(t, config1, uid2, 0)
	RevokeDeviceForLocalUserOrBust(t, config2Dev2, uid2, 0)
	RevokeDeviceForLocalUserOrBust(t, config2Dev3, uid2, 0)

	// rekey again
	err = kbfsOps1.Rekey(ctx, rootNode1.GetFolderBranch().Tlf)
	if err != nil {
		t.Fatalf("Couldn't rekey: %v", err)
	}

	// force re-encryption of the root dir
	_, _, err = kbfsOps1.CreateFile(ctx, rootNode1, "b", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	err = kbfsOps2Dev2.SyncFromServer(ctx, root2Dev2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	// device 2 should still work
	rootNode2, _, err :=
		kbfsOps2Dev2.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Got unexpected error after rekey: %v", err)
	}

	children, err := kbfsOps2Dev2.GetDirChildren(ctx, rootNode2)
	if _, ok := children["b"]; !ok {
		t.Fatalf("Device 2 couldn't see the new dir entry")
	}

	// but device 1 should now fail
	kbfsOps2 := config2.KBFSOps()
	_, _, err = kbfsOps2.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if _, ok := err.(ReadAccessError); !ok {
		t.Fatalf("Got unexpected error when reading with revoked key: %v", err)
	}

	// meanwhile, device 3 should be able to read both the new and the
	// old files
	kbfsOps2Dev3 := config2Dev3.KBFSOps()
	rootNode2Dev3, _, err :=
		kbfsOps2Dev3.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Device 3 couldn't read root: %v", err)
	}

	aNode, _, err := kbfsOps2Dev3.Lookup(ctx, rootNode2Dev3, "a")
	if err != nil {
		t.Fatalf("Device 3 couldn't lookup a: %v", err)
	}

	buf := []byte{0}
	_, err = kbfsOps2Dev3.Read(ctx, aNode, buf, 0)
	if err != nil {
		t.Fatalf("Device 3 couldn't read a: %v", err)
	}

	bNode, _, err := kbfsOps2Dev3.Lookup(ctx, rootNode2Dev3, "b")
	if err != nil {
		t.Fatalf("Device 3 couldn't lookup b: %v", err)
	}

	_, err = kbfsOps2Dev3.Read(ctx, bNode, buf, 0)
	if err != nil {
		t.Fatalf("Device 3 couldn't read b: %v", err)
	}

	// Make sure the server-side keys for the revoked device are gone
	// for all keygens.
	rmd, err := config1.MDOps().GetForTLF(ctx, rootNode1.GetFolderBranch().Tlf)
	if err != nil {
		t.Fatalf("Couldn't get latest md: %v", err)
	}
	currKeyGen := rmd.LatestKeyGeneration()
	// clear the key cache
	config2.SetKeyCache(NewKeyCacheStandard(5000))
	km2, ok := config2.KeyManager().(*KeyManagerStandard)
	if !ok {
		t.Fatal("Wrong kind of key manager for config2")
	}
	for keyGen := KeyGen(FirstValidKeyGen); keyGen <= currKeyGen; keyGen++ {
		_, err = km2.getTLFCryptKeyUsingCurrentDevice(ctx, rmd, keyGen, true)
		if err == nil {
			t.Errorf("User 2 could still fetch a key for keygen %d", keyGen)
		}
	}
}
Exemple #24
0
func TestKeyManagerSelfRekeyAcrossDevices(t *testing.T) {
	var u1, u2 libkb.NormalizedUsername = "******", "u2"
	config1, _, ctx := kbfsOpsConcurInit(t, u1, u2)
	defer CheckConfigAndShutdown(t, config1)

	config2 := ConfigAsUser(config1.(*ConfigLocal), u2)
	defer CheckConfigAndShutdown(t, config2)
	uid2, err := config2.KBPKI().GetCurrentUID(context.Background())
	if err != nil {
		t.Fatal(err)
	}

	t.Log("Create a shared folder")
	name := u1.String() + "," + u2.String()

	kbfsOps1 := config1.KBFSOps()
	rootNode1, _, err :=
		kbfsOps1.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Couldn't create folder: %v", err)
	}

	t.Log("User 1 creates a file")
	_, _, err = kbfsOps1.CreateFile(ctx, rootNode1, "a", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	t.Log("User 2 adds a device")
	// The configs don't share a Keybase Daemon so we have to do it in all
	// places.
	AddDeviceForLocalUserOrBust(t, config1, uid2)
	devIndex := AddDeviceForLocalUserOrBust(t, config2, uid2)

	config2Dev2 := ConfigAsUser(config2, u2)
	defer CheckConfigAndShutdown(t, config2Dev2)
	SwitchDeviceForLocalUserOrBust(t, config2Dev2, devIndex)

	t.Log("Check that user 2 device 2 is unable to read the file")
	// user 2 device 2 should be unable to read the data now since its device
	// wasn't registered when the folder was originally created.
	kbfsOps2Dev2 := config2Dev2.KBFSOps()
	_, _, err =
		kbfsOps2Dev2.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if _, ok := err.(ReadAccessError); !ok {
		t.Fatalf("Got unexpected error when reading with new key: %v", err)
	}

	t.Log("User 2 rekeys from device 1")
	kbfsOps2 := config2.KBFSOps()
	root2dev1, _, err := kbfsOps2.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Couldn't obtain folder: %#v", err)
	}

	err = kbfsOps2.Rekey(ctx, root2dev1.GetFolderBranch().Tlf)
	if err != nil {
		t.Fatalf("Couldn't rekey: %v", err)
	}

	t.Log("User 2 device 2 should be able to read now")
	root2dev2, _, err :=
		kbfsOps2Dev2.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Got unexpected error after rekey: %v", err)
	}

	t.Log("User 2 device 2 reads user 1's file")
	children2, err := kbfsOps2Dev2.GetDirChildren(ctx, root2dev2)
	if _, ok := children2["a"]; !ok {
		t.Fatalf("Device 2 couldn't see user 1's dir entry")
	}

	t.Log("User 2 device 2 creates a file")
	_, _, err = kbfsOps2Dev2.CreateFile(ctx, root2dev2, "b", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	t.Log("User 1 syncs from the server")
	err = kbfsOps1.SyncFromServer(ctx, rootNode1.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	t.Log("User 1 should be able to read the file that user 2 device 2 created")
	children1, err := kbfsOps1.GetDirChildren(ctx, rootNode1)
	if _, ok := children1["b"]; !ok {
		t.Fatalf("Device 1 couldn't see the new dir entry")
	}
}
// Test that deleted blocks are correctly flushed from the user cache.
func TestQuotaReclamationDeletedBlocks(t *testing.T) {
	var u1, u2 libkb.NormalizedUsername = "******", "u2"
	config1, _, ctx := kbfsOpsInitNoMocks(t, u1, u2)
	defer CheckConfigAndShutdown(t, config1)

	clock, now := newTestClockAndTimeNow()
	config1.SetClock(clock)

	// Initialize the MD using a different config
	config2 := ConfigAsUser(config1.(*ConfigLocal), u2)
	defer CheckConfigAndShutdown(t, config2)
	config2.SetClock(clock)

	name := u1.String() + "," + u2.String()
	rootNode1 := GetRootNodeOrBust(t, config1, name, false)
	data := []byte{1, 2, 3, 4, 5}
	kbfsOps1 := config1.KBFSOps()
	aNode1, _, err := kbfsOps1.CreateFile(ctx, rootNode1, "a", false)
	if err != nil {
		t.Fatalf("Couldn't create dir: %v", err)
	}
	err = kbfsOps1.Write(ctx, aNode1, data, 0)
	if err != nil {
		t.Fatalf("Couldn't write file: %v", err)
	}
	err = kbfsOps1.Sync(ctx, aNode1)
	if err != nil {
		t.Fatalf("Couldn't sync file: %v", err)
	}

	// Make two more files that share a block, only one of which will
	// be deleted.
	otherData := []byte{5, 4, 3, 2, 1}
	for _, name := range []string{"b", "c"} {
		node, _, err := kbfsOps1.CreateFile(ctx, rootNode1, name, false)
		if err != nil {
			t.Fatalf("Couldn't create dir: %v", err)
		}
		err = kbfsOps1.Write(ctx, node, otherData, 0)
		if err != nil {
			t.Fatalf("Couldn't write file: %v", err)
		}
		err = kbfsOps1.Sync(ctx, node)
		if err != nil {
			t.Fatalf("Couldn't sync file: %v", err)
		}
	}

	// u2 reads the file
	rootNode2 := GetRootNodeOrBust(t, config2, name, false)
	kbfsOps2 := config2.KBFSOps()
	aNode2, _, err := kbfsOps2.Lookup(ctx, rootNode2, "a")
	if err != nil {
		t.Fatalf("Couldn't create dir: %v", err)
	}
	data2 := make([]byte, len(data))
	_, err = kbfsOps2.Read(ctx, aNode2, data2, 0)
	if err != nil {
		t.Fatalf("Couldn't read file: %v", err)
	}
	if !bytes.Equal(data, data2) {
		t.Fatalf("Read bad data: %v", data2)
	}
	bNode2, _, err := kbfsOps2.Lookup(ctx, rootNode2, "b")
	if err != nil {
		t.Fatalf("Couldn't create dir: %v", err)
	}
	data2 = make([]byte, len(data))
	_, err = kbfsOps2.Read(ctx, bNode2, data2, 0)
	if err != nil {
		t.Fatalf("Couldn't read file: %v", err)
	}
	if !bytes.Equal(otherData, data2) {
		t.Fatalf("Read bad data: %v", data2)
	}

	// Remove two of the files
	err = kbfsOps1.RemoveEntry(ctx, rootNode1, "a")
	if err != nil {
		t.Fatalf("Couldn't remove file: %v", err)
	}
	err = kbfsOps1.RemoveEntry(ctx, rootNode1, "b")
	if err != nil {
		t.Fatalf("Couldn't remove file: %v", err)
	}

	// Wait for outstanding archives
	err = kbfsOps1.SyncFromServerForTesting(ctx, rootNode1.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	// Get the current set of blocks
	bserverLocal, ok := config1.BlockServer().(*BlockServerLocal)
	if !ok {
		t.Fatalf("Bad block server")
	}
	preQRBlocks, err := bserverLocal.getAll(rootNode1.GetFolderBranch().Tlf)
	if err != nil {
		t.Fatalf("Couldn't get blocks: %v", err)
	}

	clock.Set(now.Add(2 * config1.QuotaReclamationMinUnrefAge()))
	ops1 := kbfsOps1.(*KBFSOpsStandard).getOpsByNode(ctx, rootNode1)
	ops1.fbm.forceQuotaReclamation()
	err = ops1.fbm.waitForQuotaReclamations(ctx)
	if err != nil {
		t.Fatalf("Couldn't wait for QR: %v", err)
	}

	postQRBlocks, err := bserverLocal.getAll(rootNode1.GetFolderBranch().Tlf)
	if err != nil {
		t.Fatalf("Couldn't get blocks: %v", err)
	}

	if pre, post := totalBlockRefs(preQRBlocks),
		totalBlockRefs(postQRBlocks); post >= pre {
		t.Errorf("Blocks didn't shrink after reclamation: pre: %d, post %d",
			pre, post)
	}

	// Sync u2
	err = kbfsOps2.SyncFromServerForTesting(ctx, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	// Make a file with the other data on node 2, which uses a block
	// for which one reference has been deleted, but the other should
	// still be live.  This will cause one dedup reference, and 3 new
	// blocks (2 from the create, and 1 from the sync).
	dNode, _, err := kbfsOps2.CreateFile(ctx, rootNode2, "d", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}
	err = kbfsOps2.Write(ctx, dNode, otherData, 0)
	if err != nil {
		t.Fatalf("Couldn't write file: %v", err)
	}
	err = kbfsOps2.Sync(ctx, dNode)
	if err != nil {
		t.Fatalf("Couldn't write file: %v", err)
	}
	// Wait for outstanding archives
	err = kbfsOps2.SyncFromServerForTesting(ctx, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	// Make the same file on node 2, making sure this doesn't try to
	// reuse the same block (i.e., there are only 2 put calls).
	eNode, _, err := kbfsOps2.CreateFile(ctx, rootNode2, "e", false)
	if err != nil {
		t.Fatalf("Couldn't create dir: %v", err)
	}
	err = kbfsOps2.Write(ctx, eNode, data, 0)
	if err != nil {
		t.Fatalf("Couldn't write file: %v", err)
	}

	// Stall the puts that comes as part of the sync call.
	onWriteStalledCh := make(chan struct{}, 2)
	writeUnstallCh := make(chan struct{})
	stallKey := "requestName"
	writeValue := "write"
	config2.SetBlockOps(&stallingBlockOps{
		stallOpName: "Put",
		stallKey:    stallKey,
		stallMap: map[interface{}]staller{
			writeValue: staller{
				stalled: onWriteStalledCh,
				unstall: writeUnstallCh,
			},
		},
		delegate: config2.BlockOps(),
	})

	// Start the sync and wait for it to stall twice only.
	errChan := make(chan error)
	go func() {
		syncCtx := context.WithValue(ctx, stallKey, writeValue)
		errChan <- kbfsOps2.Sync(syncCtx, eNode)
	}()
	<-onWriteStalledCh
	<-onWriteStalledCh
	writeUnstallCh <- struct{}{}
	writeUnstallCh <- struct{}{}
	// Don't close the channel, we want to make sure other Puts get
	// stalled.
	err = <-errChan
	if err != nil {
		t.Fatalf("Couldn't sync file: %v", err)
	}

	// Wait for outstanding archives
	err = kbfsOps2.SyncFromServerForTesting(ctx, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	// Delete any blocks that happened to be put during a failed (due
	// to recoverable block errors) update.
	clock.Set(now.Add(2 * config1.QuotaReclamationMinUnrefAge()))
	ops1.fbm.forceQuotaReclamation()
	err = ops1.fbm.waitForQuotaReclamations(ctx)
	if err != nil {
		t.Fatalf("Couldn't wait for QR: %v", err)
	}

	endBlocks, err := bserverLocal.getAll(rootNode2.GetFolderBranch().Tlf)
	if err != nil {
		t.Fatalf("Couldn't get blocks: %v", err)
	}

	// There should be exactly 8 extra blocks refs (2 for the create,
	// and 2 for the write/sync, for both files above) as a result of
	// the operations, and exactly one should have more than one
	// reference.
	if pre, post := totalBlockRefs(postQRBlocks),
		totalBlockRefs(endBlocks); post != pre+8 {
		t.Errorf("Different number of blocks than expected: pre: %d, post %d",
			pre, post)
	}
	oneDedupFound := false
	for id, refs := range endBlocks {
		if len(refs) > 1 && (len(refs) > 2 || oneDedupFound) {
			t.Errorf("Block %v unexpectedly had %d references", id, len(refs))
		} else if len(refs) == 2 {
			oneDedupFound = true
		}
	}
	if !oneDedupFound {
		t.Error("No dedup reference found")
	}
}
Exemple #26
0
// This tests 2 variations of the situation where clients w/o the folder key set the rekey bit.
// In one case the client is a writer and in the other a reader. They both blindly copy the existing
// metadata and simply set the rekey bit. Then another participant rekeys the folder and they try to read.
func TestKeyManagerRekeyBit(t *testing.T) {
	var u1, u2, u3 libkb.NormalizedUsername = "******", "u2", "u3"
	config1, _, ctx := kbfsOpsConcurInit(t, u1, u2, u3)
	doShutdown1 := true
	defer func() {
		if doShutdown1 {
			CheckConfigAndShutdown(t, config1)
		}
	}()
	config1.MDServer().DisableRekeyUpdatesForTesting()

	config2 := ConfigAsUser(config1.(*ConfigLocal), u2)
	defer CheckConfigAndShutdown(t, config2)
	uid2, err := config2.KBPKI().GetCurrentUID(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	config2.MDServer().DisableRekeyUpdatesForTesting()

	config3 := ConfigAsUser(config1.(*ConfigLocal), u3)
	defer CheckConfigAndShutdown(t, config3)
	uid3, err := config3.KBPKI().GetCurrentUID(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	config3.MDServer().DisableRekeyUpdatesForTesting()

	// 2 writers 1 reader
	name := u1.String() + "," + u2.String() + "#" + u3.String()

	kbfsOps1 := config1.KBFSOps()
	rootNode1, _, err :=
		kbfsOps1.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Couldn't create folder: %v", err)
	}

	// user 1 creates a file
	_, _, err = kbfsOps1.CreateFile(ctx, rootNode1, "a", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	config2Dev2 := ConfigAsUser(config1.(*ConfigLocal), u2)
	// we don't check the config because this device can't read all of the md blocks.
	defer config2Dev2.Shutdown()
	config2Dev2.MDServer().DisableRekeyUpdatesForTesting()

	// Now give u2 a new device.  The configs don't share a Keybase
	// Daemon so we have to do it in all places.
	AddDeviceForLocalUserOrBust(t, config1, uid2)
	AddDeviceForLocalUserOrBust(t, config2, uid2)
	AddDeviceForLocalUserOrBust(t, config3, uid2)
	devIndex := AddDeviceForLocalUserOrBust(t, config2Dev2, uid2)
	SwitchDeviceForLocalUserOrBust(t, config2Dev2, devIndex)

	// user 2 should be unable to read the data now since its device
	// wasn't registered when the folder was originally created.
	kbfsOps2Dev2 := config2Dev2.KBFSOps()
	_, _, err =
		kbfsOps2Dev2.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if _, ok := err.(ReadAccessError); !ok {
		t.Fatalf("Got unexpected error when reading with new key: %v", err)
	}

	// now user 2 should set the rekey bit
	err = kbfsOps2Dev2.Rekey(ctx, rootNode1.GetFolderBranch().Tlf)
	if err != nil {
		t.Fatalf("Couldn't rekey: %v", err)
	}

	// user 1 syncs from server
	err = kbfsOps1.SyncFromServer(ctx, rootNode1.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	// user 1 should try to rekey
	err = kbfsOps1.Rekey(ctx, rootNode1.GetFolderBranch().Tlf)
	if err != nil {
		t.Fatalf("Couldn't rekey: %v", err)
	}

	// user 2 syncs from server
	err = kbfsOps2Dev2.SyncFromServer(ctx, rootNode1.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	// this device should be able to read now
	rootNode2Dev2, _, err := kbfsOps2Dev2.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Got unexpected error after rekey: %v", err)
	}

	// look for the file
	aNode, _, err := kbfsOps2Dev2.Lookup(ctx, rootNode2Dev2, "a")
	if err != nil {
		t.Fatalf("Device 2 couldn't lookup a: %v", err)
	}

	// read it
	buf := []byte{0}
	_, err = kbfsOps2Dev2.Read(ctx, aNode, buf, 0)
	if err != nil {
		t.Fatalf("Device 2 couldn't read a: %v", err)
	}

	config3Dev2 := ConfigAsUser(config1.(*ConfigLocal), u3)
	// we don't check the config because this device can't read all of the md blocks.
	defer config3Dev2.Shutdown()
	config3Dev2.MDServer().DisableRekeyUpdatesForTesting()

	// Now give u3 a new device.
	AddDeviceForLocalUserOrBust(t, config1, uid3)
	AddDeviceForLocalUserOrBust(t, config2, uid3)
	AddDeviceForLocalUserOrBust(t, config2Dev2, uid3)
	AddDeviceForLocalUserOrBust(t, config3, uid3)
	devIndex = AddDeviceForLocalUserOrBust(t, config3Dev2, uid3)
	SwitchDeviceForLocalUserOrBust(t, config3Dev2, devIndex)

	// user 3 dev 2 should be unable to read the data now since its device
	// wasn't registered when the folder was originally created.
	kbfsOps3Dev2 := config3Dev2.KBFSOps()
	_, _, err =
		kbfsOps3Dev2.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if _, ok := err.(ReadAccessError); !ok {
		t.Fatalf("Got unexpected error when reading with new key: %v", err)
	}

	// now user 3 dev 2 should set the rekey bit
	err = kbfsOps3Dev2.Rekey(ctx, rootNode1.GetFolderBranch().Tlf)
	if err != nil {
		t.Fatalf("Couldn't rekey: %v", err)
	}

	// user 2 dev 2 syncs from server
	err = kbfsOps2Dev2.SyncFromServer(ctx, rootNode1.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	// user 2 dev 2 should try to rekey
	err = kbfsOps2Dev2.Rekey(ctx, rootNode1.GetFolderBranch().Tlf)
	if err != nil {
		t.Fatalf("Couldn't rekey: %v", err)
	}

	// user 3 dev 2 syncs from server
	err = kbfsOps3Dev2.SyncFromServer(ctx, rootNode1.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}

	// this device should be able to read now
	rootNode3Dev2, _, err := kbfsOps3Dev2.GetOrCreateRootNode(ctx, name, false, MasterBranch)
	if err != nil {
		t.Fatalf("Got unexpected error after rekey: %v", err)
	}

	// look for the file
	a2Node, _, err := kbfsOps3Dev2.Lookup(ctx, rootNode3Dev2, "a")
	if err != nil {
		t.Fatalf("Device 3 couldn't lookup a: %v", err)
	}

	// read it
	buf = []byte{0}
	_, err = kbfsOps3Dev2.Read(ctx, a2Node, buf, 0)
	if err != nil {
		t.Fatalf("Device 3 couldn't read a: %v", err)
	}

	// Explicitly run the checks with config1 before the deferred shutdowns begin.
	// This way the shared mdserver hasn't been shutdown.
	CheckConfigAndShutdown(t, config1)
	doShutdown1 = false
}
// Test that quota reclamation doesn't happen while waiting for a
// requested rekey.
func TestQuotaReclamationFailAfterRekeyRequest(t *testing.T) {
	var u1, u2 libkb.NormalizedUsername = "******", "u2"
	config1, _, ctx := kbfsOpsConcurInit(t, u1, u2)
	defer CheckConfigAndShutdown(t, config1)
	clock := newTestClockNow()
	config1.SetClock(clock)

	config2 := ConfigAsUser(config1.(*ConfigLocal), u2)
	defer CheckConfigAndShutdown(t, config2)
	_, uid2, err := config2.KBPKI().GetCurrentUserInfo(context.Background())
	if err != nil {
		t.Fatal(err)
	}

	// Create a shared folder.
	name := u1.String() + "," + u2.String()
	rootNode1 := GetRootNodeOrBust(t, config1, name, false)

	config2Dev2 := ConfigAsUser(config1.(*ConfigLocal), u2)
	defer CheckConfigAndShutdown(t, config2Dev2)

	// Now give u2 a new device.  The configs don't share a Keybase
	// Daemon so we have to do it in all places.
	AddDeviceForLocalUserOrBust(t, config1, uid2)
	AddDeviceForLocalUserOrBust(t, config2, uid2)
	devIndex := AddDeviceForLocalUserOrBust(t, config2Dev2, uid2)
	SwitchDeviceForLocalUserOrBust(t, config2Dev2, devIndex)

	// user 2 should be unable to read the data now since its device
	// wasn't registered when the folder was originally created.
	_, err = GetRootNodeForTest(config2Dev2, name, false)
	if _, ok := err.(NeedSelfRekeyError); !ok {
		t.Fatalf("Got unexpected error when reading with new key: %v", err)
	}

	// Request a rekey from the new device, which will only be
	// able to set the rekey bit (copying the root MD).
	kbfsOps2Dev2 := config2Dev2.KBFSOps()
	err = kbfsOps2Dev2.Rekey(ctx, rootNode1.GetFolderBranch().Tlf)
	if err != nil {
		t.Fatalf("Couldn't rekey: %v", err)
	}

	// Make sure QR returns an error.
	ops := config2Dev2.KBFSOps().(*KBFSOpsStandard).getOpsByNode(ctx, rootNode1)
	timer := time.NewTimer(config2Dev2.QuotaReclamationPeriod())
	ops.fbm.reclamationGroup.Add(1)
	err = ops.fbm.doReclamation(timer)
	if _, ok := err.(NeedSelfRekeyError); !ok {
		t.Fatalf("Unexpected rekey error: %v", err)
	}

	// Rekey from another device.
	kbfsOps1 := config1.KBFSOps()
	err = kbfsOps1.SyncFromServerForTesting(ctx, rootNode1.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}
	err = kbfsOps1.Rekey(ctx, rootNode1.GetFolderBranch().Tlf)
	if err != nil {
		t.Fatalf("Couldn't rekey: %v", err)
	}

	// Retry the QR; should work now.
	err = kbfsOps2Dev2.SyncFromServerForTesting(ctx,
		rootNode1.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}
	ops.fbm.reclamationGroup.Add(1)
	err = ops.fbm.doReclamation(timer)
	if err != nil {
		t.Fatalf("Unexpected rekey error: %v", err)
	}
}
Exemple #28
0
// Test that, when writing multiple blocks in parallel under conflict
// resolution, one error will cancel the remaining puts and the block
// server will be consistent.
func TestCRSyncParallelBlocksErrorCleanup(t *testing.T) {
	// simulate two users
	var userName1, userName2 libkb.NormalizedUsername = "******", "u2"
	config1, _, ctx := kbfsOpsConcurInit(t, userName1, userName2)
	defer CheckConfigAndShutdown(t, config1)
	config1.MDServer().DisableRekeyUpdatesForTesting()

	config2 := ConfigAsUser(config1.(*ConfigLocal), userName2)
	defer CheckConfigAndShutdown(t, config2)
	_, _, err := config2.KBPKI().GetCurrentUserInfo(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	config2.MDServer().DisableRekeyUpdatesForTesting()

	config2.SetClock(newTestClockNow())
	name := userName1.String() + "," + userName2.String()

	// make blocks small
	blockSize := int64(5)
	config1.BlockSplitter().(*BlockSplitterSimple).maxSize = blockSize

	// create and write to a file
	rootNode := GetRootNodeOrBust(t, config1, name, false)
	kbfsOps1 := config1.KBFSOps()
	_, _, err = kbfsOps1.CreateFile(ctx, rootNode, "a", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	// look it up on user2
	rootNode2 := GetRootNodeOrBust(t, config2, name, false)

	kbfsOps2 := config2.KBFSOps()
	_, _, err = kbfsOps2.Lookup(ctx, rootNode2, "a")
	if err != nil {
		t.Fatalf("Couldn't lookup dir: %v", err)
	}
	// disable updates and CR on user 2
	c, err := DisableUpdatesForTesting(config2, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't disable updates: %v", err)
	}
	err = DisableCRForTesting(config2, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't disable updates: %v", err)
	}

	// User 1 creates a new file to start a conflict.
	_, _, err = kbfsOps1.CreateFile(ctx, rootNode, "b", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	// User 2 does one successful operation to create the first unmerged MD.
	fileNodeB, _, err := kbfsOps2.CreateFile(ctx, rootNode2, "b", false)
	if err != nil {
		t.Fatalf("Couldn't create file: %v", err)
	}

	// Now user 2 makes a big write where most of the blocks get canceled.
	// We only need to know the first time we stall.
	onSyncStalledCh := make(chan struct{}, maxParallelBlockPuts)
	syncUnstallCh := make(chan struct{})
	stallKey := "requestName"
	syncValue := "sync"

	config2.SetBlockOps(&stallingBlockOps{
		stallOpName: "Put",
		stallKey:    stallKey,
		stallMap: map[interface{}]staller{
			syncValue: staller{
				stalled: onSyncStalledCh,
				unstall: syncUnstallCh,
			},
		},
		delegate: config2.BlockOps(),
	})

	// User 2 writes some data
	fileBlocks := int64(15)
	var data []byte
	for i := int64(0); i < blockSize*fileBlocks; i++ {
		data = append(data, byte(i))
	}
	err = kbfsOps2.Write(ctx, fileNodeB, data, 0)
	if err != nil {
		t.Fatalf("Couldn't write: %v", err)
	}

	// Start the sync and wait for it to stall.
	var wg sync.WaitGroup
	wg.Add(1)
	syncCtx, cancel := context.WithCancel(context.Background())
	var syncErr error
	go func() {
		defer wg.Done()

		syncCtx = context.WithValue(syncCtx, stallKey, syncValue)
		syncErr = kbfsOps2.Sync(syncCtx, fileNodeB)
	}()
	// Wait for 2 of the blocks and let them go
	<-onSyncStalledCh
	<-onSyncStalledCh
	syncUnstallCh <- struct{}{}
	syncUnstallCh <- struct{}{}

	// Wait for the rest of the puts (this indicates that the first
	// two succeeded correctly and two more were sent to replace them)
	for i := 0; i < maxParallelBlockPuts; i++ {
		<-onSyncStalledCh
	}
	// Cancel so all other block puts fail
	cancel()
	close(syncUnstallCh)
	wg.Wait()

	// Get the mdWriterLock to be sure the sync has exited (since the
	// cleanup logic happens in a background goroutine)
	ops := getOps(config2, rootNode2.GetFolderBranch().Tlf)
	lState := makeFBOLockState()
	ops.mdWriterLock.Lock(lState)
	ops.mdWriterLock.Unlock(lState)

	// The state checker will make sure those blocks from
	// the failed sync get cleaned up.

	// Now succeed with different data so CR can happen.
	config2.SetBlockOps(config2.BlockOps().(*stallingBlockOps).delegate)
	for i := int64(0); i < blockSize*fileBlocks; i++ {
		data[i] = byte(i + 10)
	}
	err = kbfsOps2.Write(ctx, fileNodeB, data, 0)
	if err != nil {
		t.Fatalf("Couldn't write: %v", err)
	}
	err = kbfsOps2.Sync(ctx, fileNodeB)
	if err != nil {
		t.Fatalf("Couldn't sync: %v", err)
	}

	c <- struct{}{}
	err = RestartCRForTesting(config2, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't disable updates: %v", err)
	}
	err = kbfsOps2.SyncFromServerForTesting(ctx, rootNode2.GetFolderBranch())
	if err != nil {
		t.Fatalf("Couldn't sync from server: %v", err)
	}
}