[Box Backup-commit] COMMIT r3315 - in box/trunk: lib/backupstore test/backupstore

subversion at boxbackup.org subversion at boxbackup.org
Wed Apr 9 23:15:19 BST 2014


Author: chris
Date: 2014-04-09 23:15:19 +0100 (Wed, 09 Apr 2014)
New Revision: 3315

Modified:
   box/trunk/lib/backupstore/BackupStoreCheck.cpp
   box/trunk/lib/backupstore/BackupStoreContext.cpp
   box/trunk/lib/backupstore/HousekeepStoreAccount.cpp
   box/trunk/lib/backupstore/HousekeepStoreAccount.h
   box/trunk/test/backupstore/testbackupstore.cpp
Log:
Directories' entries in parent directory should track current size.

When entries are added to a directory by a command (BackupStoreContext),
and when entries are removed from a directory (by housekeeping), update the
parent directory's entry for us if our size has changed.

Make BackupStoreCheck check for, report and fix errors when directory entries
are directories and the size is wrong (as well as files).

Conflicts:

	test/backupstore/testbackupstore.cpp

Fix directories loaded without size being set, leading to warnings later.

We can't check that the old size in the parent entry matched the old real size
of the directory, unless we set the old real size in the directory.

And we don't need to pass the old directory size to
HousekeepStoreAccount::DeleteFile, because we can get it from the directory
itself.

Modified: box/trunk/lib/backupstore/BackupStoreCheck.cpp
===================================================================
--- box/trunk/lib/backupstore/BackupStoreCheck.cpp	2014-04-09 22:15:14 UTC (rev 3314)
+++ box/trunk/lib/backupstore/BackupStoreCheck.cpp	2014-04-09 22:15:19 UTC (rev 3315)
@@ -871,7 +871,6 @@
 	ASSERT(piBlock != 0);
 
 	uint8_t iflags = GetFlags(piBlock, IndexInDirBlock);
-	bool badEntry = false;
 	
 	// Is the type the same?
 	if(((iflags & Flags_IsDir) == Flags_IsDir) != rEntry.IsDir())
@@ -882,11 +881,12 @@
 			" references object " <<
 			BOX_FORMAT_OBJECTID(rEntry.GetObjectID()) <<
 			" which has a different type than expected.");
-		badEntry = true;
 		++mNumberErrorsFound;
+		return false; // remove this entry
 	}
+
 	// Check that the entry is not already contained.
-	else if(iflags & Flags_IsContained)
+	if(iflags & Flags_IsContained)
 	{
 		BOX_ERROR("Directory ID " <<
 			BOX_FORMAT_OBJECTID(DirectoryID) <<
@@ -893,62 +893,55 @@
 			" references object " <<
 			BOX_FORMAT_OBJECTID(rEntry.GetObjectID()) <<
 			" which is already contained.");
-		badEntry = true;
 		++mNumberErrorsFound;
+		return false; // remove this entry
 	}
