summaryrefslogtreecommitdiff
path: root/lib/backupstore/BackupStoreRefCountDatabase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/backupstore/BackupStoreRefCountDatabase.cpp')
-rw-r--r--lib/backupstore/BackupStoreRefCountDatabase.cpp199
1 files changed, 167 insertions, 32 deletions
diff --git a/lib/backupstore/BackupStoreRefCountDatabase.cpp b/lib/backupstore/BackupStoreRefCountDatabase.cpp
index 26f9acca..b2ea1abd 100644
--- a/lib/backupstore/BackupStoreRefCountDatabase.cpp
+++ b/lib/backupstore/BackupStoreRefCountDatabase.cpp
@@ -9,6 +9,8 @@
#include "Box.h"
+#include <stdio.h>
+
#include <algorithm>
#include "BackupStoreRefCountDatabase.h"
@@ -34,12 +36,85 @@
//
// --------------------------------------------------------------------------
BackupStoreRefCountDatabase::BackupStoreRefCountDatabase(const
- BackupStoreAccountDatabase::Entry& rAccount)
+ BackupStoreAccountDatabase::Entry& rAccount, bool ReadOnly,
+ bool Temporary, std::auto_ptr<FileStream> apDatabaseFile)
: mAccount(rAccount),
- mFilename(GetFilename(rAccount)),
- mReadOnly(true),
- mIsModified(false)
+ mFilename(GetFilename(rAccount, Temporary)),
+ mReadOnly(ReadOnly),
+ mIsModified(false),
+ mIsTemporaryFile(Temporary),
+ mapDatabaseFile(apDatabaseFile)
+{
+ ASSERT(!(ReadOnly && Temporary)); // being both doesn't make sense
+}
+
+void BackupStoreRefCountDatabase::Commit()
{
+ if (!mIsTemporaryFile)
+ {
+ THROW_EXCEPTION_MESSAGE(CommonException, Internal,
+ "Cannot commit a permanent reference count database");
+ }
+
+ if (!mapDatabaseFile.get())
+ {
+ THROW_EXCEPTION_MESSAGE(CommonException, Internal,
+ "Reference count database is already closed");
+ }
+
+ mapDatabaseFile->Close();
+ mapDatabaseFile.reset();
+
+ std::string Final_Filename = GetFilename(mAccount, false);
+
+ #ifdef WIN32
+ if(FileExists(Final_Filename) && unlink(Final_Filename.c_str()) != 0)
+ {
+ THROW_EMU_FILE_ERROR("Failed to delete old permanent refcount "
+ "database file", mFilename, CommonException,
+ OSFileError);
+ }
+ #endif
+
+ if(rename(mFilename.c_str(), Final_Filename.c_str()) != 0)
+ {
+ THROW_EMU_ERROR("Failed to rename temporary refcount database "
+ "file from " << mFilename << " to " <<
+ Final_Filename, CommonException, OSFileError);
+ }
+
+ mFilename = Final_Filename;
+ mIsModified = false;
+ mIsTemporaryFile = false;
+}
+
+void BackupStoreRefCountDatabase::Discard()
+{
+ if (!mIsTemporaryFile)
+ {
+ THROW_EXCEPTION_MESSAGE(CommonException, Internal,
+ "Cannot discard a permanent reference count database");
+ }
+
+ // Under normal conditions, we should know whether the file is still
+ // open or not, and not Discard it unless it's open. However if the
+ // final rename() fails during Commit(), the file will already be
+ // closed, and we don't want to blow up here in that case.
+ if (mapDatabaseFile.get())
+ {
+ mapDatabaseFile->Close();
+ mapDatabaseFile.reset();
+ }
+
+ if(unlink(mFilename.c_str()) != 0)
+ {
+ THROW_EMU_FILE_ERROR("Failed to delete temporary refcount "
+ "database file", mFilename, CommonException,
+ OSFileError);
+ }
+
+ mIsModified = false;
+ mIsTemporaryFile = false;
}
// --------------------------------------------------------------------------
@@ -52,16 +127,35 @@ BackupStoreRefCountDatabase::BackupStoreRefCountDatabase(const
// --------------------------------------------------------------------------
BackupStoreRefCountDatabase::~BackupStoreRefCountDatabase()
{
+ if (mIsTemporaryFile)
+ {
+ // Don't throw exceptions in a destructor.
+ BOX_ERROR("BackupStoreRefCountDatabase destroyed without "
+ "explicit commit or discard");
+ try
+ {
+ Discard();
+ }
+ catch(BoxException &e)
+ {
+ BOX_LOG_SYS_ERROR("Failed to discard BackupStoreRefCountDatabase "
+ "in destructor: " << e.what());
+ }
+ }
}
std::string BackupStoreRefCountDatabase::GetFilename(const
- BackupStoreAccountDatabase::Entry& rAccount)
+ BackupStoreAccountDatabase::Entry& rAccount, bool Temporary)
{
std::string RootDir = BackupStoreAccounts::GetAccountRoot(rAccount);
ASSERT(RootDir[RootDir.size() - 1] == '/' ||
RootDir[RootDir.size() - 1] == DIRECTORY_SEPARATOR_ASCHAR);
- std::string fn(RootDir + REFCOUNT_FILENAME ".db");
+ std::string fn(RootDir + REFCOUNT_FILENAME ".rdb");
+ if(Temporary)
+ {
+ fn += "X";
+ }
RaidFileController &rcontroller(RaidFileController::GetController());
RaidFileDiscSet rdiscSet(rcontroller.GetDiscSet(rAccount.GetDiscSet()));
return RaidFileUtil::MakeWriteFileName(rdiscSet, fn);
@@ -72,40 +166,53 @@ std::string BackupStoreRefCountDatabase::GetFilename(const
// Function
// Name: BackupStoreRefCountDatabase::Create(int32_t,
// const std::string &, int, bool)
-// Purpose: Create a new database, overwriting an existing
-// one only if AllowOverwrite is true.
+// Purpose: Create a blank database, using a temporary file that
+// you must Discard() or Commit() to make permanent.
// Created: 2003/08/28
//
// --------------------------------------------------------------------------
-void BackupStoreRefCountDatabase::Create(const
- BackupStoreAccountDatabase::Entry& rAccount, bool AllowOverwrite)
+std::auto_ptr<BackupStoreRefCountDatabase>
+ BackupStoreRefCountDatabase::Create
+ (const BackupStoreAccountDatabase::Entry& rAccount)
{
// Initial header
refcount_StreamFormat hdr;
hdr.mMagicValue = htonl(REFCOUNT_MAGIC_VALUE);
hdr.mAccountID = htonl(rAccount.GetID());
- // Generate the filename
- std::string Filename = GetFilename(rAccount);
+ std::string Filename = GetFilename(rAccount, true); // temporary
// Open the file for writing
- if (FileExists(Filename) && !AllowOverwrite)
- {
- THROW_FILE_ERROR("Failed to overwrite refcount database: "
- "not allowed here", Filename, RaidFileException,
- CannotOverwriteExistingFile);
- }
-
- int flags = O_CREAT | O_BINARY | O_RDWR;
- if (!AllowOverwrite)
+ if (FileExists(Filename))
{
- flags |= O_EXCL;
+ BOX_WARNING(BOX_FILE_MESSAGE(Filename, "Overwriting existing "
+ "temporary reference count database"));
+ if (unlink(Filename.c_str()) != 0)
+ {
+ THROW_SYS_FILE_ERROR("Failed to delete old temporary "
+ "reference count database file", Filename,
+ CommonException, OSFileError);
+ }
}
+ int flags = O_CREAT | O_BINARY | O_RDWR | O_EXCL;
std::auto_ptr<FileStream> DatabaseFile(new FileStream(Filename, flags));
// Write header
DatabaseFile->Write(&hdr, sizeof(hdr));
+
+ // Make new object
+ std::auto_ptr<BackupStoreRefCountDatabase> refcount(
+ new BackupStoreRefCountDatabase(rAccount, false, true,
+ DatabaseFile));
+
+ // The root directory must always have one reference for a database
+ // to be valid, so set that now on the new database. This will leave
+ // mIsModified set to true.
+ refcount->SetRefCount(BACKUPSTORE_ROOT_DIRECTORY_ID, 1);
+
+ // return it to caller
+ return refcount;
}
// --------------------------------------------------------------------------
@@ -122,12 +229,13 @@ void BackupStoreRefCountDatabase::Create(const
std::auto_ptr<BackupStoreRefCountDatabase> BackupStoreRefCountDatabase::Load(
const BackupStoreAccountDatabase::Entry& rAccount, bool ReadOnly)
{
- // Generate the filename
- std::string filename = GetFilename(rAccount);
+ // Generate the filename. Cannot open a temporary database, so it must
+ // be a permanent one.
+ std::string Filename = GetFilename(rAccount, false);
int flags = ReadOnly ? O_RDONLY : O_RDWR;
// Open the file for read/write
- std::auto_ptr<FileStream> dbfile(new FileStream(filename,
+ std::auto_ptr<FileStream> dbfile(new FileStream(Filename,
flags | O_BINARY));
// Read in a header
@@ -135,7 +243,7 @@ std::auto_ptr<BackupStoreRefCountDatabase> BackupStoreRefCountDatabase::Load(
if(!dbfile->ReadFullBuffer(&hdr, sizeof(hdr), 0 /* not interested in bytes read if this fails */))
{
THROW_FILE_ERROR("Failed to read refcount database: "
- "short read", filename, BackupStoreException,
+ "short read", Filename, BackupStoreException,
CouldNotLoadStoreInfo);
}
@@ -144,16 +252,14 @@ std::auto_ptr<BackupStoreRefCountDatabase> BackupStoreRefCountDatabase::Load(
(int32_t)ntohl(hdr.mAccountID) != rAccount.GetID())
{
THROW_FILE_ERROR("Failed to read refcount database: "
- "bad magic number", filename, BackupStoreException,
+ "bad magic number", Filename, BackupStoreException,
BadStoreInfoOnLoad);
}
// Make new object
- std::auto_ptr<BackupStoreRefCountDatabase> refcount(new BackupStoreRefCountDatabase(rAccount));
-
- // Put in basic location info
- refcount->mReadOnly = ReadOnly;
- refcount->mapDatabaseFile = dbfile;
+ std::auto_ptr<BackupStoreRefCountDatabase> refcount(
+ new BackupStoreRefCountDatabase(rAccount, ReadOnly, false,
+ dbfile));
// return it to caller
return refcount;
@@ -229,6 +335,7 @@ void BackupStoreRefCountDatabase::SetRefCount(int64_t ObjectID,
mapDatabaseFile->Seek(offset, SEEK_SET);
refcount_t RefCountNetOrder = htonl(NewRefCount);
mapDatabaseFile->Write(&RefCountNetOrder, sizeof(RefCountNetOrder));
+ mIsModified = true;
}
bool BackupStoreRefCountDatabase::RemoveReference(int64_t ObjectID)
@@ -240,3 +347,31 @@ bool BackupStoreRefCountDatabase::RemoveReference(int64_t ObjectID)
return (refcount > 0);
}
+int BackupStoreRefCountDatabase::ReportChangesTo(BackupStoreRefCountDatabase& rOldRefs)
+{
+ int ErrorCount = 0;
+ int64_t MaxOldObjectId = rOldRefs.GetLastObjectIDUsed();
+ int64_t MaxNewObjectId = GetLastObjectIDUsed();
+
+ for (int64_t ObjectID = BACKUPSTORE_ROOT_DIRECTORY_ID;
+ ObjectID < std::max(MaxOldObjectId, MaxNewObjectId);
+ ObjectID++)
+ {
+ typedef BackupStoreRefCountDatabase::refcount_t refcount_t;
+ refcount_t OldRefs = (ObjectID <= MaxOldObjectId) ?
+ rOldRefs.GetRefCount(ObjectID) : 0;
+ refcount_t NewRefs = (ObjectID <= MaxNewObjectId) ?
+ this->GetRefCount(ObjectID) : 0;
+
+ if (OldRefs != NewRefs)
+ {
+ BOX_WARNING("Reference count of object " <<
+ BOX_FORMAT_OBJECTID(ObjectID) <<
+ " changed from " << OldRefs <<
+ " to " << NewRefs);
+ ErrorCount++;
+ }
+ }
+
+ return ErrorCount;
+}