From subversion at boxbackup.org Mon Oct 22 21:45:46 2012 From: subversion at boxbackup.org (subversion at boxbackup.org) Date: Mon, 22 Oct 2012 21:45:46 +0100 (BST) Subject: [Box Backup-commit] COMMIT r3119 - box/trunk/lib/raidfile Message-ID: <201210222045.q9MKjkLs027704@wm.boxbackup.org> Author: chris Date: 2012-10-22 21:45:45 +0100 (Mon, 22 Oct 2012) New Revision: 3119 Modified: box/trunk/lib/raidfile/RaidFileController.cpp Log: Log the number of the disc set that didn't exist, and the number actually configured. Modified: box/trunk/lib/raidfile/RaidFileController.cpp =================================================================== --- box/trunk/lib/raidfile/RaidFileController.cpp 2012-07-20 21:37:48 UTC (rev 3118) +++ box/trunk/lib/raidfile/RaidFileController.cpp 2012-10-22 20:45:45 UTC (rev 3119) @@ -164,7 +164,8 @@ { if(DiscSetNum < 0 || DiscSetNum >= mSetList.size()) { - THROW_EXCEPTION(RaidFileException, NoSuchDiscSet) + THROW_EXCEPTION_MESSAGE(RaidFileException, NoSuchDiscSet, DiscSetNum << + " (" << mSetList.size() << " disc sets configured)") } return mSetList[DiscSetNum]; From subversion at boxbackup.org Mon Oct 22 21:46:11 2012 From: subversion at boxbackup.org (subversion at boxbackup.org) Date: Mon, 22 Oct 2012 21:46:11 +0100 (BST) Subject: [Box Backup-commit] COMMIT r3120 - box/trunk/lib/backupstore Message-ID: <201210222046.q9MKkBPN027721@wm.boxbackup.org> Author: chris Date: 2012-10-22 21:46:11 +0100 (Mon, 22 Oct 2012) New Revision: 3120 Modified: box/trunk/lib/backupstore/BackupStoreAccounts.h Log: Wrap line for readability. Modified: box/trunk/lib/backupstore/BackupStoreAccounts.h =================================================================== --- box/trunk/lib/backupstore/BackupStoreAccounts.h 2012-10-22 20:45:45 UTC (rev 3119) +++ box/trunk/lib/backupstore/BackupStoreAccounts.h 2012-10-22 20:46:11 UTC (rev 3120) @@ -31,7 +31,8 @@ BackupStoreAccounts(const BackupStoreAccounts &rToCopy); public: - void Create(int32_t ID, int DiscSet, int64_t SizeSoftLimit, int64_t SizeHardLimit, const std::string &rAsUsername); + void Create(int32_t ID, int DiscSet, int64_t SizeSoftLimit, + int64_t SizeHardLimit, const std::string &rAsUsername); bool AccountExists(int32_t ID); void GetAccountRoot(int32_t ID, std::string &rRootDirOut, int &rDiscSetOut) const; From subversion at boxbackup.org Mon Oct 22 21:47:08 2012 From: subversion at boxbackup.org (subversion at boxbackup.org) Date: Mon, 22 Oct 2012 21:47:08 +0100 (BST) Subject: [Box Backup-commit] COMMIT r3121 - box/trunk/lib/common Message-ID: <201210222047.q9MKl8R7027740@wm.boxbackup.org> Author: chris Date: 2012-10-22 21:47:08 +0100 (Mon, 22 Oct 2012) New Revision: 3121 Modified: box/trunk/lib/common/Archive.h Log: Add helper method to read a value that might not be present in an Archive (end of Archive) to avoid duplicating this code many times. Modified: box/trunk/lib/common/Archive.h =================================================================== --- box/trunk/lib/common/Archive.h 2012-10-22 20:46:11 UTC (rev 3120) +++ box/trunk/lib/common/Archive.h 2012-10-22 20:47:08 UTC (rev 3121) @@ -81,7 +81,7 @@ int privItem; Read(privItem); - if (privItem) + if(privItem) { rItemOut = true; } @@ -90,6 +90,12 @@ rItemOut = false; } } + void ReadIfPresent(bool &rItemOut, bool ValueIfNotPresent) + { + int privItem; + ReadIfPresent(privItem, ValueIfNotPresent ? 1 : 0); + rItemOut = privItem ? true : false; + } void ReadExact(uint32_t &rItemOut) { Read((int&)rItemOut); } void Read(int &rItemOut) { @@ -100,6 +106,25 @@ } rItemOut = ntohl(privItem); } + void ReadIfPresent(int &rItemOut, int ValueIfNotPresent) + { + int32_t privItem; + int bytesRead; + if(mrStream.ReadFullBuffer(&privItem, sizeof(privItem), &bytesRead)) + { + rItemOut = ntohl(privItem); + } + else if(bytesRead == 0) + { + // item is simply not present + rItemOut = ValueIfNotPresent; + } + else + { + // bad number of remaining bytes + THROW_EXCEPTION(CommonException, ArchiveBlockIncompleteRead) + } + } void Read(int64_t &rItemOut) { int64_t privItem; From subversion at boxbackup.org Mon Oct 22 21:47:59 2012 From: subversion at boxbackup.org (subversion at boxbackup.org) Date: Mon, 22 Oct 2012 21:47:59 +0100 (BST) Subject: [Box Backup-commit] COMMIT r3122 - box/trunk/lib/common Message-ID: <201210222047.q9MKlxdJ027759@wm.boxbackup.org> Author: chris Date: 2012-10-22 21:47:58 +0100 (Mon, 22 Oct 2012) New Revision: 3122 Modified: box/trunk/lib/common/StreamableMemBlock.cpp box/trunk/lib/common/StreamableMemBlock.h Log: Add a comment in header to indicate that StreamableMemBlock does not read/write raw memory blocks, but blocks with a header indicating their size, which is not always what we want. Modified: box/trunk/lib/common/StreamableMemBlock.cpp =================================================================== --- box/trunk/lib/common/StreamableMemBlock.cpp 2012-10-22 20:47:08 UTC (rev 3121) +++ box/trunk/lib/common/StreamableMemBlock.cpp 2012-10-22 20:47:58 UTC (rev 3122) @@ -3,6 +3,7 @@ // File // Name: StreamableMemBlock.cpp // Purpose: Memory blocks which can be loaded and saved from streams +// with a header indicating the size of the block. // Created: 2003/09/05 // // -------------------------------------------------------------------------- Modified: box/trunk/lib/common/StreamableMemBlock.h =================================================================== --- box/trunk/lib/common/StreamableMemBlock.h 2012-10-22 20:47:08 UTC (rev 3121) +++ box/trunk/lib/common/StreamableMemBlock.h 2012-10-22 20:47:58 UTC (rev 3122) @@ -2,7 +2,8 @@ // // File // Name: StreamableMemBlock.h -// Purpose: Memory blocks which can be loaded and saved from streams +// Purpose: Memory blocks which can be loaded and saved from streams, +// with a header indicating the size of the block. // Created: 2003/09/05 // // -------------------------------------------------------------------------- From subversion at boxbackup.org Mon Oct 22 21:48:54 2012 From: subversion at boxbackup.org (subversion at boxbackup.org) Date: Mon, 22 Oct 2012 21:48:54 +0100 (BST) Subject: [Box Backup-commit] COMMIT r3123 - box/trunk/lib/common Message-ID: <201210222048.q9MKmsFq027777@wm.boxbackup.org> Author: chris Date: 2012-10-22 21:48:54 +0100 (Mon, 22 Oct 2012) New Revision: 3123 Modified: box/trunk/lib/common/MemBlockStream.h Log: Add helper methods to get read-only access to the buffer, and its size, wrapped by a MemBlockStream. Modified: box/trunk/lib/common/MemBlockStream.h =================================================================== --- box/trunk/lib/common/MemBlockStream.h 2012-10-22 20:47:58 UTC (rev 3122) +++ box/trunk/lib/common/MemBlockStream.h 2012-10-22 20:48:54 UTC (rev 3123) @@ -41,6 +41,8 @@ virtual void Seek(pos_type Offset, int SeekType); virtual bool StreamDataLeft(); virtual bool StreamClosed(); + virtual const void* GetBuffer() const { return mpBuffer; } + virtual int GetSize() const { return mBytesInBuffer; } private: const char *mpBuffer; From subversion at boxbackup.org Mon Oct 22 21:51:20 2012 From: subversion at boxbackup.org (subversion at boxbackup.org) Date: Mon, 22 Oct 2012 21:51:20 +0100 (BST) Subject: [Box Backup-commit] COMMIT r3124 - box/trunk/lib/backupstore Message-ID: <201210222051.q9MKpKYk027803@wm.boxbackup.org> Author: chris Date: 2012-10-22 21:51:19 +0100 (Mon, 22 Oct 2012) New Revision: 3124 Modified: box/trunk/lib/backupstore/BackupStoreInfo.cpp box/trunk/lib/backupstore/BackupStoreInfo.h Log: Keep and resave any extra data present at the end of a BackupStoreInfo version 2 (Archive) file, for forwards compatibility with future extensions to the file format. Add a flag for whether an account in enabled or not in the new v2 file format, and if it's not present, default to true. Modified: box/trunk/lib/backupstore/BackupStoreInfo.cpp =================================================================== --- box/trunk/lib/backupstore/BackupStoreInfo.cpp 2012-10-22 20:48:54 UTC (rev 3123) +++ box/trunk/lib/backupstore/BackupStoreInfo.cpp 2012-10-22 20:51:19 UTC (rev 3124) @@ -19,53 +19,12 @@ #include "MemLeakFindOn.h" -// set packing to one byte -#ifdef STRUCTURE_PACKING_FOR_WIRE_USE_HEADERS -#include "BeginStructPackForWire.h" -#else -BEGIN_STRUCTURE_PACKING_FOR_WIRE -#endif - -// ****************** -// make sure the defaults in CreateNew are modified! -// ****************** -// Old version, grandfathered, do not change! -typedef struct -{ - int32_t mMagicValue; // also the version number - int32_t mAccountID; - int64_t mClientStoreMarker; - int64_t mLastObjectIDUsed; - int64_t mBlocksUsed; - int64_t mBlocksInOldFiles; - int64_t mBlocksInDeletedFiles; - int64_t mBlocksInDirectories; - int64_t mBlocksSoftLimit; - int64_t mBlocksHardLimit; - uint32_t mCurrentMarkNumber; - uint32_t mOptionsPresent; // bit mask of optional elements present - int64_t mNumberDeletedDirectories; - // Then loads of int64_t IDs for the deleted directories -} info_StreamFormat_1; - -#define INFO_MAGIC_VALUE_1 0x34832476 -#define INFO_MAGIC_VALUE_2 0x494e4632 /* INF2 */ - -// Use default packing -#ifdef STRUCTURE_PACKING_FOR_WIRE_USE_HEADERS -#include "EndStructPackForWire.h" -#else -END_STRUCTURE_PACKING_FOR_WIRE -#endif - #ifdef BOX_RELEASE_BUILD #define NUM_DELETED_DIRS_BLOCK 256 #else #define NUM_DELETED_DIRS_BLOCK 2 #endif -#define INFO_FILENAME "info" - // -------------------------------------------------------------------------- // // Function @@ -127,6 +86,7 @@ ASSERT(rRootDir[rRootDir.size() - 1] == '/' || rRootDir[rRootDir.size() - 1] == DIRECTORY_SEPARATOR_ASCHAR); info.mFilename = rRootDir + INFO_FILENAME; + info.mExtraData.SetForReading(); // extra data is empty in this case info.Save(false); } @@ -252,7 +212,7 @@ archive.Read(numDelObj); } - // Then load them in + // Then load the list of deleted directories if(numDelObj > 0) { int64_t objs[NUM_DELETED_DIRS_BLOCK]; @@ -284,7 +244,27 @@ { THROW_EXCEPTION(BackupStoreException, BadStoreInfoOnLoad) } + + if(v2) + { + Archive archive(*rf, IOStream::TimeOutInfinite); + archive.ReadIfPresent(info->mAccountEnabled, true); + } + else + { + info->mAccountEnabled = true; + } + // If there's any data left in the info file, from future additions to + // the file format, then we need to load it so that it won't be lost when + // we resave the file. + IOStream::pos_type bytesLeft = rf->BytesLeftToRead(); + if (bytesLeft > 0) + { + rf->CopyStreamTo(info->mExtraData); + } + info->mExtraData.SetForReading(); + // return it to caller return info; } @@ -412,6 +392,12 @@ tosave -= b; } } + + archive.Write(mAccountEnabled); + + mExtraData.Seek(0, IOStream::SeekType_Absolute); + mExtraData.CopyStreamTo(rf); + mExtraData.Seek(0, IOStream::SeekType_Absolute); // Commit it to disc, converting it to RAID now rf.Commit(true); @@ -718,4 +704,3 @@ mIsModified = true; } - Modified: box/trunk/lib/backupstore/BackupStoreInfo.h =================================================================== --- box/trunk/lib/backupstore/BackupStoreInfo.h 2012-10-22 20:48:54 UTC (rev 3123) +++ box/trunk/lib/backupstore/BackupStoreInfo.h 2012-10-22 20:51:19 UTC (rev 3124) @@ -14,8 +14,51 @@ #include #include +#include "CollectInBufferStream.h" + class BackupStoreCheck; +// set packing to one byte +#ifdef STRUCTURE_PACKING_FOR_WIRE_USE_HEADERS +#include "BeginStructPackForWire.h" +#else +BEGIN_STRUCTURE_PACKING_FOR_WIRE +#endif + +// ****************** +// make sure the defaults in CreateNew are modified! +// ****************** +// Old version, grandfathered, do not change! +typedef struct +{ + int32_t mMagicValue; // also the version number + int32_t mAccountID; + int64_t mClientStoreMarker; + int64_t mLastObjectIDUsed; + int64_t mBlocksUsed; + int64_t mBlocksInOldFiles; + int64_t mBlocksInDeletedFiles; + int64_t mBlocksInDirectories; + int64_t mBlocksSoftLimit; + int64_t mBlocksHardLimit; + uint32_t mCurrentMarkNumber; + uint32_t mOptionsPresent; // bit mask of optional elements present + int64_t mNumberDeletedDirectories; + // Then loads of int64_t IDs for the deleted directories +} info_StreamFormat_1; + +#define INFO_MAGIC_VALUE_1 0x34832476 +#define INFO_MAGIC_VALUE_2 0x494e4632 /* INF2 */ + +// Use default packing +#ifdef STRUCTURE_PACKING_FOR_WIRE_USE_HEADERS +#include "EndStructPackForWire.h" +#else +END_STRUCTURE_PACKING_FOR_WIRE +#endif + +#define INFO_FILENAME "info" + // -------------------------------------------------------------------------- // // Class @@ -63,6 +106,7 @@ int64_t GetNumOldFiles() const {return mNumOldFiles;} int64_t GetNumDeletedFiles() const {return mNumDeletedFiles;} int64_t GetNumDirectories() const {return mNumDirectories;} + bool IsAccountEnabled() const {return mAccountEnabled;} bool IsReadOnly() const {return mReadOnly;} int GetDiscSetNumber() const {return mDiscSet;} @@ -87,11 +131,13 @@ int64_t AllocateObjectID(); // Client marker set and get - int64_t GetClientStoreMarker() {return mClientStoreMarker;} + int64_t GetClientStoreMarker() const {return mClientStoreMarker;} void SetClientStoreMarker(int64_t ClientStoreMarker); - const std::string& GetAccountName() { return mAccountName; } + const std::string& GetAccountName() const { return mAccountName; } void SetAccountName(const std::string& rName); + const CollectInBufferStream& GetExtraData() const { return mExtraData; } + void SetAccountEnabled(bool IsEnabled) {mAccountEnabled = IsEnabled; } private: static std::auto_ptr CreateForRegeneration( @@ -129,6 +175,8 @@ int64_t mNumDeletedFiles; int64_t mNumDirectories; std::vector mDeletedDirectories; + bool mAccountEnabled; + CollectInBufferStream mExtraData; }; #endif // BACKUPSTOREINFO__H From subversion at boxbackup.org Mon Oct 22 21:54:20 2012 From: subversion at boxbackup.org (subversion at boxbackup.org) Date: Mon, 22 Oct 2012 21:54:20 +0100 (BST) Subject: [Box Backup-commit] COMMIT r3125 - box/trunk/bin/bbstoreaccounts Message-ID: <201210222054.q9MKsKnE027828@wm.boxbackup.org> Author: chris Date: 2012-10-22 21:54:20 +0100 (Mon, 22 Oct 2012) New Revision: 3125 Modified: box/trunk/bin/bbstoreaccounts/bbstoreaccounts.cpp Log: Refactor bbstoreaccounts code to reduce duplication. Add a bbstoreaccounts command to change the value of the AccountEnabled flag, and print its value in "bbstoreaccounts info" output. Modified: box/trunk/bin/bbstoreaccounts/bbstoreaccounts.cpp =================================================================== --- box/trunk/bin/bbstoreaccounts/bbstoreaccounts.cpp 2012-10-22 20:51:19 UTC (rev 3124) +++ box/trunk/bin/bbstoreaccounts/bbstoreaccounts.cpp 2012-10-22 20:54:20 UTC (rev 3125) @@ -63,31 +63,31 @@ } } -int BlockSizeOfDiscSet(int DiscSet) +int BlockSizeOfDiscSet(int discSetNum) { // Get controller, check disc set number RaidFileController &controller(RaidFileController::GetController()); - if(DiscSet < 0 || DiscSet >= controller.GetNumDiscSets()) + if(discSetNum < 0 || discSetNum >= controller.GetNumDiscSets()) { - BOX_FATAL("Disc set " << DiscSet << " does not exist."); + BOX_FATAL("Disc set " << discSetNum << " does not exist."); exit(1); } // Return block size - return controller.GetDiscSet(DiscSet).GetBlockSize(); + return controller.GetDiscSet(discSetNum).GetBlockSize(); } -std::string BlockSizeToString(int64_t Blocks, int64_t MaxBlocks, int DiscSet) +std::string BlockSizeToString(int64_t Blocks, int64_t MaxBlocks, int discSetNum) { - return FormatUsageBar(Blocks, Blocks * BlockSizeOfDiscSet(DiscSet), - MaxBlocks * BlockSizeOfDiscSet(DiscSet), + return FormatUsageBar(Blocks, Blocks * BlockSizeOfDiscSet(discSetNum), + MaxBlocks * BlockSizeOfDiscSet(discSetNum), sMachineReadableOutput); } -int64_t SizeStringToBlocks(const char *string, int DiscSet) +int64_t SizeStringToBlocks(const char *string, int discSetNum) { // Find block size - int blockSize = BlockSizeOfDiscSet(DiscSet); + int blockSize = BlockSizeOfDiscSet(discSetNum); // Get number char *endptr = (char*)string; @@ -128,10 +128,11 @@ } } -bool GetWriteLockOnAccount(NamedLock &rLock, const std::string rRootDir, int DiscSetNum) +bool GetWriteLockOnAccount(NamedLock &rLock, const std::string rRootDir, + int discSetNum) { std::string writeLockFilename; - StoreStructure::MakeWriteLockFilename(rRootDir, DiscSetNum, writeLockFilename); + StoreStructure::MakeWriteLockFilename(rRootDir, discSetNum, writeLockFilename); bool gotLock = false; int triesLeft = 8; @@ -156,49 +157,38 @@ return gotLock; } -int SetLimit(Configuration &rConfig, const std::string &rUsername, int32_t ID, const char *SoftLimitStr, const char *HardLimitStr) +bool OpenAccount(Configuration &rConfig, int32_t ID, std::string &rRootDirOut, + int &rDiscSetOut, std::auto_ptr apUser); + +int SetLimit(Configuration &rConfig, int32_t ID, const char *SoftLimitStr, + const char *HardLimitStr) { - // Become the user specified in the config file? - std::auto_ptr user; - if(!rUsername.empty()) - { - // Username specified, change... - user.reset(new UnixUser(rUsername.c_str())); - user->ChangeProcessUser(true /* temporary */); - // Change will be undone at the end of this function - } + std::string rootDir; + int discSetNum; + std::auto_ptr user; // used to reset uid when we return - // Load in the account database - std::auto_ptr db(BackupStoreAccountDatabase::Read(rConfig.GetKeyValue("AccountDatabase").c_str())); - - // Already exists? - if(!db->EntryExists(ID)) + if(!OpenAccount(rConfig, ID, rootDir, discSetNum, user)) { - BOX_ERROR("Account " << BOX_FORMAT_ACCOUNT(ID) << - " does not exist."); + BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID) + << " to change limits."); return 1; } - // Load it in - BackupStoreAccounts acc(*db); - std::string rootDir; - int discSet; - acc.GetAccountRoot(ID, rootDir, discSet); - // Attempt to lock NamedLock writeLock; - if(!GetWriteLockOnAccount(writeLock, rootDir, discSet)) + if(!GetWriteLockOnAccount(writeLock, rootDir, discSetNum)) { // Failed to get lock return 1; } // Load the info - std::auto_ptr info(BackupStoreInfo::Load(ID, rootDir, discSet, false /* Read/Write */)); + std::auto_ptr info(BackupStoreInfo::Load(ID, rootDir, + discSetNum, false /* Read/Write */)); // Change the limits - int64_t softlimit = SizeStringToBlocks(SoftLimitStr, discSet); - int64_t hardlimit = SizeStringToBlocks(HardLimitStr, discSet); + int64_t softlimit = SizeStringToBlocks(SoftLimitStr, discSetNum); + int64_t hardlimit = SizeStringToBlocks(HardLimitStr, discSetNum); CheckSoftHardLimits(softlimit, hardlimit); info->ChangeLimits(softlimit, hardlimit); @@ -212,39 +202,23 @@ return 0; } -int SetAccountName(Configuration &rConfig, const std::string &rUsername, - int32_t ID, const std::string& rNewAccountName) +int SetAccountName(Configuration &rConfig, int32_t ID, + const std::string& rNewAccountName) { - // Become the user specified in the config file? - std::auto_ptr user; - if(!rUsername.empty()) - { - // Username specified, change... - user.reset(new UnixUser(rUsername.c_str())); - user->ChangeProcessUser(true /* temporary */); - // Change will be undone at the end of this function - } + std::string rootDir; + int discSetNum; + std::auto_ptr user; // used to reset uid when we return - // Load in the account database - std::auto_ptr db(BackupStoreAccountDatabase::Read(rConfig.GetKeyValue("AccountDatabase").c_str())); - - // Already exists? - if(!db->EntryExists(ID)) + if(!OpenAccount(rConfig, ID, rootDir, discSetNum, user)) { - BOX_ERROR("Account " << BOX_FORMAT_ACCOUNT(ID) << - " does not exist."); + BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID) + << " to change name."); return 1; } - - // Load it in - BackupStoreAccounts acc(*db); - std::string rootDir; - int discSet; - acc.GetAccountRoot(ID, rootDir, discSet); - + // Attempt to lock NamedLock writeLock; - if(!GetWriteLockOnAccount(writeLock, rootDir, discSet)) + if(!GetWriteLockOnAccount(writeLock, rootDir, discSetNum)) { // Failed to get lock return 1; @@ -252,7 +226,7 @@ // Load the info std::auto_ptr info(BackupStoreInfo::Load(ID, - rootDir, discSet, false /* Read/Write */)); + rootDir, discSetNum, false /* Read/Write */)); info->SetAccountName(rNewAccountName); @@ -267,26 +241,20 @@ int AccountInfo(Configuration &rConfig, int32_t ID) { - // Load in the account database - std::auto_ptr db( - BackupStoreAccountDatabase::Read( - rConfig.GetKeyValue("AccountDatabase").c_str())); - - // Exists? - if(!db->EntryExists(ID)) + std::string rootDir; + int discSetNum; + std::auto_ptr user; // used to reset uid when we return + + if(!OpenAccount(rConfig, ID, rootDir, discSetNum, user)) { - BOX_ERROR("Account " << BOX_FORMAT_ACCOUNT(ID) << - " does not exist."); + BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID) + << " to display info."); return 1; } // Load it in - BackupStoreAccounts acc(*db); - std::string rootDir; - int discSet; - acc.GetAccountRoot(ID, rootDir, discSet); std::auto_ptr info(BackupStoreInfo::Load(ID, - rootDir, discSet, true /* ReadOnly */)); + rootDir, discSetNum, true /* ReadOnly */)); // Then print out lots of info std::cout << FormatUsageLineStart("Account ID", sMachineReadableOutput) << @@ -297,26 +265,26 @@ BOX_FORMAT_OBJECTID(info->GetLastObjectIDUsed()) << std::endl; std::cout << FormatUsageLineStart("Used", sMachineReadableOutput) << BlockSizeToString(info->GetBlocksUsed(), - info->GetBlocksHardLimit(), discSet) << std::endl; + info->GetBlocksHardLimit(), discSetNum) << std::endl; std::cout << FormatUsageLineStart("Current files", sMachineReadableOutput) << BlockSizeToString(info->GetBlocksInCurrentFiles(), - info->GetBlocksHardLimit(), discSet) << std::endl; + info->GetBlocksHardLimit(), discSetNum) << std::endl; std::cout << FormatUsageLineStart("Old files", sMachineReadableOutput) << BlockSizeToString(info->GetBlocksInOldFiles(), - info->GetBlocksHardLimit(), discSet) << std::endl; + info->GetBlocksHardLimit(), discSetNum) << std::endl; std::cout << FormatUsageLineStart("Deleted files", sMachineReadableOutput) << BlockSizeToString(info->GetBlocksInDeletedFiles(), - info->GetBlocksHardLimit(), discSet) << std::endl; + info->GetBlocksHardLimit(), discSetNum) << std::endl; std::cout << FormatUsageLineStart("Directories", sMachineReadableOutput) << BlockSizeToString(info->GetBlocksInDirectories(), - info->GetBlocksHardLimit(), discSet) << std::endl; + info->GetBlocksHardLimit(), discSetNum) << std::endl; std::cout << FormatUsageLineStart("Soft limit", sMachineReadableOutput) << BlockSizeToString(info->GetBlocksSoftLimit(), - info->GetBlocksHardLimit(), discSet) << std::endl; + info->GetBlocksHardLimit(), discSetNum) << std::endl; std::cout << FormatUsageLineStart("Hard limit", sMachineReadableOutput) << BlockSizeToString(info->GetBlocksHardLimit(), - info->GetBlocksHardLimit(), discSet) << std::endl; + info->GetBlocksHardLimit(), discSetNum) << std::endl; std::cout << FormatUsageLineStart("Client store marker", sMachineReadableOutput) << info->GetLastObjectIDUsed() << std::endl; std::cout << FormatUsageLineStart("Live Files", sMachineReadableOutput) << @@ -327,12 +295,46 @@ info->GetNumDeletedFiles() << std::endl; std::cout << FormatUsageLineStart("Directories", sMachineReadableOutput) << info->GetNumDirectories() << std::endl; + std::cout << FormatUsageLineStart("Enabled", sMachineReadableOutput) << + (info->IsAccountEnabled() ? "yes" : "no") << std::endl; return 0; } -int DeleteAccount(Configuration &rConfig, const std::string &rUsername, int32_t ID, bool AskForConfirmation) +int SetAccountEnabled(Configuration &rConfig, int32_t ID, bool enabled) { + std::string rootDir; + int discSetNum; + std::auto_ptr user; // used to reset uid when we return + + if(!OpenAccount(rConfig, ID, rootDir, discSetNum, user)) + { + BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID) + << " to change enabled flag."); + return 1; + } + + // Load it in + std::auto_ptr info(BackupStoreInfo::Load(ID, + rootDir, discSetNum, false /* ReadOnly */)); + info->SetAccountEnabled(enabled); + info->Save(); + return 0; +} + +int DeleteAccount(Configuration &rConfig, int32_t ID, bool AskForConfirmation) +{ + std::string rootDir; + int discSetNum; + std::auto_ptr user; // used to reset uid when we return + + if(!OpenAccount(rConfig, ID, rootDir, discSetNum, user)) + { + BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID) + << " for deletion."); + return 1; + } + // Check user really wants to do this if(AskForConfirmation) { @@ -346,36 +348,9 @@ } } - // Load in the account database - std::auto_ptr db(BackupStoreAccountDatabase::Read(rConfig.GetKeyValue("AccountDatabase").c_str())); - - // Exists? - if(!db->EntryExists(ID)) - { - BOX_ERROR("Account " << BOX_FORMAT_ACCOUNT(ID) << - " does not exist."); - return 1; - } - - // Get info from the database - BackupStoreAccounts acc(*db); - std::string rootDir; - int discSetNum; - acc.GetAccountRoot(ID, rootDir, discSetNum); - // Obtain a write lock, as the daemon user NamedLock writeLock; { - // Bbecome the user specified in the config file - std::auto_ptr user; - if(!rUsername.empty()) - { - // Username specified, change... - user.reset(new UnixUser(rUsername.c_str())); - user->ChangeProcessUser(true /* temporary */); - // Change will be undone at the end of this function - } - // Get a write lock if(!GetWriteLockOnAccount(writeLock, rootDir, discSetNum)) { @@ -384,8 +359,11 @@ } // Back to original user, but write is maintained + user.reset(); } + std::auto_ptr db(BackupStoreAccountDatabase::Read(rConfig.GetKeyValue("AccountDatabase").c_str())); + // Delete from account database db->DeleteEntry(ID); @@ -395,15 +373,24 @@ // Remove the store files... // First, become the user specified in the config file - std::auto_ptr user; - if(!rUsername.empty()) + std::string username; { + const Configuration &rserverConfig(rConfig.GetSubConfiguration("Server")); + if(rserverConfig.KeyExists("User")) + { + username = rserverConfig.GetKeyValue("User"); + } + } + + // Become the right user + if(!username.empty()) + { // Username specified, change... - user.reset(new UnixUser(rUsername.c_str())); + user.reset(new UnixUser(username)); user->ChangeProcessUser(true /* temporary */); - // Change will be undone at the end of this function + // Change will be undone when user goes out of scope } - + // Secondly, work out which directories need wiping std::vector toDelete; RaidFileController &rcontroller(RaidFileController::GetController()); @@ -438,8 +425,7 @@ return retcode; } -bool OpenAccount(Configuration &rConfig, int32_t ID, - const std::string &rUsername, std::string &rRootDirOut, +bool OpenAccount(Configuration &rConfig, int32_t ID, std::string &rRootDirOut, int &rDiscSetOut, std::auto_ptr apUser) { // Load in the account database @@ -456,12 +442,22 @@ // Get info from the database BackupStoreAccounts acc(*db); acc.GetAccountRoot(ID, rRootDirOut, rDiscSetOut); - + + // Get the user under which the daemon runs + std::string username; + { + const Configuration &rserverConfig(rConfig.GetSubConfiguration("Server")); + if(rserverConfig.KeyExists("User")) + { + username = rserverConfig.GetKeyValue("User"); + } + } + // Become the right user - if(!rUsername.empty()) + if(!username.empty()) { // Username specified, change... - apUser.reset(new UnixUser(rUsername)); + apUser.reset(new UnixUser(username)); apUser->ChangeProcessUser(true /* temporary */); // Change will be undone when apUser goes out of scope // in the caller. @@ -470,13 +466,13 @@ return true; } -int CheckAccount(Configuration &rConfig, const std::string &rUsername, int32_t ID, bool FixErrors, bool Quiet) +int CheckAccount(Configuration &rConfig, int32_t ID, bool FixErrors, bool Quiet) { std::string rootDir; int discSetNum; - std::auto_ptr user; + std::auto_ptr user; // used to reset uid when we return - if(!OpenAccount(rConfig, ID, rUsername, rootDir, discSetNum, user)) + if(!OpenAccount(rConfig, ID, rootDir, discSetNum, user)) { BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID) << " for checking."); @@ -490,7 +486,8 @@ return check.ErrorsFound()?1:0; } -int CreateAccount(Configuration &rConfig, const std::string &rUsername, int32_t ID, int32_t DiscNumber, int32_t SoftLimit, int32_t HardLimit) +int CreateAccount(Configuration &rConfig, int32_t ID, int32_t DiscNumber, + int32_t SoftLimit, int32_t HardLimit) { // Load in the account database std::auto_ptr db(BackupStoreAccountDatabase::Read(rConfig.GetKeyValue("AccountDatabase").c_str())); @@ -502,24 +499,33 @@ " already exists."); return 1; } + + // Get the user under which the daemon runs + std::string username; + { + const Configuration &rserverConfig(rConfig.GetSubConfiguration("Server")); + if(rserverConfig.KeyExists("User")) + { + username = rserverConfig.GetKeyValue("User"); + } + } // Create it. BackupStoreAccounts acc(*db); - acc.Create(ID, DiscNumber, SoftLimit, HardLimit, rUsername); + acc.Create(ID, DiscNumber, SoftLimit, HardLimit, username); BOX_NOTICE("Account " << BOX_FORMAT_ACCOUNT(ID) << " created."); return 0; } -int HousekeepAccountNow(Configuration &rConfig, const std::string &rUsername, - int32_t ID) +int HousekeepAccountNow(Configuration &rConfig, int32_t ID) { std::string rootDir; int discSetNum; - std::auto_ptr user; + std::auto_ptr user; // used to reset uid when we return - if(!OpenAccount(rConfig, ID, rUsername, rootDir, discSetNum, user)) + if(!OpenAccount(rConfig, ID, rootDir, discSetNum, user)) { BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID) << " for housekeeping."); @@ -559,6 +565,8 @@ " info [-m] \n" " Prints information about the specified account including number\n" " of blocks used. The -m option enable machine-readable output.\n" +" enabled \n" +" Sets the account as enabled or disabled for new logins.\n" " setlimit \n" " Changes the limits of the account as specified. Numbers are\n" " interpreted as for the 'create' command (suffixed with B, M or G)\n" @@ -645,16 +653,6 @@ ":" << errs); } - // Get the user under which the daemon runs - std::string username; - { - const Configuration &rserverConfig(config->GetSubConfiguration("Server")); - if(rserverConfig.KeyExists("User")) - { - username = rserverConfig.GetKeyValue("User"); - } - } - // Initialise the raid file controller RaidFileController &rcontroller(RaidFileController::GetController()); rcontroller.Initialise(config->GetKeyValue("RaidFileConf").c_str()); @@ -672,8 +670,10 @@ PrintUsageAndExit(); } + std::string command = argv[0]; + // Now do the command. - if(::strcmp(argv[0], "create") == 0) + if(command == "create") { // which disc? int32_t discnum; @@ -693,15 +693,40 @@ CheckSoftHardLimits(softlimit, hardlimit); // Create the account... - return CreateAccount(*config, username, id, discnum, softlimit, hardlimit); + return CreateAccount(*config, id, discnum, softlimit, hardlimit); } - else if(::strcmp(argv[0], "info") == 0) + else if(command == "info") { // Print information on this account return AccountInfo(*config, id); } - else if(::strcmp(argv[0], "setlimit") == 0) + else if(command == "enabled") { + // Change the AccountEnabled flag on this account + if(argc != 3) + { + PrintUsageAndExit(); + } + + bool enabled; + std::string enabled_string = argv[2]; + if(enabled_string == "yes") + { + enabled = true; + } + else if(enabled_string == "no") + { + enabled = false; + } + else + { + PrintUsageAndExit(); + } + + return SetAccountEnabled(*config, id, enabled); + } + else if(command == "setlimit") + { // Change the limits on this account if(argc < 4) { @@ -709,9 +734,9 @@ return 1; } - return SetLimit(*config, username, id, argv[2], argv[3]); + return SetLimit(*config, id, argv[2], argv[3]); } - else if(::strcmp(argv[0], "name") == 0) + else if(command == "name") { // Change the limits on this account if(argc != 3) @@ -720,9 +745,9 @@ return 1; } - return SetAccountName(*config, username, id, argv[2]); + return SetAccountName(*config, id, argv[2]); } - else if(::strcmp(argv[0], "delete") == 0) + else if(command == "delete") { // Delete an account bool askForConfirmation = true; @@ -730,9 +755,9 @@ { askForConfirmation = false; } - return DeleteAccount(*config, username, id, askForConfirmation); + return DeleteAccount(*config, id, askForConfirmation); } - else if(::strcmp(argv[0], "check") == 0) + else if(command == "check") { bool fixErrors = false; bool quiet = false; @@ -756,15 +781,15 @@ } // Check the account - return CheckAccount(*config, username, id, fixErrors, quiet); + return CheckAccount(*config, id, fixErrors, quiet); } - else if(::strcmp(argv[0], "housekeep") == 0) + else if(command == "housekeep") { - return HousekeepAccountNow(*config, username, id); + return HousekeepAccountNow(*config, id); } else { - BOX_ERROR("Unknown command '" << argv[0] << "'."); + BOX_ERROR("Unknown command '" << command << "'."); return 1; } @@ -773,4 +798,3 @@ MAINHELPER_END } - From subversion at boxbackup.org Mon Oct 22 21:54:50 2012 From: subversion at boxbackup.org (subversion at boxbackup.org) Date: Mon, 22 Oct 2012 21:54:50 +0100 (BST) Subject: [Box Backup-commit] COMMIT r3126 - box/trunk/lib/backupstore Message-ID: <201210222054.q9MKsoOS027845@wm.boxbackup.org> Author: chris Date: 2012-10-22 21:54:48 +0100 (Mon, 22 Oct 2012) New Revision: 3126 Modified: box/trunk/lib/backupstore/BackupCommands.cpp box/trunk/lib/backupstore/backupprotocol.txt Log: Refuse login to disabled accounts. Modified: box/trunk/lib/backupstore/BackupCommands.cpp =================================================================== --- box/trunk/lib/backupstore/BackupCommands.cpp 2012-10-22 20:54:20 UTC (rev 3125) +++ box/trunk/lib/backupstore/BackupCommands.cpp 2012-10-22 20:54:48 UTC (rev 3126) @@ -120,6 +120,13 @@ // Load the store info rContext.LoadStoreInfo(); + if(!rContext.GetBackupStoreInfo().IsAccountEnabled()) + { + BOX_WARNING("Refused login from disabled client ID " << + BOX_FORMAT_ACCOUNT(mClientID)); + return PROTOCOL_ERROR(Err_DisabledAccount); + } + // Get the last client store marker int64_t clientStoreMarker = rContext.GetClientStoreMarker(); Modified: box/trunk/lib/backupstore/backupprotocol.txt =================================================================== --- box/trunk/lib/backupstore/backupprotocol.txt 2012-10-22 20:54:20 UTC (rev 3125) +++ box/trunk/lib/backupstore/backupprotocol.txt 2012-10-22 20:54:48 UTC (rev 3126) @@ -39,6 +39,7 @@ CONSTANT Err_DoesNotExistInDirectory 13 CONSTANT Err_PatchConsistencyError 14 CONSTANT Err_MultiplyReferencedObject 15 + CONSTANT Err_DisabledAccount 16 Version 1 Command(Version) Reply int32 Version From subversion at boxbackup.org Mon Oct 22 21:56:04 2012 From: subversion at boxbackup.org (subversion at boxbackup.org) Date: Mon, 22 Oct 2012 21:56:04 +0100 (BST) Subject: [Box Backup-commit] COMMIT r3127 - box/trunk/test/backupstore Message-ID: <201210222056.q9MKu4Yw027879@wm.boxbackup.org> Author: chris Date: 2012-10-22 21:56:04 +0100 (Mon, 22 Oct 2012) New Revision: 3127 Modified: box/trunk/test/backupstore/testbackupstore.cpp Log: Test that the BackupStoreInfo AccountEnabled flag works properly, and is loaded and saved properly, and the "bbstoreaccounts enabled" command works. Test that the conversion of historic BackupStoreInfo v1 format files works properly. Modified: box/trunk/test/backupstore/testbackupstore.cpp =================================================================== --- box/trunk/test/backupstore/testbackupstore.cpp 2012-10-22 20:54:48 UTC (rev 3126) +++ box/trunk/test/backupstore/testbackupstore.cpp 2012-10-22 20:56:04 UTC (rev 3127) @@ -12,32 +12,34 @@ #include #include -#include "Test.h" -#include "autogen_BackupProtocol.h" -#include "SSLLib.h" -#include "TLSContext.h" -#include "SocketStreamTLS.h" -#include "BoxPortsAndFiles.h" +#include "Archive.h" +#include "BackupClientCryptoKeys.h" +#include "BackupClientFileAttributes.h" +#include "BackupStoreAccountDatabase.h" +#include "BackupStoreAccounts.h" #include "BackupStoreConstants.h" -#include "Socket.h" +#include "BackupStoreDirectory.h" +#include "BackupStoreException.h" +#include "BackupStoreInfo.h" #include "BackupStoreFilenameClear.h" +#include "BackupStoreRefCountDatabase.h" +#include "BackupStoreFile.h" +#include "BoxPortsAndFiles.h" #include "CollectInBufferStream.h" -#include "BackupStoreDirectory.h" -#include "BackupStoreFile.h" #include "FileStream.h" +#include "HousekeepStoreAccount.h" +#include "MemBlockStream.h" #include "RaidFileController.h" +#include "RaidFileException.h" +#include "RaidFileRead.h" #include "RaidFileWrite.h" -#include "BackupStoreInfo.h" -#include "BackupStoreException.h" -#include "RaidFileException.h" -#include "MemBlockStream.h" -#include "BackupClientFileAttributes.h" -#include "BackupClientCryptoKeys.h" +#include "SSLLib.h" #include "ServerControl.h" -#include "BackupStoreAccountDatabase.h" -#include "BackupStoreRefCountDatabase.h" -#include "BackupStoreAccounts.h" -#include "HousekeepStoreAccount.h" +#include "Socket.h" +#include "SocketStreamTLS.h" +#include "TLSContext.h" +#include "Test.h" +#include "autogen_BackupProtocol.h" #include "MemLeakFindOn.h" @@ -221,10 +223,6 @@ int test1(int argc, const char *argv[]) { - // Initialise the raid file controller - RaidFileController &rcontroller = RaidFileController::GetController(); - rcontroller.Initialise("testfiles/raidfile.conf"); - // test some basics -- encoding and decoding filenames { // Make some filenames in various ways @@ -1873,235 +1871,274 @@ int pid = LaunchServer(cmd.c_str(), "testfiles/bbstored.pid"); TEST_THAT(pid != -1 && pid != 0); - if(pid > 0) + if(pid <= 0) { - ::sleep(1); - TEST_THAT(ServerIsAlive(pid)); + return 1; + } - // BLOCK - { - // Open a connection to the server - SocketStreamTLS conn; - conn.Open(context, Socket::TypeINET, "localhost", - BOX_PORT_BBSTORED_TEST); + ::sleep(1); + TEST_THAT(ServerIsAlive(pid)); - // Make a protocol - BackupProtocolClient protocol(conn); + // BLOCK + { + // Open a connection to the server + SocketStreamTLS conn; + conn.Open(context, Socket::TypeINET, "localhost", + BOX_PORT_BBSTORED_TEST); - // Check the version - std::auto_ptr serverVersion(protocol.QueryVersion(BACKUP_STORE_SERVER_VERSION)); - TEST_THAT(serverVersion->GetVersion() == BACKUP_STORE_SERVER_VERSION); + // Make a protocol + BackupProtocolClient protocol(conn); - // Login - TEST_CHECK_THROWS(std::auto_ptr loginConf(protocol.QueryLogin(0x01234567, 0)), - ConnectionException, Conn_Protocol_UnexpectedReply); - - // Finish the connection - protocol.QueryFinished(); - } + // Check the version + std::auto_ptr serverVersion(protocol.QueryVersion(BACKUP_STORE_SERVER_VERSION)); + TEST_THAT(serverVersion->GetVersion() == BACKUP_STORE_SERVER_VERSION); - // Create an account for the test client - TEST_THAT_ABORTONFAIL(::system(BBSTOREACCOUNTS - " -c testfiles/bbstored.conf create 01234567 0 " - "10000B 20000B") == 0); + // Login + TEST_CHECK_THROWS(std::auto_ptr loginConf(protocol.QueryLogin(0x01234567, 0)), + ConnectionException, Conn_Protocol_UnexpectedReply); + + // Finish the connection + protocol.QueryFinished(); + } - TestRemoteProcessMemLeaks("bbstoreaccounts.memleaks"); + // Create an account for the test client + TEST_THAT_ABORTONFAIL(::system(BBSTOREACCOUNTS + " -c testfiles/bbstored.conf create 01234567 0 " + "10000B 20000B") == 0); + TestRemoteProcessMemLeaks("bbstoreaccounts.memleaks"); - TEST_THAT(TestDirExists("testfiles/0_0/backup/01234567")); - TEST_THAT(TestDirExists("testfiles/0_1/backup/01234567")); - TEST_THAT(TestDirExists("testfiles/0_2/backup/01234567")); - TEST_THAT(TestGetFileSize("testfiles/accounts.txt") > 8); - // make sure something is written to it + TEST_THAT(TestDirExists("testfiles/0_0/backup/01234567")); + TEST_THAT(TestDirExists("testfiles/0_1/backup/01234567")); + TEST_THAT(TestDirExists("testfiles/0_2/backup/01234567")); + TEST_THAT(TestGetFileSize("testfiles/accounts.txt") > 8); + // make sure something is written to it + + std::auto_ptr apAccounts( + BackupStoreAccountDatabase::Read("testfiles/accounts.txt")); + + std::auto_ptr apReferences( + BackupStoreRefCountDatabase::Load( + apAccounts->GetEntry(0x1234567), true)); + TEST_EQUAL(BACKUPSTORE_ROOT_DIRECTORY_ID, + apReferences->GetLastObjectIDUsed()); + TEST_EQUAL(1, apReferences->GetRefCount(BACKUPSTORE_ROOT_DIRECTORY_ID)) + apReferences.reset(); + + // Test that login fails on a disabled account + TEST_THAT_ABORTONFAIL(::system(BBSTOREACCOUNTS + " -c testfiles/bbstored.conf enabled 01234567 no") == 0); + TestRemoteProcessMemLeaks("bbstoreaccounts.memleaks"); + // BLOCK + { + // Open a connection to the server + SocketStreamTLS conn; + conn.Open(context, Socket::TypeINET, "localhost", + BOX_PORT_BBSTORED_TEST); + + // Make a protocol + BackupProtocolClient protocol(conn); + + // Check the version + std::auto_ptr serverVersion(protocol.QueryVersion(BACKUP_STORE_SERVER_VERSION)); + TEST_THAT(serverVersion->GetVersion() == BACKUP_STORE_SERVER_VERSION); + + // Login + TEST_CHECK_THROWS(std::auto_ptr + loginConf(protocol.QueryLogin(0x01234567, 0)), + ConnectionException, Conn_Protocol_UnexpectedReply); + int type, subType; + TEST_EQUAL_LINE(true, protocol.GetLastError(type, subType), + "expected a protocol error"); + TEST_EQUAL_LINE(BackupProtocolError::ErrorType, type, + "expected a BackupProtocolError"); + TEST_EQUAL_LINE(BackupProtocolError::Err_DisabledAccount, subType, + "expected an Err_DisabledAccount"); - std::auto_ptr apAccounts( - BackupStoreAccountDatabase::Read("testfiles/accounts.txt")); + // Finish the connection + protocol.QueryFinished(); + } - std::auto_ptr apReferences( - BackupStoreRefCountDatabase::Load( - apAccounts->GetEntry(0x1234567), true)); - TEST_EQUAL(BACKUPSTORE_ROOT_DIRECTORY_ID, - apReferences->GetLastObjectIDUsed()); - TEST_EQUAL(1, apReferences->GetRefCount(BACKUPSTORE_ROOT_DIRECTORY_ID)) - apReferences.reset(); + // Re-enable the account so that subsequent logins should succeed + TEST_THAT_ABORTONFAIL(::system(BBSTOREACCOUNTS + " -c testfiles/bbstored.conf enabled 01234567 yes") == 0); + TestRemoteProcessMemLeaks("bbstoreaccounts.memleaks"); - // Delete the refcount database and log in again, - // check that it is recreated automatically but with - // no objects in it, to ensure seamless upgrade. - TEST_EQUAL(0, ::unlink("testfiles/0_0/backup/01234567/refcount.db.rfw")); + // Delete the refcount database and log in again, + // check that it is recreated automatically but with + // no objects in it, to ensure seamless upgrade. + TEST_EQUAL(0, ::unlink("testfiles/0_0/backup/01234567/refcount.db.rfw")); - TLSContext context; - std::auto_ptr conn; - test_server_login("localhost", context, conn)->QueryFinished(); + std::auto_ptr conn; + test_server_login("localhost", context, conn)->QueryFinished(); - BackupStoreAccountDatabase::Entry account = - apAccounts->GetEntry(0x1234567); - apReferences = BackupStoreRefCountDatabase::Load(account, true); - TEST_EQUAL(0, apReferences->GetLastObjectIDUsed()); + BackupStoreAccountDatabase::Entry account = + apAccounts->GetEntry(0x1234567); + apReferences = BackupStoreRefCountDatabase::Load(account, true); + TEST_EQUAL(0, apReferences->GetLastObjectIDUsed()); - TEST_THAT(ServerIsAlive(pid)); + TEST_THAT(ServerIsAlive(pid)); - run_housekeeping(account); + run_housekeeping(account); - // Check that housekeeping fixed the ref counts - TEST_EQUAL(BACKUPSTORE_ROOT_DIRECTORY_ID, - apReferences->GetLastObjectIDUsed()); - TEST_EQUAL(1, apReferences->GetRefCount(BACKUPSTORE_ROOT_DIRECTORY_ID)) + // Check that housekeeping fixed the ref counts + TEST_EQUAL(BACKUPSTORE_ROOT_DIRECTORY_ID, + apReferences->GetLastObjectIDUsed()); + TEST_EQUAL(1, apReferences->GetRefCount(BACKUPSTORE_ROOT_DIRECTORY_ID)) - TEST_THAT(ServerIsAlive(pid)); + TEST_THAT(ServerIsAlive(pid)); - set_refcount(BACKUPSTORE_ROOT_DIRECTORY_ID, 1); + set_refcount(BACKUPSTORE_ROOT_DIRECTORY_ID, 1); - TEST_THAT(test_server("localhost") == 0); + TEST_THAT(test_server("localhost") == 0); - // test that all object reference counts have the - // expected values - TEST_EQUAL(ExpectedRefCounts.size() - 1, - apReferences->GetLastObjectIDUsed()); - for (unsigned int i = BACKUPSTORE_ROOT_DIRECTORY_ID; - i < ExpectedRefCounts.size(); i++) - { - TEST_EQUAL_LINE(ExpectedRefCounts[i], - apReferences->GetRefCount(i), - "object " << BOX_FORMAT_OBJECTID(i)); - } + // test that all object reference counts have the + // expected values + TEST_EQUAL(ExpectedRefCounts.size() - 1, + apReferences->GetLastObjectIDUsed()); + for (unsigned int i = BACKUPSTORE_ROOT_DIRECTORY_ID; + i < ExpectedRefCounts.size(); i++) + { + TEST_EQUAL_LINE(ExpectedRefCounts[i], + apReferences->GetRefCount(i), + "object " << BOX_FORMAT_OBJECTID(i)); + } - // Delete the refcount database again, and let - // housekeeping recreate it and fix the ref counts. - // This could also happen after upgrade, if a housekeeping - // runs before the user logs in. - apReferences.reset(); - TEST_EQUAL(0, ::unlink("testfiles/0_0/backup/01234567/refcount.db.rfw")); - run_housekeeping(account); - apReferences = BackupStoreRefCountDatabase::Load(account, true); + // Delete the refcount database again, and let + // housekeeping recreate it and fix the ref counts. + // This could also happen after upgrade, if a housekeeping + // runs before the user logs in. + apReferences.reset(); + TEST_EQUAL(0, ::unlink("testfiles/0_0/backup/01234567/refcount.db.rfw")); + run_housekeeping(account); + apReferences = BackupStoreRefCountDatabase::Load(account, true); - TEST_EQUAL(ExpectedRefCounts.size() - 1, - apReferences->GetLastObjectIDUsed()); - for (unsigned int i = BACKUPSTORE_ROOT_DIRECTORY_ID; - i < ExpectedRefCounts.size(); i++) - { - TEST_EQUAL_LINE(ExpectedRefCounts[i], - apReferences->GetRefCount(i), - "object " << BOX_FORMAT_OBJECTID(i)); - } - - // Test the deletion of objects by the housekeeping system - // First, things as they are now. - recursive_count_objects_results before = {0,0,0}; + TEST_EQUAL(ExpectedRefCounts.size() - 1, + apReferences->GetLastObjectIDUsed()); + for (unsigned int i = BACKUPSTORE_ROOT_DIRECTORY_ID; + i < ExpectedRefCounts.size(); i++) + { + TEST_EQUAL_LINE(ExpectedRefCounts[i], + apReferences->GetRefCount(i), + "object " << BOX_FORMAT_OBJECTID(i)); + } + + // Test the deletion of objects by the housekeeping system + // First, things as they are now. + recursive_count_objects_results before = {0,0,0}; - recursive_count_objects("localhost", BackupProtocolListDirectory::RootDirectory, before); - - TEST_THAT(before.objectsNotDel != 0); - TEST_THAT(before.deleted != 0); - TEST_THAT(before.old != 0); + recursive_count_objects("localhost", BackupProtocolListDirectory::RootDirectory, before); + + TEST_THAT(before.objectsNotDel != 0); + TEST_THAT(before.deleted != 0); + TEST_THAT(before.old != 0); - // Kill it - TEST_THAT(KillServer(pid)); - ::sleep(1); - TEST_THAT(!ServerIsAlive(pid)); + // Kill it + TEST_THAT(KillServer(pid)); + ::sleep(1); + TEST_THAT(!ServerIsAlive(pid)); - #ifdef WIN32 - TEST_THAT(unlink("testfiles/bbstored.pid") == 0); - #else - TestRemoteProcessMemLeaks("bbstored.memleaks"); - #endif - - // Set a new limit on the account -- leave the hard limit - // high to make sure the target for freeing space is the - // soft limit. - TEST_THAT_ABORTONFAIL(::system(BBSTOREACCOUNTS - " -c testfiles/bbstored.conf setlimit 01234567 " - "10B 20000B") == 0); - TestRemoteProcessMemLeaks("bbstoreaccounts.memleaks"); + #ifdef WIN32 + TEST_THAT(unlink("testfiles/bbstored.pid") == 0); + #else + TestRemoteProcessMemLeaks("bbstored.memleaks"); + #endif + + // Set a new limit on the account -- leave the hard limit + // high to make sure the target for freeing space is the + // soft limit. + TEST_THAT_ABORTONFAIL(::system(BBSTOREACCOUNTS + " -c testfiles/bbstored.conf setlimit 01234567 " + "10B 20000B") == 0); + TestRemoteProcessMemLeaks("bbstoreaccounts.memleaks"); - // Start things up - pid = LaunchServer(BBSTORED " testfiles/bbstored.conf", - "testfiles/bbstored.pid"); + // Start things up + pid = LaunchServer(BBSTORED " testfiles/bbstored.conf", + "testfiles/bbstored.pid"); + ::sleep(1); + TEST_THAT(ServerIsAlive(pid)); + + // wait for housekeeping to happen + printf("waiting for housekeeping:\n"); + for(int l = 0; l < 30; ++l) + { ::sleep(1); - TEST_THAT(ServerIsAlive(pid)); - - // wait for housekeeping to happen - printf("waiting for housekeeping:\n"); - for(int l = 0; l < 30; ++l) - { - ::sleep(1); - printf("."); - fflush(stdout); - } - printf("\n"); + printf("."); + fflush(stdout); + } + printf("\n"); - // Count the objects again - recursive_count_objects_results after = {0,0,0}; - recursive_count_objects("localhost", - BackupProtocolListDirectory::RootDirectory, - after); + // Count the objects again + recursive_count_objects_results after = {0,0,0}; + recursive_count_objects("localhost", + BackupProtocolListDirectory::RootDirectory, + after); - // If these tests fail then try increasing the timeout above - TEST_THAT(after.objectsNotDel == before.objectsNotDel); - TEST_THAT(after.deleted == 0); - TEST_THAT(after.old == 0); - - // Set a really small hard limit - TEST_THAT_ABORTONFAIL(::system(BBSTOREACCOUNTS - " -c testfiles/bbstored.conf setlimit 01234567 " - "10B 20B") == 0); - TestRemoteProcessMemLeaks("bbstoreaccounts.memleaks"); + // If these tests fail then try increasing the timeout above + TEST_THAT(after.objectsNotDel == before.objectsNotDel); + TEST_THAT(after.deleted == 0); + TEST_THAT(after.old == 0); + + // Set a really small hard limit + TEST_THAT_ABORTONFAIL(::system(BBSTOREACCOUNTS + " -c testfiles/bbstored.conf setlimit 01234567 " + "10B 20B") == 0); + TestRemoteProcessMemLeaks("bbstoreaccounts.memleaks"); - // Try to upload a file and create a directory, and check an error is generated - { - // Open a connection to the server - SocketStreamTLS conn; - conn.Open(context, Socket::TypeINET, "localhost", - BOX_PORT_BBSTORED_TEST); + // Try to upload a file and create a directory, and check an error is generated + { + // Open a connection to the server + SocketStreamTLS conn; + conn.Open(context, Socket::TypeINET, "localhost", + BOX_PORT_BBSTORED_TEST); - // Make a protocol - BackupProtocolClient protocol(conn); + // Make a protocol + BackupProtocolClient protocol(conn); - // Check the version - std::auto_ptr serverVersion(protocol.QueryVersion(BACKUP_STORE_SERVER_VERSION)); - TEST_THAT(serverVersion->GetVersion() == BACKUP_STORE_SERVER_VERSION); + // Check the version + std::auto_ptr serverVersion(protocol.QueryVersion(BACKUP_STORE_SERVER_VERSION)); + TEST_THAT(serverVersion->GetVersion() == BACKUP_STORE_SERVER_VERSION); - // Login - std::auto_ptr loginConf(protocol.QueryLogin(0x01234567, 0)); - - int64_t modtime = 0; - - BackupStoreFilenameClear fnx("exceed-limit"); - std::auto_ptr upload(BackupStoreFile::EncodeFile("testfiles/test3", BackupProtocolListDirectory::RootDirectory, fnx, &modtime)); - TEST_THAT(modtime != 0); + // Login + std::auto_ptr loginConf(protocol.QueryLogin(0x01234567, 0)); + + int64_t modtime = 0; + + BackupStoreFilenameClear fnx("exceed-limit"); + std::auto_ptr upload(BackupStoreFile::EncodeFile("testfiles/test3", BackupProtocolListDirectory::RootDirectory, fnx, &modtime)); + TEST_THAT(modtime != 0); - TEST_CHECK_THROWS(std::auto_ptr stored(protocol.QueryStoreFile( - BackupProtocolListDirectory::RootDirectory, - modtime, - modtime, /* use it for attr hash too */ - 0, /* diff from ID */ - fnx, - *upload)), - ConnectionException, Conn_Protocol_UnexpectedReply); + TEST_CHECK_THROWS(std::auto_ptr stored(protocol.QueryStoreFile( + BackupProtocolListDirectory::RootDirectory, + modtime, + modtime, /* use it for attr hash too */ + 0, /* diff from ID */ + fnx, + *upload)), + ConnectionException, Conn_Protocol_UnexpectedReply); - MemBlockStream attr(&modtime, sizeof(modtime)); - BackupStoreFilenameClear fnxd("exceed-limit-dir"); - TEST_CHECK_THROWS(std::auto_ptr dirCreate(protocol.QueryCreateDirectory( - BackupProtocolListDirectory::RootDirectory, - 9837429842987984LL, fnxd, attr)), - ConnectionException, Conn_Protocol_UnexpectedReply); + MemBlockStream attr(&modtime, sizeof(modtime)); + BackupStoreFilenameClear fnxd("exceed-limit-dir"); + TEST_CHECK_THROWS(std::auto_ptr dirCreate(protocol.QueryCreateDirectory( + BackupProtocolListDirectory::RootDirectory, + 9837429842987984LL, fnxd, attr)), + ConnectionException, Conn_Protocol_UnexpectedReply); - // Finish the connection - protocol.QueryFinished(); - } + // Finish the connection + protocol.QueryFinished(); + } - // Kill it again - TEST_THAT(KillServer(pid)); - ::sleep(1); - TEST_THAT(!ServerIsAlive(pid)); + // Kill it again + TEST_THAT(KillServer(pid)); + ::sleep(1); + TEST_THAT(!ServerIsAlive(pid)); - #ifdef WIN32 - TEST_THAT(unlink("testfiles/bbstored.pid") == 0); - #else - TestRemoteProcessMemLeaks("bbstored.memleaks"); - #endif - } + #ifdef WIN32 + TEST_THAT(unlink("testfiles/bbstored.pid") == 0); + #else + TestRemoteProcessMemLeaks("bbstored.memleaks"); + #endif return 0; } @@ -2149,25 +2186,18 @@ return 0; } -#ifdef WIN32 -WCHAR* ConvertUtf8ToWideString(const char* pString); -std::string ConvertPathToAbsoluteUnicode(const char *pFileName); -#endif - -int test(int argc, const char *argv[]) +void test_open_files_with_limited_win32_permissions() { #ifdef WIN32 // this had better work, or bbstored will die when combining diffs char* file = "foo"; - std::string abs = ConvertPathToAbsoluteUnicode(file); - WCHAR* wfile = ConvertUtf8ToWideString(abs.c_str()); DWORD accessRights = FILE_READ_ATTRIBUTES | FILE_LIST_DIRECTORY | FILE_READ_EA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA | FILE_WRITE_EA /*| FILE_ALL_ACCESS*/; DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; - HANDLE h1 = CreateFileW(wfile, accessRights, shareMode, + HANDLE h1 = CreateFileA(file, accessRights, shareMode, NULL, OPEN_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL); assert(h1 != INVALID_HANDLE_VALUE); TEST_THAT(h1 != INVALID_HANDLE_VALUE); @@ -2175,23 +2205,266 @@ accessRights = FILE_READ_ATTRIBUTES | FILE_LIST_DIRECTORY | FILE_READ_EA; - HANDLE h2 = CreateFileW(wfile, accessRights, shareMode, + HANDLE h2 = CreateFileA(file, accessRights, shareMode, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); assert(h2 != INVALID_HANDLE_VALUE); TEST_THAT(h2 != INVALID_HANDLE_VALUE); CloseHandle(h2); CloseHandle(h1); - delete [] wfile; - h1 = openfile("foo", O_CREAT | O_RDWR, 0); + h1 = openfile(file, O_CREAT | O_RDWR, 0); TEST_THAT(h1 != INVALID_HANDLE_VALUE); - h2 = openfile("foo", O_RDWR, 0); + h2 = openfile(file, O_RDWR, 0); TEST_THAT(h2 != INVALID_HANDLE_VALUE); CloseHandle(h2); CloseHandle(h1); #endif +} +void compare_backupstoreinfo_values_to_expected +( + const std::string& test_phase, + const info_StreamFormat_1& expected, + const BackupStoreInfo& actual +) +{ + TEST_EQUAL_LINE(ntohl(expected.mAccountID), actual.GetAccountID(), + test_phase << " AccountID"); + #define TEST_INFO_EQUAL(property) \ + TEST_EQUAL_LINE(box_ntoh64(expected.m ## property), \ + actual.Get ## property (), test_phase << " " #property) + TEST_INFO_EQUAL(ClientStoreMarker); + TEST_INFO_EQUAL(LastObjectIDUsed); + TEST_INFO_EQUAL(BlocksUsed); + TEST_INFO_EQUAL(BlocksInOldFiles); + TEST_INFO_EQUAL(BlocksInDeletedFiles); + TEST_INFO_EQUAL(BlocksInDirectories); + TEST_INFO_EQUAL(BlocksSoftLimit); + TEST_INFO_EQUAL(BlocksHardLimit); + // These attributes of the v2 structure are not supported by v1, + // so they should all be initialised to 0 + TEST_EQUAL_LINE(0, actual.GetBlocksInCurrentFiles(), + test_phase << " BlocksInCurrentFiles"); + TEST_EQUAL_LINE(0, actual.GetNumOldFiles(), + test_phase << " NumOldFiles"); + TEST_EQUAL_LINE(0, actual.GetNumDeletedFiles(), + test_phase << " NumDeletedFiles"); + TEST_EQUAL_LINE(0, actual.GetNumDirectories(), + test_phase << " NumDirectories"); + // These attributes of the old v1 structure are not actually loaded + // or used: + // TEST_INFO_EQUAL(CurrentMarkNumber); + // TEST_INFO_EQUAL(OptionsPresent); + TEST_EQUAL_LINE(box_ntoh64(expected.mNumberDeletedDirectories), + actual.GetDeletedDirectories().size(), + test_phase << " number of deleted directories"); + TEST_EQUAL_LINE(13, actual.GetDeletedDirectories()[0], + test_phase << " deleted directory 1"); + TEST_EQUAL_LINE(14, actual.GetDeletedDirectories()[1], + test_phase << " deleted directory 2"); + #undef TEST_INFO_EQUAL +} + +int test_read_old_backupstoreinfo_files() +{ + // Create an account for the test client + TEST_THAT_ABORTONFAIL(::system(BBSTOREACCOUNTS + " -c testfiles/bbstored.conf create 01234567 0 " + "10000B 20000B") == 0); + TestRemoteProcessMemLeaks("bbstoreaccounts.memleaks"); + + info_StreamFormat_1 info_v1; + info_v1.mMagicValue = htonl(INFO_MAGIC_VALUE_1); + info_v1.mAccountID = htonl(0x01234567); + info_v1.mClientStoreMarker = box_hton64(3); + info_v1.mLastObjectIDUsed = box_hton64(4); + info_v1.mBlocksUsed = box_hton64(5); + info_v1.mBlocksInOldFiles = box_hton64(6); + info_v1.mBlocksInDeletedFiles = box_hton64(7); + info_v1.mBlocksInDirectories = box_hton64(8); + info_v1.mBlocksSoftLimit = box_hton64(9); + info_v1.mBlocksHardLimit = box_hton64(10); + info_v1.mCurrentMarkNumber = htonl(11); + info_v1.mOptionsPresent = htonl(12); + info_v1.mNumberDeletedDirectories = box_hton64(2); + // Then mNumberDeletedDirectories * int64_t IDs for the deleted directories + + // Generate the filename + std::string info_filename("backup/01234567/" INFO_FILENAME); + std::auto_ptr rfw(new RaidFileWrite(0, info_filename)); + rfw->Open(/* AllowOverwrite = */ true); + rfw->Write(&info_v1, sizeof(info_v1)); + // Write mNumberDeletedDirectories * int64_t IDs for the deleted directories + std::auto_ptr apArchive(new Archive(*rfw, IOStream::TimeOutInfinite)); + apArchive->Write((int64_t) 13); + apArchive->Write((int64_t) 14); + rfw->Commit(/* ConvertToRaidNow */ true); + rfw.reset(); + + std::auto_ptr apInfo = BackupStoreInfo::Load(0x1234567, + "backup/01234567/", 0, /* ReadOnly */ false); + compare_backupstoreinfo_values_to_expected("loaded from v1", info_v1, + *apInfo); + TEST_EQUAL_LINE("", apInfo->GetAccountName(), + "account loaded from version 1 format should have no name"); + TEST_EQUAL_LINE(true, apInfo->IsAccountEnabled(), + "account loaded from version 1 format should be enabled by default"); + + apInfo->SetAccountName("bonk"); + + // Save the info again + apInfo->Save(/* allowOverwrite */ true); + + // Check that it was saved in the new Archive format + std::auto_ptr rfr(RaidFileRead::Open(0, info_filename, 0)); + int32_t magic; + if(!rfr->ReadFullBuffer(&magic, sizeof(magic), 0)) + { + THROW_FILE_ERROR("Failed to read store info file: " + "short read of magic number", info_filename, + BackupStoreException, CouldNotLoadStoreInfo); + } + TEST_EQUAL_LINE(INFO_MAGIC_VALUE_2, ntohl(magic), + "format version in newly saved BackupStoreInfo"); + rfr.reset(); + + apInfo = BackupStoreInfo::Load(0x1234567, "backup/01234567/", 0, + /* ReadOnly */ false); + compare_backupstoreinfo_values_to_expected("loaded in v1, resaved in v2", + info_v1, *apInfo); + TEST_EQUAL_LINE("bonk", apInfo->GetAccountName(), + "account loaded from version 1 format should have no name"); + TEST_EQUAL_LINE(true, apInfo->IsAccountEnabled(), + "account resaved in v2 format should preserve AccountEnabled"); + + // Check that the new AccountEnabled flag is saved properly + apInfo->SetAccountEnabled(false); + apInfo->Save(/* allowOverwrite */ true); + apInfo = BackupStoreInfo::Load(0x1234567, "backup/01234567/", 0, + /* ReadOnly */ false); + compare_backupstoreinfo_values_to_expected("saved in v2, loaded in v2", + info_v1, *apInfo); + TEST_EQUAL_LINE(false, apInfo->IsAccountEnabled(), + "AccountEnabled flag was set to false but not loaded correctly"); + apInfo->SetAccountEnabled(true); + apInfo->Save(/* allowOverwrite */ true); + apInfo = BackupStoreInfo::Load(0x1234567, "backup/01234567/", 0, + /* ReadOnly */ true); + TEST_EQUAL_LINE(true, apInfo->IsAccountEnabled(), + "AccountEnabled flag was set to true but not loaded correctly"); + + // Now save the info in v2 format without the AccountEnabled flag + // (boxbackup 0.11 format) and check that the flag is set to true + // for backwards compatibility + + rfw.reset(new RaidFileWrite(0, info_filename)); + rfw->Open(/* allowOverwrite */ true); + magic = htonl(INFO_MAGIC_VALUE_2); + apArchive.reset(new Archive(*rfw, IOStream::TimeOutInfinite)); + rfw->Write(&magic, sizeof(magic)); + apArchive->Write(apInfo->GetAccountID()); + apArchive->Write(std::string("test")); + apArchive->Write(apInfo->GetClientStoreMarker()); + apArchive->Write(apInfo->GetLastObjectIDUsed()); + apArchive->Write(apInfo->GetBlocksUsed()); + apArchive->Write(apInfo->GetBlocksInCurrentFiles()); + apArchive->Write(apInfo->GetBlocksInOldFiles()); + apArchive->Write(apInfo->GetBlocksInDeletedFiles()); + apArchive->Write(apInfo->GetBlocksInDirectories()); + apArchive->Write(apInfo->GetBlocksSoftLimit()); + apArchive->Write(apInfo->GetBlocksHardLimit()); + apArchive->Write(apInfo->GetNumFiles()); + apArchive->Write(apInfo->GetNumOldFiles()); + apArchive->Write(apInfo->GetNumDeletedFiles()); + apArchive->Write(apInfo->GetNumDirectories()); + apArchive->Write((int64_t) apInfo->GetDeletedDirectories().size()); + apArchive->Write(apInfo->GetDeletedDirectories()[0]); + apArchive->Write(apInfo->GetDeletedDirectories()[1]); + rfw->Commit(/* ConvertToRaidNow */ true); + rfw.reset(); + + apInfo = BackupStoreInfo::Load(0x1234567, "backup/01234567/", 0, + /* ReadOnly */ false); + compare_backupstoreinfo_values_to_expected("saved in v2 without " + "AccountEnabled", info_v1, *apInfo); + TEST_EQUAL_LINE("test", apInfo->GetAccountName(), + "account loaded from short v2 format should preserve AccountName"); + TEST_EQUAL_LINE(true, apInfo->IsAccountEnabled(), + "default for missing AccountEnabled should be true"); + // Rewrite using full length, so that the first 4 bytes of extra data + // doesn't get swallowed by "extra data". + apInfo->Save(/* allowOverwrite */ true); + + // Append some extra data after the known account values, to simulate a + // new addition to the store format. Check that this extra data is loaded + // and resaved with the info file. We made the mistake of deleting it in + // 0.11, let's not make the same mistake again. + CollectInBufferStream info_data; + rfr = RaidFileRead::Open(0, info_filename, 0); + rfr->CopyStreamTo(info_data); + rfr.reset(); + info_data.SetForReading(); + rfw.reset(new RaidFileWrite(0, info_filename)); + rfw->Open(/* allowOverwrite */ true); + info_data.CopyStreamTo(*rfw); + char extra_string[] = "hello!"; + MemBlockStream extra_data(extra_string, strlen(extra_string)); + extra_data.CopyStreamTo(*rfw); + rfw->Commit(/* ConvertToRaidNow */ true); + rfw.reset(); + apInfo = BackupStoreInfo::Load(0x1234567, "backup/01234567/", 0, + /* ReadOnly */ false); + TEST_EQUAL_LINE(extra_data.GetSize(), apInfo->GetExtraData().GetSize(), + "wrong amount of extra data loaded from info file"); + TEST_EQUAL_LINE(0, memcmp(extra_data.GetBuffer(), + apInfo->GetExtraData().GetBuffer(), extra_data.GetSize()), + "extra data loaded from info file has wrong contents"); + // Save the file and load again, check that the extra data is still there + apInfo->Save(/* allowOverwrite */ true); + apInfo = BackupStoreInfo::Load(0x1234567, "backup/01234567/", 0, true); + TEST_EQUAL_LINE(extra_data.GetSize(), apInfo->GetExtraData().GetSize(), + "wrong amount of extra data saved and reloaded from info file"); + TEST_EQUAL_LINE(0, memcmp(extra_data.GetBuffer(), + apInfo->GetExtraData().GetBuffer(), extra_data.GetSize()), + "extra data saved and reloaded from info file has wrong contents"); + + // Check that the new bbstoreaccounts command sets the flag properly + TEST_THAT_ABORTONFAIL(::system(BBSTOREACCOUNTS + " -c testfiles/bbstored.conf enabled 01234567 no") == 0); + TestRemoteProcessMemLeaks("bbstoreaccounts.memleaks"); + apInfo = BackupStoreInfo::Load(0x1234567, "backup/01234567/", 0, true); + TEST_EQUAL_LINE(false, apInfo->IsAccountEnabled(), + "'bbstoreaccounts disabled no' should have reset AccountEnabled flag"); + TEST_THAT_ABORTONFAIL(::system(BBSTOREACCOUNTS + " -c testfiles/bbstored.conf enabled 01234567 yes") == 0); + TestRemoteProcessMemLeaks("bbstoreaccounts.memleaks"); + apInfo = BackupStoreInfo::Load(0x1234567, "backup/01234567/", 0, true); + TEST_EQUAL_LINE(true, apInfo->IsAccountEnabled(), + "'bbstoreaccounts disabled yes' should have set AccountEnabled flag"); + + // Delete the account to leave the store in the same state as before + TEST_THAT_ABORTONFAIL(::system(BBSTOREACCOUNTS + " -c testfiles/bbstored.conf delete 01234567 yes") == 0); + TestRemoteProcessMemLeaks("bbstoreaccounts.memleaks"); + + return 0; +} + +int test(int argc, const char *argv[]) +{ + test_open_files_with_limited_win32_permissions(); + + // Initialise the raid file controller + RaidFileController &rcontroller = RaidFileController::GetController(); + rcontroller.Initialise("testfiles/raidfile.conf"); + + int ret = test_read_old_backupstoreinfo_files(); + if (ret != 0) + { + return ret; + } + // SSL library SSLLib::Initialise();