-	else
+
+	// Not already contained by another directory.
+	// Don't set the flag until later, after we finish repairing
+	// the directory and removing all bad entries.
+	
+	// Check that the container ID of the object is correct
+	if(piBlock->mContainer[IndexInDirBlock] != DirectoryID)
 	{
-		// Not already contained by another directory.
-		// Don't set the flag until later, after we finish repairing
-		// the directory and removing all bad entries.
-		
-		// Check that the container ID of the object is correct
-		if(piBlock->mContainer[IndexInDirBlock] != DirectoryID)
+		// Needs fixing...
+		if(iflags & Flags_IsDir)
 		{
-			// Needs fixing...
-			if(iflags & Flags_IsDir)
-			{
-				// Add to will fix later list
-				BOX_ERROR("Directory ID " <<
-					BOX_FORMAT_OBJECTID(rEntry.GetObjectID())
-					<< " has wrong container ID.");
-				mDirsWithWrongContainerID.push_back(rEntry.GetObjectID());
-				++mNumberErrorsFound;
-			}
-			else
-			{
-				// This is OK for files, they might move
-				BOX_INFO("File ID " <<
-					BOX_FORMAT_OBJECTID(rEntry.GetObjectID())
-					<< " has different container ID, "
-					"probably moved");
-			}
-			
-			// Fix entry for now
-			piBlock->mContainer[IndexInDirBlock] = DirectoryID;
+			// Add to will fix later list
+			BOX_ERROR("Directory ID " <<
+				BOX_FORMAT_OBJECTID(rEntry.GetObjectID())
+				<< " has wrong container ID.");
+			mDirsWithWrongContainerID.push_back(rEntry.GetObjectID());
+			++mNumberErrorsFound;
 		}
+		else
+		{
+			// This is OK for files, they might move
+			BOX_INFO("File ID " <<
+				BOX_FORMAT_OBJECTID(rEntry.GetObjectID())
+				<< " has different container ID, "
+				"probably moved");
+		}
+		
+		// Fix entry for now
+		piBlock->mContainer[IndexInDirBlock] = DirectoryID;
 	}
-	
-	// Check the object size, if it's OK and a file
-	if(!badEntry && !rEntry.IsDir())
+
+	// Check the object size
+	if(rEntry.GetSizeInBlocks() != piBlock->mObjectSizeInBlocks[IndexInDirBlock])
 	{
-		if(rEntry.GetSizeInBlocks() != piBlock->mObjectSizeInBlocks[IndexInDirBlock])
-		{
-			// Wrong size, correct it.
-			rEntry.SetSizeInBlocks(piBlock->mObjectSizeInBlocks[IndexInDirBlock]);
+		// Wrong size, correct it.
+		BOX_ERROR("Directory " << BOX_FORMAT_OBJECTID(DirectoryID) <<
+			" entry for " << BOX_FORMAT_OBJECTID(rEntry.GetObjectID()) <<
+			" has wrong size " << rEntry.GetSizeInBlocks() <<
+			", should be " << piBlock->mObjectSizeInBlocks[IndexInDirBlock]);
 
-			// Mark as changed
-			rIsModified = true;
-			++mNumberErrorsFound;
+		rEntry.SetSizeInBlocks(piBlock->mObjectSizeInBlocks[IndexInDirBlock]);
 
-			// Tell user
-			BOX_ERROR("Directory ID " <<
-				BOX_FORMAT_OBJECTID(DirectoryID) <<
-				" has wrong size for object " <<
-				BOX_FORMAT_OBJECTID(rEntry.GetObjectID()));
-		}
+		// Mark as changed
+		rIsModified = true;
+		++mNumberErrorsFound;
 	}
 
-	return !badEntry;
+	return true; // don't delete this entry
 }
-

Modified: box/trunk/lib/backupstore/BackupStoreContext.cpp
===================================================================
--- box/trunk/lib/backupstore/BackupStoreContext.cpp	2014-04-09 22:15:14 UTC (rev 3314)
+++ box/trunk/lib/backupstore/BackupStoreContext.cpp	2014-04-09 22:15:19 UTC (rev 3315)
@@ -972,6 +972,8 @@
 		// Write to disc, adjust size in store info
 		std::string dirfn;
 		MakeObjectFilename(ObjectID, dirfn);
+		int64_t old_dir_size = rDir.GetUserInfo1_SizeInBlocks();
+
 		{
 			RaidFileWrite writeDir(mStoreDiscSet, dirfn);
 			writeDir.Open(true /* allow overwriting */);
@@ -994,6 +996,7 @@
 			// Update size stored in directory
 			rDir.SetUserInfo1_SizeInBlocks(dirSize);
 		}
+
 		// Refresh revision ID in cache
 		{
 			int64_t revid = 0;
@@ -1003,6 +1006,22 @@
 			}
 			rDir.SetRevisionID(revid);
 		}
