diff options
Diffstat (limited to 'lib/backupstore/BackupStoreRefCountDatabase.cpp')
-rw-r--r-- | lib/backupstore/BackupStoreRefCountDatabase.cpp | 199 |
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; +} |