diff options
Diffstat (limited to 'lib/backupstore/BackupStoreCheck.cpp')
-rw-r--r-- | lib/backupstore/BackupStoreCheck.cpp | 383 |
1 files changed, 225 insertions, 158 deletions
diff --git a/lib/backupstore/BackupStoreCheck.cpp b/lib/backupstore/BackupStoreCheck.cpp index f2302337..b53ebf6d 100644 --- a/lib/backupstore/BackupStoreCheck.cpp +++ b/lib/backupstore/BackupStoreCheck.cpp @@ -17,11 +17,13 @@ #endif #include "autogen_BackupStoreException.h" +#include "BackupStoreAccountDatabase.h" #include "BackupStoreCheck.h" #include "BackupStoreConstants.h" #include "BackupStoreDirectory.h" #include "BackupStoreFile.h" #include "BackupStoreObjectMagic.h" +#include "BackupStoreRefCountDatabase.h" #include "RaidFileController.h" #include "RaidFileException.h" #include "RaidFileRead.h" @@ -58,7 +60,7 @@ BackupStoreCheck::BackupStoreCheck(const std::string &rStoreRoot, int DiscSetNum mBlocksInOldFiles(0), mBlocksInDeletedFiles(0), mBlocksInDirectories(0), - mNumFiles(0), + mNumCurrentFiles(0), mNumOldFiles(0), mNumDeletedFiles(0), mNumDirectories(0) @@ -78,6 +80,24 @@ BackupStoreCheck::~BackupStoreCheck() { // Clean up FreeInfo(); + + // Avoid an exception if we forget to discard mapNewRefs + if (mapNewRefs.get()) + { + // Discard() can throw exception, but destructors aren't supposed to do that, so + // just catch and log them. + try + { + mapNewRefs->Discard(); + } + catch(BoxException &e) + { + BOX_ERROR("Error while destroying BackupStoreCheck: discarding " + "the refcount database threw an exception: " << e.what()); + } + + mapNewRefs.reset(); + } } @@ -92,15 +112,21 @@ BackupStoreCheck::~BackupStoreCheck() // -------------------------------------------------------------------------- void BackupStoreCheck::Check() { - std::string writeLockFilename; - StoreStructure::MakeWriteLockFilename(mStoreRoot, mDiscSetNumber, writeLockFilename); - ASSERT(FileExists(writeLockFilename)); + if(mFixErrors) + { + std::string writeLockFilename; + StoreStructure::MakeWriteLockFilename(mStoreRoot, mDiscSetNumber, writeLockFilename); + ASSERT(FileExists(writeLockFilename)); + } if(!mQuiet && mFixErrors) { - BOX_NOTICE("Will fix errors encountered during checking."); + BOX_INFO("Will fix errors encountered during checking."); } + BackupStoreAccountDatabase::Entry account(mAccountID, mDiscSetNumber); + mapNewRefs = BackupStoreRefCountDatabase::Create(account); + // Phase 1, check objects if(!mQuiet) { @@ -109,14 +135,14 @@ void BackupStoreCheck::Check() BOX_INFO("Phase 1, check objects..."); } CheckObjects(); - + // Phase 2, check directories if(!mQuiet) { BOX_INFO("Phase 2, check directories..."); } CheckDirectories(); - + // Phase 3, check root if(!mQuiet) { @@ -138,16 +164,38 @@ void BackupStoreCheck::Check() } FixDirsWithWrongContainerID(); FixDirsWithLostDirs(); - + // Phase 6, regenerate store info if(!mQuiet) { BOX_INFO("Phase 6, regenerate store info..."); } WriteNewStoreInfo(); - -// DUMP_OBJECT_INFO - + + try + { + std::auto_ptr<BackupStoreRefCountDatabase> apOldRefs = + BackupStoreRefCountDatabase::Load(account, false); + mNumberErrorsFound += mapNewRefs->ReportChangesTo(*apOldRefs); + } + catch(BoxException &e) + { + BOX_WARNING("Reference count database was missing or " + "corrupted, cannot check it for errors."); + mNumberErrorsFound++; + } + + // force file to be saved and closed before releasing the lock below + if(mFixErrors) + { + mapNewRefs->Commit(); + } + else + { + mapNewRefs->Discard(); + } + mapNewRefs.reset(); + if(mNumberErrorsFound > 0) { BOX_WARNING("Finished checking store account ID " << @@ -250,16 +298,18 @@ void BackupStoreCheck::CheckObjects() { // Make sure the starting root dir doesn't end with '/'. std::string start(mStoreRoot); - if(start.size() > 0 && start[start.size() - 1] == '/') + if(start.size() > 0 && ( + start[start.size() - 1] == '/' || + start[start.size() - 1] == DIRECTORY_SEPARATOR_ASCHAR)) { start.resize(start.size() - 1); } - - maxDir = CheckObjectsScanDir(0, 1, mStoreRoot); + + maxDir = CheckObjectsScanDir(0, 1, start); BOX_TRACE("Max dir starting ID is " << BOX_FORMAT_OBJECTID(maxDir)); } - + // Then go through and scan all the objects within those directories for(int64_t d = 0; d <= maxDir; d += (1<<STORE_ID_SEGMENT_LENGTH)) { @@ -287,24 +337,25 @@ int64_t BackupStoreCheck::CheckObjectsScanDir(int64_t StartID, int Level, const // If any of the directories is missing, create it. RaidFileController &rcontroller(RaidFileController::GetController()); RaidFileDiscSet rdiscSet(rcontroller.GetDiscSet(mDiscSetNumber)); - + if(!rdiscSet.IsNonRaidSet()) { unsigned int numDiscs = rdiscSet.size(); - + for(unsigned int l = 0; l < numDiscs; ++l) { // build name std::string dn(rdiscSet[l] + DIRECTORY_SEPARATOR + rDirName); - struct stat st; + EMU_STRUCT_STAT st; - if(stat(dn.c_str(), &st) != 0 && errno == ENOENT) + if(EMU_STAT(dn.c_str(), &st) != 0 && + errno == ENOENT) { if(mkdir(dn.c_str(), 0755) != 0) { THROW_SYS_FILE_ERROR("Failed to " "create missing RaidFile " - "directory", dn, + "directory", dn, RaidFileException, OSError); } } @@ -334,7 +385,7 @@ int64_t BackupStoreCheck::CheckObjectsScanDir(int64_t StartID, int Level, const else { BOX_ERROR("Spurious or invalid directory " << - rDirName << DIRECTORY_SEPARATOR << + rDirName << DIRECTORY_SEPARATOR << (*i) << " found, " << (mFixErrors?"deleting":"delete manually")); ++mNumberErrorsFound; @@ -361,11 +412,11 @@ void BackupStoreCheck::CheckObjectsDir(int64_t StartID) std::string dirName; StoreStructure::MakeObjectFilename(StartID, mStoreRoot, mDiscSetNumber, dirName, false /* don't make sure the dir exists */); // Check expectations - ASSERT(dirName.size() > 4 && + ASSERT(dirName.size() > 4 && dirName[dirName.size() - 4] == DIRECTORY_SEPARATOR_ASCHAR); // Remove the filename from it dirName.resize(dirName.size() - 4); // four chars for "/o00" - + // Check directory exists if(!RaidFileRead::DirectoryExists(mDiscSetNumber, dirName)) { @@ -377,14 +428,14 @@ void BackupStoreCheck::CheckObjectsDir(int64_t StartID) std::vector<std::string> files; RaidFileRead::ReadDirectoryContents(mDiscSetNumber, dirName, RaidFileRead::DirReadType_FilesOnly, files); - + // Array of things present bool idsPresent[(1<<STORE_ID_SEGMENT_LENGTH)]; for(int l = 0; l < (1<<STORE_ID_SEGMENT_LENGTH); ++l) { idsPresent[l] = false; } - + // Parse each entry, building up a list of object IDs which are present in the dir. // This is done so that whatever order is retured from the directory, objects are scanned // in order. @@ -405,7 +456,8 @@ void BackupStoreCheck::CheckObjectsDir(int64_t StartID) fileOK = false; } // info and refcount databases are OK in the root directory - else if(*i == "info" || *i == "refcount.db") + else if(*i == "info" || *i == "refcount.db" || + *i == "refcount.rdb" || *i == "refcount.rdbX") { fileOK = true; } @@ -413,11 +465,11 @@ void BackupStoreCheck::CheckObjectsDir(int64_t StartID) { fileOK = false; } - + if(!fileOK) { // Unexpected or bad file, delete it - BOX_ERROR("Spurious file " << dirName << + BOX_ERROR("Spurious file " << dirName << DIRECTORY_SEPARATOR << (*i) << " found" << (mFixErrors?", deleting":"")); ++mNumberErrorsFound; @@ -428,7 +480,7 @@ void BackupStoreCheck::CheckObjectsDir(int64_t StartID) } } } - + // Check all the objects found in this directory for(int i = 0; i < (1<<STORE_ID_SEGMENT_LENGTH); ++i) { @@ -436,7 +488,8 @@ void BackupStoreCheck::CheckObjectsDir(int64_t StartID) { // Check the object is OK, and add entry char leaf[8]; - ::sprintf(leaf, DIRECTORY_SEPARATOR "o%02x", i); + ::snprintf(leaf, sizeof(leaf), + DIRECTORY_SEPARATOR "o%02x", i); if(!CheckAndAddObject(StartID | i, dirName + leaf)) { // File was bad, delete it @@ -480,7 +533,7 @@ bool BackupStoreCheck::CheckAndAddObject(int64_t ObjectID, std::auto_ptr<RaidFileRead> file( RaidFileRead::Open(mDiscSetNumber, rFilename)); size = file->GetDiscUsageInBlocks(); - + // Read in first four bytes -- don't have to worry about // retrying if not all bytes read as is RaidFile uint32_t signature; @@ -491,7 +544,7 @@ bool BackupStoreCheck::CheckAndAddObject(int64_t ObjectID, } // Seek back to beginning file->Seek(0, IOStream::SeekType_Absolute); - + // Then... check depending on the type switch(ntohl(signature)) { @@ -519,7 +572,7 @@ bool BackupStoreCheck::CheckAndAddObject(int64_t ObjectID, // Error caught, not a good file then, let it be deleted return false; } - + // Got a container ID? (ie check was successful) if(containerID == -1) { @@ -538,13 +591,13 @@ bool BackupStoreCheck::CheckAndAddObject(int64_t ObjectID, // If it looks like a good object, and it's non-RAID, and // this is a RAID set, then convert it to RAID. - + RaidFileController &rcontroller(RaidFileController::GetController()); RaidFileDiscSet rdiscSet(rcontroller.GetDiscSet(mDiscSetNumber)); if(!rdiscSet.IsNonRaidSet()) { // See if the file exists - RaidFileUtil::ExistType existance = + RaidFileUtil::ExistType existance = RaidFileUtil::RaidFileExists(rdiscSet, rFilename); if(existance == RaidFileUtil::NonRaid) { @@ -565,7 +618,7 @@ bool BackupStoreCheck::CheckAndAddObject(int64_t ObjectID, if(mFixErrors) { std::auto_ptr<RaidFileRead> read( - RaidFileRead::Open(mDiscSetNumber, + RaidFileRead::Open(mDiscSetNumber, rFilename)); RaidFileWrite write(mDiscSetNumber, rFilename); write.Open(true /* overwrite */); @@ -575,7 +628,7 @@ bool BackupStoreCheck::CheckAndAddObject(int64_t ObjectID, } } } - + // Report success return true; } @@ -636,7 +689,7 @@ int64_t BackupStoreCheck::CheckDirInitial(int64_t ObjectID, IOStream &rStream) // Wrong object ID return -1; } - + // Return container ID return dir.GetContainerID(); } @@ -669,7 +722,7 @@ void BackupStoreCheck::CheckDirectories() { IDBlock *pblock = i->second; int32_t bentries = (pblock == mpInfoLastBlock)?mInfoLastBlockEntries:BACKUPSTORECHECK_BLOCK_SIZE; - + for(int e = 0; e < bentries; ++e) { uint8_t flags = GetFlags(pblock, e); @@ -703,9 +756,9 @@ void BackupStoreCheck::CheckDirectories() BOX_FORMAT_OBJECTID(pblock->mID[e]) << " was OK after fixing"); } - + if(isModified && mFixErrors) - { + { BOX_WARNING("Writing modified directory to disk: " << BOX_FORMAT_OBJECTID(pblock->mID[e])); RaidFileWrite fixed(mDiscSetNumber, filename); @@ -714,64 +767,7 @@ void BackupStoreCheck::CheckDirectories() fixed.Commit(true /* convert to raid representation now */); } - // Count valid entries - BackupStoreDirectory::Iterator i(dir); - BackupStoreDirectory::Entry *en = 0; - while((en = i.Next()) != 0) - { - int32_t iIndex; - IDBlock *piBlock = LookupID(en->GetObjectID(), iIndex); - - ASSERT(piBlock != 0 || - mDirsWhichContainLostDirs.find(en->GetObjectID()) - != mDirsWhichContainLostDirs.end()); - if (piBlock) - { - // Normally it would exist and this - // check would not be necessary, but - // we might have missing directories - // that we will recreate later. - // cf mDirsWhichContainLostDirs. - uint8_t iflags = GetFlags(piBlock, iIndex); - SetFlags(piBlock, iIndex, iflags | Flags_IsContained); - } - - if(en->IsDir()) - { - mNumDirectories++; - } - else if(!en->IsFile()) - { - BOX_TRACE("Not counting object " << - BOX_FORMAT_OBJECTID(en->GetObjectID()) << - " with flags " << en->GetFlags()); - } - else // it's a good file, add to sizes - if(en->IsOld() && en->IsDeleted()) - { - BOX_WARNING("File " << - BOX_FORMAT_OBJECTID(en->GetObjectID()) << - " is both old and deleted, " - "this should not happen!"); - } - else if(en->IsOld()) - { - mNumFiles++; - mNumOldFiles++; - mBlocksInOldFiles += en->GetSizeInBlocks(); - } - else if(en->IsDeleted()) - { - mNumFiles++; - mNumDeletedFiles++; - mBlocksInDeletedFiles += en->GetSizeInBlocks(); - } - else - { - mNumFiles++; - mBlocksInCurrentFiles += en->GetSizeInBlocks(); - } - } + CountDirectoryEntries(dir); } } } @@ -798,8 +794,8 @@ bool BackupStoreCheck::CheckDirectory(BackupStoreDirectory& dir) ++mNumberErrorsFound; isModified = true; } - - // Go through, and check that everything in that directory exists and is valid + + // Go through, and check that every entry exists and is valid BackupStoreDirectory::Iterator i(dir); BackupStoreDirectory::Entry *en = 0; while((en = i.Next()) != 0) @@ -824,14 +820,14 @@ bool BackupStoreCheck::CheckDirectory(BackupStoreDirectory& dir) { // Just remove the entry badEntry = true; - BOX_ERROR("Directory ID " << + BOX_ERROR("Directory ID " << BOX_FORMAT_OBJECTID(dir.GetObjectID()) << - " references object " << + " references object " << BOX_FORMAT_OBJECTID(en->GetObjectID()) << " which does not exist."); ++mNumberErrorsFound; } - + // Is this entry worth keeping? if(badEntry) { @@ -846,16 +842,16 @@ bool BackupStoreCheck::CheckDirectory(BackupStoreDirectory& dir) { BOX_ERROR("Removing directory entry " << BOX_FORMAT_OBJECTID(*d) << " from " - "directory " << + "directory " << BOX_FORMAT_OBJECTID(dir.GetObjectID())); ++mNumberErrorsFound; dir.DeleteEntry(*d); } - + // Mark as modified restart = true; isModified = true; - + // Errors found } } @@ -863,6 +859,84 @@ bool BackupStoreCheck::CheckDirectory(BackupStoreDirectory& dir) return isModified; } +// Count valid remaining entries and the number of blocks in them. +void BackupStoreCheck::CountDirectoryEntries(BackupStoreDirectory& dir) +{ + BackupStoreDirectory::Iterator i(dir); + BackupStoreDirectory::Entry *en = 0; + while((en = i.Next()) != 0) + { + int32_t iIndex; + IDBlock *piBlock = LookupID(en->GetObjectID(), iIndex); + bool badEntry = false; + bool wasAlreadyContained = false; + + ASSERT(piBlock != 0 || + mDirsWhichContainLostDirs.find(en->GetObjectID()) + != mDirsWhichContainLostDirs.end()); + + if (piBlock) + { + // Normally it would exist and this + // check would not be necessary, but + // we might have missing directories + // that we will recreate later. + // cf mDirsWhichContainLostDirs. + uint8_t iflags = GetFlags(piBlock, iIndex); + wasAlreadyContained = (iflags & Flags_IsContained); + SetFlags(piBlock, iIndex, iflags | Flags_IsContained); + } + + if(wasAlreadyContained) + { + // don't double-count objects that are + // contained by another directory as well. + } + else if(en->IsDir()) + { + mNumDirectories++; + } + else if(!en->IsFile()) + { + BOX_TRACE("Not counting object " << + BOX_FORMAT_OBJECTID(en->GetObjectID()) << + " with flags " << en->GetFlags()); + } + else // it's a file + { + // Add to sizes? + // If piBlock was zero, then wasAlreadyContained + // might be uninitialized; but we only process + // files here, and if a file's piBlock was zero + // then badEntry would be set above, so we + // wouldn't be here. + ASSERT(!badEntry) + + // It can be both old and deleted. + // If neither, then it's current. + if(en->IsDeleted()) + { + mNumDeletedFiles++; + mBlocksInDeletedFiles += en->GetSizeInBlocks(); + } + + if(en->IsOld()) + { + mNumOldFiles++; + mBlocksInOldFiles += en->GetSizeInBlocks(); + } + + if(!en->IsDeleted() && !en->IsOld()) + { + mNumCurrentFiles++; + mBlocksInCurrentFiles += en->GetSizeInBlocks(); + } + } + + mapNewRefs->AddReference(en->GetObjectID()); + } +} + bool BackupStoreCheck::CheckDirectoryEntry(BackupStoreDirectory::Entry& rEntry, int64_t DirectoryID, bool& rIsModified) { @@ -871,8 +945,7 @@ 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 +955,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_NOTICE("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 } - |