+
+		// Update the directory entry in the grandparent, to ensure
+		// that it reflects the current size of the parent directory.
+		int64_t new_dir_size = rDir.GetUserInfo1_SizeInBlocks();
+		if(new_dir_size != old_dir_size &&
+			rDir.GetObjectID() != BACKUPSTORE_ROOT_DIRECTORY_ID)
+		{
+			BackupStoreDirectory& parent(
+				GetDirectoryInternal(rDir.GetContainerID()));
+			BackupStoreDirectory::Entry* en =
+				parent.FindEntryByID(rDir.GetObjectID());
+			ASSERT(en);
+			ASSERT(en->GetSizeInBlocks() == old_dir_size);
+			en->SetSizeInBlocks(new_dir_size);
+			SaveDirectory(parent);
+		}
 	}
 	catch(...)
 	{
@@ -1608,7 +1627,9 @@
 //		Created: 12/11/03
 //
 // --------------------------------------------------------------------------
-void BackupStoreContext::MoveObject(int64_t ObjectID, int64_t MoveFromDirectory, int64_t MoveToDirectory, const BackupStoreFilename &rNewFilename, bool MoveAllWithSameName, bool AllowMoveOverDeletedObject)
+void BackupStoreContext::MoveObject(int64_t ObjectID, int64_t MoveFromDirectory,
+	int64_t MoveToDirectory, const BackupStoreFilename &rNewFilename,
+	bool MoveAllWithSameName, bool AllowMoveOverDeletedObject)
 {
 	if(mReadOnly)
 	{
@@ -1683,8 +1704,9 @@
 		return;
 	}
 
-	// Got to be careful how this is written, as we can't guarentte that if we have two
-	// directories open, the first won't be deleted as the second is opened. (cache)
+	// Got to be careful how this is written, as we can't guarantee that
+	// if we have two directories open, the first won't be deleted as the
+	// second is opened. (cache)
 
 	// List of entries to move
 	std::vector<BackupStoreDirectory::Entry *> moving;

Modified: box/trunk/lib/backupstore/HousekeepStoreAccount.cpp
===================================================================
--- box/trunk/lib/backupstore/HousekeepStoreAccount.cpp	2014-04-09 22:15:14 UTC (rev 3314)
+++ box/trunk/lib/backupstore/HousekeepStoreAccount.cpp	2014-04-09 22:15:19 UTC (rev 3315)
@@ -368,6 +368,7 @@
 	BackupStoreDirectory dir;
 	BufferedStream buf(*dirStream);
 	dir.ReadFromStream(buf, IOStream::TimeOutInfinite);
+	dir.SetUserInfo1_SizeInBlocks(originalDirSizeInBlocks);
 	dirStream->Close();
 	
 	// Is it empty?
@@ -409,8 +410,8 @@
 					&& (en->IsDeleted() || en->IsOld()))
 				{
 					// Delete this immediately.
-					DeleteFile(ObjectID, en->GetObjectID(), dir, objectFilename,
-						originalDirSizeInBlocks, rBackupStoreInfo);
+					DeleteFile(ObjectID, en->GetObjectID(), dir,
+						objectFilename, rBackupStoreInfo);
 					
 					// flag as having done something
 					deletedSomething = true;
@@ -643,19 +644,17 @@
 		// Get the filename
 		std::string dirFilename;
 		BackupStoreDirectory dir;
-		int64_t dirSizeInBlocksOrig = 0;
 		{
 			MakeObjectFilename(i->mInDirectory, dirFilename);
 			std::auto_ptr<RaidFileRead> dirStream(RaidFileRead::Open(mStoreDiscSet, dirFilename));
-			dirSizeInBlocksOrig = dirStream->GetDiscUsageInBlocks();
 			dir.ReadFromStream(*dirStream, IOStream::TimeOutInfinite);
+			dir.SetUserInfo1_SizeInBlocks(dirStream->GetDiscUsageInBlocks());
 		}
 		
 		// Delete the file
 		BackupStoreRefCountDatabase::refcount_t refs =
 			DeleteFile(i->mInDirectory, i->mObjectID, dir,
-				dirFilename, dirSizeInBlocksOrig,
-				rBackupStoreInfo);
+				dirFilename, rBackupStoreInfo);
 		if(refs == 0)
 		{
 			BOX_INFO("Housekeeping removed " <<
@@ -702,7 +701,7 @@
 
 BackupStoreRefCountDatabase::refcount_t HousekeepStoreAccount::DeleteFile(
 	int64_t InDirectory, int64_t ObjectID, BackupStoreDirectory &rDirectory,
-	const std::string &rDirectoryFilename, int64_t OriginalDirSizeInBlocks,
+	const std::string &rDirectoryFilename,
 	BackupStoreInfo& rBackupStoreInfo)
 {
 	// Find the entry inside the directory
@@ -727,6 +726,7 @@
 				BOX_FORMAT_OBJECTID(InDirectory) << ", "
 				"indicates logic error/corruption? Run "
 				"bbstoreaccounts check <accid> fix");
+			mErrorCount++;
 			return refs;
 		}
 		
@@ -859,7 +859,6 @@
 	
 	// Save directory back to disc
 	// BLOCK
-	int64_t dirRevisedSize = 0;
 	{
 		RaidFileWrite writeDir(mStoreDiscSet, rDirectoryFilename,
 			mapNewRefs->GetRefCount(InDirectory));
@@ -867,18 +866,18 @@
 		rDirectory.WriteToStream(writeDir);
 
 		// Get the disc usage (must do this before commiting it)
-		dirRevisedSize = writeDir.GetDiscUsageInBlocks();
+		int64_t new_size = writeDir.GetDiscUsageInBlocks();
 
 		// Commit directory
 		writeDir.Commit(BACKUP_STORE_CONVERT_TO_RAID_IMMEDIATELY);
 
 		// Adjust block counts if the directory itself changed in size
-		if(dirRevisedSize > 0)
-		{
-			int64_t adjust = dirRevisedSize - OriginalDirSizeInBlocks;
-			mBlocksUsedDelta += adjust;
-			mBlocksInDirectoriesDelta += adjust;
-		}
+		int64_t original_size = rDirectory.GetUserInfo1_SizeInBlocks();
+		int64_t adjust = new_size - original_size;
+		mBlocksUsedDelta += adjust;
+		mBlocksInDirectoriesDelta += adjust;
+
+		UpdateDirectorySize(rDirectory, new_size);
 	}
 
 	// Commit any new adjusted entry
@@ -926,7 +925,76 @@
 	return 0;
 }
 
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    HousekeepStoreAccount::UpdateDirectorySize(
+//			 BackupStoreDirectory& rDirectory,
+//			 IOStream::pos_type new_size_in_blocks)
+//		Purpose: Update the directory size, modifying the parent
+//			 directory's entry for this directory if necessary.
+//		Created: 05/03/14
+//
+// --------------------------------------------------------------------------
 
+void HousekeepStoreAccount::UpdateDirectorySize(
+	BackupStoreDirectory& rDirectory,
+	IOStream::pos_type new_size_in_blocks)
+{
+#ifndef NDEBUG
+	{
+		std::string dirFilename;
+		MakeObjectFilename(rDirectory.GetObjectID(), dirFilename);
+		std::auto_ptr<RaidFileRead> dirStream(
+			RaidFileRead::Open(mStoreDiscSet, dirFilename));
+		ASSERT(new_size_in_blocks == dirStream->GetDiscUsageInBlocks());
+	}
+#endif
+
+	IOStream::pos_type old_size_in_blocks =
+		rDirectory.GetUserInfo1_SizeInBlocks();
+
+	if(new_size_in_blocks == old_size_in_blocks)
+	{
+		return;
+	}
+
+	rDirectory.SetUserInfo1_SizeInBlocks(new_size_in_blocks);
+
+	if (rDirectory.GetObjectID() == BACKUPSTORE_ROOT_DIRECTORY_ID)
+	{
+		return;
+	}
+
+	std::string parentFilename;
+	MakeObjectFilename(rDirectory.GetContainerID(), parentFilename);
+	std::auto_ptr<RaidFileRead> parentStream(
+		RaidFileRead::Open(mStoreDiscSet, parentFilename));
+	BackupStoreDirectory parent(*parentStream);
+	BackupStoreDirectory::Entry* en =
+		parent.FindEntryByID(rDirectory.GetObjectID());
+	ASSERT(en);
+
+	if (en->GetSizeInBlocks() != old_size_in_blocks)
+	{
+		BOX_WARNING("Directory " <<
+			BOX_FORMAT_OBJECTID(rDirectory.GetObjectID()) <<
+			" entry in directory " <<
+			BOX_FORMAT_OBJECTID(rDirectory.GetContainerID()) <<
+			" had incorrect size " << en->GetSizeInBlocks() <<
+			", should have been " << old_size_in_blocks);
+		mErrorCount++;
+	}
+
+	en->SetSizeInBlocks(new_size_in_blocks);
+
+	RaidFileWrite writeDir(mStoreDiscSet, parentFilename,
+		mapNewRefs->GetRefCount(rDirectory.GetContainerID()));
+	writeDir.Open(true /* allow overwriting */);
+	parent.WriteToStream(writeDir);
+	writeDir.Commit(BACKUP_STORE_CONVERT_TO_RAID_IMMEDIATELY);
+}
+
 // --------------------------------------------------------------------------
 //
 // Function
@@ -1022,11 +1090,13 @@
 			containingDirStream->GetDiscUsageInBlocks();
 		containingDir.ReadFromStream(*containingDirStream,
 			IOStream::TimeOutInfinite);
+		containingDir.SetUserInfo1_SizeInBlocks(containingDirSizeInBlocksOrig);
 	}
 
 	// Find the entry
 	BackupStoreDirectory::Entry *pdirentry = 
 		containingDir.FindEntryByID(dir.GetObjectID());
+	// TODO FIXME invert test and reduce indentation
 	if((pdirentry != 0) && pdirentry->IsDeleted())
 	{
 		// Should be deleted
@@ -1049,6 +1119,7 @@
 
 		// Commit directory
 		writeDir.Commit(BACKUP_STORE_CONVERT_TO_RAID_IMMEDIATELY);
+		UpdateDirectorySize(containingDir, dirSize);
 
 		// adjust usage counts for this directory
 		if(dirSize > 0)

Modified: box/trunk/lib/backupstore/HousekeepStoreAccount.h
===================================================================
--- box/trunk/lib/backupstore/HousekeepStoreAccount.h	2014-04-09 22:15:14 UTC (rev 3314)
+++ box/trunk/lib/backupstore/HousekeepStoreAccount.h	2014-04-09 22:15:19 UTC (rev 3315)
@@ -56,10 +56,10 @@
 		int64_t ObjectID,
 		BackupStoreDirectory &rDirectory,
 		const std::string &rDirectoryFilename,
-		int64_t OriginalDirSizeInBlocks,
 		BackupStoreInfo& rBackupStoreInfo);
+	void UpdateDirectorySize(BackupStoreDirectory &rDirectory,
+		IOStream::pos_type new_size_in_blocks);
 
-private:
 	typedef struct
 	{
 		int64_t mObjectID;

Modified: box/trunk/test/backupstore/testbackupstore.cpp
===================================================================
--- box/trunk/test/backupstore/testbackupstore.cpp	2014-04-09 22:15:14 UTC (rev 3314)
+++ box/trunk/test/backupstore/testbackupstore.cpp	2014-04-09 22:15:19 UTC (rev 3315)
@@ -775,6 +775,11 @@
 	return (result == 0);
 }
 
+int64_t create_directory(BackupProtocolCallable& protocol,
+	int64_t parent_dir_id = BACKUPSTORE_ROOT_DIRECTORY_ID);
+int64_t create_file(BackupProtocolCallable& protocol, int64_t subdirid,
+	const std::string& remote_filename = "");
+
 bool test_server_housekeeping()
 {
 	SETUP();
@@ -1079,7 +1084,7 @@
 
 	// Used to not consume the stream
 	std::auto_ptr<IOStream> upload(new ZeroStream(1000));
-	TEST_COMMAND_RETURNS_ERROR(protocol.QueryStoreFile(
+	TEST_COMMAND_RETURNS_ERROR(protocol, QueryStoreFile(
 			BACKUPSTORE_ROOT_DIRECTORY_ID,
 			0,
 			0, /* use for attr hash too */
@@ -1108,7 +1113,7 @@
 	return true;
 }
 
-int64_t create_directory(BackupProtocolCallable& protocol)
+int64_t create_directory(BackupProtocolCallable& protocol, int64_t parent_dir_id)
 {
 	// Create a directory
 	BackupStoreFilenameClear dirname("lovely_directory");
@@ -1116,8 +1121,7 @@
 	std::auto_ptr<IOStream> attr(new MemBlockStream(attr1, sizeof(attr1)));
 
 	std::auto_ptr<BackupProtocolSuccess> dirCreate(protocol.QueryCreateDirectory(
-		BACKUPSTORE_ROOT_DIRECTORY_ID,
-		FAKE_ATTR_MODIFICATION_TIME, dirname, attr));
+		parent_dir_id, FAKE_ATTR_MODIFICATION_TIME, dirname, attr));
 
 	int64_t subdirid = dirCreate->GetObjectID(); 
 	set_refcount(subdirid, 1);
@@ -1124,14 +1128,26 @@
 	return subdirid;
 }
 
-int64_t create_file(BackupProtocolCallable& protocol, int64_t subdirid)
+int64_t create_file(BackupProtocolCallable& protocol, int64_t subdirid,
+	const std::string& remote_filename)
 {
 	// Stick a file in it
 	write_test_file(0);
+
+	BackupStoreFilenameClear remote_filename_encoded;
+	if (remote_filename.empty())
+	{
+		remote_filename_encoded = uploads[0].name;
+	}
+	else
+	{
+		remote_filename_encoded = remote_filename;
+	}
+
 	std::string filename("testfiles/test0");
 	int64_t modtime;
 	std::auto_ptr<IOStream> upload(BackupStoreFile::EncodeFile(filename,
-		subdirid, uploads[0].name, &modtime));
+		subdirid, remote_filename_encoded, &modtime));
 
 	std::auto_ptr<BackupProtocolSuccess> stored(protocol.QueryStoreFile(
 		subdirid,
@@ -1138,7 +1154,7 @@
 		modtime,
 		modtime, /* use for attr hash too */
 		0,							/* diff from ID */
-		uploads[0].name,
+		remote_filename_encoded,
 		upload));
 
 	int64_t subdirfileid = stored->GetObjectID();
@@ -1770,7 +1786,7 @@
 			TEST_THAT(en != 0);
 			if(en)
 			{
-				TEST_THAT(en->GetObjectID() == dirtodelete);
+				TEST_EQUAL(dirtodelete, en->GetObjectID());
 				BackupStoreFilenameClear n("test_delete");
 				TEST_THAT(en->GetName() == n);
 			}
@@ -1785,10 +1801,185 @@
 #endif
 		apProtocol->QueryFinished();
 	}
+
+	tearDown();
+	return true;
+}
+
+int get_object_size(BackupProtocolCallable& protocol, int64_t ObjectID,
+	int64_t ContainerID)
+{
+	// Get the root directory cached in the read-only connection
+	protocol.QueryListDirectory(ContainerID, 0, // FlagsMustBeSet
+		BackupProtocolListDirectory::Flags_EXCLUDE_NOTHING,
+		false /* no attributes */);
+
+	BackupStoreDirectory dir(protocol.ReceiveStream());
+	BackupStoreDirectory::Entry *en = dir.FindEntryByID(ObjectID);
+	TEST_THAT(en != 0);
+	if (!en) return -1;
+
+	TEST_EQUAL(ObjectID, en->GetObjectID());
+	return en->GetSizeInBlocks();
+}
+
+bool test_directory_parent_entry_tracks_directory_size()
+{
+	SETUP();
+
+	BackupProtocolLocal2 protocol(0x01234567, "test", "backup/01234567/",
+		0, false);
+	BackupProtocolLocal2 protocolReadOnly(0x01234567, "test",
+		"backup/01234567/", 0, true); // read only
 	
+	int64_t subdirid = create_directory(protocol);
+
+	// Get the root directory cached in the read-only connection, and
+	// test that the initial size is correct.
+	int old_size = get_raid_file(subdirid)->GetDiscUsageInBlocks();
+	TEST_THAT(old_size > 0);
+	TEST_EQUAL(old_size, get_object_size(protocolReadOnly, subdirid,
+		BACKUPSTORE_ROOT_DIRECTORY_ID));
+
+	// TODO FIXME Sleep to ensure that file timestamp changes (win32?)
+
+	// Start adding files until the size on disk increases. This is
+	// guaranteed to happen eventually :)
+	int new_size = old_size;
+	int64_t last_added_file_id = 0;
+	std::string last_added_filename;
+
+	for (int i = 0; new_size == old_size; i++)
+	{
+		std::ostringstream name;
+		name << "testfile_" << i;
+		last_added_filename = name.str();
+		last_added_file_id = create_file(protocol, subdirid, name.str());
+		new_size = get_raid_file(subdirid)->GetDiscUsageInBlocks();
+	}
+
+	// Check that the root directory entry has been updated	
+	TEST_EQUAL(new_size, get_object_size(protocolReadOnly, subdirid,
+		BACKUPSTORE_ROOT_DIRECTORY_ID));
+
+	// Now delete an entry, and check that the size is reduced
+	protocol.QueryDeleteFile(subdirid,
+		BackupStoreFilenameClear(last_added_filename));
+
+	// Reduce the limits, to remove it permanently from the store	
+	protocol.QueryFinished();
+	protocolReadOnly.QueryFinished();
+	TEST_THAT(change_account_limits("0B", "20000B"));
+	TEST_THAT(run_housekeeping_and_check_account());
+	set_refcount(last_added_file_id, 0);
+	protocol.Reopen();
+	protocolReadOnly.Reopen();
+
+	TEST_EQUAL(old_size, get_raid_file(subdirid)->GetDiscUsageInBlocks());
+
+	// Check that the entry in the root directory was updated too
+	TEST_EQUAL(old_size, get_object_size(protocolReadOnly, subdirid,
+		BACKUPSTORE_ROOT_DIRECTORY_ID));
+
+	// Push the limits back up
+	protocol.QueryFinished();
+	protocolReadOnly.QueryFinished();
+	TEST_THAT(change_account_limits("1000B", "20000B"));
+	TEST_THAT(run_housekeeping_and_check_account());
+	protocol.Reopen();
+	protocolReadOnly.Reopen();
+
+	// Add a directory, this should push the object size back up
+	int64_t dir2id = create_directory(protocol, subdirid);
+	TEST_EQUAL(new_size, get_raid_file(subdirid)->GetDiscUsageInBlocks());
+	TEST_EQUAL(new_size, get_object_size(protocolReadOnly, subdirid,
+		BACKUPSTORE_ROOT_DIRECTORY_ID));
+
+	// Delete it again, which should reduce the object size again
+	protocol.QueryDeleteDirectory(dir2id);
+
+	// Reduce the limits, to remove it permanently from the store	
+	protocol.QueryFinished();
+	protocolReadOnly.QueryFinished();
+	TEST_THAT(change_account_limits("0B", "20000B"));
+	TEST_THAT(run_housekeeping_and_check_account());
+	set_refcount(last_added_file_id, 0);
+	protocol.Reopen();
+	protocolReadOnly.Reopen();
+
+	// Check that the entry in the root directory was updated
+	TEST_EQUAL(old_size, get_raid_file(subdirid)->GetDiscUsageInBlocks());
+	TEST_EQUAL(old_size, get_object_size(protocolReadOnly, subdirid,
+		BACKUPSTORE_ROOT_DIRECTORY_ID));
+
+	// Check that bbstoreaccounts check fix will detect and repair when
+	// a directory's parent entry has the wrong size for the directory.
+
+	protocol.QueryFinished();
+
+	std::auto_ptr<RaidFileRead> root_read(get_raid_file(BACKUPSTORE_ROOT_DIRECTORY_ID));
+	BackupStoreDirectory root(static_cast<std::auto_ptr<IOStream> >(root_read));
+	BackupStoreDirectory::Entry *en = root.FindEntryByID(subdirid);
+	TEST_THAT(en != 0);
+	if (!en) return false;
+	en->SetSizeInBlocks(1234);
+
+	std::string rfn;
+	StoreStructure::MakeObjectFilename(BACKUPSTORE_ROOT_DIRECTORY_ID,
+		"backup/01234567/" /* mStoreRoot */, 0 /* mStoreDiscSet */, 
+		rfn, false); // EnsureDirectoryExists
+	RaidFileWrite rfw(0, rfn);
+	rfw.Open(true); // AllowOverwrite
+	root.WriteToStream(rfw);
+	rfw.Commit(/* ConvertToRaidNow */ true);
+
+	TEST_EQUAL(1234, get_object_size(protocolReadOnly, subdirid,
+		BACKUPSTORE_ROOT_DIRECTORY_ID));
+	TEST_EQUAL(1, check_account_for_errors());
+	TEST_EQUAL(old_size, get_object_size(protocolReadOnly, subdirid,
+		BACKUPSTORE_ROOT_DIRECTORY_ID));
+
+	protocolReadOnly.QueryFinished();
+	tearDown();	
 	return true;
 }
 
