summaryrefslogtreecommitdiff
path: root/lib/backupstore/BackupStoreAccounts.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/backupstore/BackupStoreAccounts.cpp')
-rw-r--r--lib/backupstore/BackupStoreAccounts.cpp410
1 files changed, 401 insertions, 9 deletions
diff --git a/lib/backupstore/BackupStoreAccounts.cpp b/lib/backupstore/BackupStoreAccounts.cpp
index 18500fc1..7955b3c4 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...
@@ -151,9 +158,9 @@ void BackupStoreAccounts::GetAccountRoot(int32_t ID, std::string &rRootDirOut, i
std::string BackupStoreAccounts::MakeAccountRootDir(int32_t ID, int DiscSet)
{
char accid[64]; // big enough!
- ::sprintf(accid, "%08x" DIRECTORY_SEPARATOR, ID);
- return std::string(std::string(BOX_RAIDFILE_ROOT_BBSTORED
- DIRECTORY_SEPARATOR) + accid);
+ ::snprintf(accid, sizeof(accid) - 1, "%08x" DIRECTORY_SEPARATOR, ID);
+ return std::string(BOX_RAIDFILE_ROOT_BBSTORED DIRECTORY_SEPARATOR) +
+ accid;
}
@@ -198,6 +205,391 @@ void BackupStoreAccounts::LockAccount(int32_t ID, NamedLock& rNamedLock)
{
THROW_EXCEPTION_MESSAGE(BackupStoreException,
CouldNotLockStoreAccount, "Failed to get exclusive "
- "lock on account " << ID);
+ "lock on account " << BOX_FORMAT_ACCOUNT(ID));
}
}
+
+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();
+}
+
+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
+ int blocksize = BlockSizeOfDiscSet(discSetNum);
+ int64_t softlimit = SizeStringToBlocks(SoftLimitStr, blocksize);
+ int64_t hardlimit = SizeStringToBlocks(HardLimitStr, blocksize);
+ 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 */));
+
+ return BackupAccountControl::PrintAccountInfo(*info,
+ BlockSizeOfDiscSet(discSetNum));
+}
+
+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);
+ }
+ }
+
+ // NamedLock will throw an exception if it can't delete the lockfile,
+ // which it can't if it doesn't exist. Now that we've deleted the account,
+ // nobody can open it anyway, so it's safe to unlock.
+ writeLock.ReleaseLock();
+
+ 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
+#ifdef WIN32
+ std::string cmd("rmdir /s/q ");
+ std::string dir = *d;
+
+ // rmdir doesn't understand forward slashes, so replace them all.
+ for(std::string::iterator i = dir.begin(); i != dir.end(); i++)
+ {
+ if(*i == '/')
+ {
+ *i = '\\';
+ }
+ }
+ cmd += dir;
+#else
+ std::string cmd("rm -rf ");
+ cmd += *d;
+#endif
+ // 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,
+ bool ReturnNumErrorsFound)
+{
+ 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,
+ FixErrors ? &writeLock : NULL)) // don't need a write lock if not making changes
+ {
+ 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();
+
+ if(ReturnNumErrorsFound)
+ {
+ return check.GetNumErrorsFound();
+ }
+ else
+ {
+ 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;
+ }
+}
+