summaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorReinhard Tartler <siretart@tauware.de>2009-04-02 13:58:11 +0200
committerReinhard Tartler <siretart@tauware.de>2009-04-02 13:58:11 +0200
commita84d45498bd861c9225080232948a99c2e317bb8 (patch)
tree8f1f5fb7bf7ffbf6f24cf4a4fd6888a235dbcc08 /bin
parent25db897553a0db0f912602b375029e724f51556e (diff)
Import upstream version 0.11~rc3~r2491
Diffstat (limited to 'bin')
-rw-r--r--bin/bbackupctl/bbackupctl.cpp19
-rw-r--r--bin/bbackupd/BackupClientContext.cpp27
-rw-r--r--bin/bbackupd/BackupClientContext.h19
-rw-r--r--bin/bbackupd/BackupClientDeleteList.cpp80
-rw-r--r--bin/bbackupd/BackupClientDeleteList.h34
-rw-r--r--bin/bbackupd/BackupClientDirectoryRecord.cpp576
-rw-r--r--bin/bbackupd/BackupClientDirectoryRecord.h159
-rw-r--r--bin/bbackupd/BackupClientInodeToIDMap.cpp11
-rw-r--r--bin/bbackupd/BackupDaemon.cpp1699
-rw-r--r--bin/bbackupd/BackupDaemon.h148
-rw-r--r--bin/bbackupd/BackupDaemonInterface.h164
-rw-r--r--bin/bbackupd/Win32ServiceFunctions.cpp8
-rwxr-xr-xbin/bbackupd/bbackupd-config.in6
-rw-r--r--bin/bbackupd/bbackupd.cpp2
-rw-r--r--bin/bbackupd/win32/NotifySysAdmin.vbs62
-rw-r--r--bin/bbackupd/win32/bbackupd.conf2
-rw-r--r--bin/bbackupquery/BackupQueries.cpp1240
-rw-r--r--bin/bbackupquery/BackupQueries.h280
-rw-r--r--bin/bbackupquery/BoxBackupCompareParams.h107
-rw-r--r--bin/bbackupquery/bbackupquery.cpp79
-rw-r--r--bin/bbackupquery/documentation.txt22
-rw-r--r--bin/bbstoreaccounts/bbstoreaccounts.cpp112
-rw-r--r--bin/bbstored/BBStoreDHousekeeping.cpp16
-rw-r--r--bin/bbstored/BackupCommands.cpp165
-rw-r--r--bin/bbstored/BackupConstants.h2
-rw-r--r--bin/bbstored/BackupStoreContext.cpp (renamed from bin/bbstored/BackupContext.cpp)211
-rw-r--r--bin/bbstored/BackupStoreContext.h (renamed from bin/bbstored/BackupContext.h)50
-rw-r--r--bin/bbstored/BackupStoreDaemon.cpp34
-rw-r--r--bin/bbstored/BackupStoreDaemon.h15
-rw-r--r--bin/bbstored/HousekeepStoreAccount.cpp274
-rw-r--r--bin/bbstored/HousekeepStoreAccount.h3
-rw-r--r--bin/bbstored/backupprotocol.txt8
-rwxr-xr-xbin/bbstored/bbstored-config.in4
-rw-r--r--bin/bbstored/bbstored.cpp2
34 files changed, 3365 insertions, 2275 deletions
diff --git a/bin/bbackupctl/bbackupctl.cpp b/bin/bbackupctl/bbackupctl.cpp
index 3795cbed..2c41f614 100644
--- a/bin/bbackupctl/bbackupctl.cpp
+++ b/bin/bbackupctl/bbackupctl.cpp
@@ -15,6 +15,8 @@
#include <unistd.h>
#endif
+#include <cstdlib>
+
#include "MainHelper.h"
#include "BoxPortsAndFiles.h"
#include "BackupDaemonConfigVerify.h"
@@ -61,9 +63,7 @@ int main(int argc, const char *argv[])
MAINHELPER_START
-#if defined WIN32 && ! defined NDEBUG
- ::openlog("Box Backup (bbackupctl)", 0, 0);
-#endif
+ Logging::SetProgramName("bbackupctl");
// Filename for configuration file?
std::string configFilename;
@@ -256,7 +256,8 @@ int main(int argc, const char *argv[])
case SyncAndWaitForEnd:
{
// send a sync command
- std::string cmd("force-sync\n");
+ commandName = "force-sync";
+ std::string cmd = commandName + "\n";
connection.Write(cmd.c_str(), cmd.size());
connection.WriteAllBuffered();
@@ -336,13 +337,17 @@ int main(int argc, const char *argv[])
{
if(!quiet)
{
- BOX_INFO("Succeeded.");
+ BOX_INFO("Control command "
+ "sent: " <<
+ commandName);
}
finished = true;
}
else if(line == "error")
{
- BOX_ERROR("Check command spelling");
+ BOX_ERROR("Control command failed: " <<
+ commandName << ". Check "
+ "command spelling.");
returnCode = 1;
finished = true;
}
@@ -352,7 +357,7 @@ int main(int argc, const char *argv[])
MAINHELPER_END
-#if defined WIN32 && ! defined NDEBUG
+#if defined WIN32 && ! defined BOX_RELEASE_BUILD
closelog();
#endif
diff --git a/bin/bbackupd/BackupClientContext.cpp b/bin/bbackupd/BackupClientContext.cpp
index 4b4efd90..b978f54c 100644
--- a/bin/bbackupd/BackupClientContext.cpp
+++ b/bin/bbackupd/BackupClientContext.cpp
@@ -41,17 +41,20 @@
// --------------------------------------------------------------------------
BackupClientContext::BackupClientContext
(
- BackupDaemon &rDaemon,
+ LocationResolver &rResolver,
TLSContext &rTLSContext,
const std::string &rHostname,
+ int Port,
int32_t AccountNumber,
bool ExtendedLogging,
bool ExtendedLogToFile,
- std::string ExtendedLogFile
+ std::string ExtendedLogFile,
+ ProgressNotifier& rProgressNotifier
)
- : mrDaemon(rDaemon),
+ : mrResolver(rResolver),
mrTLSContext(rTLSContext),
mHostname(rHostname),
+ mPort(Port),
mAccountNumber(AccountNumber),
mpSocket(0),
mpConnection(0),
@@ -66,8 +69,9 @@ BackupClientContext::BackupClientContext
mStorageLimitExceeded(false),
mpExcludeFiles(0),
mpExcludeDirs(0),
- mKeepAliveTimer(0),
- mbIsManaged(false)
+ mKeepAliveTimer(0, "KeepAliveTime"),
+ mbIsManaged(false),
+ mrProgressNotifier(rProgressNotifier)
{
}
@@ -129,7 +133,8 @@ BackupProtocolClient &BackupClientContext::GetConnection()
mHostname << "'...");
// Connect!
- mpSocket->Open(mrTLSContext, Socket::TypeINET, mHostname.c_str(), BOX_PORT_BBSTORED);
+ mpSocket->Open(mrTLSContext, Socket::TypeINET,
+ mHostname.c_str(), mPort);
// And create a procotol object
mpConnection = new BackupProtocolClient(*mpSocket);
@@ -146,8 +151,8 @@ BackupProtocolClient &BackupClientContext::GetConnection()
if (!mpExtendedLogFileHandle)
{
- BOX_ERROR("Failed to open extended log "
- "file: " << strerror(errno));
+ BOX_LOG_SYS_ERROR("Failed to open extended "
+ "log file: " << mExtendedLogFile);
}
else
{
@@ -465,7 +470,7 @@ bool BackupClientContext::FindFilename(int64_t ObjectID, int64_t ContainingDirec
{
// Location name -- look up in daemon's records
std::string locPath;
- if(!mrDaemon.FindLocationPathName(elementName.GetClearFilename(), locPath))
+ if(!mrResolver.FindLocationPathName(elementName.GetClearFilename(), locPath))
{
// Didn't find the location... so can't give the local filename
return false;
@@ -504,7 +509,7 @@ void BackupClientContext::SetKeepAliveTime(int iSeconds)
{
mKeepAliveTime = iSeconds < 0 ? 0 : iSeconds;
BOX_TRACE("Set keep-alive time to " << mKeepAliveTime << " seconds");
- mKeepAliveTimer = Timer(mKeepAliveTime);
+ mKeepAliveTimer = Timer(mKeepAliveTime, "KeepAliveTime");
}
// --------------------------------------------------------------------------
@@ -564,7 +569,7 @@ void BackupClientContext::DoKeepAlive()
BOX_TRACE("KeepAliveTime reached, sending keep-alive message");
mpConnection->QueryGetIsAlive();
- mKeepAliveTimer = Timer(mKeepAliveTime);
+ mKeepAliveTimer = Timer(mKeepAliveTime, "KeepAliveTime");
}
int BackupClientContext::GetMaximumDiffingTime()
diff --git a/bin/bbackupd/BackupClientContext.h b/bin/bbackupd/BackupClientContext.h
index 152d8556..4665df2b 100644
--- a/bin/bbackupd/BackupClientContext.h
+++ b/bin/bbackupd/BackupClientContext.h
@@ -12,6 +12,8 @@
#include "BoxTime.h"
#include "BackupClientDeleteList.h"
+#include "BackupClientDirectoryRecord.h"
+#include "BackupDaemonInterface.h"
#include "BackupStoreFile.h"
#include "ExcludeList.h"
#include "Timer.h"
@@ -25,6 +27,7 @@ class BackupStoreFilenameClear;
#include <string>
+
// --------------------------------------------------------------------------
//
// Class
@@ -38,13 +41,15 @@ class BackupClientContext : public DiffTimer
public:
BackupClientContext
(
- BackupDaemon &rDaemon,
+ LocationResolver &rResolver,
TLSContext &rTLSContext,
const std::string &rHostname,
+ int32_t Port,
int32_t AccountNumber,
bool ExtendedLogging,
bool ExtendedLogToFile,
- std::string ExtendedLogFile
+ std::string ExtendedLogFile,
+ ProgressNotifier &rProgressNotifier
);
virtual ~BackupClientContext();
private:
@@ -69,6 +74,7 @@ public:
int64_t GetClientStoreMarker() const {return mClientStoreMarker;}
bool StorageLimitExceeded() {return mStorageLimitExceeded;}
+ void SetStorageLimitExceeded() {mStorageLimitExceeded = true;}
// --------------------------------------------------------------------------
//
@@ -197,10 +203,16 @@ public:
virtual int GetMaximumDiffingTime();
virtual bool IsManaged() { return mbIsManaged; }
+ ProgressNotifier& GetProgressNotifier() const
+ {
+ return mrProgressNotifier;
+ }
+
private:
- BackupDaemon &mrDaemon;
+ LocationResolver &mrResolver;
TLSContext &mrTLSContext;
std::string mHostname;
+ int mPort;
int32_t mAccountNumber;
SocketStreamTLS *mpSocket;
BackupProtocolClient *mpConnection;
@@ -219,6 +231,7 @@ private:
bool mbIsManaged;
int mKeepAliveTime;
int mMaximumDiffingTime;
+ ProgressNotifier &mrProgressNotifier;
};
#endif // BACKUPCLIENTCONTEXT__H
diff --git a/bin/bbackupd/BackupClientDeleteList.cpp b/bin/bbackupd/BackupClientDeleteList.cpp
index f6d8e0dc..b9b5b53e 100644
--- a/bin/bbackupd/BackupClientDeleteList.cpp
+++ b/bin/bbackupd/BackupClientDeleteList.cpp
@@ -42,21 +42,38 @@ BackupClientDeleteList::~BackupClientDeleteList()
{
}
+BackupClientDeleteList::FileToDelete::FileToDelete(int64_t DirectoryID,
+ const BackupStoreFilename& rFilename,
+ const std::string& rLocalPath)
+: mDirectoryID(DirectoryID),
+ mFilename(rFilename),
+ mLocalPath(rLocalPath)
+{ }
+
+BackupClientDeleteList::DirToDelete::DirToDelete(int64_t ObjectID,
+ const std::string& rLocalPath)
+: mObjectID(ObjectID),
+ mLocalPath(rLocalPath)
+{ }
+
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupClientDeleteList::AddDirectoryDelete(int64_t)
+// Name: BackupClientDeleteList::AddDirectoryDelete(int64_t,
+// const BackupStoreFilename&)
// Purpose: Add a directory to the list of directories to be deleted.
// Created: 10/11/03
//
// --------------------------------------------------------------------------
-void BackupClientDeleteList::AddDirectoryDelete(int64_t ObjectID)
+void BackupClientDeleteList::AddDirectoryDelete(int64_t ObjectID,
+ const std::string& rLocalPath)
{
// Only add the delete to the list if it's not in the "no delete" set
- if(mDirectoryNoDeleteList.find(ObjectID) == mDirectoryNoDeleteList.end())
+ if(mDirectoryNoDeleteList.find(ObjectID) ==
+ mDirectoryNoDeleteList.end())
{
// Not in the list, so should delete it
- mDirectoryList.push_back(ObjectID);
+ mDirectoryList.push_back(DirToDelete(ObjectID, rLocalPath));
}
}
@@ -64,18 +81,22 @@ void BackupClientDeleteList::AddDirectoryDelete(int64_t ObjectID)
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupClientDeleteList::AddFileDelete(int64_t, BackupStoreFilenameClear &)
+// Name: BackupClientDeleteList::AddFileDelete(int64_t,
+// const BackupStoreFilename &)
// Purpose:
// Created: 10/11/03
//
// --------------------------------------------------------------------------
-void BackupClientDeleteList::AddFileDelete(int64_t DirectoryID, const BackupStoreFilename &rFilename)
+void BackupClientDeleteList::AddFileDelete(int64_t DirectoryID,
+ const BackupStoreFilename &rFilename, const std::string& rLocalPath)
{
// Try to find it in the no delete list
- std::vector<std::pair<int64_t, BackupStoreFilename> >::iterator delEntry(mFileNoDeleteList.begin());
+ std::vector<std::pair<int64_t, BackupStoreFilename> >::iterator
+ delEntry(mFileNoDeleteList.begin());
while(delEntry != mFileNoDeleteList.end())
{
- if((delEntry)->first == DirectoryID && (delEntry)->second == rFilename)
+ if((delEntry)->first == DirectoryID
+ && (delEntry)->second == rFilename)
{
// Found!
break;
@@ -86,7 +107,8 @@ void BackupClientDeleteList::AddFileDelete(int64_t DirectoryID, const BackupStor
// Only add it to the delete list if it wasn't in the no delete list
if(delEntry == mFileNoDeleteList.end())
{
- mFileList.push_back(std::pair<int64_t, BackupStoreFilename>(DirectoryID, rFilename));
+ mFileList.push_back(FileToDelete(DirectoryID, rFilename,
+ rLocalPath));
}
}
@@ -113,18 +135,24 @@ void BackupClientDeleteList::PerformDeletions(BackupClientContext &rContext)
BackupProtocolClient &connection(rContext.GetConnection());
// Do the deletes
- for(std::vector<int64_t>::iterator i(mDirectoryList.begin()); i != mDirectoryList.end(); ++i)
+ for(std::vector<DirToDelete>::iterator i(mDirectoryList.begin());
+ i != mDirectoryList.end(); ++i)
{
- connection.QueryDeleteDirectory(*i);
+ connection.QueryDeleteDirectory(i->mObjectID);
+ rContext.GetProgressNotifier().NotifyDirectoryDeleted(
+ i->mObjectID, i->mLocalPath);
}
// Clear the directory list
mDirectoryList.clear();
// Delete the files
- for(std::vector<std::pair<int64_t, BackupStoreFilename> >::iterator i(mFileList.begin()); i != mFileList.end(); ++i)
+ for(std::vector<FileToDelete>::iterator i(mFileList.begin());
+ i != mFileList.end(); ++i)
{
- connection.QueryDeleteFile(i->first, i->second);
+ connection.QueryDeleteFile(i->mDirectoryID, i->mFilename);
+ rContext.GetProgressNotifier().NotifyFileDeleted(
+ i->mDirectoryID, i->mLocalPath);
}
}
@@ -140,7 +168,15 @@ void BackupClientDeleteList::PerformDeletions(BackupClientContext &rContext)
void BackupClientDeleteList::StopDirectoryDeletion(int64_t ObjectID)
{
// First of all, is it in the delete vector?
- std::vector<int64_t>::iterator delEntry(std::find(mDirectoryList.begin(), mDirectoryList.end(), ObjectID));
+ std::vector<DirToDelete>::iterator delEntry(mDirectoryList.begin());
+ for(; delEntry != mDirectoryList.end(); delEntry++)
+ {
+ if(delEntry->mObjectID == ObjectID)
+ {
+ // Found!
+ break;
+ }
+ }
if(delEntry != mDirectoryList.end())
{
// erase this entry
@@ -148,7 +184,8 @@ void BackupClientDeleteList::StopDirectoryDeletion(int64_t ObjectID)
}
else
{
- // Haven't been asked to delete it yet, put it in the no delete list
+ // Haven't been asked to delete it yet, put it in the
+ // no delete list
mDirectoryNoDeleteList.insert(ObjectID);
}
}
@@ -162,13 +199,15 @@ void BackupClientDeleteList::StopDirectoryDeletion(int64_t ObjectID)
// Created: 19/11/03
//
// --------------------------------------------------------------------------
-void BackupClientDeleteList::StopFileDeletion(int64_t DirectoryID, const BackupStoreFilename &rFilename)
+void BackupClientDeleteList::StopFileDeletion(int64_t DirectoryID,
+ const BackupStoreFilename &rFilename)
{
// Find this in the delete list
- std::vector<std::pair<int64_t, BackupStoreFilename> >::iterator delEntry(mFileList.begin());
+ std::vector<FileToDelete>::iterator delEntry(mFileList.begin());
while(delEntry != mFileList.end())
{
- if((delEntry)->first == DirectoryID && (delEntry)->second == rFilename)
+ if(delEntry->mDirectoryID == DirectoryID
+ && delEntry->mFilename == rFilename)
{
// Found!
break;
@@ -186,10 +225,5 @@ void BackupClientDeleteList::StopFileDeletion(int64_t DirectoryID, const BackupS
// Haven't been asked to delete it yet, put it in the no delete list
mFileNoDeleteList.push_back(std::pair<int64_t, BackupStoreFilename>(DirectoryID, rFilename));
}
-
}
-
-
-
-
diff --git a/bin/bbackupd/BackupClientDeleteList.h b/bin/bbackupd/BackupClientDeleteList.h
index 5940cf50..b0fbf51a 100644
--- a/bin/bbackupd/BackupClientDeleteList.h
+++ b/bin/bbackupd/BackupClientDeleteList.h
@@ -28,22 +28,46 @@ class BackupClientContext;
// --------------------------------------------------------------------------
class BackupClientDeleteList
{
+private:
+ class FileToDelete
+ {
+ public:
+ int64_t mDirectoryID;
+ BackupStoreFilename mFilename;
+ std::string mLocalPath;
+ FileToDelete(int64_t DirectoryID,
+ const BackupStoreFilename& rFilename,
+ const std::string& rLocalPath);
+ };
+
+ class DirToDelete
+ {
+ public:
+ int64_t mObjectID;
+ std::string mLocalPath;
+ DirToDelete(int64_t ObjectID, const std::string& rLocalPath);
+ };
+
public:
BackupClientDeleteList();
~BackupClientDeleteList();
- void AddDirectoryDelete(int64_t ObjectID);
- void AddFileDelete(int64_t DirectoryID, const BackupStoreFilename &rFilename);
+ void AddDirectoryDelete(int64_t ObjectID,
+ const std::string& rLocalPath);
+ void AddFileDelete(int64_t DirectoryID,
+ const BackupStoreFilename &rFilename,
+ const std::string& rLocalPath);
void StopDirectoryDeletion(int64_t ObjectID);
- void StopFileDeletion(int64_t DirectoryID, const BackupStoreFilename &rFilename);
+ void StopFileDeletion(int64_t DirectoryID,
+ const BackupStoreFilename &rFilename);
void PerformDeletions(BackupClientContext &rContext);
private:
- std::vector<int64_t> mDirectoryList;
+ std::vector<DirToDelete> mDirectoryList;
std::set<int64_t> mDirectoryNoDeleteList; // note: things only get in this list if they're not present in mDirectoryList when they are 'added'
- std::vector<std::pair<int64_t, BackupStoreFilename> > mFileList;
+ std::vector<FileToDelete> mFileList;
std::vector<std::pair<int64_t, BackupStoreFilename> > mFileNoDeleteList;
};
diff --git a/bin/bbackupd/BackupClientDirectoryRecord.cpp b/bin/bbackupd/BackupClientDirectoryRecord.cpp
index 0a0703c2..b8d42d47 100644
--- a/bin/bbackupd/BackupClientDirectoryRecord.cpp
+++ b/bin/bbackupd/BackupClientDirectoryRecord.cpp
@@ -2,7 +2,8 @@
//
// File
// Name: BackupClientDirectoryRecord.cpp
-// Purpose: Implementation of record about directory for backup client
+// Purpose: Implementation of record about directory for
+// backup client
// Created: 2003/10/08
//
// --------------------------------------------------------------------------
@@ -100,16 +101,27 @@ void BackupClientDirectoryRecord::DeleteSubDirectories()
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::SyncParams &, int64_t, const std::string &, bool)
-// Purpose: Syncronise, recusively, a local directory with the server.
+// Name: BackupClientDirectoryRecord::SyncDirectory(i
+// BackupClientDirectoryRecord::SyncParams &,
+// int64_t, const std::string &,
+// const std::string &, bool)
+// Purpose: Recursively synchronise a local directory
+// with the server.
// Created: 2003/10/08
//
// --------------------------------------------------------------------------
-void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::SyncParams &rParams, int64_t ContainingDirectoryID,
- const std::string &rLocalPath, bool ThisDirHasJustBeenCreated)
+void BackupClientDirectoryRecord::SyncDirectory(
+ BackupClientDirectoryRecord::SyncParams &rParams,
+ int64_t ContainingDirectoryID,
+ const std::string &rLocalPath,
+ const std::string &rRemotePath,
+ bool ThisDirHasJustBeenCreated)
{
+ BackupClientContext& rContext(rParams.mrContext);
+ ProgressNotifier& rNotifier(rContext.GetProgressNotifier());
+
// Signal received by daemon?
- if(rParams.mrDaemon.StopRun())
+ if(rParams.mrRunStatusProvider.StopRun())
{
// Yes. Stop now.
THROW_EXCEPTION(BackupStoreException, SignalReceived)
@@ -118,49 +130,66 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
// Start by making some flag changes, marking this sync as not done,
// and on the immediate sub directories.
mSyncDone = false;
- for(std::map<std::string, BackupClientDirectoryRecord *>::iterator i = mSubDirectories.begin();
+ for(std::map<std::string, BackupClientDirectoryRecord *>::iterator
+ i = mSubDirectories.begin();
i != mSubDirectories.end(); ++i)
{
i->second->mSyncDone = false;
}
- // Work out the time in the future after which the file should be uploaded regardless.
- // This is a simple way to avoid having too many problems with file servers when they have
- // clients with badly out of sync clocks.
- rParams.mUploadAfterThisTimeInTheFuture = GetCurrentBoxTime() + rParams.mMaxFileTimeInFuture;
+ // Work out the time in the future after which the file should
+ // be uploaded regardless. This is a simple way to avoid having
+ // too many problems with file servers when they have clients
+ // with badly out of sync clocks.
+ rParams.mUploadAfterThisTimeInTheFuture = GetCurrentBoxTime() +
+ rParams.mMaxFileTimeInFuture;
- // Build the current state checksum to compare against while getting info from dirs
- // Note checksum is used locally only, so byte order isn't considered.
+ // Build the current state checksum to compare against while
+ // getting info from dirs. Note checksum is used locally only,
+ // so byte order isn't considered.
MD5Digest currentStateChecksum;
+ EMU_STRUCT_STAT dest_st;
// Stat the directory, to get attribute info
+ // If it's a symbolic link, we want the link target here
+ // (as we're about to back up the contents of the directory)
{
- struct stat st;
- if(::stat(rLocalPath.c_str(), &st) != 0)
+ if(EMU_STAT(rLocalPath.c_str(), &dest_st) != 0)
{
- // The directory has probably been deleted, so just ignore this error.
- // In a future scan, this deletion will be noticed, deleted from server, and this object deleted.
- rParams.GetProgressNotifier().NotifyDirStatFailed(
- this, rLocalPath, strerror(errno));
+ // The directory has probably been deleted, so
+ // just ignore this error. In a future scan, this
+ // deletion will be noticed, deleted from server,
+ // and this object deleted.
+ rNotifier.NotifyDirStatFailed(this, rLocalPath,
+ strerror(errno));
return;
}
- // Store inode number in map so directories are tracked in case they're renamed
+ // Store inode number in map so directories are tracked
+ // in case they're renamed
{
- BackupClientInodeToIDMap &idMap(rParams.mrContext.GetNewIDMap());
- idMap.AddToMap(st.st_ino, mObjectID, ContainingDirectoryID);
+ BackupClientInodeToIDMap &idMap(
+ rParams.mrContext.GetNewIDMap());
+ idMap.AddToMap(dest_st.st_ino, mObjectID,
+ ContainingDirectoryID);
}
// Add attributes to checksum
- currentStateChecksum.Add(&st.st_mode, sizeof(st.st_mode));
- currentStateChecksum.Add(&st.st_uid, sizeof(st.st_uid));
- currentStateChecksum.Add(&st.st_gid, sizeof(st.st_gid));
+ currentStateChecksum.Add(&dest_st.st_mode,
+ sizeof(dest_st.st_mode));
+ currentStateChecksum.Add(&dest_st.st_uid,
+ sizeof(dest_st.st_uid));
+ currentStateChecksum.Add(&dest_st.st_gid,
+ sizeof(dest_st.st_gid));
// Inode to be paranoid about things moving around
- currentStateChecksum.Add(&st.st_ino, sizeof(st.st_ino));
+ currentStateChecksum.Add(&dest_st.st_ino,
+ sizeof(dest_st.st_ino));
#ifdef HAVE_STRUCT_STAT_ST_FLAGS
- currentStateChecksum.Add(&st.st_flags, sizeof(st.st_flags));
+ currentStateChecksum.Add(&dest_st.st_flags,
+ sizeof(dest_st.st_flags));
#endif
StreamableMemBlock xattr;
- BackupClientFileAttributes::FillExtendedAttr(xattr, rLocalPath.c_str());
+ BackupClientFileAttributes::FillExtendedAttr(xattr,
+ rLocalPath.c_str());
currentStateChecksum.Add(xattr.GetBuffer(), xattr.GetSize());
}
@@ -170,13 +199,13 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
std::vector<std::string> files;
bool downloadDirectoryRecordBecauseOfFutureFiles = false;
- struct stat dir_st;
- if(::lstat(rLocalPath.c_str(), &dir_st) != 0)
+ EMU_STRUCT_STAT link_st;
+ if(EMU_LSTAT(rLocalPath.c_str(), &link_st) != 0)
{
// Report the error (logs and
// eventual email to administrator)
- rParams.GetProgressNotifier().NotifyFileStatFailed(this,
- rLocalPath, strerror(errno));
+ rNotifier.NotifyFileStatFailed(this, rLocalPath,
+ strerror(errno));
// FIXME move to NotifyFileStatFailed()
SetErrorWhenReadingFilesystemObject(rParams,
@@ -192,8 +221,7 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
DIR *dirHandle = 0;
try
{
- rParams.GetProgressNotifier().NotifyScanDirectory(
- this, rLocalPath);
+ rNotifier.NotifyScanDirectory(this, rLocalPath);
dirHandle = ::opendir(rLocalPath.c_str());
if(dirHandle == 0)
@@ -202,17 +230,19 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
// eventual email to administrator)
if (errno == EACCES)
{
- rParams.GetProgressNotifier().NotifyDirListFailed(
- this, rLocalPath, "Access denied");
+ rNotifier.NotifyDirListFailed(this,
+ rLocalPath, "Access denied");
}
else
{
- rParams.GetProgressNotifier().NotifyDirListFailed(this,
+ rNotifier.NotifyDirListFailed(this,
rLocalPath, strerror(errno));
}
- // Report the error (logs and eventual email to administrator)
- SetErrorWhenReadingFilesystemObject(rParams, rLocalPath.c_str());
+ // Report the error (logs and eventual email
+ // to administrator)
+ SetErrorWhenReadingFilesystemObject(rParams,
+ rLocalPath.c_str());
// Ignore this directory for now.
return;
}
@@ -228,14 +258,17 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
::memset(&checksum_info, 0, sizeof(checksum_info));
struct dirent *en = 0;
- struct stat st;
+ EMU_STRUCT_STAT file_st;
std::string filename;
while((en = ::readdir(dirHandle)) != 0)
{
rParams.mrContext.DoKeepAlive();
- // Don't need to use LinuxWorkaround_FinishDirentStruct(en, rLocalPath.c_str());
- // on Linux, as a stat is performed to get all this info
+ // Don't need to use
+ // LinuxWorkaround_FinishDirentStruct(en,
+ // rLocalPath.c_str());
+ // on Linux, as a stat is performed to
+ // get all this info
if(en->d_name[0] == '.' &&
(en->d_name[1] == '\0' || (en->d_name[1] == '.' && en->d_name[2] == '\0')))
@@ -259,11 +292,11 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
// prefer S_IFREG, S_IFDIR...
int type = en->d_type;
#else
- if(::lstat(filename.c_str(), &st) != 0)
+ if(EMU_LSTAT(filename.c_str(), &file_st) != 0)
{
// Report the error (logs and
// eventual email to administrator)
- rParams.GetProgressNotifier().NotifyFileStatFailed(this,
+ rNotifier.NotifyFileStatFailed(this,
filename, strerror(errno));
// FIXME move to NotifyFileStatFailed()
@@ -274,19 +307,18 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
continue;
}
- if(st.st_dev != dir_st.st_dev)
+ if(file_st.st_dev != dest_st.st_dev)
{
if(!(rParams.mrContext.ExcludeDir(
filename)))
{
- rParams.GetProgressNotifier()
- .NotifyMountPointSkipped(
- this, filename);
+ rNotifier.NotifyMountPointSkipped(
+ this, filename);
}
continue;
}
- int type = st.st_mode & S_IFMT;
+ int type = file_st.st_mode & S_IFMT;
#endif
if(type == S_IFREG || type == S_IFLNK)
@@ -296,8 +328,7 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
// Exclude it?
if(rParams.mrContext.ExcludeFile(filename))
{
- rParams.GetProgressNotifier()
- .NotifyFileExcluded(
+ rNotifier.NotifyFileExcluded(
this,
filename);
@@ -315,8 +346,7 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
// Exclude it?
if(rParams.mrContext.ExcludeDir(filename))
{
- rParams.GetProgressNotifier()
- .NotifyDirExcluded(
+ rNotifier.NotifyDirExcluded(
this,
filename);
@@ -327,19 +357,22 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
// Store on list
dirs.push_back(std::string(en->d_name));
}
+ else if (type == S_IFSOCK || type == S_IFIFO)
+ {
+ // removed notification for these types
+ // see Debian bug 479145, no objections
+ }
else
{
if(rParams.mrContext.ExcludeFile(filename))
{
- rParams.GetProgressNotifier()
- .NotifyFileExcluded(
+ rNotifier.NotifyFileExcluded(
this,
filename);
}
else
{
- rParams.GetProgressNotifier()
- .NotifyUnsupportedFileType(
+ rNotifier.NotifyUnsupportedFileType(
this, filename);
SetErrorWhenReadingFilesystemObject(
rParams, filename.c_str());
@@ -354,10 +387,9 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
#ifdef WIN32
// We didn't stat the file before,
// but now we need the information.
- if(::lstat(filename.c_str(), &st) != 0)
+ if(emu_stat(filename.c_str(), &file_st) != 0)
{
- rParams.GetProgressNotifier()
- .NotifyFileStatFailed(this,
+ rNotifier.NotifyFileStatFailed(this,
filename,
strerror(errno));
@@ -370,18 +402,17 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
continue;
}
- if(st.st_dev != dir_st.st_dev)
+ if(file_st.st_dev != link_st.st_dev)
{
- rParams.GetProgressNotifier()
- .NotifyMountPointSkipped(this,
+ rNotifier.NotifyMountPointSkipped(this,
filename);
continue;
}
#endif
- checksum_info.mModificationTime = FileModificationTime(st);
- checksum_info.mAttributeModificationTime = FileAttrModificationTime(st);
- checksum_info.mSize = st.st_size;
+ checksum_info.mModificationTime = FileModificationTime(file_st);
+ checksum_info.mAttributeModificationTime = FileAttrModificationTime(file_st);
+ checksum_info.mSize = file_st.st_size;
currentStateChecksum.Add(&checksum_info, sizeof(checksum_info));
currentStateChecksum.Add(en->d_name, strlen(en->d_name));
@@ -394,7 +425,7 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
// Log that this has happened
if(!rParams.mHaveLoggedWarningAboutFutureFileTimes)
{
- rParams.GetProgressNotifier().NotifyFileModifiedInFuture(
+ rNotifier.NotifyFileModifiedInFuture(
this, filename);
rParams.mHaveLoggedWarningAboutFutureFileTimes = true;
}
@@ -468,7 +499,8 @@ void BackupClientDirectoryRecord::SyncDirectory(BackupClientDirectoryRecord::Syn
}
// Do the directory reading
- bool updateCompleteSuccess = UpdateItems(rParams, rLocalPath, pdirOnStore, entriesLeftOver, files, dirs);
+ bool updateCompleteSuccess = UpdateItems(rParams, rLocalPath,
+ rRemotePath, pdirOnStore, entriesLeftOver, files, dirs);
// LAST THING! (think exception safety)
// Store the new checksum -- don't fetch things unnecessarily in the future
@@ -604,11 +636,18 @@ void BackupClientDirectoryRecord::UpdateAttributes(BackupClientDirectoryRecord::
// Created: 2003/10/09
//
// --------------------------------------------------------------------------
-bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncParams &rParams,
- const std::string &rLocalPath, BackupStoreDirectory *pDirOnStore,
+bool BackupClientDirectoryRecord::UpdateItems(
+ BackupClientDirectoryRecord::SyncParams &rParams,
+ const std::string &rLocalPath,
+ const std::string &rRemotePath,
+ BackupStoreDirectory *pDirOnStore,
std::vector<BackupStoreDirectory::Entry *> &rEntriesLeftOver,
- std::vector<std::string> &rFiles, const std::vector<std::string> &rDirs)
+ std::vector<std::string> &rFiles,
+ const std::vector<std::string> &rDirs)
{
+ BackupClientContext& rContext(rParams.mrContext);
+ ProgressNotifier& rNotifier(rContext.GetProgressNotifier());
+
bool allUpdatedSuccessfully = true;
// Decrypt all the directory entries.
@@ -634,7 +673,7 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
f != rFiles.end(); ++f)
{
// Send keep-alive message if needed
- rParams.mrContext.DoKeepAlive();
+ rContext.DoKeepAlive();
// Filename of this file
std::string filename(MakeFullPath(rLocalPath, *f));
@@ -648,10 +687,10 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
// BLOCK
{
// Stat the file
- struct stat st;
- if(::lstat(filename.c_str(), &st) != 0)
+ EMU_STRUCT_STAT st;
+ if(EMU_LSTAT(filename.c_str(), &st) != 0)
{
- rParams.GetProgressNotifier().NotifyFileStatFailed(this,
+ rNotifier.NotifyFileStatFailed(this,
filename, strerror(errno));
// Report the error (logs and
@@ -689,7 +728,8 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
if((en != 0) && ((en->GetFlags() & BackupStoreDirectory::Entry::Flags_File) == 0))
{
// Directory exists in the place of this file -- sort it out
- RemoveDirectoryInPlaceOfFile(rParams, pDirOnStore, en->GetObjectID(), *f);
+ RemoveDirectoryInPlaceOfFile(rParams, pDirOnStore,
+ en, *f);
en = 0;
}
@@ -701,7 +741,7 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
// 2) It's not in the store
// Do we know about the inode number?
- const BackupClientInodeToIDMap &idMap(rParams.mrContext.GetCurrentIDMap());
+ const BackupClientInodeToIDMap &idMap(rContext.GetCurrentIDMap());
int64_t renameObjectID = 0, renameInDirectory = 0;
if(idMap.Lookup(inodeNum, renameObjectID, renameInDirectory))
{
@@ -711,24 +751,24 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
bool isCurrentVersion = false;
box_time_t srvModTime = 0, srvAttributesHash = 0;
BackupStoreFilenameClear oldLeafname;
- if(rParams.mrContext.FindFilename(renameObjectID, renameInDirectory, localPotentialOldName, isDir, isCurrentVersion, &srvModTime, &srvAttributesHash, &oldLeafname))
+ if(rContext.FindFilename(renameObjectID, renameInDirectory, localPotentialOldName, isDir, isCurrentVersion, &srvModTime, &srvAttributesHash, &oldLeafname))
{
// Only interested if it's a file and the latest version
if(!isDir && isCurrentVersion)
{
// Check that the object we found in the ID map doesn't exist on disc
- struct stat st;
- if(::stat(localPotentialOldName.c_str(), &st) != 0 && errno == ENOENT)
+ EMU_STRUCT_STAT st;
+ if(EMU_STAT(localPotentialOldName.c_str(), &st) != 0 && errno == ENOENT)
{
// Doesn't exist locally, but does exist on the server.
// Therefore we can safely rename it to this new file.
// Get the connection to the server
- BackupProtocolClient &connection(rParams.mrContext.GetConnection());
+ BackupProtocolClient &connection(rContext.GetConnection());
// Only do this step if there is room on the server.
// This step will be repeated later when there is space available
- if(!rParams.mrContext.StorageLimitExceeded())
+ if(!rContext.StorageLimitExceeded())
{
// Rename the existing files (ie include old versions) on the server
connection.QueryMoveObject(renameObjectID, renameInDirectory, mObjectID /* move to this directory */,
@@ -736,7 +776,7 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
storeFilename);
// Stop the attempt to delete the file in the original location
- BackupClientDeleteList &rdelList(rParams.mrContext.GetDeleteList());
+ BackupClientDeleteList &rdelList(rContext.GetDeleteList());
rdelList.StopFileDeletion(renameInDirectory, oldLeafname);
// Create new entry in the directory for it
@@ -799,13 +839,15 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
if (pDirOnStore != 0 && en == 0)
{
doUpload = true;
- BOX_TRACE(filename << ": will upload "
+ BOX_TRACE("Upload decision: " <<
+ filename << ": will upload "
"(not on server)");
}
else if (modTime >= rParams.mSyncPeriodStart)
{
doUpload = true;
- BOX_TRACE(filename << ": will upload "
+ BOX_TRACE("Upload decision: " <<
+ filename << ": will upload "
"(modified since last sync)");
}
}
@@ -823,7 +865,8 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
> rParams.mMaxUploadWait)
{
doUpload = true;
- BOX_TRACE(filename << ": will upload "
+ BOX_TRACE("Upload decision: " <<
+ filename << ": will upload "
"(continually modified)");
}
@@ -840,7 +883,8 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
en->GetModificationTime() != modTime)
{
doUpload = true;
- BOX_TRACE(filename << ": will upload "
+ BOX_TRACE("Upload decision: " <<
+ filename << ": will upload "
"(mod time changed)");
}
@@ -852,64 +896,121 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
rParams.mUploadAfterThisTimeInTheFuture)
{
doUpload = true;
- BOX_TRACE(filename << ": will upload "
+ BOX_TRACE("Upload decision: " <<
+ filename << ": will upload "
"(mod time in the future)");
}
}
-
- if (!doUpload)
+
+ if (en != 0 && en->GetModificationTime() == modTime)
{
- BOX_TRACE(filename << ": will not upload "
- "(no reason to upload, mod time is "
- << modTime << " versus sync period "
- << rParams.mSyncPeriodStart << " to "
- << rParams.mSyncPeriodEnd << ")");
+ BOX_TRACE("Upload decision: " <<
+ filename << ": will not upload "
+ "(not modified since last upload)");
+ }
+ else if (!doUpload)
+ {
+ if (modTime > rParams.mSyncPeriodEnd)
+ {
+ box_time_t now = GetCurrentBoxTime();
+ int age = BoxTimeToSeconds(now -
+ modTime);
+ BOX_TRACE("Upload decision: " <<
+ filename << ": will not upload "
+ "(modified too recently: "
+ "only " << age << "seconds ago)");
+ }
+ else
+ {
+ BOX_TRACE("Upload decision: " <<
+ filename << ": will not upload "
+ "(mod time is " << modTime <<
+ " which is outside sync window, "
+ << rParams.mSyncPeriodStart << " to "
+ << rParams.mSyncPeriodEnd << ")");
+ }
}
+ bool fileSynced = true;
+
if (doUpload)
{
+ // Upload needed, don't mark sync success until
+ // we've actually done it
+ fileSynced = false;
+
// Make sure we're connected -- must connect here so we know whether
// the storage limit has been exceeded, and hence whether or not
// to actually upload the file.
- rParams.mrContext.GetConnection();
+ rContext.GetConnection();
// Only do this step if there is room on the server.
// This step will be repeated later when there is space available
- if(!rParams.mrContext.StorageLimitExceeded())
+ if(!rContext.StorageLimitExceeded())
{
- // Upload the file to the server, recording the object ID it returns
- bool noPreviousVersionOnServer = ((pDirOnStore != 0) && (en == 0));
+ // Upload the file to the server, recording the
+ // object ID it returns
+ bool noPreviousVersionOnServer =
+ ((pDirOnStore != 0) && (en == 0));
- // Surround this in a try/catch block, to catch errrors, but still continue
+ // Surround this in a try/catch block, to
+ // catch errors, but still continue
bool uploadSuccess = false;
try
{
- latestObjectID = UploadFile(rParams, filename, storeFilename, fileSize, modTime, attributesHash, noPreviousVersionOnServer);
- uploadSuccess = true;
+ latestObjectID = UploadFile(rParams,
+ filename, storeFilename,
+ fileSize, modTime,
+ attributesHash,
+ noPreviousVersionOnServer);
+
+ if (latestObjectID == 0)
+ {
+ // storage limit exceeded
+ rParams.mrContext.SetStorageLimitExceeded();
+ uploadSuccess = false;
+ allUpdatedSuccessfully = false;
+ }
+ else
+ {
+ uploadSuccess = true;
+ }
}
catch(ConnectionException &e)
{
- // Connection errors should just be passed on to the main handler, retries
- // would probably just cause more problems.
- rParams.GetProgressNotifier()
- .NotifyFileUploadException(
- this, filename, e);
+ // Connection errors should just be
+ // passed on to the main handler,
+ // retries would probably just cause
+ // more problems.
+ rNotifier.NotifyFileUploadException(
+ this, filename, e);
throw;
}
catch(BoxException &e)
{
- // an error occured -- make return code false, to show error in directory
+ if (e.GetType() == BackupStoreException::ExceptionType &&
+ e.GetSubType() == BackupStoreException::SignalReceived)
+ {
+ // abort requested, pass the
+ // exception on up.
+ throw;
+ }
+
+ // an error occured -- make return
+ // code false, to show error in directory
allUpdatedSuccessfully = false;
// Log it.
SetErrorWhenReadingFilesystemObject(rParams, filename.c_str());
- rParams.GetProgressNotifier()
- .NotifyFileUploadException(
- this, filename, e);
+ rNotifier.NotifyFileUploadException(
+ this, filename, e);
}
- // Update structures if the file was uploaded successfully.
+ // Update structures if the file was uploaded
+ // successfully.
if(uploadSuccess)
{
+ fileSynced = true;
+
// delete from pending entries
if(pendingFirstSeenTime != 0 && mpPendingEntries != 0)
{
@@ -919,28 +1020,41 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
}
else
{
- rParams.GetProgressNotifier().NotifyFileSkippedServerFull(this,
+ rNotifier.NotifyFileSkippedServerFull(this,
filename);
}
}
else if(en != 0 && en->GetAttributesHash() != attributesHash)
{
// Attributes have probably changed, upload them again.
- // If the attributes have changed enough, the directory hash will have changed too,
- // and so the dir will have been downloaded, and the entry will be available.
+ // If the attributes have changed enough, the directory
+ // hash will have changed too, and so the dir will have
+ // been downloaded, and the entry will be available.
// Get connection
- BackupProtocolClient &connection(rParams.mrContext.GetConnection());
+ BackupProtocolClient &connection(rContext.GetConnection());
// Only do this step if there is room on the server.
- // This step will be repeated later when there is space available
- if(!rParams.mrContext.StorageLimitExceeded())
+ // This step will be repeated later when there is
+ // space available
+ if(!rContext.StorageLimitExceeded())
{
- // Update store
- BackupClientFileAttributes attr;
- attr.ReadAttributes(filename.c_str(), false /* put mod times in the attributes, please */);
- MemBlockStream attrStream(attr);
- connection.QuerySetReplacementFileAttributes(mObjectID, attributesHash, storeFilename, attrStream);
+ try
+ {
+ // Update store
+ BackupClientFileAttributes attr;
+ attr.ReadAttributes(filename.c_str(), false /* put mod times in the attributes, please */);
+ MemBlockStream attrStream(attr);
+ connection.QuerySetReplacementFileAttributes(mObjectID, attributesHash, storeFilename, attrStream);
+ fileSynced = true;
+ }
+ catch (BoxException &e)
+ {
+ BOX_ERROR("Failed to read or store "
+ "file attributes for '" <<
+ filename << "', will try "
+ "again later");
+ }
}
}
@@ -976,36 +1090,56 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
if(fileSize >= rParams.mFileTrackingSizeThreshold)
{
// Get the map
- BackupClientInodeToIDMap &idMap(rParams.mrContext.GetNewIDMap());
+ BackupClientInodeToIDMap &idMap(rContext.GetNewIDMap());
// Need to get an ID from somewhere...
if(latestObjectID != 0)
{
// Use this one
+ BOX_TRACE("Storing uploaded file ID " <<
+ inodeNum << " (" << filename << ") "
+ "in ID map as object " <<
+ latestObjectID << " with parent " <<
+ mObjectID);
idMap.AddToMap(inodeNum, latestObjectID, mObjectID /* containing directory */);
}
else
{
// Don't know it -- haven't sent anything to the store, and didn't get a listing.
// Look it up in the current map, and if it's there, use that.
- const BackupClientInodeToIDMap &currentIDMap(rParams.mrContext.GetCurrentIDMap());
+ const BackupClientInodeToIDMap &currentIDMap(rContext.GetCurrentIDMap());
int64_t objid = 0, dirid = 0;
if(currentIDMap.Lookup(inodeNum, objid, dirid))
{
// Found
+ if (dirid != mObjectID)
+ {
+ BOX_WARNING("Found conflicting parent ID for file ID " << inodeNum << " (" << filename << "): expected " << mObjectID << " but found " << dirid << " (same directory used in two different locations?)");
+ }
+
ASSERT(dirid == mObjectID);
+
// NOTE: If the above assert fails, an inode number has been reused by the OS,
// or there is a problem somewhere. If this happened on a short test run, look
// into it. However, in a long running process this may happen occasionally and
- // not indiciate anything wrong.
+ // not indicate anything wrong.
// Run the release version for real life use, where this check is not made.
- idMap.AddToMap(inodeNum, objid, mObjectID /* containing directory */);
+ BOX_TRACE("Storing found file ID " <<
+ inodeNum << " (" << filename <<
+ ") in ID map as object " <<
+ objid << " with parent " <<
+ mObjectID);
+ idMap.AddToMap(inodeNum, objid,
+ mObjectID /* containing directory */);
}
}
}
- rParams.GetProgressNotifier().NotifyFileSynchronised(this,
- filename, fileSize);
+ if (fileSynced)
+ {
+ rNotifier.NotifyFileSynchronised(this, filename,
+ fileSize);
+ }
}
// Erase contents of files to save space when recursing
@@ -1014,7 +1148,8 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
// Delete the pending entries, if the map is entry
if(mpPendingEntries != 0 && mpPendingEntries->size() == 0)
{
- TRACE1("Deleting mpPendingEntries from dir ID %lld\n", mObjectID);
+ BOX_TRACE("Deleting mpPendingEntries from dir ID " <<
+ BOX_FORMAT_OBJECTID(mObjectID));
delete mpPendingEntries;
mpPendingEntries = 0;
}
@@ -1024,7 +1159,7 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
d != rDirs.end(); ++d)
{
// Send keep-alive message if needed
- rParams.mrContext.DoKeepAlive();
+ rContext.DoKeepAlive();
// Get the local filename
std::string dirname(MakeFullPath(rLocalPath, *d));
@@ -1044,21 +1179,27 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
// Check that the entry which might have been found is in fact a directory
if((en != 0) && ((en->GetFlags() & BackupStoreDirectory::Entry::Flags_Dir) == 0))
{
- // Entry exists, but is not a directory. Bad. Get rid of it.
- BackupProtocolClient &connection(rParams.mrContext.GetConnection());
+ // Entry exists, but is not a directory. Bad.
+ // Get rid of it.
+ BackupProtocolClient &connection(rContext.GetConnection());
connection.QueryDeleteFile(mObjectID /* in directory */, storeFilename);
+ rNotifier.NotifyFileDeleted(en->GetObjectID(),
+ storeFilename.GetClearFilename());
// Nothing found
en = 0;
}
- // Flag for having created directory, so can optimise the recusive call not to
- // read it again, because we know it's empty.
+ // Flag for having created directory, so can optimise the
+ // recursive call not to read it again, because we know
+ // it's empty.
bool haveJustCreatedDirOnServer = false;
// Next, see if it's in the list of sub directories
BackupClientDirectoryRecord *psubDirRecord = 0;
- std::map<std::string, BackupClientDirectoryRecord *>::iterator e(mSubDirectories.find(*d));
+ std::map<std::string, BackupClientDirectoryRecord *>::iterator
+ e(mSubDirectories.find(*d));
+
if(e != mSubDirectories.end())
{
// In the list, just use this pointer
@@ -1080,7 +1221,7 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
// No. Exists on the server, and we know about it from the listing.
subDirObjectID = en->GetObjectID();
}
- else if(rParams.mrContext.StorageLimitExceeded())
+ else if(rContext.StorageLimitExceeded())
// know we've got a connection if we get this far,
// as dir will have been modified.
{
@@ -1098,29 +1239,47 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
box_time_t attrModTime = 0;
InodeRefType inodeNum = 0;
BackupClientFileAttributes attr;
- attr.ReadAttributes(dirname.c_str(), true /* directories have zero mod times */,
- 0 /* not interested in mod time */, &attrModTime, 0 /* not file size */,
- &inodeNum);
+ bool failedToReadAttributes = false;
+
+ try
+ {
+ attr.ReadAttributes(dirname.c_str(),
+ true /* directories have zero mod times */,
+ 0 /* not interested in mod time */,
+ &attrModTime, 0 /* not file size */,
+ &inodeNum);
+ }
+ catch (BoxException &e)
+ {
+ BOX_WARNING("Failed to read attributes "
+ "of directory, cannot check "
+ "for rename, assuming new: '"
+ << dirname << "'");
+ failedToReadAttributes = true;
+ }
// Check to see if the directory been renamed
// First, do we have a record in the ID map?
int64_t renameObjectID = 0, renameInDirectory = 0;
bool renameDir = false;
- const BackupClientInodeToIDMap &idMap(rParams.mrContext.GetCurrentIDMap());
- if(idMap.Lookup(inodeNum, renameObjectID, renameInDirectory))
+ const BackupClientInodeToIDMap &idMap(
+ rContext.GetCurrentIDMap());
+
+ if(!failedToReadAttributes && idMap.Lookup(inodeNum,
+ renameObjectID, renameInDirectory))
{
// Look up on the server to get the name, to build the local filename
std::string localPotentialOldName;
bool isDir = false;
bool isCurrentVersion = false;
- if(rParams.mrContext.FindFilename(renameObjectID, renameInDirectory, localPotentialOldName, isDir, isCurrentVersion))
+ if(rContext.FindFilename(renameObjectID, renameInDirectory, localPotentialOldName, isDir, isCurrentVersion))
{
// Only interested if it's a directory
if(isDir && isCurrentVersion)
{
// Check that the object doesn't exist already
- struct stat st;
- if(::stat(localPotentialOldName.c_str(), &st) != 0 && errno == ENOENT)
+ EMU_STRUCT_STAT st;
+ if(EMU_STAT(localPotentialOldName.c_str(), &st) != 0 && errno == ENOENT)
{
// Doesn't exist locally, but does exist on the server.
// Therefore we can safely rename it.
@@ -1131,7 +1290,7 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
}
// Get connection
- BackupProtocolClient &connection(rParams.mrContext.GetConnection());
+ BackupProtocolClient &connection(rContext.GetConnection());
// Don't do a check for storage limit exceeded here, because if we get to this
// stage, a connection will have been opened, and the status known, so the check
@@ -1151,7 +1310,8 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
connection.QueryChangeDirAttributes(renameObjectID, attrModTime, attrStream);
// Stop it being deleted later
- BackupClientDeleteList &rdelList(rParams.mrContext.GetDeleteList());
+ BackupClientDeleteList &rdelList(
+ rContext.GetDeleteList());
rdelList.StopDirectoryDeletion(renameObjectID);
// This is the ID for the renamed directory
@@ -1188,12 +1348,14 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
}
}
- ASSERT(psubDirRecord != 0 || rParams.mrContext.StorageLimitExceeded());
+ ASSERT(psubDirRecord != 0 || rContext.StorageLimitExceeded());
if(psubDirRecord)
{
// Sync this sub directory too
- psubDirRecord->SyncDirectory(rParams, mObjectID, dirname, haveJustCreatedDirOnServer);
+ psubDirRecord->SyncDirectory(rParams, mObjectID,
+ dirname, rRemotePath + "/" + *d,
+ haveJustCreatedDirOnServer);
}
// Zero pointer in rEntriesLeftOver, if we have a pointer to zero
@@ -1222,20 +1384,27 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
// to a list, which is actually deleted at the very end of the session.
// If there's an error during the process, it doesn't matter if things
// aren't actually deleted, as the whole state will be reset anyway.
- BackupClientDeleteList &rdel(rParams.mrContext.GetDeleteList());
+ BackupClientDeleteList &rdel(rContext.GetDeleteList());
+
+ BackupStoreFilenameClear clear(en->GetName());
+ std::string localName = MakeFullPath(rLocalPath,
+ clear.GetClearFilename());
// Delete this entry -- file or directory?
if((en->GetFlags() & BackupStoreDirectory::Entry::Flags_File) != 0)
{
// Set a pending deletion for the file
- rdel.AddFileDelete(mObjectID, en->GetName());
+ rdel.AddFileDelete(mObjectID, en->GetName(),
+ localName);
}
else if((en->GetFlags() & BackupStoreDirectory::Entry::Flags_Dir) != 0)
{
// Set as a pending deletion for the directory
- rdel.AddDirectoryDelete(en->GetObjectID());
+ rdel.AddDirectoryDelete(en->GetObjectID(),
+ localName);
- // If there's a directory record for it in the sub directory map, delete it now
+ // If there's a directory record for it in
+ // the sub directory map, delete it now
BackupStoreFilenameClear dirname(en->GetName());
std::map<std::string, BackupClientDirectoryRecord *>::iterator e(mSubDirectories.find(dirname.GetClearFilename()));
if(e != mSubDirectories.end())
@@ -1249,8 +1418,8 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
rLocalPath,
dirname.GetClearFilename());
- TRACE1("Deleted directory record for "
- "%s\n", name.c_str());
+ BOX_TRACE("Deleted directory record "
+ "for " << name);
}
}
}
@@ -1270,14 +1439,24 @@ bool BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncP
// Created: 9/7/04
//
// --------------------------------------------------------------------------
-void BackupClientDirectoryRecord::RemoveDirectoryInPlaceOfFile(SyncParams &rParams, BackupStoreDirectory *pDirOnStore, int64_t ObjectID, const std::string &rFilename)
+void BackupClientDirectoryRecord::RemoveDirectoryInPlaceOfFile(
+ SyncParams &rParams,
+ BackupStoreDirectory* pDirOnStore,
+ BackupStoreDirectory::Entry* pEntry,
+ const std::string &rFilename)
{
// First, delete the directory
BackupProtocolClient &connection(rParams.mrContext.GetConnection());
- connection.QueryDeleteDirectory(ObjectID);
+ connection.QueryDeleteDirectory(pEntry->GetObjectID());
+
+ BackupStoreFilenameClear clear(pEntry->GetName());
+ rParams.mrContext.GetProgressNotifier().NotifyDirectoryDeleted(
+ pEntry->GetObjectID(), clear.GetClearFilename());
// Then, delete any directory record
- std::map<std::string, BackupClientDirectoryRecord *>::iterator e(mSubDirectories.find(rFilename));
+ std::map<std::string, BackupClientDirectoryRecord *>::iterator
+ e(mSubDirectories.find(rFilename));
+
if(e != mSubDirectories.end())
{
// A record exists for this, remove it
@@ -1294,16 +1473,30 @@ void BackupClientDirectoryRecord::RemoveDirectoryInPlaceOfFile(SyncParams &rPara
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::SyncParams &, const std::string &, const BackupStoreFilename &, int64_t, box_time_t, box_time_t, bool)
-// Purpose: Private. Upload a file to the server -- may send a patch instead of the whole thing
+// Name: BackupClientDirectoryRecord::UploadFile(
+// BackupClientDirectoryRecord::SyncParams &,
+// const std::string &,
+// const BackupStoreFilename &,
+// int64_t, box_time_t, box_time_t, bool)
+// Purpose: Private. Upload a file to the server. May send
+// a patch instead of the whole thing
// Created: 20/1/04
//
// --------------------------------------------------------------------------
-int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::SyncParams &rParams, const std::string &rFilename, const BackupStoreFilename &rStoreFilename,
- int64_t FileSize, box_time_t ModificationTime, box_time_t AttributesHash, bool NoPreviousVersionOnServer)
+int64_t BackupClientDirectoryRecord::UploadFile(
+ BackupClientDirectoryRecord::SyncParams &rParams,
+ const std::string &rFilename,
+ const BackupStoreFilename &rStoreFilename,
+ int64_t FileSize,
+ box_time_t ModificationTime,
+ box_time_t AttributesHash,
+ bool NoPreviousVersionOnServer)
{
+ BackupClientContext& rContext(rParams.mrContext);
+ ProgressNotifier& rNotifier(rContext.GetProgressNotifier());
+
// Get the connection
- BackupProtocolClient &connection(rParams.mrContext.GetConnection());
+ BackupProtocolClient &connection(rContext.GetConnection());
// Info
int64_t objID = 0;
@@ -1312,8 +1505,10 @@ int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::Syn
// Use a try block to catch store full errors
try
{
- // Might an old version be on the server, and is the file size over the diffing threshold?
- if(!NoPreviousVersionOnServer && FileSize >= rParams.mDiffingUploadSizeThreshold)
+ // Might an old version be on the server, and is the file
+ // size over the diffing threshold?
+ if(!NoPreviousVersionOnServer &&
+ FileSize >= rParams.mDiffingUploadSizeThreshold)
{
// YES -- try to do diff, if possible
// First, query the server to see if there's an old version available
@@ -1323,7 +1518,7 @@ int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::Syn
if(diffFromID != 0)
{
// Found an old version
- rParams.GetProgressNotifier().NotifyFileUploadingPatch(this,
+ rNotifier.NotifyFileUploadingPatch(this,
rFilename);
// Get the index
@@ -1333,7 +1528,7 @@ int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::Syn
// Diff the file
//
- rParams.mrContext.ManageDiffProcess();
+ rContext.ManageDiffProcess();
bool isCompletelyDifferent = false;
std::auto_ptr<IOStream> patchStream(
@@ -1342,11 +1537,11 @@ int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::Syn
mObjectID, /* containing directory */
rStoreFilename, diffFromID, *blockIndexStream,
connection.GetTimeout(),
- &rParams.mrContext, // DiffTimer implementation
+ &rContext, // DiffTimer implementation
0 /* not interested in the modification time */,
&isCompletelyDifferent));
- rParams.mrContext.UnManageDiffProcess();
+ rContext.UnManageDiffProcess();
//
// Upload the patch to the store
@@ -1354,6 +1549,9 @@ int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::Syn
std::auto_ptr<BackupProtocolClientSuccess> stored(connection.QueryStoreFile(mObjectID, ModificationTime,
AttributesHash, isCompletelyDifferent?(0):(diffFromID), rStoreFilename, *patchStream));
+ // Get object ID from the result
+ objID = stored->GetObjectID();
+
// Don't attempt to upload it again!
doNormalUpload = false;
}
@@ -1362,13 +1560,14 @@ int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::Syn
if(doNormalUpload)
{
// below threshold or nothing to diff from, so upload whole
- rParams.GetProgressNotifier().NotifyFileUploading(this,
- rFilename);
+ rNotifier.NotifyFileUploading(this, rFilename);
// Prepare to upload, getting a stream which will encode the file as we go along
std::auto_ptr<IOStream> upload(
BackupStoreFile::EncodeFile(rFilename.c_str(),
- mObjectID, rStoreFilename));
+ mObjectID, rStoreFilename, NULL,
+ &rParams,
+ &(rParams.mrRunStatusProvider)));
// Send to store
std::auto_ptr<BackupProtocolClientSuccess> stored(
@@ -1384,9 +1583,10 @@ int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::Syn
}
catch(BoxException &e)
{
- rParams.mrContext.UnManageDiffProcess();
+ rContext.UnManageDiffProcess();
- if(e.GetType() == ConnectionException::ExceptionType && e.GetSubType() == ConnectionException::Protocol_UnexpectedReply)
+ if(e.GetType() == ConnectionException::ExceptionType &&
+ e.GetSubType() == ConnectionException::Protocol_UnexpectedReply)
{
// Check and see what error the protocol has,
// this is more useful to users than the exception.
@@ -1397,11 +1597,15 @@ int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::Syn
&& subtype == BackupProtocolClientError::Err_StorageLimitExceeded)
{
// The hard limit was exceeded on the server, notify!
- rParams.mrDaemon.NotifySysadmin(BackupDaemon::NotifyEvent_StoreFull);
+ rParams.mrSysadminNotifier.NotifySysadmin(
+ SysadminNotifier::StoreFull);
+ // return an error code instead of
+ // throwing an exception that we
+ // can't debug.
+ return 0;
}
- rParams.GetProgressNotifier()
- .NotifyFileUploadServerError(
- this, rFilename, type, subtype);
+ rNotifier.NotifyFileUploadServerError(this,
+ rFilename, type, subtype);
}
}
@@ -1409,7 +1613,7 @@ int64_t BackupClientDirectoryRecord::UploadFile(BackupClientDirectoryRecord::Syn
throw;
}
- rParams.GetProgressNotifier().NotifyFileUploaded(this, rFilename, FileSize);
+ rNotifier.NotifyFileUploaded(this, rFilename, FileSize);
// Return the new object ID of this file
return objID;
@@ -1450,16 +1654,20 @@ void BackupClientDirectoryRecord::SetErrorWhenReadingFilesystemObject(BackupClie
// Created: 8/3/04
//
// --------------------------------------------------------------------------
-BackupClientDirectoryRecord::SyncParams::SyncParams(BackupDaemon &rDaemon,
- ProgressNotifier &rProgressNotifier, BackupClientContext &rContext)
- : mrProgressNotifier(rProgressNotifier),
- mSyncPeriodStart(0),
+BackupClientDirectoryRecord::SyncParams::SyncParams(
+ RunStatusProvider &rRunStatusProvider,
+ SysadminNotifier &rSysadminNotifier,
+ ProgressNotifier &rProgressNotifier,
+ BackupClientContext &rContext)
+ : mSyncPeriodStart(0),
mSyncPeriodEnd(0),
mMaxUploadWait(0),
mMaxFileTimeInFuture(99999999999999999LL),
mFileTrackingSizeThreshold(16*1024),
mDiffingUploadSizeThreshold(16*1024),
- mrDaemon(rDaemon),
+ mrRunStatusProvider(rRunStatusProvider),
+ mrSysadminNotifier(rSysadminNotifier),
+ mrProgressNotifier(rProgressNotifier),
mrContext(rContext),
mReadErrorsOnFilesystemObjects(false),
mUploadAfterThisTimeInTheFuture(99999999999999999LL),
diff --git a/bin/bbackupd/BackupClientDirectoryRecord.h b/bin/bbackupd/BackupClientDirectoryRecord.h
index 9e4dda7a..fce3fc04 100644
--- a/bin/bbackupd/BackupClientDirectoryRecord.h
+++ b/bin/bbackupd/BackupClientDirectoryRecord.h
@@ -13,10 +13,13 @@
#include <string>
#include <map>
-#include "BoxTime.h"
#include "BackupClientFileAttributes.h"
+#include "BackupDaemonInterface.h"
#include "BackupStoreDirectory.h"
+#include "BoxTime.h"
#include "MD5Digest.h"
+#include "ReadLoggingStream.h"
+#include "RunStatusProvider.h"
class Archive;
class BackupClientContext;
@@ -25,82 +28,6 @@ class BackupDaemon;
// --------------------------------------------------------------------------
//
// Class
-// Name: ProgressNotifier
-// Purpose: Provides methods for the backup library to inform the user
-// interface about its progress with the backup
-// Created: 2005/11/20
-//
-// --------------------------------------------------------------------------
-class BackupClientDirectoryRecord;
-
-class ProgressNotifier
-{
- public:
- virtual ~ProgressNotifier() { }
- virtual void NotifyScanDirectory(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath) = 0;
- virtual void NotifyDirStatFailed(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath,
- const std::string& rErrorMsg) = 0;
- virtual void NotifyFileStatFailed(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath,
- const std::string& rErrorMsg) = 0;
- virtual void NotifyDirListFailed(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath,
- const std::string& rErrorMsg) = 0;
- virtual void NotifyMountPointSkipped(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath) = 0;
- virtual void NotifyFileExcluded(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath) = 0;
- virtual void NotifyDirExcluded(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath) = 0;
- virtual void NotifyUnsupportedFileType(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath) = 0;
- virtual void NotifyFileReadFailed(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath,
- const std::string& rErrorMsg) = 0;
- virtual void NotifyFileModifiedInFuture(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath) = 0;
- virtual void NotifyFileSkippedServerFull(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath) = 0;
- virtual void NotifyFileUploadException(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath,
- const BoxException& rException) = 0;
- virtual void NotifyFileUploadServerError(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath,
- int type, int subtype) = 0;
- virtual void NotifyFileUploading(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath) = 0;
- virtual void NotifyFileUploadingPatch(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath) = 0;
- virtual void NotifyFileUploaded(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath,
- int64_t FileSize) = 0;
- virtual void NotifyFileSynchronised(
- const BackupClientDirectoryRecord* pDirRecord,
- const std::string& rLocalPath,
- int64_t FileSize) = 0;
-};
-
-// --------------------------------------------------------------------------
-//
-// Class
// Name: BackupClientDirectoryRecord
// Purpose: Implementation of record about directory for backup client
// Created: 2003/10/08
@@ -132,11 +59,12 @@ public:
// Created: 8/3/04
//
// --------------------------------------------------------------------------
- class SyncParams
+ class SyncParams : public ReadLoggingStream::Logger
{
public:
SyncParams(
- BackupDaemon &rDaemon,
+ RunStatusProvider &rRunStatusProvider,
+ SysadminNotifier &rSysadminNotifier,
ProgressNotifier &rProgressNotifier,
BackupClientContext &rContext);
~SyncParams();
@@ -144,7 +72,6 @@ public:
// No copying
SyncParams(const SyncParams&);
SyncParams &operator=(const SyncParams&);
- ProgressNotifier &mrProgressNotifier;
public:
// Data members are public, as accessors are not justified here
@@ -154,7 +81,9 @@ public:
box_time_t mMaxFileTimeInFuture;
int32_t mFileTrackingSizeThreshold;
int32_t mDiffingUploadSizeThreshold;
- BackupDaemon &mrDaemon;
+ RunStatusProvider &mrRunStatusProvider;
+ SysadminNotifier &mrSysadminNotifier;
+ ProgressNotifier &mrProgressNotifier;
BackupClientContext &mrContext;
bool mReadErrorsOnFilesystemObjects;
@@ -162,41 +91,79 @@ public:
box_time_t mUploadAfterThisTimeInTheFuture;
bool mHaveLoggedWarningAboutFutureFileTimes;
+ bool StopRun() { return mrRunStatusProvider.StopRun(); }
+ void NotifySysadmin(SysadminNotifier::EventCode Event)
+ {
+ mrSysadminNotifier.NotifySysadmin(Event);
+ }
ProgressNotifier& GetProgressNotifier() const
{
return mrProgressNotifier;
}
+
+ /* ReadLoggingStream::Logger implementation */
+ virtual void Log(int64_t readSize, int64_t offset,
+ int64_t length, box_time_t elapsed, box_time_t finish)
+ {
+ mrProgressNotifier.NotifyReadProgress(readSize, offset,
+ length, elapsed, finish);
+ }
+ virtual void Log(int64_t readSize, int64_t offset,
+ int64_t length)
+ {
+ mrProgressNotifier.NotifyReadProgress(readSize, offset,
+ length);
+ }
+ virtual void Log(int64_t readSize, int64_t offset)
+ {
+ mrProgressNotifier.NotifyReadProgress(readSize, offset);
+ }
};
- void SyncDirectory(SyncParams &rParams, int64_t ContainingDirectoryID, const std::string &rLocalPath,
+ void SyncDirectory(SyncParams &rParams,
+ int64_t ContainingDirectoryID,
+ const std::string &rLocalPath,
+ const std::string &rRemotePath,
bool ThisDirHasJustBeenCreated = false);
private:
void DeleteSubDirectories();
BackupStoreDirectory *FetchDirectoryListing(SyncParams &rParams);
- void UpdateAttributes(SyncParams &rParams, BackupStoreDirectory *pDirOnStore, const std::string &rLocalPath);
- bool UpdateItems(SyncParams &rParams, const std::string &rLocalPath, BackupStoreDirectory *pDirOnStore,
+ void UpdateAttributes(SyncParams &rParams,
+ BackupStoreDirectory *pDirOnStore,
+ const std::string &rLocalPath);
+ bool UpdateItems(SyncParams &rParams, const std::string &rLocalPath,
+ const std::string &rRemotePath,
+ BackupStoreDirectory *pDirOnStore,
std::vector<BackupStoreDirectory::Entry *> &rEntriesLeftOver,
- std::vector<std::string> &rFiles, const std::vector<std::string> &rDirs);
- int64_t UploadFile(SyncParams &rParams, const std::string &rFilename, const BackupStoreFilename &rStoreFilename,
- int64_t FileSize, box_time_t ModificationTime, box_time_t AttributesHash, bool NoPreviousVersionOnServer);
- void SetErrorWhenReadingFilesystemObject(SyncParams &rParams, const char *Filename);
- void RemoveDirectoryInPlaceOfFile(SyncParams &rParams, BackupStoreDirectory *pDirOnStore, int64_t ObjectID, const std::string &rFilename);
+ std::vector<std::string> &rFiles,
+ const std::vector<std::string> &rDirs);
+ int64_t UploadFile(SyncParams &rParams,
+ const std::string &rFilename,
+ const BackupStoreFilename &rStoreFilename,
+ int64_t FileSize, box_time_t ModificationTime,
+ box_time_t AttributesHash, bool NoPreviousVersionOnServer);
+ void SetErrorWhenReadingFilesystemObject(SyncParams &rParams,
+ const char *Filename);
+ void RemoveDirectoryInPlaceOfFile(SyncParams &rParams,
+ BackupStoreDirectory* pDirOnStore,
+ BackupStoreDirectory::Entry* pEntry,
+ const std::string &rFilename);
private:
- int64_t mObjectID;
+ int64_t mObjectID;
std::string mSubDirName;
- bool mInitialSyncDone;
- bool mSyncDone;
+ bool mInitialSyncDone;
+ bool mSyncDone;
// Checksum of directory contents and attributes, used to detect changes
uint8_t mStateChecksum[MD5Digest::DigestLength];
- std::map<std::string, box_time_t> *mpPendingEntries;
- std::map<std::string, BackupClientDirectoryRecord *> mSubDirectories;
+ std::map<std::string, box_time_t> *mpPendingEntries;
+ std::map<std::string, BackupClientDirectoryRecord *> mSubDirectories;
// mpPendingEntries is a pointer rather than simple a member
- // variables, because most of the time it'll be empty. This would waste a lot
- // of memory because of STL allocation policies.
+ // variable, because most of the time it'll be empty. This would
+ // waste a lot of memory because of STL allocation policies.
};
#endif // BACKUPCLIENTDIRECTORYRECORD__H
diff --git a/bin/bbackupd/BackupClientInodeToIDMap.cpp b/bin/bbackupd/BackupClientInodeToIDMap.cpp
index 0d4fd507..b9f56c5a 100644
--- a/bin/bbackupd/BackupClientInodeToIDMap.cpp
+++ b/bin/bbackupd/BackupClientInodeToIDMap.cpp
@@ -217,13 +217,16 @@ void BackupClientInodeToIDMap::AddToMap(InodeRefType InodeRef, int64_t ObjectID,
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupClientInodeToIDMap::Lookup(InodeRefType, int64_t &, int64_t &) const
-// Purpose: Looks up an inode in the map, returning true if it exists, and the object
-// ids of it and the directory it's in the reference arguments.
+// Name: BackupClientInodeToIDMap::Lookup(InodeRefType,
+// int64_t &, int64_t &) const
+// Purpose: Looks up an inode in the map, returning true if it
+// exists, and the object ids of it and the directory
+// it's in the reference arguments.
// Created: 11/11/03
//
// --------------------------------------------------------------------------
-bool BackupClientInodeToIDMap::Lookup(InodeRefType InodeRef, int64_t &rObjectIDOut, int64_t &rInDirectoryOut) const
+bool BackupClientInodeToIDMap::Lookup(InodeRefType InodeRef,
+ int64_t &rObjectIDOut, int64_t &rInDirectoryOut) const
{
#ifdef BACKIPCLIENTINODETOIDMAP_IN_MEMORY_IMPLEMENTATION
std::map<InodeRefType, std::pair<int64_t, int64_t> >::const_iterator i(mMap.find(InodeRef));
diff --git a/bin/bbackupd/BackupDaemon.cpp b/bin/bbackupd/BackupDaemon.cpp
index e762bbdc..3615b848 100644
--- a/bin/bbackupd/BackupDaemon.cpp
+++ b/bin/bbackupd/BackupDaemon.cpp
@@ -10,6 +10,7 @@
#include "Box.h"
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
@@ -47,36 +48,34 @@
#include "BoxPortsAndFiles.h"
#include "SSLLib.h"
-#include "TLSContext.h"
-#include "BackupDaemon.h"
-#include "BackupDaemonConfigVerify.h"
+#include "autogen_BackupProtocolClient.h"
+#include "autogen_ClientException.h"
+#include "autogen_ConversionException.h"
+#include "Archive.h"
#include "BackupClientContext.h"
+#include "BackupClientCryptoKeys.h"
#include "BackupClientDirectoryRecord.h"
-#include "BackupStoreDirectory.h"
#include "BackupClientFileAttributes.h"
-#include "BackupStoreFilenameClear.h"
#include "BackupClientInodeToIDMap.h"
-#include "autogen_BackupProtocolClient.h"
-#include "autogen_ConversionException.h"
-#include "BackupClientCryptoKeys.h"
-#include "BannerText.h"
+#include "BackupClientMakeExcludeList.h"
+#include "BackupDaemon.h"
+#include "BackupDaemonConfigVerify.h"
+#include "BackupStoreConstants.h"
+#include "BackupStoreDirectory.h"
+#include "BackupStoreException.h"
#include "BackupStoreFile.h"
-#include "Random.h"
+#include "BackupStoreFilenameClear.h"
+#include "BannerText.h"
+#include "Conversion.h"
#include "ExcludeList.h"
-#include "BackupClientMakeExcludeList.h"
-#include "IOStreamGetLine.h"
-#include "Utils.h"
#include "FileStream.h"
-#include "BackupStoreException.h"
-#include "BackupStoreConstants.h"
-#include "LocalProcessStream.h"
#include "IOStreamGetLine.h"
-#include "Conversion.h"
-#include "Archive.h"
-#include "Timer.h"
+#include "LocalProcessStream.h"
#include "Logging.h"
-#include "autogen_ClientException.h"
+#include "Random.h"
+#include "Timer.h"
+#include "Utils.h"
#ifdef WIN32
#include "Win32ServiceFunctions.h"
@@ -93,25 +92,6 @@ static const time_t MAX_SLEEP_TIME = 1024;
// This prevents repetative cycles of load on the server
#define SYNC_PERIOD_RANDOM_EXTRA_TIME_SHIFT_BY 6
-#ifdef WIN32
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: HelperThread()
-// Purpose: Background thread function, called by Windows,
-// calls the BackupDaemon's RunHelperThread method
-// to listen for and act on control communications
-// Created: 18/2/04
-//
-// --------------------------------------------------------------------------
-unsigned int WINAPI HelperThread(LPVOID lpParam)
-{
- ((BackupDaemon *)lpParam)->RunHelperThread();
-
- return 0;
-}
-#endif
-
// --------------------------------------------------------------------------
//
// Function
@@ -122,9 +102,23 @@ unsigned int WINAPI HelperThread(LPVOID lpParam)
// --------------------------------------------------------------------------
BackupDaemon::BackupDaemon()
: mState(BackupDaemon::State_Initialising),
- mpCommandSocketInfo(0),
+ mDeleteRedundantLocationsAfter(0),
+ mLastNotifiedEvent(SysadminNotifier::MAX),
mDeleteUnusedRootDirEntriesAfter(0),
- mLogAllFileAccess(false)
+ mClientStoreMarker(BackupClientContext::ClientStoreMarker_NotKnown),
+ mStorageLimitExceeded(false),
+ mReadErrorsOnFilesystemObjects(false),
+ mLastSyncTime(0),
+ mNextSyncTime(0),
+ mCurrentSyncStartTime(0),
+ mUpdateStoreInterval(0),
+ mDeleteStoreObjectInfoFile(false),
+ mDoSyncForcedByPreviousSyncError(false),
+ mLogAllFileAccess(false),
+ mpProgressNotifier(this),
+ mpLocationResolver(this),
+ mpRunStatusProvider(this),
+ mpSysadminNotifier(this)
#ifdef WIN32
, mInstallService(false),
mRemoveService(false),
@@ -134,40 +128,6 @@ BackupDaemon::BackupDaemon()
{
// Only ever one instance of a daemon
SSLLib::Initialise();
-
- // Initialise notification sent status
- for(int l = 0; l < NotifyEvent__MAX; ++l)
- {
- mNotificationsSent[l] = false;
- }
-
- #ifdef WIN32
- // Create the event object to signal from main thread to
- // worker when new messages are queued to be sent to the
- // command socket.
-
- mhMessageToSendEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
- if(mhMessageToSendEvent == INVALID_HANDLE_VALUE)
- {
- BOX_ERROR("Failed to create event object: error " <<
- GetLastError());
- exit(1);
- }
-
- // Create the event object to signal from worker to main thread
- // when a command has been received on the command socket.
-
- mhCommandReceivedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
- if(mhCommandReceivedEvent == INVALID_HANDLE_VALUE)
- {
- BOX_ERROR("Failed to create event object: error " <<
- GetLastError());
- exit(1);
- }
-
- // Create the critical section to protect the message queue
- InitializeCriticalSection(&mMessageQueueLock);
- #endif
}
// --------------------------------------------------------------------------
@@ -182,12 +142,6 @@ BackupDaemon::~BackupDaemon()
{
DeleteAllLocations();
DeleteAllIDMaps();
-
- if(mpCommandSocketInfo != 0)
- {
- delete mpCommandSocketInfo;
- mpCommandSocketInfo = 0;
- }
}
// --------------------------------------------------------------------------
@@ -262,12 +216,12 @@ void BackupDaemon::SetupInInitialProcess()
if(GetConfiguration().KeyExists("CommandSocket"))
{
BOX_WARNING(
- "==============================================================================\n"
- "SECURITY WARNING: This platform cannot check the credentials of connections to\n"
- "the command socket. This is a potential DoS security problem.\n"
- "Remove the CommandSocket directive from the bbackupd.conf file if bbackupctl\n"
- "is not used.\n"
- "==============================================================================\n"
+ "==============================================================================\n"
+ "SECURITY WARNING: This platform cannot check the credentials of connections to\n"
+ "the command socket. This is a potential DoS security problem.\n"
+ "Remove the CommandSocket directive from the bbackupd.conf file if bbackupctl\n"
+ "is not used.\n"
+ "==============================================================================\n"
);
}
}
@@ -294,7 +248,7 @@ void BackupDaemon::DeleteAllLocations()
// Clear the contents of the map, so it is empty
mLocations.clear();
- // And delete everything from the assoicated mount vector
+ // And delete everything from the associated mount vector
mIDMapMounts.clear();
}
@@ -322,6 +276,7 @@ int BackupDaemon::ProcessOption(signed int option)
case 'S':
{
mServiceName = optarg;
+ Logging::SetProgramName(mServiceName);
return 0;
}
@@ -356,8 +311,6 @@ int BackupDaemon::Main(const std::string &rConfigFileName)
return RemoveService(mServiceName);
}
- Logging::SetProgramName("Box Backup (" + mServiceName + ")");
-
int returnCode;
if (mRunAsService)
@@ -377,220 +330,6 @@ int BackupDaemon::Main(const std::string &rConfigFileName)
return returnCode;
}
-
-void BackupDaemon::RunHelperThread(void)
-{
- const Configuration &conf(GetConfiguration());
- mpCommandSocketInfo = new CommandSocketInfo;
- WinNamedPipeStream& rSocket(mpCommandSocketInfo->mListeningSocket);
-
- // loop until the parent process exits, or we decide
- // to kill the thread ourselves
- while (!IsTerminateWanted())
- {
- try
- {
- std::string socket = conf.GetKeyValue("CommandSocket");
- rSocket.Accept(socket);
- }
- catch (BoxException &e)
- {
- BOX_ERROR("Failed to open command socket: " <<
- e.what());
- SetTerminateWanted();
- break; // this is fatal to listening thread
- }
- catch(std::exception &e)
- {
- BOX_ERROR("Failed to open command socket: " <<
- e.what());
- SetTerminateWanted();
- break; // this is fatal to listening thread
- }
- catch(...)
- {
- BOX_ERROR("Failed to open command socket: "
- "unknown error");
- SetTerminateWanted();
- break; // this is fatal to listening thread
- }
-
- try
- {
- // Errors here do not kill the thread,
- // only the current connection.
-
- // This next section comes from Ben's original function
- // Log
- BOX_INFO("Connection from command socket");
-
- // Send a header line summarising the configuration
- // and current state
- char summary[256];
- size_t summarySize = sprintf(summary,
- "bbackupd: %d %d %d %d\nstate %d\n",
- conf.GetKeyValueBool("AutomaticBackup"),
- conf.GetKeyValueInt("UpdateStoreInterval"),
- conf.GetKeyValueInt("MinimumFileAge"),
- conf.GetKeyValueInt("MaxUploadWait"),
- mState);
-
- rSocket.Write(summary, summarySize);
- rSocket.Write("ping\n", 5);
-
- // old queued messages are not useful
- EnterCriticalSection(&mMessageQueueLock);
- mMessageList.clear();
- ResetEvent(mhMessageToSendEvent);
- LeaveCriticalSection(&mMessageQueueLock);
-
- IOStreamGetLine readLine(rSocket);
- std::string command;
-
- while (rSocket.IsConnected() && !IsTerminateWanted())
- {
- HANDLE handles[2];
- handles[0] = mhMessageToSendEvent;
- handles[1] = rSocket.GetReadableEvent();
-
- BOX_TRACE("Received command '" << command
- << "' over command socket");
-
- DWORD result = WaitForMultipleObjects(
- sizeof(handles)/sizeof(*handles),
- handles, FALSE, 1000);
-
- if(result == 0)
- {
- ResetEvent(mhMessageToSendEvent);
-
- EnterCriticalSection(&mMessageQueueLock);
- try
- {
- while (mMessageList.size() > 0)
- {
- std::string message = *(mMessageList.begin());
- mMessageList.erase(mMessageList.begin());
- printf("Sending '%s' to waiting client... ", message.c_str());
- message += "\n";
- rSocket.Write(message.c_str(),
- message.length());
-
- printf("done.\n");
- }
- }
- catch (...)
- {
- LeaveCriticalSection(&mMessageQueueLock);
- throw;
- }
- LeaveCriticalSection(&mMessageQueueLock);
- continue;
- }
- else if(result == WAIT_TIMEOUT)
- {
- continue;
- }
- else if(result != 1)
- {
- BOX_ERROR("WaitForMultipleObjects returned invalid result " << result);
- continue;
- }
-
- if(!readLine.GetLine(command))
- {
- BOX_ERROR("Failed to read line");
- continue;
- }
-
- BOX_INFO("Received command " << command <<
- " from client");
-
- bool sendOK = false;
- bool sendResponse = true;
- bool disconnect = false;
-
- // Command to process!
- if(command == "quit" || command == "")
- {
- // Close the socket.
- disconnect = true;
- sendResponse = false;
- }
- else if(command == "sync")
- {
- // Sync now!
- this->mDoSyncFlagOut = true;
- this->mSyncIsForcedOut = false;
- sendOK = true;
- SetEvent(mhCommandReceivedEvent);
- }
- else if(command == "force-sync")
- {
- // Sync now (forced -- overrides any SyncAllowScript)
- this->mDoSyncFlagOut = true;
- this->mSyncIsForcedOut = true;
- sendOK = true;
- SetEvent(mhCommandReceivedEvent);
- }
- else if(command == "reload")
- {
- // Reload the configuration
- SetReloadConfigWanted();
- sendOK = true;
- SetEvent(mhCommandReceivedEvent);
- }
- else if(command == "terminate")
- {
- // Terminate the daemon cleanly
- SetTerminateWanted();
- sendOK = true;
- SetEvent(mhCommandReceivedEvent);
- }
- else
- {
- BOX_ERROR("Received unknown command "
- "'" << command << "' "
- "from client");
- sendResponse = true;
- sendOK = false;
- }
-
- // Send a response back?
- if(sendResponse)
- {
- const char* response = sendOK ? "ok\n" : "error\n";
- rSocket.Write(
- response, strlen(response));
- }
-
- if(disconnect)
- {
- break;
- }
- }
-
- rSocket.Close();
- }
- catch(BoxException &e)
- {
- BOX_ERROR("Communication error with "
- "control client: " << e.what());
- }
- catch(std::exception &e)
- {
- BOX_ERROR("Internal error in command socket "
- "thread: " << e.what());
- }
- catch(...)
- {
- BOX_ERROR("Communication error with control client");
- }
- }
-
- CloseHandle(mhCommandReceivedEvent);
- CloseHandle(mhMessageToSendEvent);
-}
#endif
// --------------------------------------------------------------------------
@@ -606,36 +345,29 @@ void BackupDaemon::Run()
// initialise global timer mechanism
Timers::Init();
- #ifdef WIN32
- // Create a thread to handle the named pipe
- HANDLE hThread;
- unsigned int dwThreadId;
-
- hThread = (HANDLE) _beginthreadex(
- NULL, // default security attributes
- 0, // use default stack size
- HelperThread, // thread function
- this, // argument to thread function
- 0, // use default creation flags
- &dwThreadId); // returns the thread identifier
- #else
+ #ifndef WIN32
// Ignore SIGPIPE so that if a command connection is broken,
// the daemon doesn't terminate.
::signal(SIGPIPE, SIG_IGN);
+ #endif
- // Create a command socket?
- const Configuration &conf(GetConfiguration());
- if(conf.KeyExists("CommandSocket"))
- {
- // Yes, create a local UNIX socket
- mpCommandSocketInfo = new CommandSocketInfo;
- const char *socketName =
- conf.GetKeyValue("CommandSocket").c_str();
+ // Create a command socket?
+ const Configuration &conf(GetConfiguration());
+ if(conf.KeyExists("CommandSocket"))
+ {
+ // Yes, create a local UNIX socket
+ mapCommandSocketInfo.reset(new CommandSocketInfo);
+ const char *socketName =
+ conf.GetKeyValue("CommandSocket").c_str();
+ #ifdef WIN32
+ mapCommandSocketInfo->mListeningSocket.Listen(
+ socketName);
+ #else
::unlink(socketName);
- mpCommandSocketInfo->mListeningSocket.Listen(
+ mapCommandSocketInfo->mListeningSocket.Listen(
Socket::TypeUNIX, socketName);
- }
- #endif // !WIN32
+ #endif
+ }
// Handle things nicely on exceptions
try
@@ -644,16 +376,11 @@ void BackupDaemon::Run()
}
catch(...)
{
- #ifdef WIN32
- // Don't delete the socket, as the helper thread
- // is probably still using it. Let Windows clean
- // up after us.
- #else
- if(mpCommandSocketInfo != 0)
+ if(mapCommandSocketInfo.get())
{
try
{
- delete mpCommandSocketInfo;
+ mapCommandSocketInfo.reset();
}
catch(std::exception &e)
{
@@ -666,91 +393,63 @@ void BackupDaemon::Run()
BOX_WARNING("Error closing command socket "
"after exception, ignored.");
}
- mpCommandSocketInfo = 0;
}
- #endif // WIN32
Timers::Cleanup();
throw;
}
- #ifndef WIN32
- // Clean up
- if(mpCommandSocketInfo != 0)
- {
- delete mpCommandSocketInfo;
- mpCommandSocketInfo = 0;
- }
- #endif
-
+ // Clean up
+ mapCommandSocketInfo.reset();
Timers::Cleanup();
}
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupDaemon::Run2()
-// Purpose: Run function for daemon (second stage)
-// Created: 2003/10/08
-//
-// --------------------------------------------------------------------------
-void BackupDaemon::Run2()
+void BackupDaemon::InitCrypto()
{
// Read in the certificates creating a TLS context
- TLSContext tlsContext;
const Configuration &conf(GetConfiguration());
std::string certFile(conf.GetKeyValue("CertificateFile"));
std::string keyFile(conf.GetKeyValue("PrivateKeyFile"));
std::string caFile(conf.GetKeyValue("TrustedCAsFile"));
- tlsContext.Initialise(false /* as client */, certFile.c_str(), keyFile.c_str(), caFile.c_str());
+ mTlsContext.Initialise(false /* as client */, certFile.c_str(),
+ keyFile.c_str(), caFile.c_str());
// Set up the keys for various things
BackupClientCryptoKeys_Setup(conf.GetKeyValue("KeysFile").c_str());
+}
- // Setup various timings
- int maximumDiffingTime = 600;
- int keepAliveTime = 60;
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: BackupDaemon::Run2()
+// Purpose: Run function for daemon (second stage)
+// Created: 2003/10/08
+//
+// --------------------------------------------------------------------------
+void BackupDaemon::Run2()
+{
+ InitCrypto();
- // max diffing time, keep-alive time
- if(conf.KeyExists("MaximumDiffingTime"))
- {
- maximumDiffingTime = conf.GetKeyValueInt("MaximumDiffingTime");
- }
- if(conf.KeyExists("KeepAliveTime"))
- {
- keepAliveTime = conf.GetKeyValueInt("KeepAliveTime");
- }
+ const Configuration &conf(GetConfiguration());
// How often to connect to the store (approximate)
- box_time_t updateStoreInterval = SecondsToBoxTime(conf.GetKeyValueInt("UpdateStoreInterval"));
+ mUpdateStoreInterval = SecondsToBoxTime(
+ conf.GetKeyValueInt("UpdateStoreInterval"));
// But are we connecting automatically?
bool automaticBackup = conf.GetKeyValueBool("AutomaticBackup");
- // The minimum age a file needs to be before it will be considered for uploading
- box_time_t minimumFileAge = SecondsToBoxTime(conf.GetKeyValueInt("MinimumFileAge"));
-
- // The maximum time we'll wait to upload a file, regardless of how often it's modified
- box_time_t maxUploadWait = SecondsToBoxTime(conf.GetKeyValueInt("MaxUploadWait"));
- // Adjust by subtracting the minimum file age, so is relative to sync period end in comparisons
- maxUploadWait = (maxUploadWait > minimumFileAge)?(maxUploadWait - minimumFileAge):(0);
-
// When the next sync should take place -- which is ASAP
- box_time_t nextSyncTime = 0;
+ mNextSyncTime = 0;
// When the last sync started (only updated if the store was not full when the sync ended)
- box_time_t lastSyncTime = 0;
+ mLastSyncTime = 0;
// --------------------------------------------------------------------------------------------
- // And what's the current client store marker?
- int64_t clientStoreMarker =
- BackupClientContext::ClientStoreMarker_NotKnown;
- // haven't contacted the store yet
-
- bool deleteStoreObjectInfoFile = DeserializeStoreObjectInfo(
- clientStoreMarker, lastSyncTime, nextSyncTime);
+ mDeleteStoreObjectInfoFile = DeserializeStoreObjectInfo(
+ mLastSyncTime, mNextSyncTime);
// --------------------------------------------------------------------------------------------
@@ -758,91 +457,98 @@ void BackupDaemon::Run2()
// Set state
SetState(State_Idle);
+ mDoSyncForcedByPreviousSyncError = false;
+
// Loop around doing backups
do
{
// Flags used below
bool storageLimitExceeded = false;
bool doSync = false;
- bool doSyncForcedByCommand = false;
+ bool mDoSyncForcedByCommand = false;
// Is a delay necessary?
+ box_time_t currentTime;
+
+ do
{
- box_time_t currentTime;
- do
+ // Check whether we should be stopping,
+ // and don't run a sync if so.
+ if(StopRun()) break;
+
+ currentTime = GetCurrentBoxTime();
+
+ // Pause a while, but no more than
+ // MAX_SLEEP_TIME seconds (use the conditional
+ // because times are unsigned)
+ box_time_t requiredDelay =
+ (mNextSyncTime < currentTime)
+ ? (0)
+ : (mNextSyncTime - currentTime);
+
+ // If there isn't automatic backup happening,
+ // set a long delay. And limit delays at the
+ // same time.
+ if(!automaticBackup && !mDoSyncForcedByPreviousSyncError)
{
- // Check whether we should be stopping,
- // and don't run a sync if so.
- if(StopRun()) break;
-
- currentTime = GetCurrentBoxTime();
-
- // Pause a while, but no more than
- // MAX_SLEEP_TIME seconds (use the conditional
- // because times are unsigned)
- box_time_t requiredDelay =
- (nextSyncTime < currentTime)
- ? (0)
- : (nextSyncTime - currentTime);
-
- // If there isn't automatic backup happening,
- // set a long delay. And limit delays at the
- // same time.
- if(!automaticBackup || requiredDelay >
- SecondsToBoxTime(MAX_SLEEP_TIME))
+ requiredDelay = SecondsToBoxTime(MAX_SLEEP_TIME);
+ }
+ else if(requiredDelay > SecondsToBoxTime(MAX_SLEEP_TIME))
+ {
+ requiredDelay = SecondsToBoxTime(MAX_SLEEP_TIME);
+ }
+
+ // Only delay if necessary
+ if(requiredDelay > 0)
+ {
+ // Sleep somehow. There are choices
+ // on how this should be done,
+ // depending on the state of the
+ // control connection
+ if(mapCommandSocketInfo.get() != 0)
{
- requiredDelay = SecondsToBoxTime(
- MAX_SLEEP_TIME);
+ // A command socket exists,
+ // so sleep by waiting on it
+ WaitOnCommandSocket(requiredDelay,
+ doSync, mDoSyncForcedByCommand);
}
-
- // Only delay if necessary
- if(requiredDelay > 0)
+ else
{
- // Sleep somehow. There are choices
- // on how this should be done,
- // depending on the state of the
- // control connection
- if(mpCommandSocketInfo != 0)
- {
- // A command socket exists,
- // so sleep by waiting on it
- WaitOnCommandSocket(
- requiredDelay, doSync,
- doSyncForcedByCommand);
- }
- else
- {
- // No command socket or
- // connection, just do a
- // normal sleep
- time_t sleepSeconds =
- BoxTimeToSeconds(
- requiredDelay);
- ::sleep((sleepSeconds <= 0)
- ? 1
- : sleepSeconds);
- }
+ // No command socket or
+ // connection, just do a
+ // normal sleep
+ time_t sleepSeconds =
+ BoxTimeToSeconds(requiredDelay);
+ ::sleep((sleepSeconds <= 0)
+ ? 1 : sleepSeconds);
}
-
- } while((!automaticBackup || (currentTime < nextSyncTime)) && !doSync && !StopRun());
+ }
+
+ if ((automaticBackup || mDoSyncForcedByPreviousSyncError)
+ && currentTime >= mNextSyncTime)
+ {
+ doSync = true;
+ }
}
+ while(!doSync && !StopRun());
// Time of sync start, and if it's time for another sync
// (and we're doing automatic syncs), set the flag
- box_time_t currentSyncStartTime = GetCurrentBoxTime();
- if(automaticBackup && currentSyncStartTime >= nextSyncTime)
+ mCurrentSyncStartTime = GetCurrentBoxTime();
+ if((automaticBackup || mDoSyncForcedByPreviousSyncError) &&
+ mCurrentSyncStartTime >= mNextSyncTime)
{
doSync = true;
}
// Use a script to see if sync is allowed now?
- if(!doSyncForcedByCommand && doSync && !StopRun())
+ if(!mDoSyncForcedByCommand && doSync && !StopRun())
{
int d = UseScriptToSeeIfSyncAllowed();
if(d > 0)
{
// Script has asked for a delay
- nextSyncTime = GetCurrentBoxTime() +
+ mNextSyncTime = GetCurrentBoxTime() +
SecondsToBoxTime(d);
doSync = false;
}
@@ -852,383 +558,448 @@ void BackupDaemon::Run2()
// to be stopping)
if(doSync && !StopRun())
{
- // Touch a file to record times in filesystem
- TouchFileInWorkingDir("last_sync_start");
+ RunSyncNowWithExceptionHandling();
+ }
- // Tell anything connected to the command socket
- SendSyncStartOrFinish(true /* start */);
-
- // Reset statistics on uploads
- BackupStoreFile::ResetStats();
-
- // Calculate the sync period of files to examine
- box_time_t syncPeriodStart = lastSyncTime;
- box_time_t syncPeriodEnd = currentSyncStartTime -
- minimumFileAge;
+ // Set state
+ SetState(storageLimitExceeded?State_StorageLimitExceeded:State_Idle);
- if(syncPeriodStart >= syncPeriodEnd &&
- syncPeriodStart - syncPeriodEnd < minimumFileAge)
- {
- // This can happen if we receive a force-sync
- // command less than minimumFileAge after
- // the last sync. Deal with it by moving back
- // syncPeriodStart, which should not do any
- // damage.
- syncPeriodStart = syncPeriodEnd -
- SecondsToBoxTime(1);
- }
+ } while(!StopRun());
+
+ // Make sure we have a clean start next time round (if restart)
+ DeleteAllLocations();
+ DeleteAllIDMaps();
+}
- if(syncPeriodStart >= syncPeriodEnd)
- {
- BOX_ERROR("Invalid (negative) sync period: "
- "perhaps your clock is going "
- "backwards (" << syncPeriodStart <<
- " to " << syncPeriodEnd << ")");
- THROW_EXCEPTION(ClientException,
- ClockWentBackwards);
- }
+void BackupDaemon::RunSyncNowWithExceptionHandling()
+{
+ OnBackupStart();
- // Check logic
- ASSERT(syncPeriodEnd > syncPeriodStart);
- // Paranoid check on sync times
- if(syncPeriodStart >= syncPeriodEnd) continue;
-
- // Adjust syncPeriodEnd to emulate snapshot
- // behaviour properly
- box_time_t syncPeriodEndExtended = syncPeriodEnd;
- // Using zero min file age?
- if(minimumFileAge == 0)
- {
- // Add a year on to the end of the end time,
- // to make sure we sync files which are
- // modified after the scan run started.
- // Of course, they may be eligible to be
- // synced again the next time round,
- // but this should be OK, because the changes
- // only upload should upload no data.
- syncPeriodEndExtended += SecondsToBoxTime(
- (time_t)(356*24*3600));
- }
+ // Do sync
+ bool errorOccurred = false;
+ int errorCode = 0, errorSubCode = 0;
+ const char* errorString = "unknown";
- // Delete the serialised store object file,
- // so that we don't try to reload it after a
- // partially completed backup
- if(deleteStoreObjectInfoFile &&
- !DeleteStoreObjectInfo())
- {
- BOX_ERROR("Failed to delete the "
- "StoreObjectInfoFile, backup cannot "
- "continue safely.");
- THROW_EXCEPTION(ClientException,
- FailedToDeleteStoreObjectInfoFile);
- }
+ try
+ {
+ RunSyncNow();
+ }
+ catch(BoxException &e)
+ {
+ errorOccurred = true;
+ errorString = e.what();
+ errorCode = e.GetType();
+ errorSubCode = e.GetSubType();
+ }
+ catch(std::exception &e)
+ {
+ BOX_ERROR("Internal error during backup run: " << e.what());
+ errorOccurred = true;
+ errorString = e.what();
+ }
+ catch(...)
+ {
+ // TODO: better handling of exceptions here...
+ // need to be very careful
+ errorOccurred = true;
+ }
- // In case the backup throws an exception,
- // we should not try to delete the store info
- // object file again.
- deleteStoreObjectInfoFile = false;
-
- // Do sync
- bool errorOccurred = false;
- int errorCode = 0, errorSubCode = 0;
- const char* errorString = "unknown";
+ // do not retry immediately without a good reason
+ mDoSyncForcedByPreviousSyncError = false;
+
+ if(errorOccurred)
+ {
+ // Is it a berkely db failure?
+ bool isBerkelyDbFailure = false;
- try
- {
- // Set state and log start
- SetState(State_Connected);
- BOX_NOTICE("Beginning scan of local files");
+ if (errorCode == BackupStoreException::ExceptionType
+ && errorSubCode == BackupStoreException::BerkelyDBFailure)
+ {
+ isBerkelyDbFailure = true;
+ }
- std::string extendedLogFile;
- if (conf.KeyExists("ExtendedLogFile"))
- {
- extendedLogFile = conf.GetKeyValue(
- "ExtendedLogFile");
- }
-
- if (conf.KeyExists("LogAllFileAccess"))
- {
- mLogAllFileAccess =
- conf.GetKeyValueBool(
- "LogAllFileAccess");
- }
-
- // Then create a client context object (don't
- // just connect, as this may be unnecessary)
- BackupClientContext clientContext
- (
- *this,
- tlsContext,
- conf.GetKeyValue("StoreHostname"),
- conf.GetKeyValueInt("AccountNumber"),
- conf.GetKeyValueBool("ExtendedLogging"),
- conf.KeyExists("ExtendedLogFile"),
- extendedLogFile
- );
-
- // Set up the sync parameters
- BackupClientDirectoryRecord::SyncParams params(
- *this, *this, clientContext);
- params.mSyncPeriodStart = syncPeriodStart;
- params.mSyncPeriodEnd = syncPeriodEndExtended;
- // use potentially extended end time
- params.mMaxUploadWait = maxUploadWait;
- params.mFileTrackingSizeThreshold =
- conf.GetKeyValueInt(
- "FileTrackingSizeThreshold");
- params.mDiffingUploadSizeThreshold =
- conf.GetKeyValueInt(
- "DiffingUploadSizeThreshold");
- params.mMaxFileTimeInFuture =
- SecondsToBoxTime(
- conf.GetKeyValueInt(
- "MaxFileTimeInFuture"));
- mDeleteRedundantLocationsAfter =
- conf.GetKeyValueInt(
- "DeleteRedundantLocationsAfter");
-
- clientContext.SetMaximumDiffingTime(maximumDiffingTime);
- clientContext.SetKeepAliveTime(keepAliveTime);
-
- // Set store marker
- clientContext.SetClientStoreMarker(clientStoreMarker);
-
- // Set up the locations, if necessary --
- // need to do it here so we have a
- // (potential) connection to use
- if(mLocations.empty())
- {
- const Configuration &locations(
- conf.GetSubConfiguration(
- "BackupLocations"));
-
- // Make sure all the directory records
- // are set up
- SetupLocations(clientContext, locations);
- }
-
- // Get some ID maps going
- SetupIDMapsForSync();
-
- // Delete any unused directories?
- DeleteUnusedRootDirEntries(clientContext);
-
- // Notify administrator
- NotifySysadmin(NotifyEvent_BackupStart);
-
- // Go through the records, syncing them
- for(std::vector<Location *>::const_iterator
- i(mLocations.begin());
- i != mLocations.end(); ++i)
- {
- // Set current and new ID map pointers
- // in the context
- clientContext.SetIDMaps(mCurrentIDMaps[(*i)->mIDMapIndex], mNewIDMaps[(*i)->mIDMapIndex]);
-
- // Set exclude lists (context doesn't
- // take ownership)
- clientContext.SetExcludeLists(
- (*i)->mpExcludeFiles,
- (*i)->mpExcludeDirs);
-
- // Sync the directory
- (*i)->mpDirectoryRecord->SyncDirectory(
- params,
- BackupProtocolClientListDirectory::RootDirectory,
- (*i)->mPath);
+ if(isBerkelyDbFailure)
+ {
+ // Delete corrupt files
+ DeleteCorruptBerkelyDbFiles();
+ }
- // Unset exclude lists (just in case)
- clientContext.SetExcludeLists(0, 0);
- }
-
- // Errors reading any files?
- if(params.mReadErrorsOnFilesystemObjects)
- {
- // Notify administrator
- NotifySysadmin(NotifyEvent_ReadError);
- }
- else
- {
- // Unset the read error flag, so the // error is reported again if it
- // happens again
- mNotificationsSent[NotifyEvent_ReadError] = false;
- }
-
- // Perform any deletions required -- these are
- // delayed until the end to allow renaming to
- // happen neatly.
- clientContext.PerformDeletions();
+ // Clear state data
+ // Go back to beginning of time
+ mLastSyncTime = 0;
+ mClientStoreMarker = BackupClientContext::ClientStoreMarker_NotKnown; // no store marker, so download everything
+ DeleteAllLocations();
+ DeleteAllIDMaps();
- // Close any open connection
- clientContext.CloseAnyOpenConnection();
-
- // Get the new store marker
- clientStoreMarker = clientContext.GetClientStoreMarker();
-
- // Check the storage limit
- if(clientContext.StorageLimitExceeded())
- {
- // Tell the sysadmin about this
- NotifySysadmin(NotifyEvent_StoreFull);
- }
- else
- {
- // The start time of the next run is
- // the end time of this run.
- // This is only done if the storage
- // limit wasn't exceeded (as things
- // won't have been done properly if
- // it was)
- lastSyncTime = syncPeriodEnd;
-
- // unflag the storage full notify flag
- // so that next time the store is full,
- // an alert will be sent
- mNotificationsSent[NotifyEvent_StoreFull] = false;
- }
-
- // Calculate when the next sync run should be
- nextSyncTime = currentSyncStartTime +
- updateStoreInterval +
- Random::RandomInt(updateStoreInterval >>
+ // Handle restart?
+ if(StopRun())
+ {
+ BOX_NOTICE("Exception (" << errorCode
+ << "/" << errorSubCode
+ << ") due to signal");
+ OnBackupFinish();
+ return;
+ }
+
+ NotifySysadmin(SysadminNotifier::BackupError);
+
+ // If the Berkely db files get corrupted,
+ // delete them and try again immediately.
+ if(isBerkelyDbFailure)
+ {
+ BOX_ERROR("Berkely db inode map files corrupted, "
+ "deleting and restarting scan. Renamed files "
+ "and directories will not be tracked until "
+ "after this scan.");
+ ::sleep(1);
+ }
+ else
+ {
+ // Not restart/terminate, pause and retry
+ // Notify administrator
+ SetState(State_Error);
+ BOX_ERROR("Exception caught (" << errorString <<
+ " " << errorCode << "/" << errorSubCode <<
+ "), reset state and waiting to retry...");
+ ::sleep(10);
+ mNextSyncTime = mCurrentSyncStartTime +
+ SecondsToBoxTime(100) +
+ Random::RandomInt(mUpdateStoreInterval >>
SYNC_PERIOD_RANDOM_EXTRA_TIME_SHIFT_BY);
-
- // Commit the ID Maps
- CommitIDMapsAfterSync();
+ }
+ }
+ // Notify system administrator about the final state of the backup
+ else if(mReadErrorsOnFilesystemObjects)
+ {
+ NotifySysadmin(SysadminNotifier::ReadError);
+ }
+ else if(mStorageLimitExceeded)
+ {
+ NotifySysadmin(SysadminNotifier::StoreFull);
+ }
+ else
+ {
+ NotifySysadmin(SysadminNotifier::BackupOK);
+ }
+
+ // If we were retrying after an error, and this backup succeeded,
+ // then now would be a good time to stop :-)
+ mDoSyncForcedByPreviousSyncError = errorOccurred;
- // Log
- BOX_NOTICE("Finished scan of local files");
+ OnBackupFinish();
+}
- // Notify administrator
- NotifySysadmin(NotifyEvent_BackupFinish);
+void BackupDaemon::RunSyncNow()
+{
+ // Delete the serialised store object file,
+ // so that we don't try to reload it after a
+ // partially completed backup
+ if(mDeleteStoreObjectInfoFile &&
+ !DeleteStoreObjectInfo())
+ {
+ BOX_ERROR("Failed to delete the StoreObjectInfoFile, "
+ "backup cannot continue safely.");
+ THROW_EXCEPTION(ClientException,
+ FailedToDeleteStoreObjectInfoFile);
+ }
- // --------------------------------------------------------------------------------------------
+ // In case the backup throws an exception,
+ // we should not try to delete the store info
+ // object file again.
+ mDeleteStoreObjectInfoFile = false;
- // We had a successful backup, save the store
- // info. If we save successfully, we must
- // delete the file next time we start a backup
+ const Configuration &conf(GetConfiguration());
- deleteStoreObjectInfoFile =
- SerializeStoreObjectInfo(
- clientStoreMarker,
- lastSyncTime, nextSyncTime);
+ std::auto_ptr<FileLogger> fileLogger;
- // --------------------------------------------------------------------------------------------
- }
- catch(BoxException &e)
- {
- errorOccurred = true;
- errorString = e.what();
- errorCode = e.GetType();
- errorSubCode = e.GetSubType();
- }
- catch(std::exception &e)
- {
- BOX_ERROR("Internal error during "
- "backup run: " << e.what());
- errorOccurred = true;
- errorString = e.what();
- }
- catch(...)
- {
- // TODO: better handling of exceptions here...
- // need to be very careful
- errorOccurred = true;
- }
-
- if(errorOccurred)
- {
- // Is it a berkely db failure?
- bool isBerkelyDbFailure = false;
+ if (conf.KeyExists("LogFile"))
+ {
+ Log::Level level = Log::INFO;
+ if (conf.KeyExists("LogFileLevel"))
+ {
+ level = Logging::GetNamedLevel(
+ conf.GetKeyValue("LogFileLevel"));
+ }
+ fileLogger.reset(new FileLogger(conf.GetKeyValue("LogFile"),
+ level));
+ }
- if (errorCode == BackupStoreException::ExceptionType
- && errorSubCode == BackupStoreException::BerkelyDBFailure)
- {
- isBerkelyDbFailure = true;
- }
+ std::string extendedLogFile;
+ if (conf.KeyExists("ExtendedLogFile"))
+ {
+ extendedLogFile = conf.GetKeyValue("ExtendedLogFile");
+ }
+
+ if (conf.KeyExists("LogAllFileAccess"))
+ {
+ mLogAllFileAccess = conf.GetKeyValueBool("LogAllFileAccess");
+ }
+
+ // Then create a client context object (don't
+ // just connect, as this may be unnecessary)
+ BackupClientContext clientContext
+ (
+ *mpLocationResolver,
+ mTlsContext,
+ conf.GetKeyValue("StoreHostname"),
+ conf.GetKeyValueInt("StorePort"),
+ conf.GetKeyValueInt("AccountNumber"),
+ conf.GetKeyValueBool("ExtendedLogging"),
+ conf.KeyExists("ExtendedLogFile"),
+ extendedLogFile, *mpProgressNotifier
+ );
+
+ // The minimum age a file needs to be before it will be
+ // considered for uploading
+ box_time_t minimumFileAge = SecondsToBoxTime(
+ conf.GetKeyValueInt("MinimumFileAge"));
- if(isBerkelyDbFailure)
- {
- // Delete corrupt files
- DeleteCorruptBerkelyDbFiles();
- }
+ // The maximum time we'll wait to upload a file, regardless
+ // of how often it's modified
+ box_time_t maxUploadWait = SecondsToBoxTime(
+ conf.GetKeyValueInt("MaxUploadWait"));
+ // Adjust by subtracting the minimum file age, so is relative
+ // to sync period end in comparisons
+ if (maxUploadWait > minimumFileAge)
+ {
+ maxUploadWait -= minimumFileAge;
+ }
+ else
+ {
+ maxUploadWait = 0;
+ }
- // Clear state data
- syncPeriodStart = 0;
- // go back to beginning of time
- clientStoreMarker = BackupClientContext::ClientStoreMarker_NotKnown; // no store marker, so download everything
- DeleteAllLocations();
- DeleteAllIDMaps();
+ // Calculate the sync period of files to examine
+ box_time_t syncPeriodStart = mLastSyncTime;
+ box_time_t syncPeriodEnd = GetCurrentBoxTime() - minimumFileAge;
- // Handle restart?
- if(StopRun())
- {
- BOX_NOTICE("Exception (" << errorCode
- << "/" << errorSubCode
- << ") due to signal");
- return;
- }
+ if(syncPeriodStart >= syncPeriodEnd &&
+ syncPeriodStart - syncPeriodEnd < minimumFileAge)
+ {
+ // This can happen if we receive a force-sync command less
+ // than minimumFileAge after the last sync. Deal with it by
+ // moving back syncPeriodStart, which should not do any
+ // damage.
+ syncPeriodStart = syncPeriodEnd -
+ SecondsToBoxTime(1);
+ }
- // If the Berkely db files get corrupted, delete them and try again immediately
- if(isBerkelyDbFailure)
- {
- BOX_ERROR("Berkely db inode map files corrupted, deleting and restarting scan. Renamed files and directories will not be tracked until after this scan.");
- ::sleep(1);
- }
- else
- {
- // Not restart/terminate, pause and retry
- // Notify administrator
- NotifySysadmin(NotifyEvent_BackupError);
- SetState(State_Error);
- BOX_ERROR("Exception caught ("
- << errorString
- << " " << errorCode
- << "/" << errorSubCode
- << "), reset state and "
- "waiting to retry...");
- ::sleep(10);
- nextSyncTime = currentSyncStartTime +
- SecondsToBoxTime(90) +
- Random::RandomInt(
- updateStoreInterval >>
- SYNC_PERIOD_RANDOM_EXTRA_TIME_SHIFT_BY);
- }
- }
+ if(syncPeriodStart >= syncPeriodEnd)
+ {
+ BOX_ERROR("Invalid (negative) sync period: "
+ "perhaps your clock is going "
+ "backwards (" << syncPeriodStart <<
+ " to " << syncPeriodEnd << ")");
+ THROW_EXCEPTION(ClientException,
+ ClockWentBackwards);
+ }
- // Log the stats
- BOX_NOTICE("File statistics: total file size uploaded "
- << BackupStoreFile::msStats.mBytesInEncodedFiles
- << ", bytes already on server "
- << BackupStoreFile::msStats.mBytesAlreadyOnServer
- << ", encoded size "
- << BackupStoreFile::msStats.mTotalFileStreamSize);
- BackupStoreFile::ResetStats();
+ // Check logic
+ ASSERT(syncPeriodEnd > syncPeriodStart);
+ // Paranoid check on sync times
+ if(syncPeriodStart >= syncPeriodEnd) return;
+
+ // Adjust syncPeriodEnd to emulate snapshot
+ // behaviour properly
+ box_time_t syncPeriodEndExtended = syncPeriodEnd;
+
+ // Using zero min file age?
+ if(minimumFileAge == 0)
+ {
+ // Add a year on to the end of the end time,
+ // to make sure we sync files which are
+ // modified after the scan run started.
+ // Of course, they may be eligible to be
+ // synced again the next time round,
+ // but this should be OK, because the changes
+ // only upload should upload no data.
+ syncPeriodEndExtended += SecondsToBoxTime(
+ (time_t)(356*24*3600));
+ }
+
+ // Set up the sync parameters
+ BackupClientDirectoryRecord::SyncParams params(*mpRunStatusProvider,
+ *mpSysadminNotifier, *mpProgressNotifier, clientContext);
+ params.mSyncPeriodStart = syncPeriodStart;
+ params.mSyncPeriodEnd = syncPeriodEndExtended;
+ // use potentially extended end time
+ params.mMaxUploadWait = maxUploadWait;
+ params.mFileTrackingSizeThreshold =
+ conf.GetKeyValueInt("FileTrackingSizeThreshold");
+ params.mDiffingUploadSizeThreshold =
+ conf.GetKeyValueInt("DiffingUploadSizeThreshold");
+ params.mMaxFileTimeInFuture =
+ SecondsToBoxTime(conf.GetKeyValueInt("MaxFileTimeInFuture"));
+ mDeleteRedundantLocationsAfter =
+ conf.GetKeyValueInt("DeleteRedundantLocationsAfter");
+ mStorageLimitExceeded = false;
+ mReadErrorsOnFilesystemObjects = false;
- // Tell anything connected to the command socket
- SendSyncStartOrFinish(false /* finish */);
+ // Setup various timings
+ int maximumDiffingTime = 600;
+ int keepAliveTime = 60;
- // Touch a file to record times in filesystem
- TouchFileInWorkingDir("last_sync_finish");
- }
+ // max diffing time, keep-alive time
+ if(conf.KeyExists("MaximumDiffingTime"))
+ {
+ maximumDiffingTime = conf.GetKeyValueInt("MaximumDiffingTime");
+ }
+ if(conf.KeyExists("KeepAliveTime"))
+ {
+ keepAliveTime = conf.GetKeyValueInt("KeepAliveTime");
+ }
+
+ clientContext.SetMaximumDiffingTime(maximumDiffingTime);
+ clientContext.SetKeepAliveTime(keepAliveTime);
+
+ // Set store marker
+ clientContext.SetClientStoreMarker(mClientStoreMarker);
+
+ // Set up the locations, if necessary --
+ // need to do it here so we have a
+ // (potential) connection to use
+ if(mLocations.empty())
+ {
+ const Configuration &locations(
+ conf.GetSubConfiguration(
+ "BackupLocations"));
- // Set state
- SetState(storageLimitExceeded?State_StorageLimitExceeded:State_Idle);
+ // Make sure all the directory records
+ // are set up
+ SetupLocations(clientContext, locations);
+ }
+
+ mpProgressNotifier->NotifyIDMapsSetup(clientContext);
+
+ // Get some ID maps going
+ SetupIDMapsForSync();
- } while(!StopRun());
+ // Delete any unused directories?
+ DeleteUnusedRootDirEntries(clientContext);
+
+ // Go through the records, syncing them
+ for(std::vector<Location *>::const_iterator
+ i(mLocations.begin());
+ i != mLocations.end(); ++i)
+ {
+ // Set current and new ID map pointers
+ // in the context
+ clientContext.SetIDMaps(mCurrentIDMaps[(*i)->mIDMapIndex],
+ mNewIDMaps[(*i)->mIDMapIndex]);
- // Make sure we have a clean start next time round (if restart)
- DeleteAllLocations();
- DeleteAllIDMaps();
+ // Set exclude lists (context doesn't
+ // take ownership)
+ clientContext.SetExcludeLists(
+ (*i)->mpExcludeFiles,
+ (*i)->mpExcludeDirs);
+
+ // Sync the directory
+ (*i)->mpDirectoryRecord->SyncDirectory(
+ params,
+ BackupProtocolClientListDirectory::RootDirectory,
+ (*i)->mPath, std::string("/") + (*i)->mName);
+
+ // Unset exclude lists (just in case)
+ clientContext.SetExcludeLists(0, 0);
+ }
+
+ // Perform any deletions required -- these are
+ // delayed until the end to allow renaming to
+ // happen neatly.
+ clientContext.PerformDeletions();
+
+ // Close any open connection
+ clientContext.CloseAnyOpenConnection();
+
+ // Get the new store marker
+ mClientStoreMarker = clientContext.GetClientStoreMarker();
+ mStorageLimitExceeded = clientContext.StorageLimitExceeded();
+ mReadErrorsOnFilesystemObjects =
+ params.mReadErrorsOnFilesystemObjects;
+
+ if(!mStorageLimitExceeded)
+ {
+ // The start time of the next run is the end time of this
+ // run. This is only done if the storage limit wasn't
+ // exceeded (as things won't have been done properly if
+ // it was)
+ mLastSyncTime = syncPeriodEnd;
+ }
+
+ // Commit the ID Maps
+ CommitIDMapsAfterSync();
+
+ // Calculate when the next sync run should be
+ mNextSyncTime = mCurrentSyncStartTime +
+ mUpdateStoreInterval +
+ Random::RandomInt(mUpdateStoreInterval >>
+ SYNC_PERIOD_RANDOM_EXTRA_TIME_SHIFT_BY);
+
+ // --------------------------------------------------------------------------------------------
+
+ // We had a successful backup, save the store
+ // info. If we save successfully, we must
+ // delete the file next time we start a backup
+
+ mDeleteStoreObjectInfoFile =
+ SerializeStoreObjectInfo(mLastSyncTime,
+ mNextSyncTime);
+
+ // --------------------------------------------------------------------------------------------
}
+void BackupDaemon::OnBackupStart()
+{
+ // Touch a file to record times in filesystem
+ TouchFileInWorkingDir("last_sync_start");
+
+ // Reset statistics on uploads
+ BackupStoreFile::ResetStats();
+
+ // Tell anything connected to the command socket
+ SendSyncStartOrFinish(true /* start */);
+
+ // Notify administrator
+ NotifySysadmin(SysadminNotifier::BackupStart);
+
+ // Set state and log start
+ SetState(State_Connected);
+ BOX_NOTICE("Beginning scan of local files");
+}
+
+void BackupDaemon::OnBackupFinish()
+{
+ // Log
+ BOX_NOTICE("Finished scan of local files");
+
+ // Log the stats
+ BOX_NOTICE("File statistics: total file size uploaded "
+ << BackupStoreFile::msStats.mBytesInEncodedFiles
+ << ", bytes already on server "
+ << BackupStoreFile::msStats.mBytesAlreadyOnServer
+ << ", encoded size "
+ << BackupStoreFile::msStats.mTotalFileStreamSize);
+
+ // Reset statistics again
+ BackupStoreFile::ResetStats();
+
+ // Notify administrator
+ NotifySysadmin(SysadminNotifier::BackupFinish);
+
+ // Tell anything connected to the command socket
+ SendSyncStartOrFinish(false /* finish */);
+
+ // Touch a file to record times in filesystem
+ TouchFileInWorkingDir("last_sync_finish");
+}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupDaemon::UseScriptToSeeIfSyncAllowed()
-// Purpose: Private. Use a script to see if the sync should be allowed (if configured)
-// Returns -1 if it's allowed, time in seconds to wait otherwise.
+// Purpose: Private. Use a script to see if the sync should be
+// allowed now (if configured). Returns -1 if it's
+// allowed, time in seconds to wait otherwise.
// Created: 21/6/04
//
// --------------------------------------------------------------------------
@@ -1250,7 +1021,8 @@ int BackupDaemon::UseScriptToSeeIfSyncAllowed()
pid_t pid = 0;
try
{
- std::auto_ptr<IOStream> pscript(LocalProcessStream(conf.GetKeyValue("SyncAllowScript").c_str(), pid));
+ std::auto_ptr<IOStream> pscript(LocalProcessStream(
+ conf.GetKeyValue("SyncAllowScript").c_str(), pid));
// Read in the result
IOStreamGetLine getLine(*pscript);
@@ -1323,33 +1095,13 @@ int BackupDaemon::UseScriptToSeeIfSyncAllowed()
// --------------------------------------------------------------------------
void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFlagOut, bool &SyncIsForcedOut)
{
-#ifdef WIN32
- DWORD requiredDelayMs = BoxTimeToMilliSeconds(RequiredDelay);
-
- DWORD result = WaitForSingleObject(mhCommandReceivedEvent,
- (DWORD)requiredDelayMs);
-
- if(result == WAIT_OBJECT_0)
- {
- DoSyncFlagOut = this->mDoSyncFlagOut;
- SyncIsForcedOut = this->mSyncIsForcedOut;
- ResetEvent(mhCommandReceivedEvent);
- }
- else if(result == WAIT_TIMEOUT)
+ ASSERT(mapCommandSocketInfo.get());
+ if(!mapCommandSocketInfo.get())
{
- DoSyncFlagOut = false;
- SyncIsForcedOut = false;
- }
- else
- {
- BOX_ERROR("Unexpected result from WaitForSingleObject: "
- "error " << GetLastError());
+ // failure case isn't too bad
+ ::sleep(1);
+ return;
}
-
- return;
-#else // ! WIN32
- ASSERT(mpCommandSocketInfo != 0);
- if(mpCommandSocketInfo == 0) {::sleep(1); return;} // failure case isn't too bad
BOX_TRACE("Wait on command socket, delay = " << RequiredDelay);
@@ -1362,12 +1114,12 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla
if(timeout == INFTIM) timeout = 100000;
// Wait for socket connection, or handle a command?
- if(mpCommandSocketInfo->mpConnectedSocket.get() == 0)
+ if(mapCommandSocketInfo->mpConnectedSocket.get() == 0)
{
// No connection, listen for a new one
- mpCommandSocketInfo->mpConnectedSocket.reset(mpCommandSocketInfo->mListeningSocket.Accept(timeout).release());
+ mapCommandSocketInfo->mpConnectedSocket.reset(mapCommandSocketInfo->mListeningSocket.Accept(timeout).release());
- if(mpCommandSocketInfo->mpConnectedSocket.get() == 0)
+ if(mapCommandSocketInfo->mpConnectedSocket.get() == 0)
{
// If a connection didn't arrive, there was a timeout, which means we've
// waited long enough and it's time to go.
@@ -1386,7 +1138,7 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla
{
uid_t remoteEUID = 0xffff;
gid_t remoteEGID = 0xffff;
- if(mpCommandSocketInfo->mpConnectedSocket->GetPeerCredentials(remoteEUID, remoteEGID))
+ if(mapCommandSocketInfo->mpConnectedSocket->GetPeerCredentials(remoteEUID, remoteEGID))
{
// Credentials are available -- check UID
if(remoteEUID == ::getuid())
@@ -1403,7 +1155,7 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla
{
// Dump the connection
BOX_ERROR("Incoming command connection from peer had different user ID than this process, or security check could not be completed.");
- mpCommandSocketInfo->mpConnectedSocket.reset();
+ mapCommandSocketInfo->mpConnectedSocket.reset();
return;
}
else
@@ -1420,7 +1172,7 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla
conf.GetKeyValueInt("MinimumFileAge"),
conf.GetKeyValueInt("MaxUploadWait"),
mState);
- mpCommandSocketInfo->mpConnectedSocket->Write(summary, summarySize);
+ mapCommandSocketInfo->mpConnectedSocket->Write(summary, summarySize);
// Set the timeout to something very small, so we don't wait too long on waiting
// for any incoming data
@@ -1430,22 +1182,22 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla
}
// So there must be a connection now.
- ASSERT(mpCommandSocketInfo->mpConnectedSocket.get() != 0);
+ ASSERT(mapCommandSocketInfo->mpConnectedSocket.get() != 0);
// Is there a getline object ready?
- if(mpCommandSocketInfo->mpGetLine == 0)
+ if(mapCommandSocketInfo->mpGetLine == 0)
{
// Create a new one
- mpCommandSocketInfo->mpGetLine = new IOStreamGetLine(*(mpCommandSocketInfo->mpConnectedSocket.get()));
+ mapCommandSocketInfo->mpGetLine = new IOStreamGetLine(*(mapCommandSocketInfo->mpConnectedSocket.get()));
}
// Ping the remote side, to provide errors which will mean the socket gets closed
- mpCommandSocketInfo->mpConnectedSocket->Write("ping\n", 5);
+ mapCommandSocketInfo->mpConnectedSocket->Write("ping\n", 5);
// Wait for a command or something on the socket
std::string command;
- while(mpCommandSocketInfo->mpGetLine != 0 && !mpCommandSocketInfo->mpGetLine->IsEOF()
- && mpCommandSocketInfo->mpGetLine->GetLine(command, false /* no preprocessing */, timeout))
+ while(mapCommandSocketInfo->mpGetLine != 0 && !mapCommandSocketInfo->mpGetLine->IsEOF()
+ && mapCommandSocketInfo->mpGetLine->GetLine(command, false /* no preprocessing */, timeout))
{
BOX_TRACE("Receiving command '" << command
<< "' over command socket");
@@ -1490,7 +1242,7 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla
// Send a response back?
if(sendResponse)
{
- mpCommandSocketInfo->mpConnectedSocket->Write(sendOK?"ok\n":"error\n", sendOK?3:6);
+ mapCommandSocketInfo->mpConnectedSocket->Write(sendOK?"ok\n":"error\n", sendOK?3:6);
}
// Set timeout to something very small, so this just checks for data which is waiting
@@ -1498,18 +1250,39 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla
}
// Close on EOF?
- if(mpCommandSocketInfo->mpGetLine != 0 && mpCommandSocketInfo->mpGetLine->IsEOF())
+ if(mapCommandSocketInfo->mpGetLine != 0 && mapCommandSocketInfo->mpGetLine->IsEOF())
{
CloseCommandConnection();
}
}
+ catch(ConnectionException &ce)
+ {
+ BOX_NOTICE("Failed to write to command socket: " << ce.what());
+
+ // If an error occurs, and there is a connection active,
+ // just close that connection and continue. Otherwise,
+ // let the error propagate.
+
+ if(mapCommandSocketInfo->mpConnectedSocket.get() == 0)
+ {
+ throw; // thread will die
+ }
+ else
+ {
+ // Close socket and ignore error
+ CloseCommandConnection();
+ }
+ }
catch(std::exception &e)
{
- BOX_ERROR("Internal error in command socket thread: "
- << e.what());
- // If an error occurs, and there is a connection active, just close that
- // connection and continue. Otherwise, let the error propagate.
- if(mpCommandSocketInfo->mpConnectedSocket.get() == 0)
+ BOX_ERROR("Failed to write to command socket: " <<
+ e.what());
+
+ // If an error occurs, and there is a connection active,
+ // just close that connection and continue. Otherwise,
+ // let the error propagate.
+
+ if(mapCommandSocketInfo->mpConnectedSocket.get() == 0)
{
throw; // thread will die
}
@@ -1521,9 +1294,13 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla
}
catch(...)
{
- // If an error occurs, and there is a connection active, just close that
- // connection and continue. Otherwise, let the error propagate.
- if(mpCommandSocketInfo->mpConnectedSocket.get() == 0)
+ BOX_ERROR("Failed to write to command socket: unknown error");
+
+ // If an error occurs, and there is a connection active,
+ // just close that connection and continue. Otherwise,
+ // let the error propagate.
+
+ if(mapCommandSocketInfo->mpConnectedSocket.get() == 0)
{
throw; // thread will die
}
@@ -1533,7 +1310,6 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla
CloseCommandConnection();
}
}
-#endif // WIN32
}
@@ -1547,17 +1323,16 @@ void BackupDaemon::WaitOnCommandSocket(box_time_t RequiredDelay, bool &DoSyncFla
// --------------------------------------------------------------------------
void BackupDaemon::CloseCommandConnection()
{
-#ifndef WIN32
try
{
BOX_TRACE("Closing command connection");
- if(mpCommandSocketInfo->mpGetLine)
+ if(mapCommandSocketInfo->mpGetLine)
{
- delete mpCommandSocketInfo->mpGetLine;
- mpCommandSocketInfo->mpGetLine = 0;
+ delete mapCommandSocketInfo->mpGetLine;
+ mapCommandSocketInfo->mpGetLine = 0;
}
- mpCommandSocketInfo->mpConnectedSocket.reset();
+ mapCommandSocketInfo->mpConnectedSocket.reset();
}
catch(std::exception &e)
{
@@ -1568,7 +1343,6 @@ void BackupDaemon::CloseCommandConnection()
{
// Ignore any errors
}
-#endif
}
@@ -1586,27 +1360,15 @@ void BackupDaemon::SendSyncStartOrFinish(bool SendStart)
// The bbackupctl program can't rely on a state change, because it
// may never change if the server doesn't need to be contacted.
- if(mpCommandSocketInfo != NULL &&
-#ifdef WIN32
- mpCommandSocketInfo->mListeningSocket.IsConnected()
-#else
- mpCommandSocketInfo->mpConnectedSocket.get() != 0
-#endif
- )
+ if(mapCommandSocketInfo.get() &&
+ mapCommandSocketInfo->mpConnectedSocket.get() != 0)
{
std::string message = SendStart ? "start-sync" : "finish-sync";
try
{
-#ifdef WIN32
- EnterCriticalSection(&mMessageQueueLock);
- mMessageList.push_back(message);
- SetEvent(mhMessageToSendEvent);
- LeaveCriticalSection(&mMessageQueueLock);
-#else
message += "\n";
- mpCommandSocketInfo->mpConnectedSocket->Write(
+ mapCommandSocketInfo->mpConnectedSocket->Write(
message.c_str(), message.size());
-#endif
}
catch(std::exception &e)
{
@@ -1668,14 +1430,20 @@ void BackupDaemon::SetupLocations(BackupClientContext &rClientContext, const Con
// Just a check to make sure it's right.
DeleteAllLocations();
- // Going to need a copy of the root directory. Get a connection, and fetch it.
+ // Going to need a copy of the root directory. Get a connection,
+ // and fetch it.
BackupProtocolClient &connection(rClientContext.GetConnection());
- // Ask server for a list of everything in the root directory, which is a directory itself
- std::auto_ptr<BackupProtocolClientSuccess> dirreply(connection.QueryListDirectory(
+ // Ask server for a list of everything in the root directory,
+ // which is a directory itself
+ std::auto_ptr<BackupProtocolClientSuccess> dirreply(
+ connection.QueryListDirectory(
BackupProtocolClientListDirectory::RootDirectory,
- BackupProtocolClientListDirectory::Flags_Dir, // only directories
- BackupProtocolClientListDirectory::Flags_Deleted | BackupProtocolClientListDirectory::Flags_OldVersion, // exclude old/deleted stuff
+ // only directories
+ BackupProtocolClientListDirectory::Flags_Dir,
+ // exclude old/deleted stuff
+ BackupProtocolClientListDirectory::Flags_Deleted |
+ BackupProtocolClientListDirectory::Flags_OldVersion,
false /* no attributes */));
// Retrieve the directory from the stream following
@@ -1755,33 +1523,41 @@ void BackupDaemon::SetupLocations(BackupClientContext &rClientContext, const Con
#endif // HAVE_STRUCT_MNTENT_MNT_DIR
// Check sorting and that things are as we expect
ASSERT(mountPoints.size() > 0);
-#ifndef NDEBUG
+#ifndef BOX_RELEASE_BUILD
{
std::set<std::string, mntLenCompare>::reverse_iterator i(mountPoints.rbegin());
ASSERT(*i == "/");
}
-#endif // n NDEBUG
+#endif // n BOX_RELEASE_BUILD
#endif // n HAVE_STRUCT_STATFS_F_MNTONNAME || n HAVE_STRUCT_STATVFS_F_MNTONNAME
#endif // HAVE_MOUNTS
// Then... go through each of the entries in the configuration,
// making sure there's a directory created for it.
- for(std::list<std::pair<std::string, Configuration> >::const_iterator i = rLocationsConf.mSubConfigurations.begin();
- i != rLocationsConf.mSubConfigurations.end(); ++i)
- {
- BOX_TRACE("new location: " << i->first);
+ std::vector<std::string> locNames =
+ rLocationsConf.GetSubConfigurationNames();
+
+ for(std::vector<std::string>::iterator
+ pLocName = locNames.begin();
+ pLocName != locNames.end();
+ pLocName++)
+ {
+ const Configuration& rConfig(
+ rLocationsConf.GetSubConfiguration(*pLocName));
+ BOX_TRACE("new location: " << *pLocName);
+
// Create a record for it
std::auto_ptr<Location> apLoc(new Location);
try
{
// Setup names in the location record
- apLoc->mName = i->first;
- apLoc->mPath = i->second.GetKeyValue("Path");
+ apLoc->mName = *pLocName;
+ apLoc->mPath = rConfig.GetKeyValue("Path");
// Read the exclude lists from the Configuration
- apLoc->mpExcludeFiles = BackupClientMakeExcludeList_Files(i->second);
- apLoc->mpExcludeDirs = BackupClientMakeExcludeList_Dirs(i->second);
+ apLoc->mpExcludeFiles = BackupClientMakeExcludeList_Files(rConfig);
+ apLoc->mpExcludeDirs = BackupClientMakeExcludeList_Dirs(rConfig);
// Does this exist on the server?
// Remove from dir object early, so that if we fail
@@ -1814,10 +1590,9 @@ void BackupDaemon::SetupLocations(BackupClientContext &rClientContext, const Con
if(::statfs(apLoc->mPath.c_str(), &s) != 0)
#endif // HAVE_STRUCT_STATVFS_F_MNTONNAME
{
- BOX_WARNING("Failed to stat location "
+ BOX_LOG_SYS_WARNING("Failed to stat location "
"path '" << apLoc->mPath <<
- "' (" << strerror(errno) <<
- "), skipping location '" <<
+ "', skipping location '" <<
apLoc->mName << "'");
continue;
}
@@ -1929,7 +1704,8 @@ void BackupDaemon::SetupLocations(BackupClientContext &rClientContext, const Con
// Create and store the directory object for the root of this location
ASSERT(oid != 0);
- BackupClientDirectoryRecord *precord = new BackupClientDirectoryRecord(oid, i->first);
+ BackupClientDirectoryRecord *precord =
+ new BackupClientDirectoryRecord(oid, *pLocName);
apLoc->mpDirectoryRecord.reset(precord);
// Push it back on the vector of locations
@@ -2007,8 +1783,8 @@ void BackupDaemon::SetupLocations(BackupClientContext &rClientContext, const Con
// --------------------------------------------------------------------------
void BackupDaemon::SetupIDMapsForSync()
{
- // Need to do different things depending on whether it's an in memory implementation,
- // or whether it's all stored on disc.
+ // Need to do different things depending on whether it's an
+ // in memory implementation, or whether it's all stored on disc.
#ifdef BACKIPCLIENTINODETOIDMAP_IN_MEMORY_IMPLEMENTATION
@@ -2016,8 +1792,8 @@ void BackupDaemon::SetupIDMapsForSync()
DeleteIDMapVector(mNewIDMaps);
FillIDMapVector(mNewIDMaps, true /* new maps */);
- // Then make sure that the current maps have objects, even if they are empty
- // (for the very first run)
+ // Then make sure that the current maps have objects,
+ // even if they are empty (for the very first run)
if(mCurrentIDMaps.empty())
{
FillIDMapVector(mCurrentIDMaps, false /* current maps */);
@@ -2191,9 +1967,8 @@ void BackupDaemon::CommitIDMapsAfterSync()
#endif
if(::rename(newmap.c_str(), target.c_str()) != 0)
{
- BOX_ERROR("failed to rename ID map: " << newmap
- << " to " << target << ": "
- << strerror(errno));
+ BOX_LOG_SYS_ERROR("Failed to rename ID map: " <<
+ newmap << " to " << target);
THROW_EXCEPTION(CommonException, OSFileError)
}
}
@@ -2281,20 +2056,14 @@ void BackupDaemon::SetState(int State)
sprintf(newState, "state %d", State);
std::string message = newState;
-#ifdef WIN32
- EnterCriticalSection(&mMessageQueueLock);
- mMessageList.push_back(newState);
- SetEvent(mhMessageToSendEvent);
- LeaveCriticalSection(&mMessageQueueLock);
-#else
message += "\n";
- if(mpCommandSocketInfo == 0)
+ if(!mapCommandSocketInfo.get())
{
return;
}
- if(mpCommandSocketInfo->mpConnectedSocket.get() == 0)
+ if(mapCommandSocketInfo->mpConnectedSocket.get() == 0)
{
return;
}
@@ -2302,22 +2071,27 @@ void BackupDaemon::SetState(int State)
// Something connected to the command socket, tell it about the new state
try
{
- mpCommandSocketInfo->mpConnectedSocket->Write(message.c_str(),
+ mapCommandSocketInfo->mpConnectedSocket->Write(message.c_str(),
message.length());
}
+ catch(ConnectionException &ce)
+ {
+ BOX_NOTICE("Failed to write state to command socket: " <<
+ ce.what());
+ CloseCommandConnection();
+ }
catch(std::exception &e)
{
- BOX_ERROR("Internal error while writing state "
- "to command socket: " << e.what());
+ BOX_ERROR("Failed to write state to command socket: " <<
+ e.what());
CloseCommandConnection();
}
catch(...)
{
- BOX_ERROR("Internal error while writing state "
- "to command socket: unknown error");
+ BOX_ERROR("Failed to write state to command socket: "
+ "unknown error");
CloseCommandConnection();
}
-#endif
}
@@ -2351,7 +2125,7 @@ void BackupDaemon::TouchFileInWorkingDir(const char *Filename)
// Created: 25/2/04
//
// --------------------------------------------------------------------------
-void BackupDaemon::NotifySysadmin(int Event)
+void BackupDaemon::NotifySysadmin(SysadminNotifier::EventCode Event)
{
static const char *sEventNames[] =
{
@@ -2360,31 +2134,47 @@ void BackupDaemon::NotifySysadmin(int Event)
"backup-error",
"backup-start",
"backup-finish",
+ "backup-ok",
0
};
- BOX_TRACE("sizeof(sEventNames) == " << sizeof(sEventNames));
- BOX_TRACE("sizeof(*sEventNames) == " << sizeof(*sEventNames));
- BOX_TRACE("NotifyEvent__MAX == " << NotifyEvent__MAX);
- ASSERT((sizeof(sEventNames)/sizeof(*sEventNames)) == NotifyEvent__MAX + 1);
+ // BOX_TRACE("sizeof(sEventNames) == " << sizeof(sEventNames));
+ // BOX_TRACE("sizeof(*sEventNames) == " << sizeof(*sEventNames));
+ // BOX_TRACE("NotifyEvent__MAX == " << NotifyEvent__MAX);
+ ASSERT((sizeof(sEventNames)/sizeof(*sEventNames)) == SysadminNotifier::MAX + 1);
- BOX_TRACE("BackupDaemon::NotifySysadmin() called, event = " <<
- sEventNames[Event]);
-
- if(Event < 0 || Event >= NotifyEvent__MAX)
+ if(Event < 0 || Event >= SysadminNotifier::MAX)
{
+ BOX_ERROR("BackupDaemon::NotifySysadmin() called for "
+ "invalid event code " << Event);
THROW_EXCEPTION(BackupStoreException,
BadNotifySysadminEventCode);
}
- // Don't send lots of repeated messages
- if(mNotificationsSent[Event] &&
- Event != NotifyEvent_BackupStart &&
- Event != NotifyEvent_BackupFinish)
+ BOX_TRACE("BackupDaemon::NotifySysadmin() called, event = " <<
+ sEventNames[Event]);
+
+ if(!GetConfiguration().KeyExists("NotifyAlways") ||
+ !GetConfiguration().GetKeyValueBool("NotifyAlways"))
{
- BOX_WARNING("Suppressing duplicate notification about " <<
- sEventNames[Event]);
- return;
+ // Don't send lots of repeated messages
+ // Note: backup-start and backup-finish will always be
+ // logged, because mLastNotifiedEvent is never set to
+ // these values and therefore they are never "duplicates".
+ if(mLastNotifiedEvent == Event)
+ {
+ if(Event == SysadminNotifier::BackupOK)
+ {
+ BOX_INFO("Suppressing duplicate notification "
+ "about " << sEventNames[Event]);
+ }
+ else
+ {
+ BOX_WARNING("Suppressing duplicate notification "
+ "about " << sEventNames[Event]);
+ }
+ return;
+ }
}
// Is there a notification script?
@@ -2392,10 +2182,10 @@ void BackupDaemon::NotifySysadmin(int Event)
if(!conf.KeyExists("NotifyScript"))
{
// Log, and then return
- if(Event != NotifyEvent_BackupStart &&
- Event != NotifyEvent_BackupFinish)
+ if(Event != SysadminNotifier::BackupStart &&
+ Event != SysadminNotifier::BackupFinish)
{
- BOX_ERROR("Not notifying administrator about event "
+ BOX_INFO("Not notifying administrator about event "
<< sEventNames[Event] << " -- set NotifyScript "
"to do this in future");
}
@@ -2407,20 +2197,22 @@ void BackupDaemon::NotifySysadmin(int Event)
sEventNames[Event]);
// Log what we're about to do
- BOX_NOTICE("About to notify administrator about event "
+ BOX_INFO("About to notify administrator about event "
<< sEventNames[Event] << ", running script '"
<< script << "'");
// Then do it
- if(::system(script.c_str()) != 0)
+ int returnCode = ::system(script.c_str());
+ if(returnCode != 0)
{
- BOX_ERROR("Notify script returned an error code. ('"
- << script << "')");
+ BOX_WARNING("Notify script returned error code: " <<
+ returnCode << " ('" << script << "')");
+ }
+ else if(Event != SysadminNotifier::BackupStart &&
+ Event != SysadminNotifier::BackupFinish)
+ {
+ mLastNotifiedEvent = Event;
}
-
- // Flag that this is done so the administrator isn't constantly
- // bombarded with lots of errors
- mNotificationsSent[Event] = true;
}
@@ -2461,13 +2253,13 @@ void BackupDaemon::DeleteUnusedRootDirEntries(BackupClientContext &rContext)
// Entries to delete, and it's the right time to do so...
BOX_NOTICE("Deleting unused locations from store root...");
BackupProtocolClient &connection(rContext.GetConnection());
- for(std::vector<std::pair<int64_t,std::string> >::iterator i(mUnusedRootDirEntries.begin()); i != mUnusedRootDirEntries.end(); ++i)
+ for(std::vector<std::pair<int64_t,std::string> >::iterator
+ i(mUnusedRootDirEntries.begin());
+ i != mUnusedRootDirEntries.end(); ++i)
{
connection.QueryDeleteDirectory(i->first);
-
- // Log this
- BOX_NOTICE("Deleted " << i->second << " (ID " << i->first
- << ") from store root");
+ rContext.GetProgressNotifier().NotifyFileDeleted(
+ i->first, i->second);
}
// Reset state
@@ -2738,9 +2530,12 @@ BackupDaemon::CommandSocketInfo::~CommandSocketInfo()
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupDaemon::SerializeStoreObjectInfo(int64_t aClientStoreMarker, box_time_t theLastSyncTime, box_time_t theNextSyncTime)
-// Purpose: Serializes remote directory and file information into a stream of bytes, using an Archive abstraction.
-//
+// Name: BackupDaemon::SerializeStoreObjectInfo(
+// box_time_t theLastSyncTime,
+// box_time_t theNextSyncTime)
+// Purpose: Serializes remote directory and file information
+// into a stream of bytes, using an Archive
+// abstraction.
// Created: 2005/04/11
//
// --------------------------------------------------------------------------
@@ -2749,7 +2544,8 @@ static const int STOREOBJECTINFO_MAGIC_ID_VALUE = 0x7777525F;
static const std::string STOREOBJECTINFO_MAGIC_ID_STRING = "BBACKUPD-STATE";
static const int STOREOBJECTINFO_VERSION = 2;
-bool BackupDaemon::SerializeStoreObjectInfo(int64_t aClientStoreMarker, box_time_t theLastSyncTime, box_time_t theNextSyncTime) const
+bool BackupDaemon::SerializeStoreObjectInfo(box_time_t theLastSyncTime,
+ box_time_t theNextSyncTime) const
{
if(!GetConfiguration().KeyExists("StoreObjectInfoFile"))
{
@@ -2778,7 +2574,7 @@ bool BackupDaemon::SerializeStoreObjectInfo(int64_t aClientStoreMarker, box_time
anArchive.Write(STOREOBJECTINFO_MAGIC_ID_STRING);
anArchive.Write(STOREOBJECTINFO_VERSION);
anArchive.Write(GetLoadedConfigModifiedTime());
- anArchive.Write(aClientStoreMarker);
+ anArchive.Write(mClientStoreMarker);
anArchive.Write(theLastSyncTime);
anArchive.Write(theNextSyncTime);
@@ -2830,15 +2626,13 @@ bool BackupDaemon::SerializeStoreObjectInfo(int64_t aClientStoreMarker, box_time
}
catch(std::exception &e)
{
- BOX_ERROR("Internal error writing store object "
- "info file (" << StoreObjectInfoFile << "): "
- << e.what());
+ BOX_ERROR("Failed to write StoreObjectInfoFile: " <<
+ StoreObjectInfoFile << ": " << e.what());
}
catch(...)
{
- BOX_ERROR("Internal error writing store object "
- "info file (" << StoreObjectInfoFile << "): "
- "unknown error");
+ BOX_ERROR("Failed to write StoreObjectInfoFile: " <<
+ StoreObjectInfoFile << ": unknown error");
}
return created;
@@ -2847,13 +2641,17 @@ bool BackupDaemon::SerializeStoreObjectInfo(int64_t aClientStoreMarker, box_time
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupDaemon::DeserializeStoreObjectInfo(int64_t & aClientStoreMarker, box_time_t & theLastSyncTime, box_time_t & theNextSyncTime)
-// Purpose: Deserializes remote directory and file information from a stream of bytes, using an Archive abstraction.
-//
+// Name: BackupDaemon::DeserializeStoreObjectInfo(
+// box_time_t & theLastSyncTime,
+// box_time_t & theNextSyncTime)
+// Purpose: Deserializes remote directory and file information
+// from a stream of bytes, using an Archive
+// abstraction.
// Created: 2005/04/11
//
// --------------------------------------------------------------------------
-bool BackupDaemon::DeserializeStoreObjectInfo(int64_t & aClientStoreMarker, box_time_t & theLastSyncTime, box_time_t & theNextSyncTime)
+bool BackupDaemon::DeserializeStoreObjectInfo(box_time_t & theLastSyncTime,
+ box_time_t & theNextSyncTime)
{
//
//
@@ -2945,7 +2743,7 @@ bool BackupDaemon::DeserializeStoreObjectInfo(int64_t & aClientStoreMarker, box_
//
// this is it, go at it
//
- anArchive.Read(aClientStoreMarker);
+ anArchive.Read(mClientStoreMarker);
anArchive.Read(theLastSyncTime);
anArchive.Read(theNextSyncTime);
@@ -3023,7 +2821,7 @@ bool BackupDaemon::DeserializeStoreObjectInfo(int64_t & aClientStoreMarker, box_
DeleteAllLocations();
- aClientStoreMarker = BackupClientContext::ClientStoreMarker_NotKnown;
+ mClientStoreMarker = BackupClientContext::ClientStoreMarker_NotKnown;
theLastSyncTime = 0;
theNextSyncTime = 0;
@@ -3057,9 +2855,10 @@ bool BackupDaemon::DeleteStoreObjectInfo() const
// Check to see if the file exists
if(!FileExists(storeObjectInfoFile.c_str()))
{
- // File doesn't exist -- so can't be deleted. But something isn't quite right, so log a message
- BOX_WARNING("Store object info file did not exist when it "
- "was supposed to. (" << storeObjectInfoFile << ")");
+ // File doesn't exist -- so can't be deleted. But something
+ // isn't quite right, so log a message
+ BOX_WARNING("StoreObjectInfoFile did not exist when it "
+ "was supposed to: " << storeObjectInfoFile);
// Return true to stop things going around in a loop
return true;
@@ -3068,8 +2867,8 @@ bool BackupDaemon::DeleteStoreObjectInfo() const
// Actually delete it
if(::unlink(storeObjectInfoFile.c_str()) != 0)
{
- BOX_ERROR("Failed to delete the old store object info file: "
- << storeObjectInfoFile << ": "<< strerror(errno));
+ BOX_LOG_SYS_ERROR("Failed to delete the old "
+ "StoreObjectInfoFile: " << storeObjectInfoFile);
return false;
}
diff --git a/bin/bbackupd/BackupDaemon.h b/bin/bbackupd/BackupDaemon.h
index 62f9c393..0c864abd 100644
--- a/bin/bbackupd/BackupDaemon.h
+++ b/bin/bbackupd/BackupDaemon.h
@@ -14,16 +14,20 @@
#include <string>
#include <memory>
+#include "BackupClientContext.h"
+#include "BackupClientDirectoryRecord.h"
#include "BoxTime.h"
#include "Daemon.h"
-#include "BackupClientDirectoryRecord.h"
+#include "Logging.h"
#include "Socket.h"
#include "SocketListen.h"
#include "SocketStream.h"
-#include "Logging.h"
+#include "TLSContext.h"
+
#include "autogen_BackupProtocolClient.h"
#ifdef WIN32
+ #include "WinNamedPipeListener.h"
#include "WinNamedPipeStream.h"
#endif
@@ -43,7 +47,8 @@ class Archive;
// Created: 2003/10/08
//
// --------------------------------------------------------------------------
-class BackupDaemon : public Daemon, ProgressNotifier
+class BackupDaemon : public Daemon, ProgressNotifier, LocationResolver,
+RunStatusProvider, SysadminNotifier
{
public:
BackupDaemon();
@@ -52,10 +57,10 @@ public:
private:
// methods below do partial (specialized) serialization of
// client state only
- bool SerializeStoreObjectInfo(int64_t aClientStoreMarker,
- box_time_t theLastSyncTime, box_time_t theNextSyncTime) const;
- bool DeserializeStoreObjectInfo(int64_t & aClientStoreMarker,
- box_time_t & theLastSyncTime, box_time_t & theNextSyncTime);
+ bool SerializeStoreObjectInfo(box_time_t theLastSyncTime,
+ box_time_t theNextSyncTime) const;
+ bool DeserializeStoreObjectInfo(box_time_t & theLastSyncTime,
+ box_time_t & theNextSyncTime);
bool DeleteStoreObjectInfo() const;
BackupDaemon(const BackupDaemon &);
@@ -65,6 +70,14 @@ public:
std::string GetOptionString();
int ProcessOption(signed int option);
int Main(const std::string &rConfigFileName);
+
+ // This shouldn't be here, but apparently gcc on
+ // Windows has no idea about inherited methods...
+ virtual int Main(const char *DefaultConfigFile, int argc,
+ const char *argv[])
+ {
+ return Daemon::Main(DefaultConfigFile, argc, argv);
+ }
#endif
void Run();
@@ -88,21 +101,22 @@ public:
int GetState() {return mState;}
// Allow other classes to call this too
- enum
- {
- NotifyEvent_StoreFull = 0,
- NotifyEvent_ReadError,
- NotifyEvent_BackupError,
- NotifyEvent_BackupStart,
- NotifyEvent_BackupFinish,
- NotifyEvent__MAX
- // When adding notifications, remember to add strings to NotifySysadmin()
- };
- void NotifySysadmin(int Event);
+ void NotifySysadmin(SysadminNotifier::EventCode Event);
private:
void Run2();
+public:
+ void InitCrypto();
+ void RunSyncNowWithExceptionHandling();
+ void RunSyncNow();
+ void OnBackupStart();
+ void OnBackupFinish();
+ // TouchFileInWorkingDir is only here for use by Boxi.
+ // This does NOT constitute an API!
+ void TouchFileInWorkingDir(const char *Filename);
+
+private:
void DeleteAllLocations();
void SetupLocations(BackupClientContext &rClientContext, const Configuration &rLocationsConf);
@@ -126,8 +140,6 @@ private:
void CloseCommandConnection();
void SendSyncStartOrFinish(bool SendStart);
- void TouchFileInWorkingDir(const char *Filename);
-
void DeleteUnusedRootDirEntries(BackupClientContext &rContext);
#ifdef PLATFORM_CANNOT_FIND_PEER_UID_OF_UNIX_SOCKET
@@ -137,7 +149,7 @@ private:
int UseScriptToSeeIfSyncAllowed();
-private:
+public:
class Location
{
public:
@@ -157,7 +169,11 @@ private:
ExcludeList *mpExcludeFiles;
ExcludeList *mpExcludeDirs;
};
-
+
+ typedef const std::vector<Location *> Locations;
+ Locations GetLocations() { return mLocations; }
+
+private:
int mState; // what the daemon is currently doing
std::vector<Location *> mLocations;
@@ -179,7 +195,8 @@ private:
CommandSocketInfo &operator=(const CommandSocketInfo &);
public:
#ifdef WIN32
- WinNamedPipeStream mListeningSocket;
+ WinNamedPipeListener<1 /* listen backlog */> mListeningSocket;
+ std::auto_ptr<WinNamedPipeStream> mpConnectedSocket;
#else
SocketListen<SocketStream, 1 /* listen backlog */> mListeningSocket;
std::auto_ptr<SocketStream> mpConnectedSocket;
@@ -188,23 +205,51 @@ private:
};
// Using a socket?
- CommandSocketInfo *mpCommandSocketInfo;
+ std::auto_ptr<CommandSocketInfo> mapCommandSocketInfo;
// Stop notifications being repeated.
- bool mNotificationsSent[NotifyEvent__MAX];
+ SysadminNotifier::EventCode mLastNotifiedEvent;
// Unused entries in the root directory wait a while before being deleted
box_time_t mDeleteUnusedRootDirEntriesAfter; // time to delete them
std::vector<std::pair<int64_t,std::string> > mUnusedRootDirEntries;
+ int64_t mClientStoreMarker;
+ bool mStorageLimitExceeded;
+ bool mReadErrorsOnFilesystemObjects;
+ box_time_t mLastSyncTime, mNextSyncTime;
+ box_time_t mCurrentSyncStartTime, mUpdateStoreInterval;
+ TLSContext mTlsContext;
+ bool mDeleteStoreObjectInfoFile;
+ bool mDoSyncForcedByPreviousSyncError;
+
public:
bool StopRun() { return this->Daemon::StopRun(); }
+ bool StorageLimitExceeded() { return mStorageLimitExceeded; }
private:
bool mLogAllFileAccess;
+public:
+ ProgressNotifier* GetProgressNotifier() { return mpProgressNotifier; }
+ LocationResolver* GetLocationResolver() { return mpLocationResolver; }
+ RunStatusProvider* GetRunStatusProvider() { return mpRunStatusProvider; }
+ SysadminNotifier* GetSysadminNotifier() { return mpSysadminNotifier; }
+ void SetProgressNotifier (ProgressNotifier* p) { mpProgressNotifier = p; }
+ void SetLocationResolver (LocationResolver* p) { mpLocationResolver = p; }
+ void SetRunStatusProvider(RunStatusProvider* p) { mpRunStatusProvider = p; }
+ void SetSysadminNotifier (SysadminNotifier* p) { mpSysadminNotifier = p; }
+
+private:
+ ProgressNotifier* mpProgressNotifier;
+ LocationResolver* mpLocationResolver;
+ RunStatusProvider* mpRunStatusProvider;
+ SysadminNotifier* mpSysadminNotifier;
+
/* ProgressNotifier implementation */
public:
+ virtual void NotifyIDMapsSetup(BackupClientContext& rContext) { }
+
virtual void NotifyScanDirectory(
const BackupClientDirectoryRecord* pDirRecord,
const std::string& rLocalPath)
@@ -387,7 +432,7 @@ public:
{
if (mLogAllFileAccess)
{
- BOX_INFO("Uploading complete file: " << rLocalPath);
+ BOX_NOTICE("Uploading complete file: " << rLocalPath);
}
}
virtual void NotifyFileUploadingPatch(
@@ -396,7 +441,7 @@ public:
{
if (mLogAllFileAccess)
{
- BOX_INFO("Uploading patch to file: " << rLocalPath);
+ BOX_NOTICE("Uploading patch to file: " << rLocalPath);
}
}
virtual void NotifyFileUploaded(
@@ -406,7 +451,7 @@ public:
{
if (mLogAllFileAccess)
{
- BOX_INFO("Uploaded file: " << rLocalPath);
+ BOX_NOTICE("Uploaded file: " << rLocalPath);
}
}
virtual void NotifyFileSynchronised(
@@ -419,18 +464,51 @@ public:
BOX_INFO("Synchronised file: " << rLocalPath);
}
}
+ virtual void NotifyDirectoryDeleted(
+ int64_t ObjectID,
+ const std::string& rRemotePath)
+ {
+ if (mLogAllFileAccess)
+ {
+ BOX_NOTICE("Deleted directory: " << rRemotePath <<
+ " (ID " << BOX_FORMAT_OBJECTID(ObjectID) <<
+ ")");
+ }
+ }
+ virtual void NotifyFileDeleted(
+ int64_t ObjectID,
+ const std::string& rRemotePath)
+ {
+ if (mLogAllFileAccess)
+ {
+ BOX_NOTICE("Deleted file: " << rRemotePath <<
+ " (ID " << BOX_FORMAT_OBJECTID(ObjectID) <<
+ ")");
+ }
+ }
+ virtual void NotifyReadProgress(int64_t readSize, int64_t offset,
+ int64_t length, box_time_t elapsed, box_time_t finish)
+ {
+ BOX_TRACE("Read " << readSize << " bytes at " << offset <<
+ ", " << (length - offset) << " remain, eta " <<
+ BoxTimeToSeconds(finish - elapsed) << "s");
+ }
+ virtual void NotifyReadProgress(int64_t readSize, int64_t offset,
+ int64_t length)
+ {
+ BOX_TRACE("Read " << readSize << " bytes at " << offset <<
+ ", " << (length - offset) << " remain");
+ }
+ virtual void NotifyReadProgress(int64_t readSize, int64_t offset)
+ {
+ BOX_TRACE("Read " << readSize << " bytes at " << offset <<
+ ", unknown bytes remaining");
+ }
#ifdef WIN32
- public:
- void RunHelperThread(void);
-
private:
- bool mDoSyncFlagOut, mSyncIsForcedOut;
bool mInstallService, mRemoveService, mRunAsService;
std::string mServiceName;
- HANDLE mhMessageToSendEvent, mhCommandReceivedEvent;
- CRITICAL_SECTION mMessageQueueLock;
- std::vector<std::string> mMessageList;
#endif
};
diff --git a/bin/bbackupd/BackupDaemonInterface.h b/bin/bbackupd/BackupDaemonInterface.h
new file mode 100644
index 00000000..5bbdd427
--- /dev/null
+++ b/bin/bbackupd/BackupDaemonInterface.h
@@ -0,0 +1,164 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: BackupDaemonInterface.h
+// Purpose: Interfaces for managing a BackupDaemon
+// Created: 2008/12/30
+//
+// --------------------------------------------------------------------------
+
+#ifndef BACKUPDAEMONINTERFACE__H
+#define BACKUPDAEMONINTERFACE__H
+
+#include <string>
+// #include <map>
+
+// #include "BackupClientFileAttributes.h"
+// #include "BackupStoreDirectory.h"
+#include "BoxTime.h"
+// #include "MD5Digest.h"
+// #include "ReadLoggingStream.h"
+// #include "RunStatusProvider.h"
+
+class Archive;
+class BackupClientContext;
+class BackupDaemon;
+
+// --------------------------------------------------------------------------
+//
+// Class
+// Name: SysadminNotifier
+// Purpose: Provides a NotifySysadmin() method to send mail to the sysadmin
+// Created: 2005/11/15
+//
+// --------------------------------------------------------------------------
+class SysadminNotifier
+{
+ public:
+ virtual ~SysadminNotifier() { }
+
+ typedef enum
+ {
+ StoreFull = 0,
+ ReadError,
+ BackupError,
+ BackupStart,
+ BackupFinish,
+ BackupOK,
+ MAX
+ // When adding notifications, remember to add
+ // strings to NotifySysadmin()
+ }
+ EventCode;
+
+ virtual void NotifySysadmin(EventCode Event) = 0;
+};
+
+// --------------------------------------------------------------------------
+//
+// Class
+// Name: ProgressNotifier
+// Purpose: Provides methods for the backup library to inform the user
+// interface about its progress with the backup
+// Created: 2005/11/20
+//
+// --------------------------------------------------------------------------
+
+class BackupClientContext;
+class BackupClientDirectoryRecord;
+
+class ProgressNotifier
+{
+ public:
+ virtual ~ProgressNotifier() { }
+ virtual void NotifyIDMapsSetup(BackupClientContext& rContext) = 0;
+ virtual void NotifyScanDirectory(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath) = 0;
+ virtual void NotifyDirStatFailed(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath,
+ const std::string& rErrorMsg) = 0;
+ virtual void NotifyFileStatFailed(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath,
+ const std::string& rErrorMsg) = 0;
+ virtual void NotifyDirListFailed(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath,
+ const std::string& rErrorMsg) = 0;
+ virtual void NotifyMountPointSkipped(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath) = 0;
+ virtual void NotifyFileExcluded(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath) = 0;
+ virtual void NotifyDirExcluded(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath) = 0;
+ virtual void NotifyUnsupportedFileType(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath) = 0;
+ virtual void NotifyFileReadFailed(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath,
+ const std::string& rErrorMsg) = 0;
+ virtual void NotifyFileModifiedInFuture(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath) = 0;
+ virtual void NotifyFileSkippedServerFull(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath) = 0;
+ virtual void NotifyFileUploadException(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath,
+ const BoxException& rException) = 0;
+ virtual void NotifyFileUploadServerError(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath,
+ int type, int subtype) = 0;
+ virtual void NotifyFileUploading(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath) = 0;
+ virtual void NotifyFileUploadingPatch(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath) = 0;
+ virtual void NotifyFileUploaded(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath,
+ int64_t FileSize) = 0;
+ virtual void NotifyFileSynchronised(
+ const BackupClientDirectoryRecord* pDirRecord,
+ const std::string& rLocalPath,
+ int64_t FileSize) = 0;
+ virtual void NotifyDirectoryDeleted(
+ int64_t ObjectID,
+ const std::string& rRemotePath) = 0;
+ virtual void NotifyFileDeleted(
+ int64_t ObjectID,
+ const std::string& rRemotePath) = 0;
+ virtual void NotifyReadProgress(int64_t readSize, int64_t offset,
+ int64_t length, box_time_t elapsed, box_time_t finish) = 0;
+ virtual void NotifyReadProgress(int64_t readSize, int64_t offset,
+ int64_t length) = 0;
+ virtual void NotifyReadProgress(int64_t readSize, int64_t offset) = 0;
+};
+
+// --------------------------------------------------------------------------
+//
+// Class
+// Name: LocationResolver
+// Purpose: Interface for classes that can resolve locations to paths,
+// like BackupDaemon
+// Created: 2003/10/08
+//
+// --------------------------------------------------------------------------
+class LocationResolver
+{
+public:
+ virtual ~LocationResolver() { }
+ virtual bool FindLocationPathName(const std::string &rLocationName,
+ std::string &rPathOut) const = 0;
+};
+
+#endif // BACKUPDAEMONINTERFACE__H
diff --git a/bin/bbackupd/Win32ServiceFunctions.cpp b/bin/bbackupd/Win32ServiceFunctions.cpp
index a7bf6bd9..2df914a7 100644
--- a/bin/bbackupd/Win32ServiceFunctions.cpp
+++ b/bin/bbackupd/Win32ServiceFunctions.cpp
@@ -203,12 +203,12 @@ int InstallService(const char* pConfigFileName, const std::string& rServiceName)
{
if (pConfigFileName != NULL)
{
- struct stat st;
+ EMU_STRUCT_STAT st;
if (emu_stat(pConfigFileName, &st) != 0)
{
- BOX_ERROR("Failed to open configuration file '" <<
- pConfigFileName << "': " << strerror(errno));
+ BOX_LOG_SYS_ERROR("Failed to open configuration file "
+ "'" << pConfigFileName << "'");
return 1;
}
@@ -221,7 +221,7 @@ int InstallService(const char* pConfigFileName, const std::string& rServiceName)
}
}
- SC_HANDLE scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
+ SC_HANDLE scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
if (!scm)
{
diff --git a/bin/bbackupd/bbackupd-config.in b/bin/bbackupd/bbackupd-config.in
index 16ddb75c..925dcc3e 100755
--- a/bin/bbackupd/bbackupd-config.in
+++ b/bin/bbackupd/bbackupd-config.in
@@ -26,7 +26,7 @@ Parameters:
explicitly, using bbackupctl sync
account-num (hexdecimal) and server-hostname
are supplied by the server administrator
- working-dir is usually @localstatedir_expanded@
+ working-dir is usually @localstatedir_expanded@/bbackupd
backup directories is list of directories to back up
__E
@@ -227,7 +227,7 @@ SUBJECT="BACKUP PROBLEM on host $hostname"
SENDTO="$current_username"
if [ "\$1" = "" ]; then
- echo "Usage: $0 <store-full|read-error|backup-error|backup-start|backup-finish>" >&2
+ echo "Usage: \$0 <store-full|read-error|backup-error|backup-start|backup-finish>" >&2
exit 2
elif [ "\$1" = store-full ]; then
$sendmail \$SENDTO <<EOM
@@ -577,7 +577,7 @@ What you need to do now...
more files will be backed up. You want to know about this.
6) Start the backup daemon with the command
- @bindir_expanded@/bbackupd$daemon_args
+ @sbindir_expanded@/bbackupd$daemon_args
in /etc/rc.local, or your local equivalent.
Note that bbackupd must run as root.
__E
diff --git a/bin/bbackupd/bbackupd.cpp b/bin/bbackupd/bbackupd.cpp
index a0f275b3..d334a2df 100644
--- a/bin/bbackupd/bbackupd.cpp
+++ b/bin/bbackupd/bbackupd.cpp
@@ -29,7 +29,7 @@ int main(int argc, const char *argv[])
MAINHELPER_START
- Logging::SetProgramName("Box Backup (bbackupd)");
+ Logging::SetProgramName("bbackupd");
Logging::ToConsole(true);
Logging::ToSyslog (true);
diff --git a/bin/bbackupd/win32/NotifySysAdmin.vbs b/bin/bbackupd/win32/NotifySysAdmin.vbs
index 49082887..712d92da 100644
--- a/bin/bbackupd/win32/NotifySysAdmin.vbs
+++ b/bin/bbackupd/win32/NotifySysAdmin.vbs
@@ -10,44 +10,62 @@ Dim smtpserver
Set WshNet = CreateObject("WScript.Network")
hostname = WshNet.ComputerName
-account = "0a1"
+account = "0x1"
from = "boxbackup@" & hostname
sendto = "admin@example.com"
-subjtmpl = "BACKUP PROBLEM on host " & hostname
smtpserver = "smtp.example.com"
+subjtmpl = "BACKUP PROBLEM on host " & hostname
Set args = WScript.Arguments
If args(0) = "store-full" Then
subject = subjtmpl & " (store full)"
- body = "The store account for "&hostname&" is full." & vbCrLf & vbCrLf & _
- "=============================" & vbCrLf & _
- "FILES ARE NOT BEING BACKED UP" & vbCrLf & _
- "=============================" & vbCrLf & vbCrLf & _
- "Please adjust the limits on account "&account&" on server "&hostname&"." _
- & vbCrLf
+ body = "The store account for "&hostname&" is full." & vbCrLf & _
+ vbCrLf & _
+ "=============================" & vbCrLf & _
+ "FILES ARE NOT BEING BACKED UP" & vbCrLf & _
+ "=============================" & vbCrLf & _
+ vbCrLf & _
+ "Please adjust the limits on account "&account&" on server "&hostname&"." _
+ & vbCrLf
SendMail from,sendto,subject,body
ElseIf args(0) = "read-error" Then
subject = subjtmpl & " (read errors)"
- body = "Errors occured reading some files or directories for backup on "&hostname&"." _
- & vbCrLf & vbCrLf & _
- "===================================" & vbCrLf & _
- "THESE FILES ARE NOT BEING BACKED UP" & vbCrLf & _
- "===================================" & vbCrLf & vbCrLf & _
- "Check the logs on "&hostname&" for the files and directories which caused" & _
- "these errors, and take appropraite action." & vbCrLf & vbCrLf & _
- "Other files are being backed up." & vbCrLf
+ body = "Errors occurred reading some files or directories " & _
+ "for backup on " & hostname & "." & vbCrLf & _
+ vbCrLf & _
+ "===================================" & vbCrLf & _
+ "THESE FILES ARE NOT BEING BACKED UP" & vbCrLf & _
+ "===================================" & vbCrLf & vbCrLf & _
+ "Check the logs on "&hostname&" for the files and " & _
+ "directories which caused" & vbCrLf & _
+ "these errors, and take appropriate action." & vbCrLf & _
+ vbCrLf & _
+ "Other files are being backed up." & vbCrLf
+ SendMail from,sendto,subject,body
+ElseIf args(0) = "backup-error" Then
+ subject = subjtmpl & " (read errors)"
+ body = "An error occurred during the backup on "&hostname&"." _
+ & vbCrLf & vbCrLf & _
+ "==========================" & vbCrLf & _
+ "FILES MAY NOT BE BACKED UP" & vbCrLf & _
+ "==========================" & vbCrLf & _
+ vbCrLf & _
+ "Check the logs on "&hostname&" for more " & _
+ "information about the error, " & vbCrLf & _
+ "and take appropriate action." & vbCrLf
SendMail from,sendto,subject,body
-ElseIf args(0) = "backup-start" Or args(0) = "backup-finish" Then
+ElseIf args(0) = "backup-start" Or args(0) = "backup-finish" _
+ Or args(0) = "backup-ok" Then
' do nothing for these messages by default
Else
subject = subjtmpl & " (unknown)"
body = "The backup daemon on "&hostname&" reported an unknown error." _
- & vbCrLf & vbCrLf & _
- "==========================" & vbCrLf & _
- "FILES MAY NOT BE BACKED UP" & vbCrLf & _
- "==========================" & vbCrLf & vbCrLf & _
- "Please check the logs on "&hostname&"." & vbCrLf
+ & vbCrLf & vbCrLf & _
+ "==========================" & vbCrLf & _
+ "FILES MAY NOT BE BACKED UP" & vbCrLf & _
+ "==========================" & vbCrLf & vbCrLf & _
+ "Please check the logs on "&hostname&"." & vbCrLf
SendMail from,sendto,subject,body
End If
diff --git a/bin/bbackupd/win32/bbackupd.conf b/bin/bbackupd/win32/bbackupd.conf
index 6c987f7d..b0793b29 100644
--- a/bin/bbackupd/win32/bbackupd.conf
+++ b/bin/bbackupd/win32/bbackupd.conf
@@ -173,7 +173,7 @@ Server
# If a directive ends in Regex, then it is a regular expression rather than a
# explicit full pathname. See:
#
-# http://bbdev.fluffy.co.uk/trac/wiki/Win32Regex
+# http://www.boxbackup.org/trac/wiki/Win32Regex
#
# for more information about regular expressions on Windows.
#
diff --git a/bin/bbackupquery/BackupQueries.cpp b/bin/bbackupquery/BackupQueries.cpp
index b6984641..687dcb05 100644
--- a/bin/bbackupquery/BackupQueries.cpp
+++ b/bin/bbackupquery/BackupQueries.cpp
@@ -13,7 +13,6 @@
#include <unistd.h>
#endif
-#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
@@ -25,29 +24,33 @@
#include <dirent.h>
#endif
-#include <set>
+#include <cstring>
#include <limits>
+#include <iostream>
+#include <ostream>
+#include <set>
+#include "BackupClientFileAttributes.h"
+#include "BackupClientMakeExcludeList.h"
+#include "BackupClientRestore.h"
#include "BackupQueries.h"
-#include "Utils.h"
-#include "Configuration.h"
-#include "autogen_BackupProtocolClient.h"
-#include "BackupStoreFilenameClear.h"
#include "BackupStoreDirectory.h"
-#include "IOStream.h"
-#include "BoxTimeToText.h"
-#include "FileStream.h"
+#include "BackupStoreException.h"
#include "BackupStoreFile.h"
-#include "TemporaryDirectory.h"
-#include "FileModificationTime.h"
-#include "BackupClientFileAttributes.h"
+#include "BackupStoreFilenameClear.h"
+#include "BoxTimeToText.h"
#include "CommonException.h"
-#include "BackupClientRestore.h"
-#include "BackupStoreException.h"
+#include "Configuration.h"
#include "ExcludeList.h"
-#include "BackupClientMakeExcludeList.h"
-#include "PathUtils.h"
+#include "FileModificationTime.h"
+#include "FileStream.h"
+#include "IOStream.h"
#include "Logging.h"
+#include "PathUtils.h"
+#include "SelfFlushingStream.h"
+#include "TemporaryDirectory.h"
+#include "Utils.h"
+#include "autogen_BackupProtocolClient.h"
#include "MemLeakFindOn.h"
@@ -68,8 +71,10 @@
// Created: 2003/10/10
//
// --------------------------------------------------------------------------
-BackupQueries::BackupQueries(BackupProtocolClient &rConnection, const Configuration &rConfiguration)
- : mrConnection(rConnection),
+BackupQueries::BackupQueries(BackupProtocolClient &rConnection,
+ const Configuration &rConfiguration, bool readWrite)
+ : mReadWrite(readWrite),
+ mrConnection(rConnection),
mrConfiguration(rConfiguration),
mQuitNow(false),
mRunningAsRoot(false),
@@ -115,7 +120,13 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine)
if(Command[0] == 's' && Command[1] == 'h' && Command[2] == ' ' && Command[3] != '\0')
{
// Yes, run shell command
- ::system(Command + 3);
+ int result = ::system(Command + 3);
+ if(result != 0)
+ {
+ BOX_WARNING("System command returned error code " <<
+ result);
+ SetReturnCode(ReturnCode::Command_Error);
+ }
return;
}
@@ -209,28 +220,36 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine)
{ "getobject", "" },
{ "get", "i" },
{ "compare", "alcqAEQ" },
- { "restore", "dri" },
+ { "restore", "drif" },
{ "help", "" },
- { "usage", "" },
+ { "usage", "m" },
{ "undelete", "" },
+ { "delete", "" },
{ NULL, NULL }
};
- #define COMMAND_Quit 0
- #define COMMAND_Exit 1
- #define COMMAND_List 2
- #define COMMAND_pwd 3
- #define COMMAND_cd 4
- #define COMMAND_lcd 5
- #define COMMAND_sh 6
- #define COMMAND_GetObject 7
- #define COMMAND_Get 8
- #define COMMAND_Compare 9
- #define COMMAND_Restore 10
- #define COMMAND_Help 11
- #define COMMAND_Usage 12
- #define COMMAND_Undelete 13
- static const char *alias[] = {"ls", 0};
- static const int aliasIs[] = {COMMAND_List, 0};
+
+ typedef enum
+ {
+ Command_Quit = 0,
+ Command_Exit,
+ Command_List,
+ Command_pwd,
+ Command_cd,
+ Command_lcd,
+ Command_sh,
+ Command_GetObject,
+ Command_Get,
+ Command_Compare,
+ Command_Restore,
+ Command_Help,
+ Command_Usage,
+ Command_Undelete,
+ Command_Delete,
+ }
+ CommandType;
+
+ static const char *alias[] = {"ls", 0};
+ static const int aliasIs[] = {Command_List, 0};
// Work out which command it is...
int cmd = 0;
@@ -284,25 +303,25 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine)
}
}
- if(cmd != COMMAND_Quit && cmd != COMMAND_Exit)
+ if(cmd != Command_Quit && cmd != Command_Exit)
{
// If not a quit command, set the return code to zero
- SetReturnCode(0);
+ SetReturnCode(ReturnCode::Command_OK);
}
// Handle command
switch(cmd)
{
- case COMMAND_Quit:
- case COMMAND_Exit:
+ case Command_Quit:
+ case Command_Exit:
mQuitNow = true;
break;
- case COMMAND_List:
+ case Command_List:
CommandList(args, opts);
break;
- case COMMAND_pwd:
+ case Command_pwd:
{
// Simple implementation, so do it here
BOX_INFO(GetCurrentDirectoryName() << " (" <<
@@ -310,47 +329,52 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine)
}
break;
- case COMMAND_cd:
+ case Command_cd:
CommandChangeDir(args, opts);
break;
- case COMMAND_lcd:
+ case Command_lcd:
CommandChangeLocalDir(args);
break;
- case COMMAND_sh:
+ case Command_sh:
BOX_ERROR("The command to run must be specified as an argument.");
break;
- case COMMAND_GetObject:
+ case Command_GetObject:
CommandGetObject(args, opts);
break;
- case COMMAND_Get:
+ case Command_Get:
CommandGet(args, opts);
break;
- case COMMAND_Compare:
+ case Command_Compare:
CommandCompare(args, opts);
break;
- case COMMAND_Restore:
+ case Command_Restore:
CommandRestore(args, opts);
break;
- case COMMAND_Usage:
- CommandUsage();
+ case Command_Usage:
+ CommandUsage(opts);
break;
- case COMMAND_Help:
+ case Command_Help:
CommandHelp(args);
break;
- case COMMAND_Undelete:
+ case Command_Undelete:
CommandUndelete(args, opts);
break;
+ case Command_Delete:
+ CommandDelete(args, opts);
+ break;
+
default:
+ BOX_ERROR("Unknown command: " << Command);
break;
}
}
@@ -402,6 +426,7 @@ void BackupQueries::CommandList(const std::vector<std::string> &args, const bool
{
BOX_ERROR("Directory '" << args[0] << "' not found "
"on store.");
+ SetReturnCode(ReturnCode::Command_Error);
return;
}
}
@@ -427,11 +452,28 @@ void BackupQueries::List(int64_t DirID, const std::string &rListRoot, const bool
if(!opts[LIST_OPTION_ALLOWDELETED]) excludeFlags |= BackupProtocolClientListDirectory::Flags_Deleted;
// Do communication
- mrConnection.QueryListDirectory(
- DirID,
- BackupProtocolClientListDirectory::Flags_INCLUDE_EVERYTHING, // both files and directories
- excludeFlags,
- true /* want attributes */);
+ try
+ {
+ mrConnection.QueryListDirectory(
+ DirID,
+ BackupProtocolClientListDirectory::Flags_INCLUDE_EVERYTHING,
+ // both files and directories
+ excludeFlags,
+ true /* want attributes */);
+ }
+ catch (std::exception &e)
+ {
+ BOX_ERROR("Failed to list directory: " << e.what());
+ SetReturnCode(ReturnCode::Command_Error);
+ return;
+ }
+ catch (...)
+ {
+ BOX_ERROR("Failed to list directory: unknown error");
+ SetReturnCode(ReturnCode::Command_Error);
+ return;
+ }
+
// Retrieve the directory from the stream following
BackupStoreDirectory dir;
@@ -574,15 +616,18 @@ void BackupQueries::List(int64_t DirID, const std::string &rListRoot, const bool
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupQueries::FindDirectoryObjectID(const std::string &)
-// Purpose: Find the object ID of a directory on the store, or return 0 for not found.
-// If pStack != 0, the object is set to the stack of directories.
-// Will start from the current directory stack.
+// Name: BackupQueries::FindDirectoryObjectID(const
+// std::string &)
+// Purpose: Find the object ID of a directory on the store,
+// or return 0 for not found. If pStack != 0, the
+// object is set to the stack of directories.
+// Will start from the current directory stack.
// Created: 2003/10/10
//
// --------------------------------------------------------------------------
-int64_t BackupQueries::FindDirectoryObjectID(const std::string &rDirName, bool AllowOldVersion,
- bool AllowDeletedDirs, std::vector<std::pair<std::string, int64_t> > *pStack)
+int64_t BackupQueries::FindDirectoryObjectID(const std::string &rDirName,
+ bool AllowOldVersion, bool AllowDeletedDirs,
+ std::vector<std::pair<std::string, int64_t> > *pStack)
{
// Split up string into elements
std::vector<std::string> dirElements;
@@ -747,6 +792,7 @@ void BackupQueries::CommandChangeDir(const std::vector<std::string> &args, const
if(args.size() != 1 || args[0].size() == 0)
{
BOX_ERROR("Incorrect usage. cd [-o] [-d] <directory>");
+ SetReturnCode(ReturnCode::Command_Error);
return;
}
@@ -764,6 +810,7 @@ void BackupQueries::CommandChangeDir(const std::vector<std::string> &args, const
if(id == 0)
{
BOX_ERROR("Directory '" << args[0] << "' not found.");
+ SetReturnCode(ReturnCode::Command_Error);
return;
}
@@ -785,7 +832,7 @@ void BackupQueries::CommandChangeLocalDir(const std::vector<std::string> &args)
if(args.size() != 1 || args[0].size() == 0)
{
BOX_ERROR("Incorrect usage. lcd <local-directory>");
- SetReturnCode(COMMAND_RETURN_ERROR);
+ SetReturnCode(ReturnCode::Command_Error);
return;
}
@@ -795,7 +842,7 @@ void BackupQueries::CommandChangeLocalDir(const std::vector<std::string> &args)
if(!ConvertConsoleToUtf8(args[0].c_str(), dirName))
{
BOX_ERROR("Failed to convert path from console encoding.");
- SetReturnCode(COMMAND_RETURN_ERROR);
+ SetReturnCode(ReturnCode::Command_Error);
return;
}
int result = ::chdir(dirName.c_str());
@@ -810,11 +857,11 @@ void BackupQueries::CommandChangeLocalDir(const std::vector<std::string> &args)
}
else
{
- BOX_ERROR("Error changing to directory '" <<
- args[0] << ": " << strerror(errno));
+ BOX_LOG_SYS_ERROR("Failed to change to directory "
+ "'" << args[0] << "'");
}
- SetReturnCode(COMMAND_RETURN_ERROR);
+ SetReturnCode(ReturnCode::Command_Error);
return;
}
@@ -822,9 +869,8 @@ void BackupQueries::CommandChangeLocalDir(const std::vector<std::string> &args)
char wd[PATH_MAX];
if(::getcwd(wd, PATH_MAX) == 0)
{
- BOX_ERROR("Error getting current directory: " <<
- strerror(errno));
- SetReturnCode(COMMAND_RETURN_ERROR);
+ BOX_LOG_SYS_ERROR("Error getting current directory");
+ SetReturnCode(ReturnCode::Command_Error);
return;
}
@@ -832,7 +878,7 @@ void BackupQueries::CommandChangeLocalDir(const std::vector<std::string> &args)
if(!ConvertUtf8ToConsole(wd, dirName))
{
BOX_ERROR("Failed to convert new path from console encoding.");
- SetReturnCode(COMMAND_RETURN_ERROR);
+ SetReturnCode(ReturnCode::Command_Error);
return;
}
BOX_INFO("Local current directory is now '" << dirName << "'.");
@@ -868,8 +914,8 @@ void BackupQueries::CommandGetObject(const std::vector<std::string> &args, const
}
// Does file exist?
- struct stat st;
- if(::stat(args[1].c_str(), &st) == 0 || errno != ENOENT)
+ EMU_STRUCT_STAT st;
+ if(EMU_STAT(args[1].c_str(), &st) == 0 || errno != ENOENT)
{
BOX_ERROR("The local file '" << args[1] << " already exists.");
return;
@@ -907,6 +953,117 @@ void BackupQueries::CommandGetObject(const std::vector<std::string> &args, const
}
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: BackupQueries::FindFileID(const std::string&
+// rNameOrIdString, const bool *options,
+// int64_t *pDirIdOut, std::string* pFileNameOut)
+// Purpose: Locate a file on the store (either by name or by
+// object ID, depending on opts['i'], where name can
+// include a path) and return the file ID, placing the
+// directory ID in *pDirIdOut and the filename part
+// of the path (if not looking up by ID and not NULL)
+// in *pFileNameOut.
+// Created: 2008-09-12
+//
+// --------------------------------------------------------------------------
+int64_t BackupQueries::FindFileID(const std::string& rNameOrIdString,
+ const bool *opts, int64_t *pDirIdOut, std::string* pFileNameOut,
+ int16_t flagsInclude, int16_t flagsExclude, int16_t* pFlagsOut)
+{
+ // Find object ID somehow
+ int64_t fileId;
+ int64_t dirId = GetCurrentDirectoryID();
+ std::string fileName = rNameOrIdString;
+
+ if(!opts['i'])
+ {
+ // does this remote filename include a path?
+ std::string::size_type index = fileName.rfind('/');
+ if(index != std::string::npos)
+ {
+ std::string dirName(fileName.substr(0, index));
+ fileName = fileName.substr(index + 1);
+
+ dirId = FindDirectoryObjectID(dirName);
+ if(dirId == 0)
+ {
+ BOX_ERROR("Directory '" << dirName <<
+ "' not found.");
+ return 0;
+ }
+ }
+
+ if(pFileNameOut)
+ {
+ *pFileNameOut = fileName;
+ }
+ }
+
+ BackupStoreFilenameClear fn(fileName);
+
+ // Need to look it up in the current directory
+ mrConnection.QueryListDirectory(
+ dirId, flagsInclude, flagsExclude,
+ true /* do want attributes */);
+
+ // Retrieve the directory from the stream following
+ BackupStoreDirectory dir;
+ std::auto_ptr<IOStream> dirstream(mrConnection.ReceiveStream());
+ dir.ReadFromStream(*dirstream, mrConnection.GetTimeout());
+ BackupStoreDirectory::Entry *en;
+
+ if(opts['i'])
+ {
+ // Specified as ID.
+ fileId = ::strtoll(rNameOrIdString.c_str(), 0, 16);
+ if(fileId == std::numeric_limits<long long>::min() ||
+ fileId == std::numeric_limits<long long>::max() ||
+ fileId == 0)
+ {
+ BOX_ERROR("Not a valid object ID (specified in hex).");
+ return 0;
+ }
+
+ // Check that the item is actually in the directory
+ en = dir.FindEntryByID(fileId);
+ if(en == 0)
+ {
+ BOX_ERROR("File ID " <<
+ BOX_FORMAT_OBJECTID(fileId) <<
+ " not found in current directory on store.\n"
+ "(You can only access files by ID from the "
+ "current directory.)");
+ return 0;
+ }
+ }
+ else
+ {
+ // Specified by name, find the object in the directory to get the ID
+ BackupStoreDirectory::Iterator i(dir);
+ en = i.FindMatchingClearName(fn);
+ if(en == 0)
+ {
+ BOX_ERROR("Filename '" << rNameOrIdString << "' "
+ "not found in current directory on store.\n"
+ "(Subdirectories in path not searched.)");
+ return 0;
+ }
+
+ fileId = en->GetObjectID();
+ }
+
+ *pDirIdOut = dirId;
+
+ if(pFlagsOut)
+ {
+ *pFlagsOut = en->GetFlags();
+ }
+
+ return fileId;
+}
+
// --------------------------------------------------------------------------
//
@@ -929,120 +1086,72 @@ void BackupQueries::CommandGet(std::vector<std::string> args, const bool *opts)
}
// Find object ID somehow
- int64_t fileId;
- int64_t dirId = GetCurrentDirectoryID();
+ int64_t fileId, dirId;
std::string localName;
- // BLOCK
- {
#ifdef WIN32
- for (std::vector<std::string>::iterator
- i = args.begin(); i != args.end(); i++)
+ for (std::vector<std::string>::iterator
+ i = args.begin(); i != args.end(); i++)
+ {
+ std::string out;
+ if(!ConvertConsoleToUtf8(i->c_str(), out))
{
- std::string out;
- if(!ConvertConsoleToUtf8(i->c_str(), out))
- {
- BOX_ERROR("Failed to convert encoding.");
- return;
- }
- *i = out;
+ BOX_ERROR("Failed to convert encoding.");
+ return;
}
+ *i = out;
+ }
#endif
- std::string fileName(args[0]);
+ int16_t flagsExclude;
- if(!opts['i'])
- {
- // does this remote filename include a path?
- std::string::size_type index = fileName.rfind('/');
- if(index != std::string::npos)
- {
- std::string dirName(fileName.substr(0, index));
- fileName = fileName.substr(index + 1);
-
- dirId = FindDirectoryObjectID(dirName);
- if(dirId == 0)
- {
- BOX_ERROR("Directory '" << dirName <<
- "' not found.");
- return;
- }
- }
- }
+ if(opts['i'])
+ {
+ // can retrieve anything by ID
+ flagsExclude = BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING;
+ }
+ else
+ {
+ // only current versions by name
+ flagsExclude =
+ BackupProtocolClientListDirectory::Flags_OldVersion |
+ BackupProtocolClientListDirectory::Flags_Deleted;
+ }
- BackupStoreFilenameClear fn(fileName);
- // Need to look it up in the current directory
- mrConnection.QueryListDirectory(
- dirId,
- BackupProtocolClientListDirectory::Flags_File, // just files
- (opts['i'])?(BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING):(BackupProtocolClientListDirectory::Flags_OldVersion | BackupProtocolClientListDirectory::Flags_Deleted), // only current versions
- false /* don't want attributes */);
+ fileId = FindFileID(args[0], opts, &dirId, &localName,
+ BackupProtocolClientListDirectory::Flags_File, // just files
+ flagsExclude, NULL /* don't care about flags found */);
- // Retrieve the directory from the stream following
- BackupStoreDirectory dir;
- std::auto_ptr<IOStream> dirstream(mrConnection.ReceiveStream());
- dir.ReadFromStream(*dirstream, mrConnection.GetTimeout());
+ if (fileId == 0)
+ {
+ // error already reported
+ return;
+ }
- if(opts['i'])
+ if(opts['i'])
+ {
+ // Specified as ID. Must have a local name in the arguments
+ // (check at beginning of function ensures this)
+ localName = args[1];
+ }
+ else
+ {
+ // Specified by name. Local name already set by FindFileID,
+ // but may be overridden by user supplying a second argument.
+ if(args.size() == 2)
{
- // Specified as ID.
- fileId = ::strtoll(args[0].c_str(), 0, 16);
- if(fileId == std::numeric_limits<long long>::min() ||
- fileId == std::numeric_limits<long long>::max() ||
- fileId == 0)
- {
- BOX_ERROR("Not a valid object ID (specified in hex).");
- return;
- }
-
- // Check that the item is actually in the directory
- if(dir.FindEntryByID(fileId) == 0)
- {
- BOX_ERROR("File ID " <<
- BOX_FORMAT_OBJECTID(fileId) <<
- " not found in current "
- "directory on store.\n"
- "(You can only download files by ID "
- "from the current directory.)");
- return;
- }
-
- // Must have a local name in the arguments (check at beginning of function ensures this)
localName = args[1];
}
- else
- {
- // Specified by name, find the object in the directory to get the ID
- BackupStoreDirectory::Iterator i(dir);
- BackupStoreDirectory::Entry *en = i.FindMatchingClearName(fn);
-
- if(en == 0)
- {
- BOX_ERROR("Filename '" << args[0] << "' "
- "not found in current "
- "directory on store.\n"
- "(Subdirectories in path not "
- "searched.)");
- return;
- }
-
- fileId = en->GetObjectID();
-
- // Local name is the last argument, which is either
- // the looked up filename, or a filename specified
- // by the user.
- localName = args[args.size() - 1];
- }
}
// Does local file already exist? (don't want to overwrite)
- struct stat st;
- if(::stat(localName.c_str(), &st) == 0 || errno != ENOENT)
+ EMU_STRUCT_STAT st;
+ if(EMU_STAT(localName.c_str(), &st) == 0 || errno != ENOENT)
{
BOX_ERROR("The local file " << localName << " already exists, "
"will not overwrite it.");
- SetReturnCode(COMMAND_RETURN_ERROR);
+ SetReturnCode(ReturnCode::Command_Error);
return;
}
@@ -1081,7 +1190,6 @@ void BackupQueries::CommandGet(std::vector<std::string> args, const bool *opts)
}
}
-
// --------------------------------------------------------------------------
//
// Function
@@ -1090,58 +1198,17 @@ void BackupQueries::CommandGet(std::vector<std::string> args, const bool *opts)
// Created: 29/1/04
//
// --------------------------------------------------------------------------
-BackupQueries::CompareParams::CompareParams()
- : mQuickCompare(false),
- mIgnoreExcludes(false),
- mIgnoreAttributes(false),
- mDifferences(0),
- mDifferencesExplainedByModTime(0),
- mUncheckedFiles(0),
- mExcludedDirs(0),
- mExcludedFiles(0),
- mpExcludeFiles(0),
- mpExcludeDirs(0),
- mLatestFileUploadTime(0)
-{
-}
-
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::CompareParams::~CompareParams()
-// Purpose: Destructor
-// Created: 29/1/04
-//
-// --------------------------------------------------------------------------
-BackupQueries::CompareParams::~CompareParams()
-{
- DeleteExcludeLists();
-}
-
-
-// --------------------------------------------------------------------------
-//
-// Function
-// Name: BackupQueries::CompareParams::DeleteExcludeLists()
-// Purpose: Delete the include lists contained
-// Created: 29/1/04
-//
-// --------------------------------------------------------------------------
-void BackupQueries::CompareParams::DeleteExcludeLists()
-{
- if(mpExcludeFiles != 0)
- {
- delete mpExcludeFiles;
- mpExcludeFiles = 0;
- }
- if(mpExcludeDirs != 0)
- {
- delete mpExcludeDirs;
- mpExcludeDirs = 0;
- }
-}
-
+BackupQueries::CompareParams::CompareParams(bool QuickCompare,
+ bool IgnoreExcludes, bool IgnoreAttributes,
+ box_time_t LatestFileUploadTime)
+: BoxBackupCompareParams(QuickCompare, IgnoreExcludes, IgnoreAttributes,
+ LatestFileUploadTime),
+ mDifferences(0),
+ mDifferencesExplainedByModTime(0),
+ mUncheckedFiles(0),
+ mExcludedDirs(0),
+ mExcludedFiles(0)
+{ }
// --------------------------------------------------------------------------
//
@@ -1153,24 +1220,19 @@ void BackupQueries::CompareParams::DeleteExcludeLists()
// --------------------------------------------------------------------------
void BackupQueries::CommandCompare(const std::vector<std::string> &args, const bool *opts)
{
- // Parameters, including count of differences
- BackupQueries::CompareParams params;
- params.mQuickCompare = opts['q'];
- params.mQuietCompare = opts['Q'];
- params.mIgnoreExcludes = opts['E'];
- params.mIgnoreAttributes = opts['A'];
+ box_time_t LatestFileUploadTime = GetCurrentBoxTime();
// Try and work out the time before which all files should be on the server
{
std::string syncTimeFilename(mrConfiguration.GetKeyValue("DataDirectory") + DIRECTORY_SEPARATOR_ASCHAR);
syncTimeFilename += "last_sync_start";
// Stat it to get file time
- struct stat st;
- if(::stat(syncTimeFilename.c_str(), &st) == 0)
+ EMU_STRUCT_STAT st;
+ if(EMU_STAT(syncTimeFilename.c_str(), &st) == 0)
{
// Files modified after this time shouldn't be on the server, so report errors slightly differently
- params.mLatestFileUploadTime = FileModificationTime(st)
- - SecondsToBoxTime(mrConfiguration.GetKeyValueInt("MinimumFileAge"));
+ LatestFileUploadTime = FileModificationTime(st) -
+ SecondsToBoxTime(mrConfiguration.GetKeyValueInt("MinimumFileAge"));
}
else
{
@@ -1179,8 +1241,16 @@ void BackupQueries::CommandCompare(const std::vector<std::string> &args, const b
}
}
+ // Parameters, including count of differences
+ BackupQueries::CompareParams params(opts['q'], // quick compare?
+ opts['E'], // ignore excludes
+ opts['A'], // ignore attributes
+ LatestFileUploadTime);
+
+ params.mQuietCompare = opts['Q'];
+
// Quick compare?
- if(params.mQuickCompare)
+ if(params.QuickCompare())
{
BOX_WARNING("Quick compare used -- file attributes are not "
"checked.");
@@ -1189,11 +1259,16 @@ void BackupQueries::CommandCompare(const std::vector<std::string> &args, const b
if(!opts['l'] && opts['a'] && args.size() == 0)
{
// Compare all locations
- const Configuration &locations(mrConfiguration.GetSubConfiguration("BackupLocations"));
- for(std::list<std::pair<std::string, Configuration> >::const_iterator i = locations.mSubConfigurations.begin();
- i != locations.mSubConfigurations.end(); ++i)
+ const Configuration &rLocations(
+ mrConfiguration.GetSubConfiguration("BackupLocations"));
+ std::vector<std::string> locNames =
+ rLocations.GetSubConfigurationNames();
+ for(std::vector<std::string>::iterator
+ pLocName = locNames.begin();
+ pLocName != locNames.end();
+ pLocName++)
{
- CompareLocation(i->first, params);
+ CompareLocation(*pLocName, params);
}
}
else if(opts['l'] && !opts['a'] && args.size() == 1)
@@ -1206,7 +1281,7 @@ void BackupQueries::CommandCompare(const std::vector<std::string> &args, const b
// Compare directory to directory
// Can't be bothered to do all the hard work to work out which location it's on, and hence which exclude list
- if(!params.mIgnoreExcludes)
+ if(!params.IgnoreExcludes())
{
BOX_ERROR("Cannot use excludes on directory to directory comparison -- use -E flag to specify ignored excludes.");
return;
@@ -1241,15 +1316,15 @@ void BackupQueries::CommandCompare(const std::vector<std::string> &args, const b
{
if (params.mUncheckedFiles != 0)
{
- SetReturnCode(COMPARE_RETURN_ERROR);
+ SetReturnCode(ReturnCode::Compare_Error);
}
else if (params.mDifferences != 0)
{
- SetReturnCode(COMPARE_RETURN_DIFFERENT);
+ SetReturnCode(ReturnCode::Compare_Different);
}
else
{
- SetReturnCode(COMPARE_RETURN_SAME);
+ SetReturnCode(ReturnCode::Compare_Same);
}
}
}
@@ -1263,7 +1338,8 @@ void BackupQueries::CommandCompare(const std::vector<std::string> &args, const b
// Created: 2003/10/13
//
// --------------------------------------------------------------------------
-void BackupQueries::CompareLocation(const std::string &rLocation, BackupQueries::CompareParams &rParams)
+void BackupQueries::CompareLocation(const std::string &rLocation,
+ BoxBackupCompareParams &rParams)
{
// Find the location's sub configuration
const Configuration &locations(mrConfiguration.GetSubConfiguration("BackupLocations"));
@@ -1287,45 +1363,36 @@ void BackupQueries::CompareLocation(const std::string &rLocation, BackupQueries:
}
#endif
- try
- {
- // Generate the exclude lists
- if(!rParams.mIgnoreExcludes)
- {
- rParams.mpExcludeFiles = BackupClientMakeExcludeList_Files(loc);
- rParams.mpExcludeDirs = BackupClientMakeExcludeList_Dirs(loc);
- }
-
- // Then get it compared
- Compare(std::string("/") + rLocation,
- loc.GetKeyValue("Path"), rParams);
- }
- catch(...)
+ // Generate the exclude lists
+ if(!rParams.IgnoreExcludes())
{
- // Clean up
- rParams.DeleteExcludeLists();
- throw;
+ rParams.LoadExcludeLists(loc);
}
-
- // Delete exclude lists
- rParams.DeleteExcludeLists();
+
+ // Then get it compared
+ Compare(std::string("/") + rLocation, loc.GetKeyValue("Path"), rParams);
}
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupQueries::Compare(const std::string &, const std::string &, BackupQueries::CompareParams &)
+// Name: BackupQueries::Compare(const std::string &,
+// const std::string &, BackupQueries::CompareParams &)
// Purpose: Compare a store directory against a local directory
// Created: 2003/10/13
//
// --------------------------------------------------------------------------
-void BackupQueries::Compare(const std::string &rStoreDir, const std::string &rLocalDir, BackupQueries::CompareParams &rParams)
+void BackupQueries::Compare(const std::string &rStoreDir,
+ const std::string &rLocalDir, BoxBackupCompareParams &rParams)
{
#ifdef WIN32
+ std::string localDirEncoded;
std::string storeDirEncoded;
+ if(!ConvertConsoleToUtf8(rLocalDir.c_str(), localDirEncoded)) return;
if(!ConvertConsoleToUtf8(rStoreDir.c_str(), storeDirEncoded)) return;
#else
+ const std::string& localDirEncoded(rLocalDir);
const std::string& storeDirEncoded(rStoreDir);
#endif
@@ -1335,19 +1402,22 @@ void BackupQueries::Compare(const std::string &rStoreDir, const std::string &rLo
// Found?
if(dirID == 0)
{
- BOX_WARNING("Local directory '" << rLocalDir << "' exists, "
- "but server directory '" << rStoreDir << "' does not "
- "exist.");
- rParams.mDifferences ++;
+ bool modifiedAfterLastSync = false;
+
+ EMU_STRUCT_STAT st;
+ if(EMU_STAT(rLocalDir.c_str(), &st) == 0)
+ {
+ if(FileAttrModificationTime(st) >
+ rParams.LatestFileUploadTime())
+ {
+ modifiedAfterLastSync = true;
+ }
+ }
+
+ rParams.NotifyRemoteFileMissing(localDirEncoded,
+ storeDirEncoded, modifiedAfterLastSync);
return;
}
-
-#ifdef WIN32
- std::string localDirEncoded;
- if(!ConvertConsoleToUtf8(rLocalDir.c_str(), localDirEncoded)) return;
-#else
- std::string localDirEncoded(rLocalDir);
-#endif
// Go!
Compare(dirID, storeDirEncoded, localDirEncoded, rParams);
@@ -1363,56 +1433,36 @@ void BackupQueries::Compare(const std::string &rStoreDir, const std::string &rLo
// Created: 2003/10/13
//
// --------------------------------------------------------------------------
-void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const std::string &rLocalDir, BackupQueries::CompareParams &rParams)
+void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir,
+ const std::string &rLocalDir, BoxBackupCompareParams &rParams)
{
-#ifdef WIN32
- // By this point, rStoreDir and rLocalDir should be in UTF-8 encoding
-
- std::string localDirDisplay;
- std::string storeDirDisplay;
-
- if(!ConvertUtf8ToConsole(rLocalDir.c_str(), localDirDisplay)) return;
- if(!ConvertUtf8ToConsole(rStoreDir.c_str(), storeDirDisplay)) return;
-#else
- const std::string& localDirDisplay(rLocalDir);
- const std::string& storeDirDisplay(rStoreDir);
-#endif
+ rParams.NotifyDirComparing(rLocalDir, rStoreDir);
// Get info on the local directory
- struct stat st;
- if(::lstat(rLocalDir.c_str(), &st) != 0)
+ EMU_STRUCT_STAT st;
+ if(EMU_LSTAT(rLocalDir.c_str(), &st) != 0)
{
// What kind of error?
- if(errno == ENOTDIR)
- {
- BOX_WARNING("Local object '" << localDirDisplay << "' "
- "is a file, server object '" <<
- storeDirDisplay << "' is a directory.");
- rParams.mDifferences ++;
- }
- else if(errno == ENOENT)
+ if(errno == ENOTDIR || errno == ENOENT)
{
- BOX_WARNING("Local directory '" << localDirDisplay <<
- "' does not exist (compared to server "
- "directory '" << storeDirDisplay << "').");
- rParams.mDifferences ++;
+ rParams.NotifyLocalDirMissing(rLocalDir, rStoreDir);
}
else
{
- BOX_WARNING("Failed to access local directory '" <<
- localDirDisplay << ": " << strerror(errno) <<
- "'.");
- rParams.mUncheckedFiles ++;
+ rParams.NotifyLocalDirAccessFailed(rLocalDir, rStoreDir);
}
return;
}
// Get the directory listing from the store
mrConnection.QueryListDirectory(
- DirID,
- BackupProtocolClientListDirectory::Flags_INCLUDE_EVERYTHING, // get everything
- BackupProtocolClientListDirectory::Flags_OldVersion | BackupProtocolClientListDirectory::Flags_Deleted, // except for old versions and deleted files
- true /* want attributes */);
+ DirID,
+ BackupProtocolClientListDirectory::Flags_INCLUDE_EVERYTHING,
+ // get everything
+ BackupProtocolClientListDirectory::Flags_OldVersion |
+ BackupProtocolClientListDirectory::Flags_Deleted,
+ // except for old versions and deleted files
+ true /* want attributes */);
// Retrieve the directory from the stream following
BackupStoreDirectory dir;
@@ -1422,8 +1472,7 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
// Test out the attributes
if(!dir.HasAttributes())
{
- BOX_WARNING("Store directory '" << storeDirDisplay << "' "
- "doesn't have attributes.");
+ rParams.NotifyStoreDirMissingAttributes(rLocalDir, rStoreDir);
}
else
{
@@ -1436,12 +1485,27 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
localAttr.ReadAttributes(rLocalDir.c_str(),
true /* directories have zero mod times */);
- if(!(attr.Compare(localAttr, true, true /* ignore modification times */)))
+ if(attr.Compare(localAttr, true, true /* ignore modification times */))
{
- BOX_WARNING("Local directory '" << localDirDisplay <<
- "' has different attributes to store "
- "directory '" << storeDirDisplay << "'.");
- rParams.mDifferences ++;
+ rParams.NotifyDirCompared(rLocalDir, rStoreDir,
+ false, false /* actually we didn't check :) */);
+ }
+ else
+ {
+ bool modifiedAfterLastSync = false;
+
+ EMU_STRUCT_STAT st;
+ if(EMU_STAT(rLocalDir.c_str(), &st) == 0)
+ {
+ if(FileAttrModificationTime(st) >
+ rParams.LatestFileUploadTime())
+ {
+ modifiedAfterLastSync = true;
+ }
+ }
+
+ rParams.NotifyDirCompared(rLocalDir, rStoreDir,
+ true, modifiedAfterLastSync);
}
}
@@ -1449,11 +1513,10 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
DIR *dirhandle = ::opendir(rLocalDir.c_str());
if(dirhandle == 0)
{
- BOX_WARNING("Failed to open local directory '" <<
- localDirDisplay << "': " << strerror(errno));
- rParams.mUncheckedFiles ++;
+ rParams.NotifyLocalDirAccessFailed(rLocalDir, rStoreDir);
return;
}
+
try
{
// Read the files and directories into sets
@@ -1484,8 +1547,8 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
#ifndef HAVE_VALID_DIRENT_D_TYPE
std::string fn(MakeFullPath
(rLocalDir, localDirEn->d_name));
- struct stat st;
- if(::lstat(fn.c_str(), &st) != 0)
+ EMU_STRUCT_STAT st;
+ if(EMU_LSTAT(fn.c_str(), &st) != 0)
{
THROW_EXCEPTION(CommonException, OSFileError)
}
@@ -1518,8 +1581,8 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
// Close directory
if(::closedir(dirhandle) != 0)
{
- BOX_ERROR("Failed to close local directory '" <<
- localDirDisplay << "': " << strerror(errno));
+ BOX_LOG_SYS_ERROR("Failed to close local directory "
+ "'" << rLocalDir << "'");
}
dirhandle = 0;
@@ -1557,36 +1620,30 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
for(std::set<std::pair<std::string, BackupStoreDirectory::Entry *> >::const_iterator i = storeFiles.begin(); i != storeFiles.end(); ++i)
{
const std::string& fileName(i->first);
-#ifdef WIN32
- // File name is also in UTF-8 encoding,
- // need to convert to console
- std::string fileNameDisplay;
- if(!ConvertUtf8ToConsole(i->first.c_str(),
- fileNameDisplay)) return;
-#else
- const std::string& fileNameDisplay(i->first);
-#endif
- std::string localPath(MakeFullPath
- (rLocalDir, fileName));
- std::string localPathDisplay(MakeFullPath
- (localDirDisplay, fileNameDisplay));
- std::string storePathDisplay
- (storeDirDisplay + "/" + fileNameDisplay);
+ std::string localPath(MakeFullPath(rLocalDir, fileName));
+ std::string storePath(rStoreDir + "/" + fileName);
+ rParams.NotifyFileComparing(localPath, storePath);
+
// Does the file exist locally?
string_set_iter_t local(localFiles.find(fileName));
if(local == localFiles.end())
{
// Not found -- report
- BOX_WARNING("Local file '" <<
- localPathDisplay << "' does not exist, "
- "but store file '" <<
- storePathDisplay << "' does.");
- rParams.mDifferences ++;
+ rParams.NotifyLocalFileMissing(localPath,
+ storePath);
}
else
- {
+ {
+ int64_t fileSize = 0;
+
+ EMU_STRUCT_STAT st;
+ if(EMU_STAT(localPath.c_str(), &st) == 0)
+ {
+ fileSize = st.st_size;
+ }
+
try
{
// Files the same flag?
@@ -1594,8 +1651,10 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
// File modified after last sync flag
bool modifiedAfterLastSync = false;
+
+ bool hasDifferentAttribs = false;
- if(rParams.mQuickCompare)
+ if(rParams.QuickCompare())
{
// Compare file -- fetch it
mrConnection.QueryGetBlockIndexByID(i->second->GetObjectID());
@@ -1640,7 +1699,7 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
BackupClientFileAttributes localAttr;
box_time_t fileModTime = 0;
localAttr.ReadAttributes(localPath.c_str(), false /* don't zero mod times */, &fileModTime);
- modifiedAfterLastSync = (fileModTime > rParams.mLatestFileUploadTime);
+ modifiedAfterLastSync = (fileModTime > rParams.LatestFileUploadTime());
bool ignoreAttrModTime = true;
#ifdef WIN32
@@ -1649,7 +1708,7 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
ignoreAttrModTime = false;
#endif
- if(!rParams.mIgnoreAttributes &&
+ if(!rParams.IgnoreAttributes() &&
#ifdef PLATFORM_DISABLE_SYMLINK_ATTRIB_COMPARE
!fileOnServerStream->IsSymLink() &&
#endif
@@ -1657,128 +1716,43 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
ignoreAttrModTime,
fileOnServerStream->IsSymLink() /* ignore modification time if it's a symlink */))
{
- BOX_WARNING("Local file '" <<
- localPathDisplay <<
- "' has different attributes "
- "to store file '" <<
- storePathDisplay <<
- "'.");
- rParams.mDifferences ++;
- if(modifiedAfterLastSync)
- {
- rParams.mDifferencesExplainedByModTime ++;
- BOX_INFO("(the file above was modified after the last sync time -- might be reason for difference)");
- }
- else if(i->second->HasAttributes())
- {
- BOX_INFO("(the file above has had new attributes applied)\n");
- }
+ hasDifferentAttribs = true;
}
// Compare contents, if it's a regular file not a link
// Remember, we MUST read the entire stream from the server.
+ SelfFlushingStream flushObject(*objectStream);
+
if(!fileOnServerStream->IsSymLink())
{
+ SelfFlushingStream flushFile(*fileOnServerStream);
// Open the local file
FileStream l(localPath.c_str());
-
- // Size
- IOStream::pos_type fileSizeLocal = l.BytesLeftToRead();
- IOStream::pos_type fileSizeServer = 0;
-
- // Test the contents
- char buf1[2048];
- char buf2[2048];
- while(fileOnServerStream->StreamDataLeft() && l.StreamDataLeft())
- {
- int size = fileOnServerStream->Read(buf1, sizeof(buf1), mrConnection.GetTimeout());
- fileSizeServer += size;
-
- if(l.Read(buf2, size) != size
- || ::memcmp(buf1, buf2, size) != 0)
- {
- equal = false;
- break;
- }
- }
-
- // Check read all the data from the server and file -- can't be equal if local and remote aren't the same length
- // Can't use StreamDataLeft() test on file, because if it's the same size, it won't know
- // it's EOF yet.
- if(fileOnServerStream->StreamDataLeft() || fileSizeServer != fileSizeLocal)
- {
- equal = false;
- }
-
- // Must always read the entire decoded stream, if it's not a symlink
- if(fileOnServerStream->StreamDataLeft())
- {
- // Absorb all the data remaining
- char buffer[2048];
- while(fileOnServerStream->StreamDataLeft())
- {
- fileOnServerStream->Read(buffer, sizeof(buffer), mrConnection.GetTimeout());
- }
- }
-
- // Must always read the entire encoded stream
- if(objectStream->StreamDataLeft())
- {
- // Absorb all the data remaining
- char buffer[2048];
- while(objectStream->StreamDataLeft())
- {
- objectStream->Read(buffer, sizeof(buffer), mrConnection.GetTimeout());
- }
- }
+ equal = l.CompareWith(*fileOnServerStream,
+ mrConnection.GetTimeout());
}
}
- // Report if not equal.
- if(!equal)
- {
- BOX_WARNING("Local file '" <<
- localPathDisplay << "' "
- "has different contents "
- "to store file '" <<
- storePathDisplay <<
- "'.");
- rParams.mDifferences ++;
- if(modifiedAfterLastSync)
- {
- rParams.mDifferencesExplainedByModTime ++;
- BOX_INFO("(the file above was modified after the last sync time -- might be reason for difference)");
- }
- else if(i->second->HasAttributes())
- {
- BOX_INFO("(the file above has had new attributes applied)\n");
- }
- }
+ rParams.NotifyFileCompared(localPath,
+ storePath, fileSize,
+ hasDifferentAttribs, !equal,
+ modifiedAfterLastSync,
+ i->second->HasAttributes());
}
catch(BoxException &e)
{
- BOX_ERROR("Failed to fetch and compare "
- "'" <<
- storePathDisplay.c_str() <<
- "': error " << e.what() <<
- " (" << e.GetType() <<
- "/" << e.GetSubType() << ")");
- rParams.mUncheckedFiles ++;
+ rParams.NotifyDownloadFailed(localPath,
+ storePath, fileSize, e);
}
catch(std::exception &e)
{
- BOX_ERROR("Failed to fetch and compare "
- "'" <<
- storePathDisplay.c_str() <<
- "': " << e.what());
+ rParams.NotifyDownloadFailed(localPath,
+ storePath, fileSize, e);
}
catch(...)
{
- BOX_ERROR("Failed to fetch and compare "
- "'" <<
- storePathDisplay.c_str() <<
- "': unknown error");
- rParams.mUncheckedFiles ++;
+ rParams.NotifyDownloadFailed(localPath,
+ storePath, fileSize);
}
// Remove from set so that we know it's been compared
@@ -1786,53 +1760,34 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
}
}
- // Report any files which exist on the locally, but not on the store
+ // Report any files which exist locally, but not on the store
for(string_set_iter_t i = localFiles.begin(); i != localFiles.end(); ++i)
{
-#ifdef WIN32
- // File name is also in UTF-8 encoding,
- // need to convert to console
- std::string fileNameDisplay;
- if(!ConvertUtf8ToConsole(i->c_str(), fileNameDisplay))
- return;
-#else
- const std::string& fileNameDisplay(*i);
-#endif
-
- std::string localPath(MakeFullPath
- (rLocalDir, *i));
- std::string localPathDisplay(MakeFullPath
- (localDirDisplay, fileNameDisplay));
- std::string storePathDisplay
- (storeDirDisplay + "/" + fileNameDisplay);
+ std::string localPath(MakeFullPath(rLocalDir, *i));
+ std::string storePath(rStoreDir + "/" + *i);
// Should this be ignored (ie is excluded)?
- if(rParams.mpExcludeFiles == 0 ||
- !(rParams.mpExcludeFiles->IsExcluded(localPath)))
+ if(!rParams.IsExcludedFile(localPath))
{
- BOX_WARNING("Local file '" <<
- localPathDisplay <<
- "' exists, but store file '" <<
- storePathDisplay <<
- "' does not.");
- rParams.mDifferences ++;
+ bool modifiedAfterLastSync = false;
- // Check the file modification time
+ EMU_STRUCT_STAT st;
+ if(EMU_STAT(localPath.c_str(), &st) == 0)
{
- struct stat st;
- if(::stat(localPath.c_str(), &st) == 0)
+ if(FileModificationTime(st) >
+ rParams.LatestFileUploadTime())
{
- if(FileModificationTime(st) > rParams.mLatestFileUploadTime)
- {
- rParams.mDifferencesExplainedByModTime ++;
- BOX_INFO("(the file above was modified after the last sync time -- might be reason for difference)");
- }
+ modifiedAfterLastSync = true;
}
}
+
+ rParams.NotifyRemoteFileMissing(localPath,
+ storePath, modifiedAfterLastSync);
}
else
{
- rParams.mExcludedFiles ++;
+ rParams.NotifyExcludedFile(localPath,
+ storePath);
}
}
@@ -1843,99 +1798,69 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
// Now do the directories, recursively to check subdirectories
for(std::set<std::pair<std::string, BackupStoreDirectory::Entry *> >::const_iterator i = storeDirs.begin(); i != storeDirs.end(); ++i)
{
-#ifdef WIN32
- // Directory name is also in UTF-8 encoding,
- // need to convert to console
- std::string subdirNameDisplay;
- if(!ConvertUtf8ToConsole(i->first.c_str(),
- subdirNameDisplay))
- return;
-#else
- const std::string& subdirNameDisplay(i->first);
-#endif
-
- std::string localPath(MakeFullPath
- (rLocalDir, i->first));
- std::string localPathDisplay(MakeFullPath
- (localDirDisplay, subdirNameDisplay));
- std::string storePathDisplay
- (storeDirDisplay + "/" + subdirNameDisplay);
+ std::string localPath(MakeFullPath(rLocalDir, i->first));
+ std::string storePath(rLocalDir + "/" + i->first);
// Does the directory exist locally?
string_set_iter_t local(localDirs.find(i->first));
if(local == localDirs.end() &&
- rParams.mpExcludeDirs != NULL &&
- rParams.mpExcludeDirs->IsExcluded(localPath))
+ rParams.IsExcludedDir(localPath))
{
- // Not found -- report
- BOX_WARNING("Local directory '" <<
- localPathDisplay << "' is excluded, "
- "but store directory '" <<
- storePathDisplay << "' still exists.");
- rParams.mDifferences ++;
+ rParams.NotifyExcludedFileNotDeleted(localPath,
+ storePath);
}
else if(local == localDirs.end())
{
// Not found -- report
- BOX_WARNING("Local directory '" <<
- localPathDisplay << "' does not exist, "
- "but store directory '" <<
- storePathDisplay << "' does.");
- rParams.mDifferences ++;
+ rParams.NotifyRemoteFileMissing(localPath,
+ storePath, false);
}
- else if(rParams.mpExcludeDirs != NULL &&
- rParams.mpExcludeDirs->IsExcluded(localPath))
+ else if(rParams.IsExcludedDir(localPath))
{
// don't recurse into excluded directories
}
else
{
// Compare directory
- Compare(i->second->GetObjectID(), rStoreDir + "/" + i->first, localPath, rParams);
+ Compare(i->second->GetObjectID(),
+ rStoreDir + "/" + i->first,
+ localPath, rParams);
// Remove from set so that we know it's been compared
localDirs.erase(local);
}
}
- // Report any files which exist on the locally, but not on the store
- for(std::set<std::string>::const_iterator i = localDirs.begin(); i != localDirs.end(); ++i)
+ // Report any directories which exist locally, but not on the store
+ for(std::set<std::string>::const_iterator
+ i = localDirs.begin();
+ i != localDirs.end(); ++i)
{
-#ifdef WIN32
- // File name is also in UTF-8 encoding,
- // need to convert to console
- std::string fileNameDisplay;
- if(!ConvertUtf8ToConsole(i->c_str(), fileNameDisplay))
- return;
-#else
- const std::string& fileNameDisplay(*i);
-#endif
-
- std::string localPath(MakeFullPath
- (rLocalDir, *i));
- std::string localPathDisplay(MakeFullPath
- (localDirDisplay, fileNameDisplay));
-
- std::string storePath
- (rStoreDir + "/" + *i);
- std::string storePathDisplay
- (storeDirDisplay + "/" + fileNameDisplay);
+ std::string localPath(MakeFullPath(rLocalDir, *i));
+ std::string storePath(rStoreDir + "/" + *i);
// Should this be ignored (ie is excluded)?
- if(rParams.mpExcludeDirs == 0 || !(rParams.mpExcludeDirs->IsExcluded(localPath)))
+ if(!rParams.IsExcludedDir(localPath))
{
- BOX_WARNING("Local directory '" <<
- localPathDisplay << "' exists, but "
- "store directory '" <<
- storePathDisplay << "' does not.");
- rParams.mDifferences ++;
+ bool modifiedAfterLastSync = false;
+
+ // Check the dir modification time
+ EMU_STRUCT_STAT st;
+ if(EMU_STAT(localPath.c_str(), &st) == 0 &&
+ FileModificationTime(st) >
+ rParams.LatestFileUploadTime())
+ {
+ modifiedAfterLastSync = true;
+ }
+
+ rParams.NotifyRemoteFileMissing(localPath,
+ storePath, modifiedAfterLastSync);
}
else
{
- rParams.mExcludedDirs ++;
+ rParams.NotifyExcludedDir(localPath, storePath);
}
}
-
}
catch(...)
{
@@ -1943,6 +1868,7 @@ void BackupQueries::Compare(int64_t DirID, const std::string &rStoreDir, const s
{
::closedir(dirhandle);
}
+ throw;
}
}
@@ -1960,7 +1886,7 @@ void BackupQueries::CommandRestore(const std::vector<std::string> &args, const b
// Check arguments
if(args.size() != 2)
{
- BOX_ERROR("Incorrect usage. restore [-d] [-r] [-i] <remote-name> <local-name>");
+ BOX_ERROR("Incorrect usage. restore [-drif] <remote-name> <local-name>");
return;
}
@@ -2023,18 +1949,19 @@ void BackupQueries::CommandRestore(const std::vector<std::string> &args, const b
localName.c_str(),
true /* print progress dots */, restoreDeleted,
false /* don't undelete after restore! */,
- opts['r'] /* resume? */);
+ opts['r'] /* resume? */,
+ opts['f'] /* force continue after errors */);
}
catch(std::exception &e)
{
BOX_ERROR("Failed to restore: " << e.what());
- SetReturnCode(COMMAND_RETURN_ERROR);
+ SetReturnCode(ReturnCode::Command_Error);
return;
}
catch(...)
{
BOX_ERROR("Failed to restore: unknown exception");
- SetReturnCode(COMMAND_RETURN_ERROR);
+ SetReturnCode(ReturnCode::Command_Error);
return;
}
@@ -2044,33 +1971,38 @@ void BackupQueries::CommandRestore(const std::vector<std::string> &args, const b
BOX_INFO("Restore complete.");
break;
+ case Restore_CompleteWithErrors:
+ BOX_WARNING("Restore complete, but some files could not be "
+ "restored.");
+ break;
+
case Restore_ResumePossible:
- BOX_ERROR("Resume possible -- repeat command with -r flag to resume");
- SetReturnCode(COMMAND_RETURN_ERROR);
+ BOX_ERROR("Resume possible -- repeat command with -r flag "
+ "to resume.");
+ SetReturnCode(ReturnCode::Command_Error);
break;
case Restore_TargetExists:
- BOX_ERROR("The target directory exists. You cannot restore over an existing directory.");
- SetReturnCode(COMMAND_RETURN_ERROR);
+ BOX_ERROR("The target directory exists. You cannot restore "
+ "over an existing directory.");
+ SetReturnCode(ReturnCode::Command_Error);
break;
- #ifdef WIN32
case Restore_TargetPathNotFound:
BOX_ERROR("The target directory path does not exist.\n"
"To restore to a directory whose parent "
"does not exist, create the parent first.");
- SetReturnCode(COMMAND_RETURN_ERROR);
+ SetReturnCode(ReturnCode::Command_Error);
break;
- #endif
case Restore_UnknownError:
BOX_ERROR("Unknown error during restore.");
- SetReturnCode(COMMAND_RETURN_ERROR);
+ SetReturnCode(ReturnCode::Command_Error);
break;
default:
BOX_ERROR("Unknown restore result " << result << ".");
- SetReturnCode(COMMAND_RETURN_ERROR);
+ SetReturnCode(ReturnCode::Command_Error);
break;
}
}
@@ -2131,49 +2063,46 @@ void BackupQueries::CommandHelp(const std::vector<std::string> &args)
// Created: 19/4/04
//
// --------------------------------------------------------------------------
-void BackupQueries::CommandUsage()
+void BackupQueries::CommandUsage(const bool *opts)
{
+ bool MachineReadable = opts['m'];
+
// Request full details from the server
std::auto_ptr<BackupProtocolClientAccountUsage> usage(mrConnection.QueryGetAccountUsage());
// Display each entry in turn
int64_t hardLimit = usage->GetBlocksHardLimit();
int32_t blockSize = usage->GetBlockSize();
- CommandUsageDisplayEntry("Used", usage->GetBlocksUsed(), hardLimit, blockSize);
- CommandUsageDisplayEntry("Old files", usage->GetBlocksInOldFiles(), hardLimit, blockSize);
- CommandUsageDisplayEntry("Deleted files", usage->GetBlocksInDeletedFiles(), hardLimit, blockSize);
- CommandUsageDisplayEntry("Directories", usage->GetBlocksInDirectories(), hardLimit, blockSize);
- CommandUsageDisplayEntry("Soft limit", usage->GetBlocksSoftLimit(), hardLimit, blockSize);
- CommandUsageDisplayEntry("Hard limit", hardLimit, hardLimit, blockSize);
+ CommandUsageDisplayEntry("Used", usage->GetBlocksUsed(), hardLimit,
+ blockSize, MachineReadable);
+ CommandUsageDisplayEntry("Old files", usage->GetBlocksInOldFiles(),
+ hardLimit, blockSize, MachineReadable);
+ CommandUsageDisplayEntry("Deleted files", usage->GetBlocksInDeletedFiles(),
+ hardLimit, blockSize, MachineReadable);
+ CommandUsageDisplayEntry("Directories", usage->GetBlocksInDirectories(),
+ hardLimit, blockSize, MachineReadable);
+ CommandUsageDisplayEntry("Soft limit", usage->GetBlocksSoftLimit(),
+ hardLimit, blockSize, MachineReadable);
+ CommandUsageDisplayEntry("Hard limit", hardLimit, hardLimit, blockSize,
+ MachineReadable);
}
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupQueries::CommandUsageDisplayEntry(const char *, int64_t, int64_t, int32_t)
+// Name: BackupQueries::CommandUsageDisplayEntry(const char *,
+// int64_t, int64_t, int32_t, bool)
// Purpose: Display an entry in the usage table
// Created: 19/4/04
//
// --------------------------------------------------------------------------
-void BackupQueries::CommandUsageDisplayEntry(const char *Name, int64_t Size, int64_t HardLimit, int32_t BlockSize)
+void BackupQueries::CommandUsageDisplayEntry(const char *Name, int64_t Size,
+int64_t HardLimit, int32_t BlockSize, bool MachineReadable)
{
- // Calculate size in Mb
- double mb = (((double)Size) * ((double)BlockSize)) / ((double)(1024*1024));
- int64_t percent = (Size * 100) / HardLimit;
-
- // Bar graph
- char bar[41];
- unsigned int b = (int)((Size * (sizeof(bar)-1)) / HardLimit);
- if(b > sizeof(bar)-1) {b = sizeof(bar)-1;}
- for(unsigned int l = 0; l < b; l++)
- {
- bar[l] = '*';
- }
- bar[b] = '\0';
-
- // Print the entryj
- ::printf("%14s %10.1fMb %3d%% %s\n", Name, mb, (int32_t)percent, bar);
+ std::cout << FormatUsageLineStart(Name, MachineReadable) <<
+ FormatUsageBar(Size, Size * BlockSize, HardLimit * BlockSize,
+ MachineReadable) << std::endl;
}
@@ -2187,10 +2116,17 @@ void BackupQueries::CommandUsageDisplayEntry(const char *Name, int64_t Size, int
// --------------------------------------------------------------------------
void BackupQueries::CommandUndelete(const std::vector<std::string> &args, const bool *opts)
{
+ if (!mReadWrite)
+ {
+ BOX_ERROR("This command requires a read-write connection. "
+ "Please reconnect with the -w option.");
+ return;
+ }
+
// Check arguments
if(args.size() != 1)
{
- BOX_ERROR("Incorrect usage. undelete <directory-name>");
+ BOX_ERROR("Incorrect usage. undelete <name> or undelete -i <object-id>");
return;
}
@@ -2200,23 +2136,133 @@ void BackupQueries::CommandUndelete(const std::vector<std::string> &args, const
#else
const std::string& storeDirEncoded(args[0]);
#endif
-
- // Get directory ID
- int64_t dirID = FindDirectoryObjectID(storeDirEncoded,
- false /* no old versions */, true /* find deleted dirs */);
-
- // Allowable?
- if(dirID == 0)
+
+ // Find object ID somehow
+ int64_t fileId, parentId;
+ std::string fileName;
+ int16_t flagsOut;
+
+ fileId = FindFileID(storeDirEncoded, opts, &parentId, &fileName,
+ /* include files and directories */
+ BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING,
+ /* include old and deleted files */
+ BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING,
+ &flagsOut);
+
+ if (fileId == 0)
{
- BOX_ERROR("Directory '" << args[0] << "' not found on server.");
+ // error already reported
return;
}
- if(dirID == BackupProtocolClientListDirectory::RootDirectory)
+
+ // Undelete it on the store
+ try
+ {
+ // Undelete object
+ if(flagsOut & BackupProtocolClientListDirectory::Flags_File)
+ {
+ mrConnection.QueryUndeleteFile(parentId, fileId);
+ }
+ else
+ {
+ mrConnection.QueryUndeleteDirectory(fileId);
+ }
+ }
+ catch (BoxException &e)
+ {
+ BOX_ERROR("Failed to undelete object: " <<
+ e.what());
+ }
+ catch(std::exception &e)
+ {
+ BOX_ERROR("Failed to undelete object: " <<
+ e.what());
+ }
+ catch(...)
+ {
+ BOX_ERROR("Failed to undelete object: unknown error");
+ }
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: BackupQueries::CommandDelete(const
+// std::vector<std::string> &, const bool *)
+// Purpose: Deletes a file
+// Created: 23/11/03
+//
+// --------------------------------------------------------------------------
+void BackupQueries::CommandDelete(const std::vector<std::string> &args,
+ const bool *opts)
+{
+ if (!mReadWrite)
+ {
+ BOX_ERROR("This command requires a read-write connection. "
+ "Please reconnect with the -w option.");
+ return;
+ }
+
+ // Check arguments
+ if(args.size() != 1)
{
- BOX_ERROR("Cannot undelete the root directory.");
+ BOX_ERROR("Incorrect usage. delete <name>");
return;
}
- // Undelete
- mrConnection.QueryUndeleteDirectory(dirID);
+#ifdef WIN32
+ std::string storeDirEncoded;
+ if(!ConvertConsoleToUtf8(args[0].c_str(), storeDirEncoded)) return;
+#else
+ const std::string& storeDirEncoded(args[0]);
+#endif
+
+ // Find object ID somehow
+ int64_t fileId, parentId;
+ std::string fileName;
+ int16_t flagsOut;
+
+ fileId = FindFileID(storeDirEncoded, opts, &parentId, &fileName,
+ /* include files and directories */
+ BackupProtocolClientListDirectory::Flags_EXCLUDE_NOTHING,
+ /* exclude old and deleted files */
+ BackupProtocolClientListDirectory::Flags_OldVersion |
+ BackupProtocolClientListDirectory::Flags_Deleted,
+ &flagsOut);
+
+ if (fileId == 0)
+ {
+ // error already reported
+ return;
+ }
+
+ BackupStoreFilenameClear fn(fileName);
+
+ // Delete it on the store
+ try
+ {
+ // Delete object
+ if(flagsOut & BackupProtocolClientListDirectory::Flags_File)
+ {
+ mrConnection.QueryDeleteFile(parentId, fn);
+ }
+ else
+ {
+ mrConnection.QueryDeleteDirectory(fileId);
+ }
+ }
+ catch (BoxException &e)
+ {
+ BOX_ERROR("Failed to delete object: " <<
+ e.what());
+ }
+ catch(std::exception &e)
+ {
+ BOX_ERROR("Failed to delete object: " <<
+ e.what());
+ }
+ catch(...)
+ {
+ BOX_ERROR("Failed to delete object: unknown error");
+ }
}
diff --git a/bin/bbackupquery/BackupQueries.h b/bin/bbackupquery/BackupQueries.h
index b2ef8cc2..392aa428 100644
--- a/bin/bbackupquery/BackupQueries.h
+++ b/bin/bbackupquery/BackupQueries.h
@@ -14,6 +14,7 @@
#include <string>
#include "BoxTime.h"
+#include "BoxBackupCompareParams.h"
class BackupProtocolClient;
class Configuration;
@@ -30,7 +31,9 @@ class ExcludeList;
class BackupQueries
{
public:
- BackupQueries(BackupProtocolClient &rConnection, const Configuration &rConfiguration);
+ BackupQueries(BackupProtocolClient &rConnection,
+ const Configuration &rConfiguration,
+ bool readWrite);
~BackupQueries();
private:
BackupQueries(const BackupQueries &);
@@ -54,43 +57,282 @@ private:
void CommandCompare(const std::vector<std::string> &args, const bool *opts);
void CommandRestore(const std::vector<std::string> &args, const bool *opts);
void CommandUndelete(const std::vector<std::string> &args, const bool *opts);
- void CommandUsage();
- void CommandUsageDisplayEntry(const char *Name, int64_t Size, int64_t HardLimit, int32_t BlockSize);
+ void CommandDelete(const std::vector<std::string> &args,
+ const bool *opts);
+ void CommandUsage(const bool *opts);
+ void CommandUsageDisplayEntry(const char *Name, int64_t Size,
+ int64_t HardLimit, int32_t BlockSize, bool MachineReadable);
void CommandHelp(const std::vector<std::string> &args);
// Implementations
- void List(int64_t DirID, const std::string &rListRoot, const bool *opts, bool FirstLevel);
- class CompareParams
+ void List(int64_t DirID, const std::string &rListRoot, const bool *opts,
+ bool FirstLevel);
+
+public:
+ class CompareParams : public BoxBackupCompareParams
{
public:
- CompareParams();
- ~CompareParams();
- void DeleteExcludeLists();
- bool mQuickCompare;
+ CompareParams(bool QuickCompare, bool IgnoreExcludes,
+ bool IgnoreAttributes, box_time_t LatestFileUploadTime);
+
bool mQuietCompare;
- bool mIgnoreExcludes;
- bool mIgnoreAttributes;
int mDifferences;
int mDifferencesExplainedByModTime;
int mUncheckedFiles;
int mExcludedDirs;
int mExcludedFiles;
- const ExcludeList *mpExcludeFiles;
- const ExcludeList *mpExcludeDirs;
- box_time_t mLatestFileUploadTime;
+
+ std::string ConvertForConsole(const std::string& rUtf8String)
+ {
+ #ifdef WIN32
+ std::string output;
+
+ if(!ConvertUtf8ToConsole(rUtf8String.c_str(), output))
+ {
+ BOX_WARNING("Character set conversion failed "
+ "on string: " << rUtf8String);
+ return rUtf8String;
+ }
+
+ return output;
+ #else
+ return rUtf8String;
+ #endif
+ }
+
+ virtual void NotifyLocalDirMissing(const std::string& rLocalPath,
+ const std::string& rRemotePath)
+ {
+ BOX_WARNING("Local directory '" <<
+ ConvertForConsole(rLocalPath) << "' "
+ "does not exist, but remote directory does.");
+ mDifferences ++;
+ }
+
+ virtual void NotifyLocalDirAccessFailed(
+ const std::string& rLocalPath,
+ const std::string& rRemotePath)
+ {
+ BOX_LOG_SYS_WARNING("Failed to access local directory "
+ "'" << ConvertForConsole(rLocalPath) << "'");
+ mUncheckedFiles ++;
+ }
+
+ virtual void NotifyStoreDirMissingAttributes(
+ const std::string& rLocalPath,
+ const std::string& rRemotePath)
+ {
+ BOX_WARNING("Store directory '" <<
+ ConvertForConsole(rRemotePath) << "' "
+ "doesn't have attributes.");
+ }
+
+ virtual void NotifyRemoteFileMissing(
+ const std::string& rLocalPath,
+ const std::string& rRemotePath,
+ bool modifiedAfterLastSync)
+ {
+ BOX_WARNING("Local file '" <<
+ ConvertForConsole(rLocalPath) << "' "
+ "exists, but remote file '" <<
+ ConvertForConsole(rRemotePath) << "' "
+ "does not.");
+ mDifferences ++;
+
+ if(modifiedAfterLastSync)
+ {
+ mDifferencesExplainedByModTime ++;
+ BOX_INFO("(the file above was modified after "
+ "the last sync time -- might be "
+ "reason for difference)");
+ }
+ }
+
+ virtual void NotifyLocalFileMissing(
+ const std::string& rLocalPath,
+ const std::string& rRemotePath)
+ {
+ BOX_WARNING("Remote file '" <<
+ ConvertForConsole(rRemotePath) << "' "
+ "exists, but local file '" <<
+ ConvertForConsole(rLocalPath) << "' does not.");
+ mDifferences ++;
+ }
+
+ virtual void NotifyExcludedFileNotDeleted(
+ const std::string& rLocalPath,
+ const std::string& rRemotePath)
+ {
+ BOX_WARNING("Local file '" <<
+ ConvertForConsole(rLocalPath) << "' "
+ "is excluded, but remote file '" <<
+ ConvertForConsole(rRemotePath) << "' "
+ "still exists.");
+ mDifferences ++;
+ }
+
+ virtual void NotifyDownloadFailed(const std::string& rLocalPath,
+ const std::string& rRemotePath, int64_t NumBytes,
+ BoxException& rException)
+ {
+ BOX_ERROR("Failed to download remote file '" <<
+ ConvertForConsole(rRemotePath) << "': " <<
+ rException.what() << " (" <<
+ rException.GetType() << "/" <<
+ rException.GetSubType() << ")");
+ mUncheckedFiles ++;
+ }
+
+ virtual void NotifyDownloadFailed(const std::string& rLocalPath,
+ const std::string& rRemotePath, int64_t NumBytes,
+ std::exception& rException)
+ {
+ BOX_ERROR("Failed to download remote file '" <<
+ ConvertForConsole(rRemotePath) << "': " <<
+ rException.what());
+ mUncheckedFiles ++;
+ }
+
+ virtual void NotifyDownloadFailed(const std::string& rLocalPath,
+ const std::string& rRemotePath, int64_t NumBytes)
+ {
+ BOX_ERROR("Failed to download remote file '" <<
+ ConvertForConsole(rRemotePath));
+ mUncheckedFiles ++;
+ }
+
+ virtual void NotifyExcludedFile(const std::string& rLocalPath,
+ const std::string& rRemotePath)
+ {
+ mExcludedFiles ++;
+ }
+
+ virtual void NotifyExcludedDir(const std::string& rLocalPath,
+ const std::string& rRemotePath)
+ {
+ mExcludedDirs ++;
+ }
+
+ virtual void NotifyDirComparing(const std::string& rLocalPath,
+ const std::string& rRemotePath)
+ {
+ }
+
+ virtual void NotifyDirCompared(
+ const std::string& rLocalPath,
+ const std::string& rRemotePath,
+ bool HasDifferentAttributes,
+ bool modifiedAfterLastSync)
+ {
+ if(HasDifferentAttributes)
+ {
+ BOX_WARNING("Local directory '" <<
+ ConvertForConsole(rLocalPath) << "' "
+ "has different attributes to "
+ "store directory '" <<
+ ConvertForConsole(rRemotePath) << "'.");
+ mDifferences ++;
+
+ if(modifiedAfterLastSync)
+ {
+ mDifferencesExplainedByModTime ++;
+ BOX_INFO("(the directory above was "
+ "modified after the last sync "
+ "time -- might be reason for "
+ "difference)");
+ }
+ }
+ }
+
+ virtual void NotifyFileComparing(const std::string& rLocalPath,
+ const std::string& rRemotePath)
+ {
+ }
+
+ virtual void NotifyFileCompared(const std::string& rLocalPath,
+ const std::string& rRemotePath, int64_t NumBytes,
+ bool HasDifferentAttributes, bool HasDifferentContents,
+ bool ModifiedAfterLastSync, bool NewAttributesApplied)
+ {
+ int NewDifferences = 0;
+
+ if(HasDifferentAttributes)
+ {
+ BOX_WARNING("Local file '" <<
+ ConvertForConsole(rLocalPath) << "' "
+ "has different attributes to "
+ "store file '" <<
+ ConvertForConsole(rRemotePath) << "'.");
+ NewDifferences ++;
+ }
+
+ if(HasDifferentContents)
+ {
+ BOX_WARNING("Local file '" <<
+ ConvertForConsole(rLocalPath) << "' "
+ "has different contents to "
+ "store file '" <<
+ ConvertForConsole(rRemotePath) << "'.");
+ NewDifferences ++;
+ }
+
+ if(HasDifferentAttributes || HasDifferentContents)
+ {
+ if(ModifiedAfterLastSync)
+ {
+ mDifferencesExplainedByModTime +=
+ NewDifferences;
+ BOX_INFO("(the file above was modified "
+ "after the last sync time -- "
+ "might be reason for difference)");
+ }
+ else if(NewAttributesApplied)
+ {
+ BOX_INFO("(the file above has had new "
+ "attributes applied)\n");
+ }
+ }
+
+ mDifferences += NewDifferences;
+ }
};
- void CompareLocation(const std::string &rLocation, CompareParams &rParams);
- void Compare(const std::string &rStoreDir, const std::string &rLocalDir, CompareParams &rParams);
- void Compare(int64_t DirID, const std::string &rStoreDir, const std::string &rLocalDir, CompareParams &rParams);
+ void CompareLocation(const std::string &rLocation,
+ BoxBackupCompareParams &rParams);
+ void Compare(const std::string &rStoreDir,
+ const std::string &rLocalDir, BoxBackupCompareParams &rParams);
+ void Compare(int64_t DirID, const std::string &rStoreDir,
+ const std::string &rLocalDir, BoxBackupCompareParams &rParams);
+
+public:
+
+ class ReturnCode
+ {
+ public:
+ enum {
+ Command_OK = 0,
+ Compare_Same = 1,
+ Compare_Different,
+ Compare_Error,
+ Command_Error,
+ } Type;
+ };
+
+private:
// Utility functions
- int64_t FindDirectoryObjectID(const std::string &rDirName, bool AllowOldVersion = false,
- bool AllowDeletedDirs = false, std::vector<std::pair<std::string, int64_t> > *pStack = 0);
+ int64_t FindDirectoryObjectID(const std::string &rDirName,
+ bool AllowOldVersion = false, bool AllowDeletedDirs = false,
+ std::vector<std::pair<std::string, int64_t> > *pStack = 0);
+ int64_t FindFileID(const std::string& rNameOrIdString,
+ const bool *opts, int64_t *pDirIdOut,
+ std::string* pFileNameOut, int16_t flagsInclude,
+ int16_t flagsExclude, int16_t* pFlagsOut);
int64_t GetCurrentDirectoryID();
std::string GetCurrentDirectoryName();
void SetReturnCode(int code) {mReturnCode = code;}
private:
+ bool mReadWrite;
BackupProtocolClient &mrConnection;
const Configuration &mrConfiguration;
bool mQuitNow;
diff --git a/bin/bbackupquery/BoxBackupCompareParams.h b/bin/bbackupquery/BoxBackupCompareParams.h
new file mode 100644
index 00000000..c58759a2
--- /dev/null
+++ b/bin/bbackupquery/BoxBackupCompareParams.h
@@ -0,0 +1,107 @@
+// --------------------------------------------------------------------------
+//
+// File
+// Name: BoxBackupCompareParams.h
+// Purpose: Parameters and notifiers for a compare operation
+// Created: 2008/12/30
+//
+// --------------------------------------------------------------------------
+
+#ifndef BOXBACKUPCOMPAREPARAMS__H
+#define BOXBACKUPCOMPAREPARAMS__H
+
+#include <memory>
+#include <string>
+
+#include "BoxTime.h"
+#include "ExcludeList.h"
+#include "BackupClientMakeExcludeList.h"
+
+// --------------------------------------------------------------------------
+//
+// Class
+// Name: BoxBackupCompareParams
+// Purpose: Parameters and notifiers for a compare operation
+// Created: 2003/10/10
+//
+// --------------------------------------------------------------------------
+class BoxBackupCompareParams
+{
+private:
+ std::auto_ptr<const ExcludeList> mapExcludeFiles, mapExcludeDirs;
+ bool mQuickCompare;
+ bool mIgnoreExcludes;
+ bool mIgnoreAttributes;
+ box_time_t mLatestFileUploadTime;
+
+public:
+ BoxBackupCompareParams(bool QuickCompare, bool IgnoreExcludes,
+ bool IgnoreAttributes, box_time_t LatestFileUploadTime)
+ : mQuickCompare(QuickCompare),
+ mIgnoreExcludes(IgnoreExcludes),
+ mIgnoreAttributes(IgnoreAttributes),
+ mLatestFileUploadTime(LatestFileUploadTime)
+ { }
+
+ virtual ~BoxBackupCompareParams() { }
+
+ bool QuickCompare() { return mQuickCompare; }
+ bool IgnoreExcludes() { return mIgnoreExcludes; }
+ bool IgnoreAttributes() { return mIgnoreAttributes; }
+ box_time_t LatestFileUploadTime() { return mLatestFileUploadTime; }
+
+ void LoadExcludeLists(const Configuration& rLoc)
+ {
+ mapExcludeFiles.reset(BackupClientMakeExcludeList_Files(rLoc));
+ mapExcludeDirs.reset(BackupClientMakeExcludeList_Dirs(rLoc));
+ }
+ bool IsExcludedFile(const std::string& rLocalPath)
+ {
+ if (!mapExcludeFiles.get()) return false;
+ return mapExcludeFiles->IsExcluded(rLocalPath);
+ }
+ bool IsExcludedDir(const std::string& rLocalPath)
+ {
+ if (!mapExcludeDirs.get()) return false;
+ return mapExcludeDirs->IsExcluded(rLocalPath);
+ }
+
+ virtual void NotifyLocalDirMissing(const std::string& rLocalPath,
+ const std::string& rRemotePath) = 0;
+ virtual void NotifyLocalDirAccessFailed(const std::string& rLocalPath,
+ const std::string& rRemotePath) = 0;
+ virtual void NotifyStoreDirMissingAttributes(const std::string& rLocalPath,
+ const std::string& rRemotePath) = 0;
+ virtual void NotifyRemoteFileMissing(const std::string& rLocalPath,
+ const std::string& rRemotePath,
+ bool modifiedAfterLastSync) = 0;
+ virtual void NotifyLocalFileMissing(const std::string& rLocalPath,
+ const std::string& rRemotePath) = 0;
+ virtual void NotifyExcludedFileNotDeleted(const std::string& rLocalPath,
+ const std::string& rRemotePath) = 0;
+ virtual void NotifyDownloadFailed(const std::string& rLocalPath,
+ const std::string& rRemotePath, int64_t NumBytes,
+ BoxException& rException) = 0;
+ virtual void NotifyDownloadFailed(const std::string& rLocalPath,
+ const std::string& rRemotePath, int64_t NumBytes,
+ std::exception& rException) = 0;
+ virtual void NotifyDownloadFailed(const std::string& rLocalPath,
+ const std::string& rRemotePath, int64_t NumBytes) = 0;
+ virtual void NotifyExcludedFile(const std::string& rLocalPath,
+ const std::string& rRemotePath) = 0;
+ virtual void NotifyExcludedDir(const std::string& rLocalPath,
+ const std::string& rRemotePath) = 0;
+ virtual void NotifyDirComparing(const std::string& rLocalPath,
+ const std::string& rRemotePath) = 0;
+ virtual void NotifyDirCompared(const std::string& rLocalPath,
+ const std::string& rRemotePath, bool HasDifferentAttributes,
+ bool modifiedAfterLastSync) = 0;
+ virtual void NotifyFileComparing(const std::string& rLocalPath,
+ const std::string& rRemotePath) = 0;
+ virtual void NotifyFileCompared(const std::string& rLocalPath,
+ const std::string& rRemotePath, int64_t NumBytes,
+ bool HasDifferentAttributes, bool HasDifferentContents,
+ bool modifiedAfterLastSync, bool newAttributesApplied) = 0;
+};
+
+#endif // BOXBACKUPCOMPAREPARAMS__H
diff --git a/bin/bbackupquery/bbackupquery.cpp b/bin/bbackupquery/bbackupquery.cpp
index c9d6b715..33860dcf 100644
--- a/bin/bbackupquery/bbackupquery.cpp
+++ b/bin/bbackupquery/bbackupquery.cpp
@@ -37,6 +37,8 @@
#endif
#endif
+#include <cstdlib>
+
#include "MainHelper.h"
#include "BoxPortsAndFiles.h"
#include "BackupDaemonConfigVerify.h"
@@ -61,10 +63,13 @@ void PrintUsageAndExit()
#ifdef WIN32
"[-u] "
#endif
- "\n\t[-c config_file] [-l log_file] [commands]\n"
+ "\n"
+ "\t[-c config_file] [-o log_file] [-O log_file_level]\n"
+ "\t[-l protocol_log_file] [commands]\n"
+ "\n"
"As many commands as you require.\n"
"If commands are multiple words, remember to enclose the command in quotes.\n"
- "Remember to use quit command if you don't want to drop into interactive mode.\n");
+ "Remember to use the quit command unless you want to end up in interactive mode.\n");
exit(1);
}
@@ -90,7 +95,7 @@ int main(int argc, const char *argv[])
#endif
// Really don't want trace statements happening, even in debug mode
- #ifndef NDEBUG
+ #ifndef BOX_RELEASE_BUILD
BoxDebugTraceOn = false;
#endif
@@ -106,24 +111,26 @@ int main(int argc, const char *argv[])
#endif
// Flags
- bool quiet = false;
bool readWrite = false;
- Logging::SetProgramName("Box Backup (bbackupquery)");
+ Logging::SetProgramName("bbackupquery");
- #ifdef NDEBUG
+ #ifdef BOX_RELEASE_BUILD
int masterLevel = Log::NOTICE; // need an int to do math with
#else
int masterLevel = Log::INFO; // need an int to do math with
#endif
#ifdef WIN32
- const char* validOpts = "qvwuc:l:";
+ const char* validOpts = "qvwuc:l:o:O:W:";
bool unicodeConsole = false;
#else
- const char* validOpts = "qvwc:l:";
+ const char* validOpts = "qvwc:l:o:O:W:";
#endif
+ std::string fileLogFile;
+ Log::Level fileLogLevel = Log::INVALID;
+
// See if there's another entry on the command line
int c;
while((c = getopt(argc, (char * const *)argv, validOpts)) != -1)
@@ -132,9 +139,6 @@ int main(int argc, const char *argv[])
{
case 'q':
{
- // Quiet mode
- quiet = true;
-
if(masterLevel == Log::NOTHING)
{
BOX_FATAL("Too many '-q': "
@@ -159,6 +163,17 @@ int main(int argc, const char *argv[])
}
break;
+ case 'W':
+ {
+ masterLevel = Logging::GetNamedLevel(optarg);
+ if (masterLevel == Log::INVALID)
+ {
+ BOX_FATAL("Invalid logging level");
+ return 2;
+ }
+ }
+ break;
+
case 'w':
// Read/write mode
readWrite = true;
@@ -174,8 +189,24 @@ int main(int argc, const char *argv[])
logFile = ::fopen(optarg, "w");
if(logFile == 0)
{
- BOX_ERROR("Failed to open log file '" <<
- optarg << "': " << strerror(errno));
+ BOX_LOG_SYS_ERROR("Failed to open log file "
+ "'" << optarg << "'");
+ }
+ break;
+
+ case 'o':
+ fileLogFile = optarg;
+ fileLogLevel = Log::EVERYTHING;
+ break;
+
+ case 'O':
+ {
+ fileLogLevel = Logging::GetNamedLevel(optarg);
+ if (fileLogLevel == Log::INVALID)
+ {
+ BOX_FATAL("Invalid logging level");
+ return 2;
+ }
}
break;
@@ -196,6 +227,19 @@ int main(int argc, const char *argv[])
Logging::SetGlobalLevel((Log::Level)masterLevel);
+ std::auto_ptr<FileLogger> fileLogger;
+ if (fileLogLevel != Log::INVALID)
+ {
+ fileLogger.reset(new FileLogger(fileLogFile, fileLogLevel));
+ }
+
+ bool quiet = false;
+ if (masterLevel < Log::NOTICE)
+ {
+ // Quiet mode
+ quiet = true;
+ }
+
// Print banner?
if(!quiet)
{
@@ -260,7 +304,9 @@ int main(int argc, const char *argv[])
// 2. Connect to server
if(!quiet) BOX_INFO("Connecting to store...");
SocketStreamTLS socket;
- socket.Open(tlsContext, Socket::TypeINET, conf.GetKeyValue("StoreHostname").c_str(), BOX_PORT_BBSTORED);
+ socket.Open(tlsContext, Socket::TypeINET,
+ conf.GetKeyValue("StoreHostname").c_str(),
+ conf.GetKeyValueInt("StorePort"));
// 3. Make a protocol, and handshake
if(!quiet) BOX_INFO("Handshake with store...");
@@ -291,7 +337,7 @@ int main(int argc, const char *argv[])
if(!quiet) printf("Login complete.\n\nType \"help\" for a list of commands.\n\n");
// Set up a context for our work
- BackupQueries context(connection, conf);
+ BackupQueries context(connection, conf, readWrite);
// Start running commands... first from the command line
{
@@ -377,7 +423,8 @@ int main(int argc, const char *argv[])
#ifdef WIN32
// Clean up our sockets
- WSACleanup();
+ // FIXME we should do this, but I get an abort() when I try
+ // WSACleanup();
#endif
MAINHELPER_END
diff --git a/bin/bbackupquery/documentation.txt b/bin/bbackupquery/documentation.txt
index 42217edc..d32bf200 100644
--- a/bin/bbackupquery/documentation.txt
+++ b/bin/bbackupquery/documentation.txt
@@ -116,7 +116,7 @@ compare <store-dir-name> <local-dir-name>
This can be used for automated tests.
<
-> restore [-d] [-r] [-i] <directory-name> <local-directory-name>
+> restore [-drif] <directory-name> <local-directory-name>
Restores a directory to the local disc. The local directory specified
must not exist (unless a previous restore is being restarted).
@@ -126,6 +126,7 @@ compare <store-dir-name> <local-dir-name>
-d -- restore a deleted directory or deleted files inside
-r -- resume an interrupted restoration
-i -- directory name is actually an ID
+ -f -- force restore to continue if errors are encountered
If a restore operation is interrupted for any reason, it can be restarted
using the -r switch. Restore progress information is saved in a file at
@@ -141,10 +142,12 @@ compare <store-dir-name> <local-dir-name>
stored format, which is encrypted and compressed.
<
-> usage
+> usage [-m]
Show space used on the server for this account.
+ -m -- display the output in machine-readable form
+
Used: Total amount of space used on the server.
Old files: Space used by old files
Deleted files: Space used by deleted files
@@ -158,6 +161,21 @@ compare <store-dir-name> <local-dir-name>
files is near zero.
<
+> undelete <directory-name>
+undelete -i <object-id>
+
+ Removes the deleted flag from the specified directory name (in the
+ current directory) or hex object ID. Be careful not to use this
+ command where a directory already exists with the same name which is
+ not marked as deleted.
+<
+
+> delete <file-name>
+
+ Sets the deleted flag on the specified file name (in the current
+ directory, or with a relative path).
+<
+
> quit
End session and exit.
diff --git a/bin/bbstoreaccounts/bbstoreaccounts.cpp b/bin/bbstoreaccounts/bbstoreaccounts.cpp
index 6f079d30..71114ef4 100644
--- a/bin/bbstoreaccounts/bbstoreaccounts.cpp
+++ b/bin/bbstoreaccounts/bbstoreaccounts.cpp
@@ -9,12 +9,17 @@
#include "Box.h"
-#include <unistd.h>
+#include <limits.h>
#include <stdio.h>
+#include <unistd.h>
+
#include <sys/types.h>
-#include <limits.h>
-#include <vector>
+
#include <algorithm>
+#include <cstring>
+#include <iostream>
+#include <ostream>
+#include <vector>
#include "BoxPortsAndFiles.h"
#include "BackupStoreConfigVerify.h"
@@ -27,12 +32,15 @@
#include "NamedLock.h"
#include "UnixUser.h"
#include "BackupStoreCheck.h"
+#include "Utils.h"
#include "MemLeakFindOn.h"
// max size of soft limit as percent of hard limit
#define MAX_SOFT_LIMIT_SIZE 97
+bool sMachineReadableOutput = false;
+
void CheckSoftHardLimits(int64_t SoftLimit, int64_t HardLimit)
{
if(SoftLimit >= HardLimit)
@@ -62,22 +70,11 @@ int BlockSizeOfDiscSet(int DiscSet)
return controller.GetDiscSet(DiscSet).GetBlockSize();
}
-const char *BlockSizeToString(int64_t Blocks, int DiscSet)
+std::string BlockSizeToString(int64_t Blocks, int64_t MaxBlocks, int DiscSet)
{
- // Not reentrant, nor can be used in the same function call twice, etc.
- static char string[256];
-
- // Work out size in Mb.
- double mb = (Blocks * BlockSizeOfDiscSet(DiscSet)) / (1024.0*1024.0);
-
- // Format string
-#ifdef WIN32
- sprintf(string, "%I64d (%.2fMb)", Blocks, mb);
-#else
- sprintf(string, "%lld (%.2fMb)", Blocks, mb);
-#endif
-
- return string;
+ return FormatUsageBar(Blocks, Blocks * BlockSizeOfDiscSet(DiscSet),
+ MaxBlocks * BlockSizeOfDiscSet(DiscSet),
+ sMachineReadableOutput);
}
int64_t SizeStringToBlocks(const char *string, int DiscSet)
@@ -118,7 +115,7 @@ int64_t SizeStringToBlocks(const char *string, int DiscSet)
default:
BOX_FATAL(string << " has an invalid units specifier "
- "(use B for blocks, M for Mb, G for Gb, eg 2Gb)");
+ "(use B for blocks, M for MB, G for GB, eg 2GB)");
exit(1);
break;
}
@@ -211,7 +208,9 @@ int SetLimit(Configuration &rConfig, const std::string &rUsername, int32_t ID, c
int AccountInfo(Configuration &rConfig, int32_t ID)
{
// Load in the account database
- std::auto_ptr<BackupStoreAccountDatabase> db(BackupStoreAccountDatabase::Read(rConfig.GetKeyValue("AccountDatabase").c_str()));
+ std::auto_ptr<BackupStoreAccountDatabase> db(
+ BackupStoreAccountDatabase::Read(
+ rConfig.GetKeyValue("AccountDatabase").c_str()));
// Exists?
if(!db->EntryExists(ID))
@@ -226,18 +225,34 @@ int AccountInfo(Configuration &rConfig, int32_t ID)
std::string rootDir;
int discSet;
acc.GetAccountRoot(ID, rootDir, discSet);
- std::auto_ptr<BackupStoreInfo> info(BackupStoreInfo::Load(ID, rootDir, discSet, true /* ReadOnly */));
+ std::auto_ptr<BackupStoreInfo> info(BackupStoreInfo::Load(ID,
+ rootDir, discSet, true /* ReadOnly */));
// Then print out lots of info
- printf(" Account ID: %08x\n", ID);
- printf(" Last object ID: %lld\n", info->GetLastObjectIDUsed());
- printf(" Blocks used: %s\n", BlockSizeToString(info->GetBlocksUsed(), discSet));
- printf(" Blocks used by old files: %s\n", BlockSizeToString(info->GetBlocksInOldFiles(), discSet));
- printf("Blocks used by deleted files: %s\n", BlockSizeToString(info->GetBlocksInDeletedFiles(), discSet));
- printf(" Blocks used by directories: %s\n", BlockSizeToString(info->GetBlocksInDirectories(), discSet));
- printf(" Block soft limit: %s\n", BlockSizeToString(info->GetBlocksSoftLimit(), discSet));
- printf(" Block hard limit: %s\n", BlockSizeToString(info->GetBlocksHardLimit(), discSet));
- printf(" Client store marker: %lld\n", info->GetClientStoreMarker());
+ std::cout << FormatUsageLineStart("Account ID", sMachineReadableOutput) <<
+ BOX_FORMAT_ACCOUNT(ID) << std::endl;
+ std::cout << FormatUsageLineStart("Last object ID", sMachineReadableOutput) <<
+ BOX_FORMAT_OBJECTID(info->GetLastObjectIDUsed()) << std::endl;
+ std::cout << FormatUsageLineStart("Used", sMachineReadableOutput) <<
+ BlockSizeToString(info->GetBlocksUsed(),
+ info->GetBlocksHardLimit(), discSet) << std::endl;
+ std::cout << FormatUsageLineStart("Old files", sMachineReadableOutput) <<
+ BlockSizeToString(info->GetBlocksInOldFiles(),
+ info->GetBlocksHardLimit(), discSet) << std::endl;
+ std::cout << FormatUsageLineStart("Deleted files", sMachineReadableOutput) <<
+ BlockSizeToString(info->GetBlocksInDeletedFiles(),
+ info->GetBlocksHardLimit(), discSet) << std::endl;
+ std::cout << FormatUsageLineStart("Directories", sMachineReadableOutput) <<
+ BlockSizeToString(info->GetBlocksInDirectories(),
+ info->GetBlocksHardLimit(), discSet) << std::endl;
+ std::cout << FormatUsageLineStart("Soft limit", sMachineReadableOutput) <<
+ BlockSizeToString(info->GetBlocksSoftLimit(),
+ info->GetBlocksHardLimit(), discSet) << std::endl;
+ std::cout << FormatUsageLineStart("Hard limit", sMachineReadableOutput) <<
+ BlockSizeToString(info->GetBlocksHardLimit(),
+ info->GetBlocksHardLimit(), discSet) << std::endl;
+ std::cout << FormatUsageLineStart("Client store marker", sMachineReadableOutput) <<
+ info->GetLastObjectIDUsed() << std::endl;
return 0;
}
@@ -409,8 +424,32 @@ int CreateAccount(Configuration &rConfig, const std::string &rUsername, int32_t
void PrintUsageAndExit()
{
- printf("Usage: bbstoreaccounts [-c config_file] action account_id [args]\nAccount ID is integer specified in hex\n");
- exit(1);
+ printf(
+"Usage: bbstoreaccounts [-c config_file] action account_id [args]\n"
+"Account ID is integer specified in hex\n"
+"\n"
+"Commands (and arguments):\n"
+" create <account> <discnum> <softlimit> <hardlimit>\n"
+" Creates the specified account number (in hex with no 0x) on the\n"
+" specified raidfile disc set number (see raidfile.conf for valid\n"
+" set numbers) with the specified soft and hard limits (in blocks\n"
+" if suffixed with B, MB with M, GB with G)\n"
+" info [-m] <account>\n"
+" Prints information about the specified account including number\n"
+" of blocks used. The -m option enable machine-readable output.\n"
+" setlimit <accounts> <softlimit> <hardlimit>\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"
+" delete <account> [yes]\n"
+" Deletes the specified account. Prompts for confirmation unless\n"
+" the optional 'yes' parameter is provided.\n"
+" check <account> [fix] [quiet]\n"
+" Checks the specified account for errors. If the 'fix' option is\n"
+" provided, any errors discovered that can be fixed automatically\n"
+" will be fixed. If the 'quiet' option is provided, less output is\n"
+" produced.\n"
+ );
+ exit(2);
}
int main(int argc, const char *argv[])
@@ -420,6 +459,8 @@ int main(int argc, const char *argv[])
MAINHELPER_START
+ Logging::SetProgramName("bbstoreaccounts");
+
// Filename for configuration file?
std::string configFilename;
@@ -431,7 +472,7 @@ int main(int argc, const char *argv[])
// See if there's another entry on the command line
int c;
- while((c = getopt(argc, (char * const *)argv, "c:")) != -1)
+ while((c = getopt(argc, (char * const *)argv, "c:m")) != -1)
{
switch(c)
{
@@ -440,6 +481,11 @@ int main(int argc, const char *argv[])
configFilename = optarg;
break;
+ case 'm':
+ // enable machine readable output
+ sMachineReadableOutput = true;
+ break;
+
case '?':
default:
PrintUsageAndExit();
diff --git a/bin/bbstored/BBStoreDHousekeeping.cpp b/bin/bbstored/BBStoreDHousekeeping.cpp
index 16a1432a..1c1767ca 100644
--- a/bin/bbstored/BBStoreDHousekeeping.cpp
+++ b/bin/bbstored/BBStoreDHousekeeping.cpp
@@ -46,6 +46,12 @@ void BackupStoreDaemon::HousekeepingProcess()
{
RunHousekeepingIfNeeded();
+ // Stop early?
+ if(StopRun())
+ {
+ break;
+ }
+
// Calculate how long should wait before doing the next
// housekeeping run
int64_t timeNow = GetCurrentBoxTime();
@@ -155,7 +161,11 @@ void BackupStoreDaemon::RunHousekeepingIfNeeded()
void BackupStoreDaemon::OnIdle()
{
- #ifdef WIN32
+ if (!IsSingleProcess())
+ {
+ return;
+ }
+
if (!mHousekeepingInited)
{
HousekeepingInit();
@@ -163,7 +173,6 @@ void BackupStoreDaemon::OnIdle()
}
RunHousekeepingIfNeeded();
- #endif
}
// --------------------------------------------------------------------------
@@ -193,7 +202,8 @@ bool BackupStoreDaemon::CheckForInterProcessMsg(int AccountNum, int MaximumWaitT
std::string line;
if(mInterProcessComms.GetLine(line, false /* no pre-processing */, MaximumWaitTime))
{
- TRACE1("Housekeeping received command '%s' over interprocess comms\n", line.c_str());
+ BOX_TRACE("Housekeeping received command '" << line <<
+ "' over interprocess comms");
int account = 0;
diff --git a/bin/bbstored/BackupCommands.cpp b/bin/bbstored/BackupCommands.cpp
index bca52c04..38cda234 100644
--- a/bin/bbstored/BackupCommands.cpp
+++ b/bin/bbstored/BackupCommands.cpp
@@ -13,25 +13,25 @@
#include <sstream>
#include "autogen_BackupProtocolServer.h"
+#include "autogen_RaidFileException.h"
#include "BackupConstants.h"
-#include "BackupContext.h"
-#include "CollectInBufferStream.h"
+#include "BackupStoreContext.h"
+#include "BackupStoreConstants.h"
#include "BackupStoreDirectory.h"
#include "BackupStoreException.h"
#include "BackupStoreFile.h"
-#include "StreamableMemBlock.h"
-#include "BackupStoreConstants.h"
-#include "RaidFileController.h"
#include "BackupStoreInfo.h"
-#include "RaidFileController.h"
+#include "BufferedStream.h"
+#include "CollectInBufferStream.h"
#include "FileStream.h"
#include "InvisibleTempFileStream.h"
-#include "BufferedStream.h"
+#include "RaidFileController.h"
+#include "StreamableMemBlock.h"
#include "MemLeakFindOn.h"
#define CHECK_PHASE(phase) \
- if(rContext.GetPhase() != BackupContext::phase) \
+ if(rContext.GetPhase() != BackupStoreContext::phase) \
{ \
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError( \
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_NotInRightProtocolPhase)); \
@@ -47,12 +47,12 @@
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerVersion::DoCommand(Protocol &, BackupContext &)
+// Name: BackupProtocolServerVersion::DoCommand(Protocol &, BackupStoreContext &)
// Purpose: Return the current version, or an error if the requested version isn't allowed
// Created: 2003/08/20
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerVersion::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerVersion::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Version)
@@ -64,7 +64,7 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerVersion::DoCommand(BackupProto
}
// Mark the next phase
- rContext.SetPhase(BackupContext::Phase_Login);
+ rContext.SetPhase(BackupStoreContext::Phase_Login);
// Return our version
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerVersion(BACKUP_STORE_SERVER_VERSION));
@@ -73,12 +73,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerVersion::DoCommand(BackupProto
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerLogin::DoCommand(Protocol &, BackupContext &)
+// Name: BackupProtocolServerLogin::DoCommand(Protocol &, BackupStoreContext &)
// Purpose: Return the current version, or an error if the requested version isn't allowed
// Created: 2003/08/20
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerLogin::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerLogin::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Login)
@@ -131,7 +131,7 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerLogin::DoCommand(BackupProtoco
int64_t clientStoreMarker = rContext.GetClientStoreMarker();
// Mark the next phase
- rContext.SetPhase(BackupContext::Phase_Commands);
+ rContext.SetPhase(BackupStoreContext::Phase_Commands);
// Log login
BOX_NOTICE("Login from Client ID " <<
@@ -151,12 +151,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerLogin::DoCommand(BackupProtoco
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerFinished::DoCommand(Protocol &, BackupContext &)
+// Name: BackupProtocolServerFinished::DoCommand(Protocol &, BackupStoreContext &)
// Purpose: Marks end of conversation (Protocol framework handles this)
// Created: 2003/08/20
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerFinished::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerFinished::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
BOX_NOTICE("Session finished for Client ID " <<
BOX_FORMAT_ACCOUNT(rContext.GetClientID()));
@@ -172,47 +172,72 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerFinished::DoCommand(BackupProt
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerListDirectory::DoCommand(Protocol &, BackupContext &)
+// Name: BackupProtocolServerListDirectory::DoCommand(Protocol &, BackupStoreContext &)
// Purpose: Command to list a directory
// Created: 2003/09/02
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerListDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerListDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
- // Ask the context for a directory
- const BackupStoreDirectory &rdir(rContext.GetDirectory(mObjectID));
-
// Store the listing to a stream
std::auto_ptr<CollectInBufferStream> stream(new CollectInBufferStream);
- rdir.WriteToStream(*stream, mFlagsMustBeSet, mFlagsNotToBeSet, mSendAttributes,
- false /* never send dependency info to the client */);
+
+ try
+ {
+ // Ask the context for a directory
+ const BackupStoreDirectory &rdir(
+ rContext.GetDirectory(mObjectID));
+ rdir.WriteToStream(*stream, mFlagsMustBeSet,
+ mFlagsNotToBeSet, mSendAttributes,
+ false /* never send dependency info to the client */);
+ }
+ catch (RaidFileException &e)
+ {
+ if (e.GetSubType() == RaidFileException::RaidFileDoesntExist)
+ {
+ return std::auto_ptr<ProtocolObject>(
+ new BackupProtocolServerError(
+ BackupProtocolServerError::ErrorType,
+ BackupProtocolServerError::Err_DoesNotExist));
+ }
+ throw;
+ }
+
stream->SetForReading();
// Get the protocol to send the stream
rProtocol.SendStreamAfterCommand(stream.release());
- return std::auto_ptr<ProtocolObject>(new BackupProtocolServerSuccess(mObjectID));
+ return std::auto_ptr<ProtocolObject>(
+ new BackupProtocolServerSuccess(mObjectID));
}
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerStoreFile::DoCommand(Protocol &, BackupContext &)
+// Name: BackupProtocolServerStoreFile::DoCommand(Protocol &, BackupStoreContext &)
// Purpose: Command to store a file on the server
// Created: 2003/09/02
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerStoreFile::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerStoreFile::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
+
+ std::auto_ptr<ProtocolObject> hookResult =
+ rContext.StartCommandHook(*this);
+ if(hookResult.get())
+ {
+ return hookResult;
+ }
// Check that the diff from file actually exists, if it's specified
if(mDiffFromFileID != 0)
{
- if(!rContext.ObjectExists(mDiffFromFileID, BackupContext::ObjectExists_File))
+ if(!rContext.ObjectExists(mDiffFromFileID, BackupStoreContext::ObjectExists_File))
{
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerError(
BackupProtocolServerError::ErrorType, BackupProtocolServerError::Err_DiffFromFileDoesNotExist));
@@ -257,12 +282,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerStoreFile::DoCommand(BackupPro
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerGetObject::DoCommand(Protocol &, BackupContext &)
+// Name: BackupProtocolServerGetObject::DoCommand(Protocol &, BackupStoreContext &)
// Purpose: Command to get an arbitary object from the server
// Created: 2003/09/03
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerGetObject::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerGetObject::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
@@ -285,13 +310,13 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerGetObject::DoCommand(BackupPro
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerGetFile::DoCommand(Protocol &, BackupContext &)
+// Name: BackupProtocolServerGetFile::DoCommand(Protocol &, BackupStoreContext &)
// Purpose: Command to get an file object from the server -- may have to do a bit of
// work to get the object.
// Created: 2003/09/03
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerGetFile::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerGetFile::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
@@ -457,12 +482,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerGetFile::DoCommand(BackupProto
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerCreateDirectory::DoCommand(Protocol &, BackupContext &)
+// Name: BackupProtocolServerCreateDirectory::DoCommand(Protocol &, BackupStoreContext &)
// Purpose: Create directory command
// Created: 2003/09/04
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerCreateDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerCreateDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
@@ -500,12 +525,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerCreateDirectory::DoCommand(Bac
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerChangeDirAttributes::DoCommand(Protocol &, BackupContext &)
+// Name: BackupProtocolServerChangeDirAttributes::DoCommand(Protocol &, BackupStoreContext &)
// Purpose: Change attributes on directory
// Created: 2003/09/06
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerChangeDirAttributes::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerChangeDirAttributes::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
@@ -528,12 +553,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerChangeDirAttributes::DoCommand
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerSetReplacementFileAttributes::DoCommand(Protocol &, BackupContext &)
+// Name: BackupProtocolServerSetReplacementFileAttributes::DoCommand(Protocol &, BackupStoreContext &)
// Purpose: Change attributes on directory
// Created: 2003/09/06
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerSetReplacementFileAttributes::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerSetReplacementFileAttributes::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
@@ -563,12 +588,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerSetReplacementFileAttributes::
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerDeleteFile::DoCommand(BackupProtocolServer &, BackupContext &)
+// Name: BackupProtocolServerDeleteFile::DoCommand(BackupProtocolServer &, BackupStoreContext &)
// Purpose: Delete a file
// Created: 2003/10/21
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerDeleteFile::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerDeleteFile::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
@@ -585,12 +610,36 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerDeleteFile::DoCommand(BackupPr
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerDeleteDirectory::DoCommand(BackupProtocolServer &, BackupContext &)
+// Name: BackupProtocolServerUndeleteFile::DoCommand(
+// BackupProtocolServer &, BackupStoreContext &)
+// Purpose: Undelete a file
+// Created: 2008-09-12
+//
+// --------------------------------------------------------------------------
+std::auto_ptr<ProtocolObject> BackupProtocolServerUndeleteFile::DoCommand(
+ BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
+{
+ CHECK_PHASE(Phase_Commands)
+ CHECK_WRITEABLE_SESSION
+
+ // Context handles this
+ bool result = rContext.UndeleteFile(mObjectID, mInDirectory);
+
+ // return the object ID or zero for not found
+ return std::auto_ptr<ProtocolObject>(
+ new BackupProtocolServerSuccess(result ? mObjectID : 0));
+}
+
+
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: BackupProtocolServerDeleteDirectory::DoCommand(BackupProtocolServer &, BackupStoreContext &)
// Purpose: Delete a directory
// Created: 2003/10/21
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerDeleteDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerDeleteDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
@@ -613,12 +662,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerDeleteDirectory::DoCommand(Bac
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerUndeleteDirectory::DoCommand(BackupProtocolServer &, BackupContext &)
+// Name: BackupProtocolServerUndeleteDirectory::DoCommand(BackupProtocolServer &, BackupStoreContext &)
// Purpose: Undelete a directory
// Created: 23/11/03
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerUndeleteDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerUndeleteDirectory::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
@@ -640,12 +689,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerUndeleteDirectory::DoCommand(B
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerSetClientStoreMarker::DoCommand(BackupProtocolServer &, BackupContext &)
+// Name: BackupProtocolServerSetClientStoreMarker::DoCommand(BackupProtocolServer &, BackupStoreContext &)
// Purpose: Command to set the client's store marker
// Created: 2003/10/29
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerSetClientStoreMarker::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerSetClientStoreMarker::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
@@ -661,12 +710,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerSetClientStoreMarker::DoComman
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerMoveObject::DoCommand(BackupProtocolServer &, BackupContext &)
+// Name: BackupProtocolServerMoveObject::DoCommand(BackupProtocolServer &, BackupStoreContext &)
// Purpose: Command to move an object from one directory to another
// Created: 2003/11/12
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerMoveObject::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerMoveObject::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
CHECK_WRITEABLE_SESSION
@@ -704,12 +753,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerMoveObject::DoCommand(BackupPr
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerGetObjectName::DoCommand(BackupProtocolServer &, BackupContext &)
+// Name: BackupProtocolServerGetObjectName::DoCommand(BackupProtocolServer &, BackupStoreContext &)
// Purpose: Command to find the name of an object
// Created: 12/11/03
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerGetObjectName::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerGetObjectName::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
@@ -730,7 +779,7 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerGetObjectName::DoCommand(Backu
do
{
// Check the directory really exists
- if(!rContext.ObjectExists(dirID, BackupContext::ObjectExists_Directory))
+ if(!rContext.ObjectExists(dirID, BackupStoreContext::ObjectExists_Directory))
{
return std::auto_ptr<ProtocolObject>(new BackupProtocolServerObjectName(BackupProtocolServerObjectName::NumNameElements_ObjectDoesntExist, 0, 0, 0));
}
@@ -795,12 +844,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerGetObjectName::DoCommand(Backu
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerGetBlockIndexByID::DoCommand(BackupProtocolServer &, BackupContext &)
+// Name: BackupProtocolServerGetBlockIndexByID::DoCommand(BackupProtocolServer &, BackupStoreContext &)
// Purpose: Get the block index from a file, by ID
// Created: 19/1/04
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerGetBlockIndexByID::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerGetBlockIndexByID::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
@@ -821,12 +870,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerGetBlockIndexByID::DoCommand(B
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerGetBlockIndexByName::DoCommand(BackupProtocolServer &, BackupContext &)
+// Name: BackupProtocolServerGetBlockIndexByName::DoCommand(BackupProtocolServer &, BackupStoreContext &)
// Purpose: Get the block index from a file, by name within a directory
// Created: 19/1/04
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerGetBlockIndexByName::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerGetBlockIndexByName::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
@@ -873,12 +922,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerGetBlockIndexByName::DoCommand
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerGetAccountUsage::DoCommand(BackupProtocolServer &, BackupContext &)
+// Name: BackupProtocolServerGetAccountUsage::DoCommand(BackupProtocolServer &, BackupStoreContext &)
// Purpose: Return the amount of disc space used
// Created: 19/4/04
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerGetAccountUsage::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerGetAccountUsage::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
@@ -904,12 +953,12 @@ std::auto_ptr<ProtocolObject> BackupProtocolServerGetAccountUsage::DoCommand(Bac
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupProtocolServerGetIsAlive::DoCommand(BackupProtocolServer &, BackupContext &)
+// Name: BackupProtocolServerGetIsAlive::DoCommand(BackupProtocolServer &, BackupStoreContext &)
// Purpose: Return the amount of disc space used
// Created: 19/4/04
//
// --------------------------------------------------------------------------
-std::auto_ptr<ProtocolObject> BackupProtocolServerGetIsAlive::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+std::auto_ptr<ProtocolObject> BackupProtocolServerGetIsAlive::DoCommand(BackupProtocolServer &rProtocol, BackupStoreContext &rContext)
{
CHECK_PHASE(Phase_Commands)
diff --git a/bin/bbstored/BackupConstants.h b/bin/bbstored/BackupConstants.h
index 515b3bcd..19d06a15 100644
--- a/bin/bbstored/BackupConstants.h
+++ b/bin/bbstored/BackupConstants.h
@@ -10,8 +10,6 @@
#ifndef BACKUPCONSTANTS__H
#define BACKUPCONSTANTS__H
-#define BACKUP_STORE_DEFAULT_ACCOUNT_DATABASE_FILE "/etc/box/backupstoreaccounts"
-
// 15 minutes to timeout (milliseconds)
#define BACKUP_STORE_TIMEOUT (15*60*1000)
diff --git a/bin/bbstored/BackupContext.cpp b/bin/bbstored/BackupStoreContext.cpp
index 16388099..990be05d 100644
--- a/bin/bbstored/BackupContext.cpp
+++ b/bin/bbstored/BackupStoreContext.cpp
@@ -1,7 +1,7 @@
// --------------------------------------------------------------------------
//
// File
-// Name: BackupContext.cpp
+// Name: BackupStoreContext.cpp
// Purpose: Context for backup store server
// Created: 2003/08/20
//
@@ -11,7 +11,7 @@
#include <stdio.h>
-#include "BackupContext.h"
+#include "BackupStoreContext.h"
#include "RaidFileWrite.h"
#include "RaidFileRead.h"
#include "BackupStoreDirectory.h"
@@ -33,7 +33,7 @@
// Maximum number of directories to keep in the cache
// When the cache is bigger than this, everything gets
// deleted.
-#ifdef NDEBUG
+#ifdef BOX_RELEASE_BUILD
#define MAX_CACHE_SIZE 32
#else
#define MAX_CACHE_SIZE 2
@@ -48,31 +48,33 @@
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::BackupContext()
+// Name: BackupStoreContext::BackupStoreContext()
// Purpose: Constructor
// Created: 2003/08/20
//
// --------------------------------------------------------------------------
-BackupContext::BackupContext(int32_t ClientID, BackupStoreDaemon &rDaemon)
+BackupStoreContext::BackupStoreContext(int32_t ClientID,
+ HousekeepingInterface &rDaemon)
: mClientID(ClientID),
mrDaemon(rDaemon),
mProtocolPhase(Phase_START),
mClientHasAccount(false),
mStoreDiscSet(-1),
mReadOnly(true),
- mSaveStoreInfoDelay(STORE_INFO_SAVE_DELAY)
+ mSaveStoreInfoDelay(STORE_INFO_SAVE_DELAY),
+ mpTestHook(NULL)
{
}
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::~BackupContext()
+// Name: BackupStoreContext::~BackupStoreContext()
// Purpose: Destructor
// Created: 2003/08/20
//
// --------------------------------------------------------------------------
-BackupContext::~BackupContext()
+BackupStoreContext::~BackupStoreContext()
{
// Delete the objects in the cache
for(std::map<int64_t, BackupStoreDirectory*>::iterator i(mDirectoryCache.begin()); i != mDirectoryCache.end(); ++i)
@@ -85,12 +87,12 @@ BackupContext::~BackupContext()
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::CleanUp()
+// Name: BackupStoreContext::CleanUp()
// Purpose: Clean up after a connection
// Created: 16/12/03
//
// --------------------------------------------------------------------------
-void BackupContext::CleanUp()
+void BackupStoreContext::CleanUp()
{
// Make sure the store info is saved, if it has been loaded, isn't read only and has been modified
if(mpStoreInfo.get() && !(mpStoreInfo->IsReadOnly()) && mpStoreInfo->IsModified())
@@ -102,12 +104,12 @@ void BackupContext::CleanUp()
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::ReceivedFinishCommand()
+// Name: BackupStoreContext::ReceivedFinishCommand()
// Purpose: Called when the finish command is received by the protocol
// Created: 16/12/03
//
// --------------------------------------------------------------------------
-void BackupContext::ReceivedFinishCommand()
+void BackupStoreContext::ReceivedFinishCommand()
{
if(!mReadOnly && mpStoreInfo.get())
{
@@ -120,12 +122,12 @@ void BackupContext::ReceivedFinishCommand()
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::AttemptToGetWriteLock()
+// Name: BackupStoreContext::AttemptToGetWriteLock()
// Purpose: Attempt to get a write lock for the store, and if so, unset the read only flags
// Created: 2003/09/02
//
// --------------------------------------------------------------------------
-bool BackupContext::AttemptToGetWriteLock()
+bool BackupStoreContext::AttemptToGetWriteLock()
{
// Make the filename of the write lock file
std::string writeLockFile;
@@ -166,12 +168,12 @@ bool BackupContext::AttemptToGetWriteLock()
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::LoadStoreInfo()
+// Name: BackupStoreContext::LoadStoreInfo()
// Purpose: Load the store info from disc
// Created: 2003/09/03
//
// --------------------------------------------------------------------------
-void BackupContext::LoadStoreInfo()
+void BackupStoreContext::LoadStoreInfo()
{
if(mpStoreInfo.get() != 0)
{
@@ -195,12 +197,12 @@ void BackupContext::LoadStoreInfo()
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::SaveStoreInfo(bool)
+// Name: BackupStoreContext::SaveStoreInfo(bool)
// Purpose: Potentially delayed saving of the store info
// Created: 16/12/03
//
// --------------------------------------------------------------------------
-void BackupContext::SaveStoreInfo(bool AllowDelay)
+void BackupStoreContext::SaveStoreInfo(bool AllowDelay)
{
if(mpStoreInfo.get() == 0)
{
@@ -233,13 +235,13 @@ void BackupContext::SaveStoreInfo(bool AllowDelay)
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::MakeObjectFilename(int64_t, std::string &, bool)
+// Name: BackupStoreContext::MakeObjectFilename(int64_t, std::string &, bool)
// Purpose: Create the filename of an object in the store, optionally creating the
// containing directory if it doesn't already exist.
// Created: 2003/09/02
//
// --------------------------------------------------------------------------
-void BackupContext::MakeObjectFilename(int64_t ObjectID, std::string &rOutput, bool EnsureDirectoryExists)
+void BackupStoreContext::MakeObjectFilename(int64_t ObjectID, std::string &rOutput, bool EnsureDirectoryExists)
{
// Delegate to utility function
StoreStructure::MakeObjectFilename(ObjectID, mStoreRoot, mStoreDiscSet, rOutput, EnsureDirectoryExists);
@@ -249,7 +251,7 @@ void BackupContext::MakeObjectFilename(int64_t ObjectID, std::string &rOutput, b
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::GetDirectoryInternal(int64_t)
+// Name: BackupStoreContext::GetDirectoryInternal(int64_t)
// Purpose: Return a reference to a directory. Valid only until the
// next time a function which affects directories is called.
// Mainly this funciton, and creation of files.
@@ -257,7 +259,7 @@ void BackupContext::MakeObjectFilename(int64_t ObjectID, std::string &rOutput, b
// Created: 2003/09/02
//
// --------------------------------------------------------------------------
-BackupStoreDirectory &BackupContext::GetDirectoryInternal(int64_t ObjectID)
+BackupStoreDirectory &BackupStoreContext::GetDirectoryInternal(int64_t ObjectID)
{
// Get the filename
std::string filename;
@@ -277,9 +279,17 @@ BackupStoreDirectory &BackupContext::GetDirectoryInternal(int64_t ObjectID)
if(revID == item->second->GetRevisionID())
{
// Looks good... return the cached object
+ BOX_TRACE("Returning object " <<
+ BOX_FORMAT_OBJECTID(ObjectID) <<
+ " from cache, modtime = " << revID);
return *(item->second);
}
+ BOX_TRACE("Refreshing object " <<
+ BOX_FORMAT_OBJECTID(ObjectID) <<
+ " in cache, modtime changed from " <<
+ item->second->GetRevisionID() << " to " << revID);
+
// Delete this cached object
delete item->second;
mDirectoryCache.erase(item);
@@ -335,12 +345,12 @@ BackupStoreDirectory &BackupContext::GetDirectoryInternal(int64_t ObjectID)
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::AllocateObjectID()
+// Name: BackupStoreContext::AllocateObjectID()
// Purpose: Allocate a new object ID, tolerant of failures to save store info
// Created: 16/12/03
//
// --------------------------------------------------------------------------
-int64_t BackupContext::AllocateObjectID()
+int64_t BackupStoreContext::AllocateObjectID()
{
if(mpStoreInfo.get() == 0)
{
@@ -374,7 +384,8 @@ int64_t BackupContext::AllocateObjectID()
// Mark that the store info should be saved as soon as possible
mSaveStoreInfoDelay = 0;
- TRACE1("When allocating object ID, found that %lld is already in use\n", id);
+ BOX_WARNING("When allocating object ID, found that " <<
+ BOX_FORMAT_OBJECTID(id) << " is already in use");
}
THROW_EXCEPTION(BackupStoreException, CouldNotFindUnusedIDDuringAllocation)
@@ -384,13 +395,13 @@ int64_t BackupContext::AllocateObjectID()
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::AddFile(IOStream &, int64_t, int64_t, int64_t, const BackupStoreFilename &, bool)
+// Name: BackupStoreContext::AddFile(IOStream &, int64_t, int64_t, int64_t, const BackupStoreFilename &, bool)
// Purpose: Add a file to the store, from a given stream, into a specified directory.
// Returns object ID of the new file.
// Created: 2003/09/03
//
// --------------------------------------------------------------------------
-int64_t BackupContext::AddFile(IOStream &rFile, int64_t InDirectory, int64_t ModificationTime,
+int64_t BackupStoreContext::AddFile(IOStream &rFile, int64_t InDirectory, int64_t ModificationTime,
int64_t AttributesHash, int64_t DiffFromFileID, const BackupStoreFilename &rFilename,
bool MarkFileWithSameNameAsOldVersions)
{
@@ -672,18 +683,19 @@ int64_t BackupContext::AddFile(IOStream &rFile, int64_t InDirectory, int64_t Mod
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::DeleteFile(const BackupStoreFilename &, int64_t, int64_t &)
+// Name: BackupStoreContext::DeleteFile(const BackupStoreFilename &, int64_t, int64_t &)
// Purpose: Deletes a file, returning true if the file existed. Object ID returned too, set to zero if not found.
// Created: 2003/10/21
//
// --------------------------------------------------------------------------
-bool BackupContext::DeleteFile(const BackupStoreFilename &rFilename, int64_t InDirectory, int64_t &rObjectIDOut)
+bool BackupStoreContext::DeleteFile(const BackupStoreFilename &rFilename, int64_t InDirectory, int64_t &rObjectIDOut)
{
// Essential checks!
if(mpStoreInfo.get() == 0)
{
THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded)
}
+
if(mReadOnly)
{
THROW_EXCEPTION(BackupStoreException, ContextIsReadOnly)
@@ -748,23 +760,102 @@ bool BackupContext::DeleteFile(const BackupStoreFilename &rFilename, int64_t InD
RemoveDirectoryFromCache(InDirectory);
throw;
}
-
return fileExisted;
}
+// --------------------------------------------------------------------------
+//
+// Function
+// Name: BackupStoreContext::DeleteFile(const BackupStoreFilename &, int64_t, int64_t &)
+// Purpose: Deletes a file, returning true if the file existed. Object ID returned too, set to zero if not found.
+// Created: 2003/10/21
+//
+// --------------------------------------------------------------------------
+bool BackupStoreContext::UndeleteFile(int64_t ObjectID, int64_t InDirectory)
+{
+ // Essential checks!
+ if(mpStoreInfo.get() == 0)
+ {
+ THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded)
+ }
+
+ if(mReadOnly)
+ {
+ THROW_EXCEPTION(BackupStoreException, ContextIsReadOnly)
+ }
+
+ // Find the directory the file is in (will exception if it fails)
+ BackupStoreDirectory &dir(GetDirectoryInternal(InDirectory));
+
+ // Setup flags
+ bool fileExisted = false;
+ bool madeChanges = false;
+
+ // Count of deleted blocks
+ int64_t blocksDel = 0;
+
+ try
+ {
+ // Iterate through directory, only looking at files which have been deleted
+ BackupStoreDirectory::Iterator i(dir);
+ BackupStoreDirectory::Entry *e = 0;
+ while((e = i.Next(BackupStoreDirectory::Entry::Flags_File |
+ BackupStoreDirectory::Entry::Flags_Deleted, 0)) != 0)
+ {
+ // Compare name
+ if(e->GetObjectID() == ObjectID)
+ {
+ // Check that it's definitely already deleted
+ ASSERT((e->GetFlags() & BackupStoreDirectory::Entry::Flags_Deleted) != 0);
+ // Clear deleted flag
+ e->RemoveFlags(BackupStoreDirectory::Entry::Flags_Deleted);
+ // Mark as made a change
+ madeChanges = true;
+ blocksDel -= e->GetSizeInBlocks();
+
+ // Is this the last version?
+ if((e->GetFlags() & BackupStoreDirectory::Entry::Flags_OldVersion) == 0)
+ {
+ // Yes. It's been found.
+ fileExisted = true;
+ }
+ }
+ }
+
+ // Save changes?
+ if(madeChanges)
+ {
+ // Save the directory back
+ SaveDirectory(dir, InDirectory);
+
+ // Modify the store info, and write
+ mpStoreInfo->ChangeBlocksInDeletedFiles(blocksDel);
+
+ // Maybe postponed save of store info
+ SaveStoreInfo();
+ }
+ }
+ catch(...)
+ {
+ RemoveDirectoryFromCache(InDirectory);
+ throw;
+ }
+
+ return fileExisted;
+}
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::RemoveDirectoryFromCache(int64_t)
+// Name: BackupStoreContext::RemoveDirectoryFromCache(int64_t)
// Purpose: Remove directory from cache
// Created: 2003/09/04
//
// --------------------------------------------------------------------------
-void BackupContext::RemoveDirectoryFromCache(int64_t ObjectID)
+void BackupStoreContext::RemoveDirectoryFromCache(int64_t ObjectID)
{
std::map<int64_t, BackupStoreDirectory*>::iterator item(mDirectoryCache.find(ObjectID));
if(item != mDirectoryCache.end())
@@ -780,12 +871,12 @@ void BackupContext::RemoveDirectoryFromCache(int64_t ObjectID)
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::SaveDirectory(BackupStoreDirectory &, int64_t)
+// Name: BackupStoreContext::SaveDirectory(BackupStoreDirectory &, int64_t)
// Purpose: Save directory back to disc, update time in cache
// Created: 2003/09/04
//
// --------------------------------------------------------------------------
-void BackupContext::SaveDirectory(BackupStoreDirectory &rDir, int64_t ObjectID)
+void BackupStoreContext::SaveDirectory(BackupStoreDirectory &rDir, int64_t ObjectID)
{
if(mpStoreInfo.get() == 0)
{
@@ -842,12 +933,12 @@ void BackupContext::SaveDirectory(BackupStoreDirectory &rDir, int64_t ObjectID)
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::AddDirectory(int64_t, const BackupStoreFilename &, bool &)
+// Name: BackupStoreContext::AddDirectory(int64_t, const BackupStoreFilename &, bool &)
// Purpose: Creates a directory (or just returns the ID of an existing one). rAlreadyExists set appropraitely.
// Created: 2003/09/04
//
// --------------------------------------------------------------------------
-int64_t BackupContext::AddDirectory(int64_t InDirectory, const BackupStoreFilename &rFilename, const StreamableMemBlock &Attributes, int64_t AttributesModTime, bool &rAlreadyExists)
+int64_t BackupStoreContext::AddDirectory(int64_t InDirectory, const BackupStoreFilename &rFilename, const StreamableMemBlock &Attributes, int64_t AttributesModTime, bool &rAlreadyExists)
{
if(mpStoreInfo.get() == 0)
{
@@ -936,12 +1027,12 @@ int64_t BackupContext::AddDirectory(int64_t InDirectory, const BackupStoreFilena
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::DeleteFile(const BackupStoreFilename &, int64_t, int64_t &, bool)
+// Name: BackupStoreContext::DeleteFile(const BackupStoreFilename &, int64_t, int64_t &, bool)
// Purpose: Recusively deletes a directory (or undeletes if Undelete = true)
// Created: 2003/10/21
//
// --------------------------------------------------------------------------
-void BackupContext::DeleteDirectory(int64_t ObjectID, bool Undelete)
+void BackupStoreContext::DeleteDirectory(int64_t ObjectID, bool Undelete)
{
// Essential checks!
if(mpStoreInfo.get() == 0)
@@ -1018,12 +1109,12 @@ void BackupContext::DeleteDirectory(int64_t ObjectID, bool Undelete)
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::DeleteDirectoryRecurse(BackupStoreDirectory &, int64_t)
+// Name: BackupStoreContext::DeleteDirectoryRecurse(BackupStoreDirectory &, int64_t)
// Purpose: Private. Deletes a directory depth-first recusively.
// Created: 2003/10/21
//
// --------------------------------------------------------------------------
-void BackupContext::DeleteDirectoryRecurse(int64_t ObjectID, int64_t &rBlocksDeletedOut, bool Undelete)
+void BackupStoreContext::DeleteDirectoryRecurse(int64_t ObjectID, int64_t &rBlocksDeletedOut, bool Undelete)
{
try
{
@@ -1120,12 +1211,12 @@ void BackupContext::DeleteDirectoryRecurse(int64_t ObjectID, int64_t &rBlocksDel
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::ChangeDirAttributes(int64_t, const StreamableMemBlock &, int64_t)
+// Name: BackupStoreContext::ChangeDirAttributes(int64_t, const StreamableMemBlock &, int64_t)
// Purpose: Change the attributes of a directory
// Created: 2003/09/06
//
// --------------------------------------------------------------------------
-void BackupContext::ChangeDirAttributes(int64_t Directory, const StreamableMemBlock &Attributes, int64_t AttributesModTime)
+void BackupStoreContext::ChangeDirAttributes(int64_t Directory, const StreamableMemBlock &Attributes, int64_t AttributesModTime)
{
if(mpStoreInfo.get() == 0)
{
@@ -1157,12 +1248,12 @@ void BackupContext::ChangeDirAttributes(int64_t Directory, const StreamableMemBl
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::ChangeFileAttributes(int64_t, int64_t, const StreamableMemBlock &, int64_t)
+// Name: BackupStoreContext::ChangeFileAttributes(int64_t, int64_t, const StreamableMemBlock &, int64_t)
// Purpose: Sets the attributes on a directory entry. Returns true if the object existed, false if it didn't.
// Created: 2003/09/06
//
// --------------------------------------------------------------------------
-bool BackupContext::ChangeFileAttributes(const BackupStoreFilename &rFilename, int64_t InDirectory, const StreamableMemBlock &Attributes, int64_t AttributesHash, int64_t &rObjectIDOut)
+bool BackupStoreContext::ChangeFileAttributes(const BackupStoreFilename &rFilename, int64_t InDirectory, const StreamableMemBlock &Attributes, int64_t AttributesHash, int64_t &rObjectIDOut)
{
if(mpStoreInfo.get() == 0)
{
@@ -1222,12 +1313,12 @@ bool BackupContext::ChangeFileAttributes(const BackupStoreFilename &rFilename, i
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::ObjectExists(int64_t)
+// Name: BackupStoreContext::ObjectExists(int64_t)
// Purpose: Test to see if an object of this ID exists in the store
// Created: 2003/09/03
//
// --------------------------------------------------------------------------
-bool BackupContext::ObjectExists(int64_t ObjectID, int MustBe)
+bool BackupStoreContext::ObjectExists(int64_t ObjectID, int MustBe)
{
if(mpStoreInfo.get() == 0)
{
@@ -1293,12 +1384,12 @@ bool BackupContext::ObjectExists(int64_t ObjectID, int MustBe)
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::OpenObject(int64_t)
+// Name: BackupStoreContext::OpenObject(int64_t)
// Purpose: Opens an object
// Created: 2003/09/03
//
// --------------------------------------------------------------------------
-std::auto_ptr<IOStream> BackupContext::OpenObject(int64_t ObjectID)
+std::auto_ptr<IOStream> BackupStoreContext::OpenObject(int64_t ObjectID)
{
if(mpStoreInfo.get() == 0)
{
@@ -1315,12 +1406,12 @@ std::auto_ptr<IOStream> BackupContext::OpenObject(int64_t ObjectID)
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::GetClientStoreMarker()
+// Name: BackupStoreContext::GetClientStoreMarker()
// Purpose: Retrieve the client store marker
// Created: 2003/10/29
//
// --------------------------------------------------------------------------
-int64_t BackupContext::GetClientStoreMarker()
+int64_t BackupStoreContext::GetClientStoreMarker()
{
if(mpStoreInfo.get() == 0)
{
@@ -1334,12 +1425,12 @@ int64_t BackupContext::GetClientStoreMarker()
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::GetStoreDiscUsageInfo(int64_t &, int64_t &, int64_t &)
+// Name: BackupStoreContext::GetStoreDiscUsageInfo(int64_t &, int64_t &, int64_t &)
// Purpose: Get disc usage info from store info
// Created: 1/1/04
//
// --------------------------------------------------------------------------
-void BackupContext::GetStoreDiscUsageInfo(int64_t &rBlocksUsed, int64_t &rBlocksSoftLimit, int64_t &rBlocksHardLimit)
+void BackupStoreContext::GetStoreDiscUsageInfo(int64_t &rBlocksUsed, int64_t &rBlocksSoftLimit, int64_t &rBlocksHardLimit)
{
if(mpStoreInfo.get() == 0)
{
@@ -1355,12 +1446,12 @@ void BackupContext::GetStoreDiscUsageInfo(int64_t &rBlocksUsed, int64_t &rBlocks
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::HardLimitExceeded()
+// Name: BackupStoreContext::HardLimitExceeded()
// Purpose: Returns true if the hard limit has been exceeded
// Created: 1/1/04
//
// --------------------------------------------------------------------------
-bool BackupContext::HardLimitExceeded()
+bool BackupStoreContext::HardLimitExceeded()
{
if(mpStoreInfo.get() == 0)
{
@@ -1374,12 +1465,12 @@ bool BackupContext::HardLimitExceeded()
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::SetClientStoreMarker(int64_t)
+// Name: BackupStoreContext::SetClientStoreMarker(int64_t)
// Purpose: Sets the client store marker, and commits it to disc
// Created: 2003/10/29
//
// --------------------------------------------------------------------------
-void BackupContext::SetClientStoreMarker(int64_t ClientStoreMarker)
+void BackupStoreContext::SetClientStoreMarker(int64_t ClientStoreMarker)
{
if(mpStoreInfo.get() == 0)
{
@@ -1398,12 +1489,12 @@ void BackupContext::SetClientStoreMarker(int64_t ClientStoreMarker)
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::MoveObject(int64_t, int64_t, int64_t, const BackupStoreFilename &, bool)
+// Name: BackupStoreContext::MoveObject(int64_t, int64_t, int64_t, const BackupStoreFilename &, bool)
// Purpose: Move an object (and all objects with the same name) from one directory to another
// Created: 12/11/03
//
// --------------------------------------------------------------------------
-void BackupContext::MoveObject(int64_t ObjectID, int64_t MoveFromDirectory, int64_t MoveToDirectory, const BackupStoreFilename &rNewFilename, bool MoveAllWithSameName, bool AllowMoveOverDeletedObject)
+void BackupStoreContext::MoveObject(int64_t ObjectID, int64_t MoveFromDirectory, int64_t MoveToDirectory, const BackupStoreFilename &rNewFilename, bool MoveAllWithSameName, bool AllowMoveOverDeletedObject)
{
if(mReadOnly)
{
@@ -1643,12 +1734,12 @@ void BackupContext::MoveObject(int64_t ObjectID, int64_t MoveFromDirectory, int6
// --------------------------------------------------------------------------
//
// Function
-// Name: BackupContext::GetBackupStoreInfo()
+// Name: BackupStoreContext::GetBackupStoreInfo()
// Purpose: Return the backup store info object, exception if it isn't loaded
// Created: 19/4/04
//
// --------------------------------------------------------------------------
-const BackupStoreInfo &BackupContext::GetBackupStoreInfo() const
+const BackupStoreInfo &BackupStoreContext::GetBackupStoreInfo() const
{
if(mpStoreInfo.get() == 0)
{
diff --git a/bin/bbstored/BackupContext.h b/bin/bbstored/BackupStoreContext.h
index 18f2f25c..4cfdb601 100644
--- a/bin/bbstored/BackupContext.h
+++ b/bin/bbstored/BackupStoreContext.h
@@ -1,7 +1,7 @@
// --------------------------------------------------------------------------
//
// File
-// Name: BackupContext.h
+// Name: BackupStoreContext.h
// Purpose: Context for backup store server
// Created: 2003/08/20
//
@@ -15,6 +15,7 @@
#include <memory>
#include "NamedLock.h"
+#include "ProtocolObject.h"
#include "Utils.h"
class BackupStoreDirectory;
@@ -22,23 +23,31 @@ class BackupStoreFilename;
class BackupStoreDaemon;
class BackupStoreInfo;
class IOStream;
+class BackupProtocolObject;
class StreamableMemBlock;
+class HousekeepingInterface
+{
+ public:
+ virtual ~HousekeepingInterface() { }
+ virtual void SendMessageToHousekeepingProcess(const void *Msg, int MsgLen) = 0;
+};
+
// --------------------------------------------------------------------------
//
// Class
-// Name: BackupContext
+// Name: BackupStoreContext
// Purpose: Context for backup store server
// Created: 2003/08/20
//
// --------------------------------------------------------------------------
-class BackupContext
+class BackupStoreContext
{
public:
- BackupContext(int32_t ClientID, BackupStoreDaemon &rDaemon);
- ~BackupContext();
+ BackupStoreContext(int32_t ClientID, HousekeepingInterface &rDaemon);
+ ~BackupStoreContext();
private:
- BackupContext(const BackupContext &rToCopy);
+ BackupStoreContext(const BackupStoreContext &rToCopy);
public:
void ReceivedFinishCommand();
@@ -83,7 +92,7 @@ public:
// --------------------------------------------------------------------------
//
// Function
- // Name: BackupContext::GetDirectory(int64_t)
+ // Name: BackupStoreContext::GetDirectory(int64_t)
// Purpose: Return a reference to a directory. Valid only until the
// next time a function which affects directories is called.
// Mainly this funciton, and creation of files.
@@ -103,6 +112,7 @@ public:
void ChangeDirAttributes(int64_t Directory, const StreamableMemBlock &Attributes, int64_t AttributesModTime);
bool ChangeFileAttributes(const BackupStoreFilename &rFilename, int64_t InDirectory, const StreamableMemBlock &Attributes, int64_t AttributesHash, int64_t &rObjectIDOut);
bool DeleteFile(const BackupStoreFilename &rFilename, int64_t InDirectory, int64_t &rObjectIDOut);
+ bool UndeleteFile(int64_t ObjectID, int64_t InDirectory);
void DeleteDirectory(int64_t ObjectID, bool Undelete = false);
void MoveObject(int64_t ObjectID, int64_t MoveFromDirectory, int64_t MoveToDirectory, const BackupStoreFilename &rNewFilename, bool MoveAllWithSameName, bool AllowMoveOverDeletedObject);
@@ -129,7 +139,7 @@ private:
private:
int32_t mClientID;
- BackupStoreDaemon &mrDaemon;
+ HousekeepingInterface &mrDaemon;
int mProtocolPhase;
bool mClientHasAccount;
std::string mStoreRoot; // has final directory separator
@@ -143,6 +153,30 @@ private:
// Directory cache
std::map<int64_t, BackupStoreDirectory*> mDirectoryCache;
+
+public:
+ class TestHook
+ {
+ public:
+ virtual std::auto_ptr<ProtocolObject> StartCommand(BackupProtocolObject&
+ rCommand) = 0;
+ virtual ~TestHook() { }
+ };
+ void SetTestHook(TestHook& rTestHook)
+ {
+ mpTestHook = &rTestHook;
+ }
+ std::auto_ptr<ProtocolObject> StartCommandHook(BackupProtocolObject& rCommand)
+ {
+ if(mpTestHook)
+ {
+ return mpTestHook->StartCommand(rCommand);
+ }
+ return std::auto_ptr<ProtocolObject>();
+ }
+
+private:
+ TestHook* mpTestHook;
};
#endif // BACKUPCONTEXT__H
diff --git a/bin/bbstored/BackupStoreDaemon.cpp b/bin/bbstored/BackupStoreDaemon.cpp
index 28e28176..4de0a078 100644
--- a/bin/bbstored/BackupStoreDaemon.cpp
+++ b/bin/bbstored/BackupStoreDaemon.cpp
@@ -17,7 +17,7 @@
#include <syslog.h>
#endif
-#include "BackupContext.h"
+#include "BackupStoreContext.h"
#include "BackupStoreDaemon.h"
#include "BackupStoreConfigVerify.h"
#include "autogen_BackupProtocolServer.h"
@@ -43,7 +43,8 @@ BackupStoreDaemon::BackupStoreDaemon()
mHaveForkedHousekeeping(false),
mIsHousekeepingProcess(false),
mHousekeepingInited(false),
- mInterProcessComms(mInterProcessCommsSocket)
+ mInterProcessComms(mInterProcessCommsSocket),
+ mpTestHook(NULL)
{
}
@@ -171,11 +172,12 @@ void BackupStoreDaemon::Run()
const Configuration &config(GetConfiguration());
mExtendedLogging = config.GetKeyValueBool("ExtendedLogging");
-#ifdef WIN32
- // Housekeeping runs synchronously on Win32
-#else
- // Fork off housekeeping daemon -- must only do this the first time Run() is called
- if(!mHaveForkedHousekeeping)
+ // Fork off housekeeping daemon -- must only do this the first
+ // time Run() is called. Housekeeping runs synchronously on Win32
+ // because IsSingleProcess() is always true
+
+#ifndef WIN32
+ if(!IsSingleProcess() && !mHaveForkedHousekeeping)
{
// Open a socket pair for communication
int sv[2] = {-1,-1};
@@ -205,7 +207,9 @@ void BackupStoreDaemon::Run()
// Log that housekeeping started
BOX_INFO("Housekeeping process started");
// Ignore term and hup
- // Parent will handle these and alert the child via the socket, don't want to randomly die
+ // Parent will handle these and alert the
+ // child via the socket, don't want to
+ // randomly die!
::signal(SIGHUP, SIG_IGN);
::signal(SIGTERM, SIG_IGN);
}
@@ -317,10 +321,18 @@ void BackupStoreDaemon::Connection2(SocketStreamTLS &rStream)
}
// Make ps listings clearer
- SetProcessTitle("client %08x", id);
+ std::ostringstream tag;
+ tag << "client=" << BOX_FORMAT_ACCOUNT(id);
+ SetProcessTitle(tag.str().c_str());
+ Logging::Tagger tagWithClientID(tag.str());
// Create a context, using this ID
- BackupContext context(id, *this);
+ BackupStoreContext context(id, *this);
+
+ if (mpTestHook)
+ {
+ context.SetTestHook(*mpTestHook);
+ }
// See if the client has an account?
if(mpAccounts && mpAccounts->AccountExists(id))
@@ -352,7 +364,7 @@ void BackupStoreDaemon::LogConnectionStats(const char *commonName,
const SocketStreamTLS &s)
{
// Log the amount of data transferred
- BOX_INFO("Connection statistics for " << commonName << ":"
+ BOX_NOTICE("Connection statistics for " << commonName << ":"
" IN=" << s.GetBytesRead() <<
" OUT=" << s.GetBytesWritten() <<
" TOTAL=" << (s.GetBytesRead() + s.GetBytesWritten()));
diff --git a/bin/bbstored/BackupStoreDaemon.h b/bin/bbstored/BackupStoreDaemon.h
index 387a1d5b..a5d216f4 100644
--- a/bin/bbstored/BackupStoreDaemon.h
+++ b/bin/bbstored/BackupStoreDaemon.h
@@ -13,6 +13,7 @@
#include "ServerTLS.h"
#include "BoxPortsAndFiles.h"
#include "BackupConstants.h"
+#include "BackupStoreContext.h"
#include "IOStreamGetLine.h"
class BackupStoreAccounts;
@@ -27,7 +28,8 @@ class HousekeepStoreAccount;
// Created: 2003/08/20
//
// --------------------------------------------------------------------------
-class BackupStoreDaemon : public ServerTLS<BOX_PORT_BBSTORED>
+class BackupStoreDaemon : public ServerTLS<BOX_PORT_BBSTORED>,
+ HousekeepingInterface
{
friend class HousekeepStoreAccount;
@@ -38,7 +40,7 @@ private:
BackupStoreDaemon(const BackupStoreDaemon &rToCopy);
public:
- // For BackupContext to communicate with housekeeping process
+ // For BackupStoreContext to communicate with housekeeping process
void SendMessageToHousekeepingProcess(const void *Msg, int MsgLen)
{
#ifndef WIN32
@@ -81,6 +83,15 @@ private:
void HousekeepingInit();
void RunHousekeepingIfNeeded();
int64_t mLastHousekeepingRun;
+
+public:
+ void SetTestHook(BackupStoreContext::TestHook& rTestHook)
+ {
+ mpTestHook = &rTestHook;
+ }
+
+private:
+ BackupStoreContext::TestHook* mpTestHook;
};
diff --git a/bin/bbstored/HousekeepStoreAccount.cpp b/bin/bbstored/HousekeepStoreAccount.cpp
index 9f4239e7..dbb9b544 100644
--- a/bin/bbstored/HousekeepStoreAccount.cpp
+++ b/bin/bbstored/HousekeepStoreAccount.cpp
@@ -85,16 +85,19 @@ void HousekeepStoreAccount::DoHousekeeping()
{
// Attempt to lock the account
std::string writeLockFilename;
- StoreStructure::MakeWriteLockFilename(mStoreRoot, mStoreDiscSet, writeLockFilename);
+ StoreStructure::MakeWriteLockFilename(mStoreRoot, mStoreDiscSet,
+ writeLockFilename);
NamedLock writeLock;
- if(!writeLock.TryAndGetLock(writeLockFilename.c_str(), 0600 /* restrictive file permissions */))
+ if(!writeLock.TryAndGetLock(writeLockFilename.c_str(),
+ 0600 /* restrictive file permissions */))
{
// Couldn't lock the account -- just stop now
return;
}
// Load the store info to find necessary info for the housekeeping
- std::auto_ptr<BackupStoreInfo> info(BackupStoreInfo::Load(mAccountID, mStoreRoot, mStoreDiscSet, false /* Read/Write */));
+ std::auto_ptr<BackupStoreInfo> info(BackupStoreInfo::Load(mAccountID,
+ mStoreRoot, mStoreDiscSet, false /* Read/Write */));
// Calculate how much should be deleted
mDeletionSizeTarget = info->GetBlocksUsed() - info->GetBlocksSoftLimit();
@@ -104,14 +107,18 @@ void HousekeepStoreAccount::DoHousekeeping()
}
// Scan the directory for potential things to delete
- // This will also remove elegiable items marked with RemoveASAP
+ // This will also remove eligible items marked with RemoveASAP
bool continueHousekeeping = ScanDirectory(BACKUPSTORE_ROOT_DIRECTORY_ID);
- // If scan directory stopped for some reason, probably parent instructed to teminate, stop now.
+ // If scan directory stopped for some reason, probably parent
+ // instructed to terminate, stop now.
if(!continueHousekeeping)
{
- // If any files were marked "delete now", then update the size of the store.
- if(mBlocksUsedDelta != 0 || mBlocksInOldFilesDelta != 0 || mBlocksInDeletedFilesDelta != 0)
+ // If any files were marked "delete now", then update
+ // the size of the store.
+ if(mBlocksUsedDelta != 0 ||
+ mBlocksInOldFilesDelta != 0 ||
+ mBlocksInDeletedFilesDelta != 0)
{
info->ChangeBlocksUsed(mBlocksUsedDelta);
info->ChangeBlocksInOldFiles(mBlocksInOldFilesDelta);
@@ -124,8 +131,8 @@ void HousekeepStoreAccount::DoHousekeeping()
return;
}
- // Log any difference in opinion between the values recorded in the store info, and
- // the values just calculated for space usage.
+ // Log any difference in opinion between the values recorded in
+ // the store info, and the values just calculated for space usage.
// BLOCK
{
int64_t used = info->GetBlocksUsed();
@@ -133,9 +140,12 @@ void HousekeepStoreAccount::DoHousekeeping()
int64_t usedDeleted = info->GetBlocksInDeletedFiles();
int64_t usedDirectories = info->GetBlocksInDirectories();
- // If the counts were wrong, taking into account RemoveASAP items deleted, log a message
- if((used + mBlocksUsedDelta) != mBlocksUsed || (usedOld + mBlocksInOldFilesDelta) != mBlocksInOldFiles
- || (usedDeleted + mBlocksInDeletedFilesDelta) != mBlocksInDeletedFiles || usedDirectories != mBlocksInDirectories)
+ // If the counts were wrong, taking into account RemoveASAP
+ // items deleted, log a message
+ if((used + mBlocksUsedDelta) != mBlocksUsed
+ || (usedOld + mBlocksInOldFilesDelta) != mBlocksInOldFiles
+ || (usedDeleted + mBlocksInDeletedFilesDelta) != mBlocksInDeletedFiles
+ || usedDirectories != mBlocksInDirectories)
{
// Log this
BOX_ERROR("Housekeeping on account " <<
@@ -153,18 +163,25 @@ void HousekeepStoreAccount::DoHousekeeping()
}
// If the current values don't match, store them
- if(used != mBlocksUsed || usedOld != mBlocksInOldFiles
- || usedDeleted != mBlocksInDeletedFiles || usedDirectories != (mBlocksInDirectories + mBlocksInDirectoriesDelta))
+ if(used != mBlocksUsed
+ || usedOld != mBlocksInOldFiles
+ || usedDeleted != mBlocksInDeletedFiles
+ || usedDirectories != (mBlocksInDirectories + mBlocksInDirectoriesDelta))
{
// Set corrected values in store info
- info->CorrectAllUsedValues(mBlocksUsed, mBlocksInOldFiles, mBlocksInDeletedFiles, mBlocksInDirectories + mBlocksInDirectoriesDelta);
+ info->CorrectAllUsedValues(mBlocksUsed,
+ mBlocksInOldFiles, mBlocksInDeletedFiles,
+ mBlocksInDirectories + mBlocksInDirectoriesDelta);
info->Save();
}
}
- // Reset the delta counts for files, as they will include RemoveASAP flagged files deleted
- // during the initial scan.
- int64_t removeASAPBlocksUsedDelta = mBlocksUsedDelta; // keep for reporting
+ // Reset the delta counts for files, as they will include
+ // RemoveASAP flagged files deleted during the initial scan.
+
+ // keep for reporting
+ int64_t removeASAPBlocksUsedDelta = mBlocksUsedDelta;
+
mBlocksUsedDelta = 0;
mBlocksInOldFilesDelta = 0;
mBlocksInDeletedFilesDelta = 0;
@@ -172,7 +189,8 @@ void HousekeepStoreAccount::DoHousekeeping()
// Go and delete items from the accounts
bool deleteInterrupted = DeleteFiles();
- // If that wasn't interrupted, remove any empty directories which are also marked as deleted in their containing directory
+ // If that wasn't interrupted, remove any empty directories which
+ // are also marked as deleted in their containing directory
if(!deleteInterrupted)
{
deleteInterrupted = DeleteEmptyDirectories();
@@ -190,8 +208,9 @@ void HousekeepStoreAccount::DoHousekeeping()
(deleteInterrupted?" and was interrupted":""));
}
- // Make sure the delta's won't cause problems if the counts are really wrong, and
- // it wasn't fixed because the store was updated during the scan.
+ // Make sure the delta's won't cause problems if the counts are
+ // really wrong, and it wasn't fixed because the store was
+ // updated during the scan.
if(mBlocksUsedDelta < (0 - info->GetBlocksUsed()))
{
mBlocksUsedDelta = (0 - info->GetBlocksUsed());
@@ -218,7 +237,8 @@ void HousekeepStoreAccount::DoHousekeeping()
// Save the store info back
info->Save();
- // Explicity release the lock (would happen automatically on going out of scope, included for code clarity)
+ // Explicity release the lock (would happen automatically on
+ // going out of scope, included for code clarity)
writeLock.ReleaseLock();
}
@@ -243,8 +263,9 @@ void HousekeepStoreAccount::MakeObjectFilename(int64_t ObjectID, std::string &rF
//
// Function
// Name: HousekeepStoreAccount::ScanDirectory(int64_t)
-// Purpose: Private. Scan a directory for potenitally deleteable items, and
-// add them to the list. Returns true if the scan should continue.
+// Purpose: Private. Scan a directory for potentially deleteable
+// items, and add them to the list. Returns true if the
+// scan should continue.
// Created: 11/12/03
//
// --------------------------------------------------------------------------
@@ -253,9 +274,12 @@ bool HousekeepStoreAccount::ScanDirectory(int64_t ObjectID)
#ifndef WIN32
if((--mCountUntilNextInterprocessMsgCheck) <= 0)
{
- mCountUntilNextInterprocessMsgCheck = POLL_INTERPROCESS_MSG_CHECK_FREQUENCY;
+ mCountUntilNextInterprocessMsgCheck =
+ POLL_INTERPROCESS_MSG_CHECK_FREQUENCY;
+
// Check for having to stop
- if(mrDaemon.CheckForInterProcessMsg(mAccountID)) // include account ID here as the specified account is locked
+ // Include account ID here as the specified account is locked
+ if(mrDaemon.CheckForInterProcessMsg(mAccountID))
{
// Need to abort now
return false;
@@ -268,7 +292,8 @@ bool HousekeepStoreAccount::ScanDirectory(int64_t ObjectID)
MakeObjectFilename(ObjectID, objectFilename);
// Open it.
- std::auto_ptr<RaidFileRead> dirStream(RaidFileRead::Open(mStoreDiscSet, objectFilename));
+ std::auto_ptr<RaidFileRead> dirStream(RaidFileRead::Open(mStoreDiscSet,
+ objectFilename));
// Add the size of the directory on disc to the size being calculated
int64_t originalDirSizeInBlocks = dirStream->GetDiscUsageInBlocks();
@@ -290,8 +315,8 @@ bool HousekeepStoreAccount::ScanDirectory(int64_t ObjectID)
// BLOCK
{
- // Remove any files which are marked for removal as soon as they become old
- // or deleted.
+ // Remove any files which are marked for removal as soon
+ // as they become old or deleted.
bool deletedSomething = false;
do
{
@@ -324,7 +349,8 @@ bool HousekeepStoreAccount::ScanDirectory(int64_t ObjectID)
// Add files to the list of potential deletions
// map to count the distance from the mark
- std::map<std::pair<BackupStoreFilename, int32_t>, int32_t> markVersionAges;
+ typedef std::pair<std::string, int32_t> version_t;
+ std::map<version_t, int32_t> markVersionAges;
// map of pair (filename, mark number) -> version age
// NOTE: use a reverse iterator to allow the distance from mark stuff to work
@@ -342,7 +368,10 @@ bool HousekeepStoreAccount::ScanDirectory(int64_t ObjectID)
// Work out ages of this version from the last mark
int32_t enVersionAge = 0;
- std::map<std::pair<BackupStoreFilename, int32_t>, int32_t>::iterator enVersionAgeI(markVersionAges.find(std::pair<BackupStoreFilename, int32_t>(en->GetName(), en->GetMarkNumber())));
+ std::map<version_t, int32_t>::iterator enVersionAgeI(
+ markVersionAges.find(
+ version_t(en->GetName().GetEncodedFilename(),
+ en->GetMarkNumber())));
if(enVersionAgeI != markVersionAges.end())
{
enVersionAge = enVersionAgeI->second + 1;
@@ -350,7 +379,7 @@ bool HousekeepStoreAccount::ScanDirectory(int64_t ObjectID)
}
else
{
- markVersionAges[std::pair<BackupStoreFilename, int32_t>(en->GetName(), en->GetMarkNumber())] = enVersionAge;
+ markVersionAges[version_t(en->GetName().GetEncodedFilename(), en->GetMarkNumber())] = enVersionAge;
}
// enVersionAge is now the age of this version.
@@ -364,6 +393,9 @@ bool HousekeepStoreAccount::ScanDirectory(int64_t ObjectID)
d.mSizeInBlocks = en->GetSizeInBlocks();
d.mMarkNumber = en->GetMarkNumber();
d.mVersionAgeWithinMark = enVersionAge;
+ d.mIsFlagDeleted = (enFlags &
+ BackupStoreDirectory::Entry::Flags_Deleted)
+ ? true : false;
// Add it to the list
mPotentialDeletions.insert(d);
@@ -541,6 +573,10 @@ bool HousekeepStoreAccount::DeleteFiles()
// Delete the file
DeleteFile(i->mInDirectory, i->mObjectID, dir, dirFilename, dirSizeInBlocksOrig);
+ BOX_INFO("Housekeeping removed " <<
+ (i->mIsFlagDeleted ? "deleted" : "old") <<
+ " file " << BOX_FORMAT_OBJECTID(i->mObjectID) <<
+ " from dir " << BOX_FORMAT_OBJECTID(i->mInDirectory));
// Stop if the deletion target has been matched or exceeded
// (checking here rather than at the beginning will tend to reduce the
@@ -786,99 +822,115 @@ bool HousekeepStoreAccount::DeleteEmptyDirectories()
continue;
}
- // Load up the directory to potentially delete
- std::string dirFilename;
- BackupStoreDirectory dir;
- int64_t dirSizeInBlocks = 0;
- {
- MakeObjectFilename(*i, dirFilename);
- // Check it actually exists (just in case it gets added twice to the list)
- if(!RaidFileRead::FileExists(mStoreDiscSet, dirFilename))
- {
- // doesn't exist, next!
- continue;
- }
- // load
- std::auto_ptr<RaidFileRead> dirStream(RaidFileRead::Open(mStoreDiscSet, dirFilename));
- dirSizeInBlocks = dirStream->GetDiscUsageInBlocks();
- dir.ReadFromStream(*dirStream, IOStream::TimeOutInfinite);
- }
+ DeleteEmptyDirectory(*i, toExamine);
+ }
- // Make sure this directory is actually empty
- if(dir.GetNumberOfEntries() != 0)
- {
- // Not actually empty, try next one
- continue;
- }
+ // Remove contents of empty directories
+ mEmptyDirectories.clear();
+ // Swap in new, so it's examined next time round
+ mEmptyDirectories.swap(toExamine);
+ }
+
+ // Not interrupted
+ return false;
+}
- // Candiate for deletion... open containing directory
- std::string containingDirFilename;
- BackupStoreDirectory containingDir;
- int64_t containingDirSizeInBlocksOrig = 0;
- {
- MakeObjectFilename(dir.GetContainerID(), containingDirFilename);
- std::auto_ptr<RaidFileRead> containingDirStream(RaidFileRead::Open(mStoreDiscSet, containingDirFilename));
- containingDirSizeInBlocksOrig = containingDirStream->GetDiscUsageInBlocks();
- containingDir.ReadFromStream(*containingDirStream, IOStream::TimeOutInfinite);
- }
+void HousekeepStoreAccount::DeleteEmptyDirectory(int64_t dirId,
+ std::vector<int64_t>& rToExamine)
+{
+ // Load up the directory to potentially delete
+ std::string dirFilename;
+ BackupStoreDirectory dir;
+ int64_t dirSizeInBlocks = 0;
- // Find the entry
- BackupStoreDirectory::Entry *pdirentry = containingDir.FindEntryByID(dir.GetObjectID());
- if((pdirentry != 0) && ((pdirentry->GetFlags() & BackupStoreDirectory::Entry::Flags_Deleted) != 0))
- {
- // Should be deleted
- containingDir.DeleteEntry(dir.GetObjectID());
+ // BLOCK
+ {
+ MakeObjectFilename(dirId, dirFilename);
+ // Check it actually exists (just in case it gets
+ // added twice to the list)
+ if(!RaidFileRead::FileExists(mStoreDiscSet, dirFilename))
+ {
+ // doesn't exist, next!
+ return;
+ }
+ // load
+ std::auto_ptr<RaidFileRead> dirStream(
+ RaidFileRead::Open(mStoreDiscSet, dirFilename));
+ dirSizeInBlocks = dirStream->GetDiscUsageInBlocks();
+ dir.ReadFromStream(*dirStream, IOStream::TimeOutInfinite);
+ }
- // Is the containing dir now a candidate for deletion?
- if(containingDir.GetNumberOfEntries() == 0)
- {
- toExamine.push_back(containingDir.GetObjectID());
- }
+ // Make sure this directory is actually empty
+ if(dir.GetNumberOfEntries() != 0)
+ {
+ // Not actually empty, try next one
+ return;
+ }
- // Write revised parent directory
- RaidFileWrite writeDir(mStoreDiscSet, containingDirFilename);
- writeDir.Open(true /* allow overwriting */);
- containingDir.WriteToStream(writeDir);
+ // Candidate for deletion... open containing directory
+ std::string containingDirFilename;
+ BackupStoreDirectory containingDir;
+ int64_t containingDirSizeInBlocksOrig = 0;
+ {
+ MakeObjectFilename(dir.GetContainerID(), containingDirFilename);
+ std::auto_ptr<RaidFileRead> containingDirStream(
+ RaidFileRead::Open(mStoreDiscSet,
+ containingDirFilename));
+ containingDirSizeInBlocksOrig =
+ containingDirStream->GetDiscUsageInBlocks();
+ containingDir.ReadFromStream(*containingDirStream,
+ IOStream::TimeOutInfinite);
+ }
- // get the disc usage (must do this before commiting it)
- int64_t dirSize = writeDir.GetDiscUsageInBlocks();
+ // Find the entry
+ BackupStoreDirectory::Entry *pdirentry =
+ containingDir.FindEntryByID(dir.GetObjectID());
+ if((pdirentry != 0) && ((pdirentry->GetFlags() & BackupStoreDirectory::Entry::Flags_Deleted) != 0))
+ {
+ // Should be deleted
+ containingDir.DeleteEntry(dir.GetObjectID());
- // Commit directory
- writeDir.Commit(BACKUP_STORE_CONVERT_TO_RAID_IMMEDIATELY);
+ // Is the containing dir now a candidate for deletion?
+ if(containingDir.GetNumberOfEntries() == 0)
+ {
+ rToExamine.push_back(containingDir.GetObjectID());
+ }
- // adjust usage counts for this directory
- if(dirSize > 0)
- {
- int64_t adjust = dirSize - containingDirSizeInBlocksOrig;
- mBlocksUsedDelta += adjust;
- mBlocksInDirectoriesDelta += adjust;
- }
+ // Write revised parent directory
+ RaidFileWrite writeDir(mStoreDiscSet, containingDirFilename);
+ writeDir.Open(true /* allow overwriting */);
+ containingDir.WriteToStream(writeDir);
- // Delete the directory itself
- {
- RaidFileWrite del(mStoreDiscSet, dirFilename);
- del.Delete();
- }
+ // get the disc usage (must do this before commiting it)
+ int64_t dirSize = writeDir.GetDiscUsageInBlocks();
- // And adjust usage counts for the directory that's just been deleted
- mBlocksUsedDelta -= dirSizeInBlocks;
- mBlocksInDirectoriesDelta -= dirSizeInBlocks;
+ // Commit directory
+ writeDir.Commit(BACKUP_STORE_CONVERT_TO_RAID_IMMEDIATELY);
- // Update count
- ++mEmptyDirectoriesDeleted;
- }
+ // adjust usage counts for this directory
+ if(dirSize > 0)
+ {
+ int64_t adjust = dirSize - containingDirSizeInBlocksOrig;
+ mBlocksUsedDelta += adjust;
+ mBlocksInDirectoriesDelta += adjust;
}
- // Remove contents of empty directories
- mEmptyDirectories.clear();
- // Swap in new, so it's examined next time round
- mEmptyDirectories.swap(toExamine);
- }
-
- // Not interrupted
- return false;
-}
+ // Delete the directory itself
+ {
+ RaidFileWrite del(mStoreDiscSet, dirFilename);
+ del.Delete();
+ }
+ BOX_INFO("Housekeeping removed empty deleted dir " <<
+ BOX_FORMAT_OBJECTID(dirId));
+ // And adjust usage counts for the directory that's
+ // just been deleted
+ mBlocksUsedDelta -= dirSizeInBlocks;
+ mBlocksInDirectoriesDelta -= dirSizeInBlocks;
+ // Update count
+ ++mEmptyDirectoriesDeleted;
+ }
+}
diff --git a/bin/bbstored/HousekeepStoreAccount.h b/bin/bbstored/HousekeepStoreAccount.h
index 6c8f251d..5c2a9885 100644
--- a/bin/bbstored/HousekeepStoreAccount.h
+++ b/bin/bbstored/HousekeepStoreAccount.h
@@ -42,6 +42,8 @@ private:
bool ScanDirectory(int64_t ObjectID);
bool DeleteFiles();
bool DeleteEmptyDirectories();
+ void DeleteEmptyDirectory(int64_t dirId,
+ std::vector<int64_t>& rToExamine);
void DeleteFile(int64_t InDirectory, int64_t ObjectID, BackupStoreDirectory &rDirectory, const std::string &rDirectoryFilename, int64_t OriginalDirSizeInBlocks);
private:
@@ -52,6 +54,7 @@ private:
int64_t mSizeInBlocks;
int32_t mMarkNumber;
int32_t mVersionAgeWithinMark; // 0 == current, 1 latest old version, etc
+ bool mIsFlagDeleted; // false for files flagged "Old"
} DelEn;
struct DelEnCompare
diff --git a/bin/bbstored/backupprotocol.txt b/bin/bbstored/backupprotocol.txt
index 62d837ff..3eca514a 100644
--- a/bin/bbstored/backupprotocol.txt
+++ b/bin/bbstored/backupprotocol.txt
@@ -4,7 +4,7 @@
Name Backup
IdentString Box-Backup:v=C
-ServerContextClass BackupContext BackupContext.h
+ServerContextClass BackupStoreContext BackupStoreContext.h
ClientType Filename BackupStoreFilenameClear BackupStoreFilenameClear.h
ServerType Filename BackupStoreFilename BackupStoreFilename.h
@@ -204,6 +204,12 @@ GetBlockIndexByName 35 Command(Success)
# stream of the block index follows the reply if found ID != 0
+UndeleteFile 36 Command(Success)
+ int64 InDirectory
+ int64 ObjectID
+ # will return 0 if the object couldn't be found in the specified directory
+
+
# -------------------------------------------------------------------------------------
# Information commands
# -------------------------------------------------------------------------------------
diff --git a/bin/bbstored/bbstored-config.in b/bin/bbstored/bbstored-config.in
index 33cfb39a..5ad96d50 100755
--- a/bin/bbstored/bbstored-config.in
+++ b/bin/bbstored/bbstored-config.in
@@ -196,7 +196,7 @@ TimeBetweenHousekeeping = 900
Server
{
- PidFile = @localstatedir_expanded@/bbstored.pid
+ PidFile = @localstatedir_expanded@/run/bbstored.pid
User = $username
ListenAddresses = inet:$server
CertificateFile = $certificate
@@ -234,7 +234,7 @@ What you need to do now...
4) Create accounts with bbstoreaccounts
5) Start the backup store daemon with the command
- @bindir_expanded@/bbstored$daemon_args
+ @sbindir_expanded@/bbstored$daemon_args
in /etc/rc.local, or your local equivalent.
===================================================================
diff --git a/bin/bbstored/bbstored.cpp b/bin/bbstored/bbstored.cpp
index 54858dd4..21a9e5f1 100644
--- a/bin/bbstored/bbstored.cpp
+++ b/bin/bbstored/bbstored.cpp
@@ -18,7 +18,7 @@ int main(int argc, const char *argv[])
{
MAINHELPER_START
- Logging::SetProgramName("Box Backup (bbstored)");
+ Logging::SetProgramName("bbstored");
Logging::ToConsole(true);
Logging::ToSyslog (true);