summaryrefslogtreecommitdiff
path: root/lib/backupstore
diff options
context:
space:
mode:
Diffstat (limited to 'lib/backupstore')
-rw-r--r--lib/backupstore/BackupStoreAccounts.cpp491
-rw-r--r--lib/backupstore/BackupStoreAccounts.h34
2 files changed, 520 insertions, 5 deletions
diff --git a/lib/backupstore/BackupStoreAccounts.cpp b/lib/backupstore/BackupStoreAccounts.cpp
index 18500fc1..5aed4a6d 100644
--- a/lib/backupstore/BackupStoreAccounts.cpp
+++ b/lib/backupstore/BackupStoreAccounts.cpp
@@ -9,19 +9,29 @@
#include "Box.h"
-#include <stdio.h>
+#include <algorithm>
+#include <climits>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
#include "BackupStoreAccounts.h"
#include "BackupStoreAccountDatabase.h"
+#include "BackupStoreCheck.h"
+#include "BackupStoreConfigVerify.h"
#include "BackupStoreConstants.h"
#include "BackupStoreDirectory.h"
#include "BackupStoreException.h"
#include "BackupStoreInfo.h"
#include "BackupStoreRefCountDatabase.h"
#include "BoxPortsAndFiles.h"
+#include "HousekeepStoreAccount.h"
+#include "NamedLock.h"
+#include "RaidFileController.h"
#include "RaidFileWrite.h"
#include "StoreStructure.h"
#include "UnixUser.h"
+#include "Utils.h"
#include "MemLeakFindOn.h"
@@ -110,10 +120,7 @@ void BackupStoreAccounts::Create(int32_t ID, int DiscSet, int64_t SizeSoftLimit,
info->Save();
// Create the refcount database
- BackupStoreRefCountDatabase::CreateNew(Entry);
- std::auto_ptr<BackupStoreRefCountDatabase> refcount(
- BackupStoreRefCountDatabase::Load(Entry, false));
- refcount->AddReference(BACKUPSTORE_ROOT_DIRECTORY_ID);
+ BackupStoreRefCountDatabase::Create(Entry)->Commit();
}
// As the original user...
@@ -201,3 +208,477 @@ void BackupStoreAccounts::LockAccount(int32_t ID, NamedLock& rNamedLock)
"lock on account " << ID);
}
}
+
+BackupStoreAccountsControl::BackupStoreAccountsControl(
+ const Configuration& config, bool machineReadableOutput)
+: mConfig(config),
+ mMachineReadableOutput(machineReadableOutput)
+{ }
+
+void BackupStoreAccountsControl::CheckSoftHardLimits(int64_t SoftLimit, int64_t HardLimit)
+{
+ if(SoftLimit > HardLimit)
+ {
+ BOX_FATAL("Soft limit must be less than the hard limit.");
+ exit(1);
+ }
+ if(SoftLimit > ((HardLimit * MAX_SOFT_LIMIT_SIZE) / 100))
+ {
+ BOX_WARNING("We recommend setting the soft limit below " <<
+ MAX_SOFT_LIMIT_SIZE << "% of the hard limit, or " <<
+ HumanReadableSize((HardLimit * MAX_SOFT_LIMIT_SIZE)
+ / 100) << " in this case.");
+ }
+}
+
+int BackupStoreAccountsControl::BlockSizeOfDiscSet(int discSetNum)
+{
+ // Get controller, check disc set number
+ RaidFileController &controller(RaidFileController::GetController());
+ if(discSetNum < 0 || discSetNum >= controller.GetNumDiscSets())
+ {
+ BOX_FATAL("Disc set " << discSetNum << " does not exist.");
+ exit(1);
+ }
+
+ // Return block size
+ return controller.GetDiscSet(discSetNum).GetBlockSize();
+}
+
+std::string BackupStoreAccountsControl::BlockSizeToString(int64_t Blocks, int64_t MaxBlocks, int discSetNum)
+{
+ return FormatUsageBar(Blocks, Blocks * BlockSizeOfDiscSet(discSetNum),
+ MaxBlocks * BlockSizeOfDiscSet(discSetNum),
+ mMachineReadableOutput);
+}
+
+int64_t BackupStoreAccountsControl::SizeStringToBlocks(const char *string, int discSetNum)
+{
+ // Find block size
+ int blockSize = BlockSizeOfDiscSet(discSetNum);
+
+ // Get number
+ char *endptr = (char*)string;
+ int64_t number = strtol(string, &endptr, 0);
+ if(endptr == string || number == LONG_MIN || number == LONG_MAX)
+ {
+ BOX_FATAL("'" << string << "' is not a valid number.");
+ exit(1);
+ }
+
+ // Check units
+ switch(*endptr)
+ {
+ case 'M':
+ case 'm':
+ // Units: Mb
+ return (number * 1024*1024) / blockSize;
+ break;
+
+ case 'G':
+ case 'g':
+ // Units: Gb
+ return (number * 1024*1024*1024) / blockSize;
+ break;
+
+ case 'B':
+ case 'b':
+ // Units: Blocks
+ // Easy! Just return the number specified.
+ return number;
+ break;
+
+ default:
+ BOX_FATAL(string << " has an invalid units specifier "
+ "(use B for blocks, M for MB, G for GB, eg 2GB)");
+ exit(1);
+ break;
+ }
+}
+
+int BackupStoreAccountsControl::SetLimit(int32_t ID, const char *SoftLimitStr,
+ const char *HardLimitStr)
+{
+ std::string rootDir;
+ int discSetNum;
+ std::auto_ptr<UnixUser> user; // used to reset uid when we return
+ NamedLock writeLock;
+
+ if(!OpenAccount(ID, rootDir, discSetNum, user, &writeLock))
+ {
+ BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID)
+ << " to change limits.");
+ return 1;
+ }
+
+ // Load the info
+ std::auto_ptr<BackupStoreInfo> info(BackupStoreInfo::Load(ID, rootDir,
+ discSetNum, false /* Read/Write */));
+
+ // Change the limits
+ int64_t softlimit = SizeStringToBlocks(SoftLimitStr, discSetNum);
+ int64_t hardlimit = SizeStringToBlocks(HardLimitStr, discSetNum);
+ CheckSoftHardLimits(softlimit, hardlimit);
+ info->ChangeLimits(softlimit, hardlimit);
+
+ // Save
+ info->Save();
+
+ BOX_NOTICE("Limits on account " << BOX_FORMAT_ACCOUNT(ID) <<
+ " changed to " << softlimit << " soft, " <<
+ hardlimit << " hard.");
+
+ return 0;
+}
+
+int BackupStoreAccountsControl::SetAccountName(int32_t ID, const std::string& rNewAccountName)
+{
+ std::string rootDir;
+ int discSetNum;
+ std::auto_ptr<UnixUser> user; // used to reset uid when we return
+ NamedLock writeLock;
+
+ if(!OpenAccount(ID, rootDir, discSetNum, user, &writeLock))
+ {
+ BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID)
+ << " to change name.");
+ return 1;
+ }
+
+ // Load the info
+ std::auto_ptr<BackupStoreInfo> info(BackupStoreInfo::Load(ID,
+ rootDir, discSetNum, false /* Read/Write */));
+
+ info->SetAccountName(rNewAccountName);
+
+ // Save
+ info->Save();
+
+ BOX_NOTICE("Account " << BOX_FORMAT_ACCOUNT(ID) <<
+ " name changed to " << rNewAccountName);
+
+ return 0;
+}
+
+int BackupStoreAccountsControl::PrintAccountInfo(int32_t ID)
+{
+ std::string rootDir;
+ int discSetNum;
+ std::auto_ptr<UnixUser> user; // used to reset uid when we return
+
+ if(!OpenAccount(ID, rootDir, discSetNum, user,
+ NULL /* no write lock needed for this read-only operation */))
+ {
+ BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID)
+ << " to display info.");
+ return 1;
+ }
+
+ // Load it in
+ std::auto_ptr<BackupStoreInfo> info(BackupStoreInfo::Load(ID,
+ rootDir, discSetNum, true /* ReadOnly */));
+
+ // Then print out lots of info
+ std::cout << FormatUsageLineStart("Account ID", mMachineReadableOutput) <<
+ BOX_FORMAT_ACCOUNT(ID) << std::endl;
+ std::cout << FormatUsageLineStart("Account Name", mMachineReadableOutput) <<
+ info->GetAccountName() << std::endl;
+ std::cout << FormatUsageLineStart("Last object ID", mMachineReadableOutput) <<
+ BOX_FORMAT_OBJECTID(info->GetLastObjectIDUsed()) << std::endl;
+ std::cout << FormatUsageLineStart("Used", mMachineReadableOutput) <<
+ BlockSizeToString(info->GetBlocksUsed(),
+ info->GetBlocksHardLimit(), discSetNum) << std::endl;
+ std::cout << FormatUsageLineStart("Current files",
+ mMachineReadableOutput) <<
+ BlockSizeToString(info->GetBlocksInCurrentFiles(),
+ info->GetBlocksHardLimit(), discSetNum) << std::endl;
+ std::cout << FormatUsageLineStart("Old files", mMachineReadableOutput) <<
+ BlockSizeToString(info->GetBlocksInOldFiles(),
+ info->GetBlocksHardLimit(), discSetNum) << std::endl;
+ std::cout << FormatUsageLineStart("Deleted files", mMachineReadableOutput) <<
+ BlockSizeToString(info->GetBlocksInDeletedFiles(),
+ info->GetBlocksHardLimit(), discSetNum) << std::endl;
+ std::cout << FormatUsageLineStart("Directories", mMachineReadableOutput) <<
+ BlockSizeToString(info->GetBlocksInDirectories(),
+ info->GetBlocksHardLimit(), discSetNum) << std::endl;
+ std::cout << FormatUsageLineStart("Soft limit", mMachineReadableOutput) <<
+ BlockSizeToString(info->GetBlocksSoftLimit(),
+ info->GetBlocksHardLimit(), discSetNum) << std::endl;
+ std::cout << FormatUsageLineStart("Hard limit", mMachineReadableOutput) <<
+ BlockSizeToString(info->GetBlocksHardLimit(),
+ info->GetBlocksHardLimit(), discSetNum) << std::endl;
+ std::cout << FormatUsageLineStart("Client store marker", mMachineReadableOutput) <<
+ info->GetLastObjectIDUsed() << std::endl;
+ std::cout << FormatUsageLineStart("Current Files", mMachineReadableOutput) <<
+ info->GetNumCurrentFiles() << std::endl;
+ std::cout << FormatUsageLineStart("Old Files", mMachineReadableOutput) <<
+ info->GetNumOldFiles() << std::endl;
+ std::cout << FormatUsageLineStart("Deleted Files", mMachineReadableOutput) <<
+ info->GetNumDeletedFiles() << std::endl;
+ std::cout << FormatUsageLineStart("Directories", mMachineReadableOutput) <<
+ info->GetNumDirectories() << std::endl;
+ std::cout << FormatUsageLineStart("Enabled", mMachineReadableOutput) <<
+ (info->IsAccountEnabled() ? "yes" : "no") << std::endl;
+
+ return 0;
+}
+
+int BackupStoreAccountsControl::SetAccountEnabled(int32_t ID, bool enabled)
+{
+ std::string rootDir;
+ int discSetNum;
+ std::auto_ptr<UnixUser> user; // used to reset uid when we return
+ NamedLock writeLock;
+
+ if(!OpenAccount(ID, rootDir, discSetNum, user, &writeLock))
+ {
+ BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID)
+ << " to change enabled flag.");
+ return 1;
+ }
+
+ // Load it in
+ std::auto_ptr<BackupStoreInfo> info(BackupStoreInfo::Load(ID,
+ rootDir, discSetNum, false /* ReadOnly */));
+ info->SetAccountEnabled(enabled);
+ info->Save();
+ return 0;
+}
+
+int BackupStoreAccountsControl::DeleteAccount(int32_t ID, bool AskForConfirmation)
+{
+ std::string rootDir;
+ int discSetNum;
+ std::auto_ptr<UnixUser> user; // used to reset uid when we return
+ NamedLock writeLock;
+
+ // Obtain a write lock, as the daemon user
+ if(!OpenAccount(ID, rootDir, discSetNum, user, &writeLock))
+ {
+ BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID)
+ << " for deletion.");
+ return 1;
+ }
+
+ // Check user really wants to do this
+ if(AskForConfirmation)
+ {
+ BOX_WARNING("Really delete account " <<
+ BOX_FORMAT_ACCOUNT(ID) << "? (type 'yes' to confirm)");
+ char response[256];
+ if(::fgets(response, sizeof(response), stdin) == 0 || ::strcmp(response, "yes\n") != 0)
+ {
+ BOX_NOTICE("Deletion cancelled.");
+ return 0;
+ }
+ }
+
+ // Back to original user, but write lock is maintained
+ user.reset();
+
+ std::auto_ptr<BackupStoreAccountDatabase> db(
+ BackupStoreAccountDatabase::Read(
+ mConfig.GetKeyValue("AccountDatabase")));
+
+ // Delete from account database
+ db->DeleteEntry(ID);
+
+ // Write back to disc
+ db->Write();
+
+ // Remove the store files...
+
+ // First, become the user specified in the config file
+ std::string username;
+ {
+ const Configuration &rserverConfig(mConfig.GetSubConfiguration("Server"));
+ if(rserverConfig.KeyExists("User"))
+ {
+ username = rserverConfig.GetKeyValue("User");
+ }
+ }
+
+ // Become the right user
+ if(!username.empty())
+ {
+ // Username specified, change...
+ user.reset(new UnixUser(username));
+ user->ChangeProcessUser(true /* temporary */);
+ // Change will be undone when user goes out of scope
+ }
+
+ // Secondly, work out which directories need wiping
+ std::vector<std::string> toDelete;
+ RaidFileController &rcontroller(RaidFileController::GetController());
+ RaidFileDiscSet discSet(rcontroller.GetDiscSet(discSetNum));
+ for(RaidFileDiscSet::const_iterator i(discSet.begin()); i != discSet.end(); ++i)
+ {
+ if(std::find(toDelete.begin(), toDelete.end(), *i) == toDelete.end())
+ {
+ toDelete.push_back((*i) + DIRECTORY_SEPARATOR + rootDir);
+ }
+ }
+
+#ifdef WIN32
+ // Cannot remove files while holding a lock on them
+ writeLock.ReleaseLock();
+#endif
+
+ int retcode = 0;
+
+ // Thirdly, delete the directories...
+ for(std::vector<std::string>::const_iterator d(toDelete.begin()); d != toDelete.end(); ++d)
+ {
+ BOX_NOTICE("Deleting store directory " << (*d) << "...");
+ // Just use the rm command to delete the files
+ std::string cmd("rm -rf ");
+ cmd += *d;
+ // Run command
+ if(::system(cmd.c_str()) != 0)
+ {
+ BOX_ERROR("Failed to delete files in " << (*d) <<
+ ", delete them manually.");
+ retcode = 1;
+ }
+ }
+
+ // Success!
+ return retcode;
+}
+
+bool BackupStoreAccountsControl::OpenAccount(int32_t ID, std::string &rRootDirOut,
+ int &rDiscSetOut, std::auto_ptr<UnixUser> apUser, NamedLock* pLock)
+{
+ // Load in the account database
+ std::auto_ptr<BackupStoreAccountDatabase> db(
+ BackupStoreAccountDatabase::Read(
+ mConfig.GetKeyValue("AccountDatabase")));
+
+ // Exists?
+ if(!db->EntryExists(ID))
+ {
+ BOX_ERROR("Account " << BOX_FORMAT_ACCOUNT(ID) <<
+ " does not exist.");
+ return false;
+ }
+
+ // 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(mConfig.GetSubConfiguration("Server"));
+ if(rserverConfig.KeyExists("User"))
+ {
+ username = rserverConfig.GetKeyValue("User");
+ }
+ }
+
+ // Become the right user
+ if(!username.empty())
+ {
+ // Username specified, change...
+ apUser.reset(new UnixUser(username));
+ apUser->ChangeProcessUser(true /* temporary */);
+ // Change will be undone when apUser goes out of scope
+ // in the caller.
+ }
+
+ if(pLock)
+ {
+ acc.LockAccount(ID, *pLock);
+ }
+
+ return true;
+}
+
+int BackupStoreAccountsControl::CheckAccount(int32_t ID, bool FixErrors, bool Quiet)
+{
+ std::string rootDir;
+ int discSetNum;
+ std::auto_ptr<UnixUser> user; // used to reset uid when we return
+ NamedLock writeLock;
+
+ if(!OpenAccount(ID, rootDir, discSetNum, user, &writeLock))
+ {
+ BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID)
+ << " for checking.");
+ return 1;
+ }
+
+ // Check it
+ BackupStoreCheck check(rootDir, discSetNum, ID, FixErrors, Quiet);
+ check.Check();
+
+ return check.ErrorsFound()?1:0;
+}
+
+int BackupStoreAccountsControl::CreateAccount(int32_t ID, int32_t DiscNumber,
+ int32_t SoftLimit, int32_t HardLimit)
+{
+ // Load in the account database
+ std::auto_ptr<BackupStoreAccountDatabase> db(
+ BackupStoreAccountDatabase::Read(
+ mConfig.GetKeyValue("AccountDatabase")));
+
+ // Already exists?
+ if(db->EntryExists(ID))
+ {
+ BOX_ERROR("Account " << BOX_FORMAT_ACCOUNT(ID) <<
+ " already exists.");
+ return 1;
+ }
+
+ // Get the user under which the daemon runs
+ std::string username;
+ {
+ const Configuration &rserverConfig(mConfig.GetSubConfiguration("Server"));
+ if(rserverConfig.KeyExists("User"))
+ {
+ username = rserverConfig.GetKeyValue("User");
+ }
+ }
+
+ // Create it.
+ BackupStoreAccounts acc(*db);
+ acc.Create(ID, DiscNumber, SoftLimit, HardLimit, username);
+
+ BOX_NOTICE("Account " << BOX_FORMAT_ACCOUNT(ID) << " created.");
+
+ return 0;
+}
+
+int BackupStoreAccountsControl::HousekeepAccountNow(int32_t ID)
+{
+ std::string rootDir;
+ int discSetNum;
+ std::auto_ptr<UnixUser> user; // used to reset uid when we return
+
+ if(!OpenAccount(ID, rootDir, discSetNum, user,
+ NULL /* housekeeping locks the account itself */))
+ {
+ BOX_ERROR("Failed to open account " << BOX_FORMAT_ACCOUNT(ID)
+ << " for housekeeping.");
+ return 1;
+ }
+
+ HousekeepStoreAccount housekeeping(ID, rootDir, discSetNum, NULL);
+ bool success = housekeeping.DoHousekeeping();
+
+ if(!success)
+ {
+ BOX_ERROR("Failed to lock account " << BOX_FORMAT_ACCOUNT(ID)
+ << " for housekeeping: perhaps a client is "
+ "still connected?");
+ return 1;
+ }
+ else
+ {
+ BOX_TRACE("Finished housekeeping on account " <<
+ BOX_FORMAT_ACCOUNT(ID));
+ return 0;
+ }
+}
+
diff --git a/lib/backupstore/BackupStoreAccounts.h b/lib/backupstore/BackupStoreAccounts.h
index 3163f15c..5ee67312 100644
--- a/lib/backupstore/BackupStoreAccounts.h
+++ b/lib/backupstore/BackupStoreAccounts.h
@@ -51,5 +51,39 @@ private:
BackupStoreAccountDatabase &mrDatabase;
};
+class Configuration;
+class UnixUser;
+
+class BackupStoreAccountsControl
+{
+private:
+ const Configuration& mConfig;
+ bool mMachineReadableOutput;
+
+public:
+ BackupStoreAccountsControl(const Configuration& config,
+ bool machineReadableOutput = false);
+
+ void CheckSoftHardLimits(int64_t SoftLimit, int64_t HardLimit);
+ int BlockSizeOfDiscSet(int discSetNum);
+ std::string BlockSizeToString(int64_t Blocks, int64_t MaxBlocks, int discSetNum);
+ int64_t SizeStringToBlocks(const char *string, int discSetNum);
+ bool OpenAccount(int32_t ID, std::string &rRootDirOut,
+ int &rDiscSetOut, std::auto_ptr<UnixUser> apUser, NamedLock* pLock);
+ int SetLimit(int32_t ID, const char *SoftLimitStr,
+ const char *HardLimitStr);
+ int SetAccountName(int32_t ID, const std::string& rNewAccountName);
+ int PrintAccountInfo(int32_t ID);
+ int SetAccountEnabled(int32_t ID, bool enabled);
+ int DeleteAccount(int32_t ID, bool AskForConfirmation);
+ int CheckAccount(int32_t ID, bool FixErrors, bool Quiet);
+ int CreateAccount(int32_t ID, int32_t DiscNumber, int32_t SoftLimit,
+ int32_t HardLimit);
+ int HousekeepAccountNow(int32_t ID);
+};
+
+// max size of soft limit as percent of hard limit
+#define MAX_SOFT_LIMIT_SIZE 97
+
#endif // BACKUPSTOREACCOUNTS__H