summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/backupstore/BackupStoreCheck.cpp93
-rw-r--r--lib/backupstore/BackupStoreContext.cpp28
-rw-r--r--lib/backupstore/HousekeepStoreAccount.cpp101
-rw-r--r--lib/backupstore/HousekeepStoreAccount.h4
-rw-r--r--test/backupstore/testbackupstore.cpp208
5 files changed, 356 insertions, 78 deletions
diff --git a/lib/backupstore/BackupStoreCheck.cpp b/lib/backupstore/BackupStoreCheck.cpp
index 70fe4a1e..05a901fa 100644
--- a/lib/backupstore/BackupStoreCheck.cpp
+++ b/lib/backupstore/BackupStoreCheck.cpp
@@ -871,7 +871,6 @@ bool BackupStoreCheck::CheckDirectoryEntry(BackupStoreDirectory::Entry& rEntry,
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,73 +881,67 @@ bool BackupStoreCheck::CheckDirectoryEntry(BackupStoreDirectory::Entry& rEntry,
" 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) <<
" 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
}
-
diff --git a/lib/backupstore/BackupStoreContext.cpp b/lib/backupstore/BackupStoreContext.cpp
index 33c2a660..974c4e3e 100644
--- a/lib/backupstore/BackupStoreContext.cpp
+++ b/lib/backupstore/BackupStoreContext.cpp
@@ -972,6 +972,8 @@ void BackupStoreContext::SaveDirectory(BackupStoreDirectory &rDir)
// 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 @@ void BackupStoreContext::SaveDirectory(BackupStoreDirectory &rDir)
// Update size stored in directory
rDir.SetUserInfo1_SizeInBlocks(dirSize);
}
+
// Refresh revision ID in cache
{
int64_t revid = 0;
@@ -1003,6 +1006,22 @@ void BackupStoreContext::SaveDirectory(BackupStoreDirectory &rDir)
}
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 @@ void BackupStoreContext::SetClientStoreMarker(int64_t ClientStoreMarker)
// 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 @@ void BackupStoreContext::MoveObject(int64_t ObjectID, int64_t MoveFromDirectory,
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;
diff --git a/lib/backupstore/HousekeepStoreAccount.cpp b/lib/backupstore/HousekeepStoreAccount.cpp
index 0bcc6a44..2bcc59ae 100644
--- a/lib/backupstore/HousekeepStoreAccount.cpp
+++ b/lib/backupstore/HousekeepStoreAccount.cpp
@@ -368,6 +368,7 @@ bool HousekeepStoreAccount::ScanDirectory(int64_t ObjectID,
BackupStoreDirectory dir;
BufferedStream buf(*dirStream);
dir.ReadFromStream(buf, IOStream::TimeOutInfinite);
+ dir.SetUserInfo1_SizeInBlocks(originalDirSizeInBlocks);
dirStream->Close();
// Is it empty?
@@ -409,8 +410,8 @@ bool HousekeepStoreAccount::ScanDirectory(int64_t ObjectID,
&& (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 @@ bool HousekeepStoreAccount::DeleteFiles(BackupStoreInfo& rBackupStoreInfo)
// 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 @@ bool HousekeepStoreAccount::DeleteFiles(BackupStoreInfo& rBackupStoreInfo)
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 @@ BackupStoreRefCountDatabase::refcount_t HousekeepStoreAccount::DeleteFile(
BOX_FORMAT_OBJECTID(InDirectory) << ", "
"indicates logic error/corruption? Run "
"bbstoreaccounts check <accid> fix");
+ mErrorCount++;
return refs;
}
@@ -859,7 +859,6 @@ BackupStoreRefCountDatabase::refcount_t HousekeepStoreAccount::DeleteFile(
// Save directory back to disc
// BLOCK
- int64_t dirRevisedSize = 0;
{
RaidFileWrite writeDir(mStoreDiscSet, rDirectoryFilename,
mapNewRefs->GetRefCount(InDirectory));
@@ -867,18 +866,18 @@ BackupStoreRefCountDatabase::refcount_t HousekeepStoreAccount::DeleteFile(
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,6 +925,75 @@ BackupStoreRefCountDatabase::refcount_t HousekeepStoreAccount::DeleteFile(
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);
+}
// --------------------------------------------------------------------------
//
@@ -1022,11 +1090,13 @@ void HousekeepStoreAccount::DeleteEmptyDirectory(int64_t dirId,
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 @@ void HousekeepStoreAccount::DeleteEmptyDirectory(int64_t dirId,
// Commit directory
writeDir.Commit(BACKUP_STORE_CONVERT_TO_RAID_IMMEDIATELY);
+ UpdateDirectorySize(containingDir, dirSize);
// adjust usage counts for this directory
if(dirSize > 0)
diff --git a/lib/backupstore/HousekeepStoreAccount.h b/lib/backupstore/HousekeepStoreAccount.h
index ff57d1a1..ff9e9ffe 100644
--- a/lib/backupstore/HousekeepStoreAccount.h
+++ b/lib/backupstore/HousekeepStoreAccount.h
@@ -56,10 +56,10 @@ private:
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;
diff --git a/test/backupstore/testbackupstore.cpp b/test/backupstore/testbackupstore.cpp
index 0e1d7a7a..04472a5d 100644
--- a/test/backupstore/testbackupstore.cpp
+++ b/test/backupstore/testbackupstore.cpp
@@ -775,6 +775,11 @@ bool change_account_limits(const char* soft, const char* hard)
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 @@ bool test_server_housekeeping()
// 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 @@ bool test_server_housekeeping()
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,29 +1121,40 @@ int64_t create_directory(BackupProtocolCallable& protocol)
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);
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,
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 @@ bool test_multiple_uploads()
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,7 +1801,182 @@ bool test_multiple_uploads()
#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;
}
@@ -2740,6 +2931,7 @@ int test(int argc, const char *argv[])
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());