+bool test_cannot_open_multiple_writable_connections()
+{
+	SETUP();
+	TEST_THAT_THROWONFAIL(StartServer());
+
+	// First try a local protocol. This works even on Windows.
+	BackupProtocolLocal2 protocolWritable(0x01234567, "test",
+		"backup/01234567/", 0, false); // Not read-only
+
+	// Set the client store marker
+	protocolWritable.QuerySetClientStoreMarker(0x8732523ab23aLL);
+
+	// First try a local protocol. This works even on Windows.
+	BackupProtocolLocal2 protocolWritable2(0x01234567, "test",
+		"backup/01234567/", 0, false); // Not read-only
+	assert_writable_connection_fails(protocolWritable2);
+
+	BackupProtocolLocal2 protocolReadOnly(0x01234567, "test",
+		"backup/01234567/", 0, true); // Read-only
+	TEST_EQUAL(0x8732523ab23aLL,
+		assert_readonly_connection_succeeds(protocolReadOnly));
+
+	// Try network connections too.
+	TLSContext context;
+
+	BackupProtocolClient protocolWritable3(open_conn("localhost", context));
+	assert_writable_connection_fails(protocolWritable3);
+
+	BackupProtocolClient protocolReadOnly2(open_conn("localhost", context));
+	TEST_EQUAL(0x8732523ab23aLL,
+		assert_readonly_connection_succeeds(protocolReadOnly2));
+
+	tearDown();
+	return true;
+}
+
 bool test_encoding()
 {
 	// Now test encoded files
@@ -2740,6 +2931,7 @@
 	TEST_THAT(test_bbstoreaccounts_create());
 	TEST_THAT(test_bbstoreaccounts_delete());
 	TEST_THAT(test_backupstore_directory());
+	TEST_THAT(test_directory_parent_entry_tracks_directory_size());
 	TEST_THAT(test_encoding());
 	TEST_THAT(test_symlinks());
 	TEST_THAT(test_store_info());




More information about the Boxbackup-commit mailing list