[Box Backup-dev] COMMIT r320 - in box/gary/boxbackup-0.09-mod: bin/bbackupd bin/bbstored lib/backupclient lib/common

boxbackup-dev@fluffy.co.uk boxbackup-dev@fluffy.co.uk
Tue, 17 Jan 2006 22:27:29 +0000 (GMT)


Author: chris
Date: 2006-01-17 22:27:22 +0000 (Tue, 17 Jan 2006)
New Revision: 320

Added:
   box/gary/boxbackup-0.09-mod/lib/common/Archive.h
Modified:
   box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupClientContext.cpp
   box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupClientContext.h
   box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupClientDirectoryRecord.cpp
   box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupClientDirectoryRecord.h
   box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupDaemon.cpp
   box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupDaemon.h
   box/gary/boxbackup-0.09-mod/bin/bbstored/BackupCommands.cpp
   box/gary/boxbackup-0.09-mod/bin/bbstored/backupprotocol.txt
   box/gary/boxbackup-0.09-mod/lib/backupclient/BackupDaemonConfigVerify.cpp
   box/gary/boxbackup-0.09-mod/lib/backupclient/BackupStoreFile.h
   box/gary/boxbackup-0.09-mod/lib/backupclient/BackupStoreFileDiff.cpp
   box/gary/boxbackup-0.09-mod/lib/common/Configuration.cpp
   box/gary/boxbackup-0.09-mod/lib/common/Configuration.h
   box/gary/boxbackup-0.09-mod/lib/common/ExcludeList.cpp
   box/gary/boxbackup-0.09-mod/lib/common/ExcludeList.h
Log:
* boxbackup-0.09-mod
- Uploaded Gary's patches


Modified: box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupClientContext.cpp
===================================================================
--- box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupClientContext.cpp	2006-01-17 22:24:47 UTC (rev 319)
+++ box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupClientContext.cpp	2006-01-17 22:27:22 UTC (rev 320)
@@ -49,6 +49,8 @@
 #include "Box.h"
 
 #include <syslog.h>
+#include <signal.h>
+#include <sys/time.h>
 
 #include "BoxPortsAndFiles.h"
 #include "BoxTime.h"
@@ -59,6 +61,7 @@
 #include "BackupStoreException.h"
 #include "BackupDaemon.h"
 #include "autogen_BackupProtocolClient.h"
+#include "BackupStoreFile.h"
 
 #include "MemLeakFindOn.h"
 
@@ -85,7 +88,8 @@
 	  mpNewIDMap(0),
 	  mStorageLimitExceeded(false),
 	  mpExcludeFiles(0),
-	  mpExcludeDirs(0)
+	  mpExcludeDirs(0),
+	  mbIsManaged(false)
 {
 }
 
@@ -488,5 +492,171 @@
 	// Found
 	return true;
 }
-
-
+
+//
+//
+//
+
+// maximum time to spend diffing
+static int sMaximumDiffTime = 10;
+// maximum time of SSL inactivity (keep-alive interval)
+static int sKeepAliveTime = 0;
+// total time elapsed to keep track of what is going on
+static time_t sTimeMgmtEpoch = 0;
+
+void BackupClientContext::SetMaximumDiffingTime(int iSeconds)
+{
+	sMaximumDiffTime = iSeconds < 0 ? 0 : iSeconds;
+	TRACE1("Set maximum diffing time to %d seconds\n", sMaximumDiffTime);
+}
+
+void BackupClientContext::SetKeepAliveTime(int iSeconds)
+{
+	sKeepAliveTime = iSeconds < 0 ? 0 : iSeconds;
+	TRACE1("Set keep-alive time to %d seconds\n", sKeepAliveTime);
+}
+
+static BackupClientContext* pThisCtxInst = NULL;
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    static TimerSigHandler(int)
+//		Purpose: Signal handler
+//		Created: 19/3/04
+//
+// --------------------------------------------------------------------------
+void TimerSigHandler(int iUnused)
+{
+	ASSERT(pThisCtxInst);
+	ASSERT(sTimeMgmtEpoch > 0);
+
+	time_t tTotalRunIntvl = time(NULL) - sTimeMgmtEpoch;
+	if (sMaximumDiffTime > 0 && tTotalRunIntvl >= sMaximumDiffTime)
+	{
+		TRACE0("MaximumDiffingTime reached - suspending file diff\n");
+		BackupStoreFile::SuspendFileDiff();
+	}
+	else if (sKeepAliveTime > 0)
+	{
+		// well, we have only two sources of timer events...
+		TRACE0("KeepAliveTime reached - initiating keep-alive\n");
+		pThisCtxInst->DoKeepAlive();
+	}
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    BackupClientContext::ManageDiffProcess()
+//		Purpose: Initiates a file diff control timer
+//		Created: 04/19/2005
+//
+// --------------------------------------------------------------------------
+void BackupClientContext::ManageDiffProcess()
+{
+	if (mbIsManaged || !mpConnection)
+		return;
+
+	ASSERT(pThisCtxInst == NULL);
+	ASSERT(sTimeMgmtEpoch == 0);
+
+#ifdef PLATFORM_CYGWIN
+	::signal(SIGALRM, TimerSigHandler);
+#else
+	::signal(SIGVTALRM, TimerSigHandler);
+#endif // PLATFORM_CYGWIN
+
+	struct itimerval timeout;
+	memset(&timeout, 0, sizeof(timeout));
+
+	//
+	//
+	//
+	if (sMaximumDiffTime <= 0 && sKeepAliveTime <= 0)
+	{
+		TRACE0("Diff control not requested - letting things run wild\n");
+		return;
+	}
+	else if (sMaximumDiffTime > 0 && sKeepAliveTime > 0)
+	{
+		timeout.it_value.tv_sec = sKeepAliveTime < sMaximumDiffTime ? sKeepAliveTime : sMaximumDiffTime;
+		timeout.it_interval.tv_sec = sKeepAliveTime < sMaximumDiffTime ? sKeepAliveTime : sMaximumDiffTime;
+	}
+	else
+	{
+		timeout.it_value.tv_sec = sKeepAliveTime > 0 ? sKeepAliveTime : sMaximumDiffTime;
+		timeout.it_interval.tv_sec = sKeepAliveTime > 0 ? sKeepAliveTime : sMaximumDiffTime;
+	}
+
+	// avoid race
+	pThisCtxInst = this;
+	sTimeMgmtEpoch = time(NULL);
+
+#ifdef PLATFORM_CYGWIN
+	if(::setitimer(ITIMER_REAL, &timeout, NULL) != 0)
+#else
+	if(::setitimer(ITIMER_VIRTUAL, &timeout, NULL) != 0)
+#endif // PLATFORM_CYGWIN
+	{
+		sTimeMgmtEpoch = 0;
+		pThisCtxInst = NULL;
+
+		TRACE0("WARNING: couldn't set file diff control timeout\n");
+		THROW_EXCEPTION(BackupStoreException, Internal)
+	}
+
+	mbIsManaged = true;
+	TRACE0("Initiated timer for file diff control\n");
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    BackupClientContext::UnManageDiffProcess()
+//		Purpose: suspends file diff control timer
+//		Created: 04/19/2005
+//
+// --------------------------------------------------------------------------
+void BackupClientContext::UnManageDiffProcess()
+{
+	if (!mbIsManaged /* don't test for active connection, just do it */)
+		return;
+
+	ASSERT(pThisCtxInst != NULL);
+
+	struct itimerval timeout;
+	memset(&timeout, 0, sizeof(timeout));
+
+#ifdef PLATFORM_CYGWIN
+	if(::setitimer(ITIMER_REAL, &timeout, NULL) != 0)
+#else
+	if(::setitimer(ITIMER_VIRTUAL, &timeout, NULL) != 0)
+#endif // PLATFORM_CYGWIN
+	{
+		TRACE0("WARNING: couldn't clear file diff control timeout\n");
+		THROW_EXCEPTION(BackupStoreException, Internal)
+	}
+
+	mbIsManaged = false;
+	pThisCtxInst = NULL;
+	sTimeMgmtEpoch = 0;
+
+	TRACE0("Suspended timer for file diff control\n");
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    BackupClientContext::DoKeepAlive()
+//		Purpose: Does something inconsequential over the SSL link to keep it up
+//		Created: 04/19/2005
+//
+// --------------------------------------------------------------------------
+void BackupClientContext::DoKeepAlive()
+{
+	if (!mpConnection)
+		return;
+
+	mpConnection->QueryGetIsAlive();
+}

Modified: box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupClientContext.h
===================================================================
--- box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupClientContext.h	2006-01-17 22:24:47 UTC (rev 319)
+++ box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupClientContext.h	2006-01-17 22:27:22 UTC (rev 320)
@@ -172,7 +172,56 @@
 	bool FindFilename(int64_t ObjectID, int64_t ContainingDirectory, std::string &rPathOut, bool &rIsDirectoryOut,
 		bool &rIsCurrentVersionOut, box_time_t *pModTimeOnServer = 0, box_time_t *pAttributesHashOnServer = 0,
 		BackupStoreFilenameClear *pLeafname = 0); // not const as may connect to server
+
+	// --------------------------------------------------------------------------
+	//
+	// Function
+	//		Name:    BackupClientContext::SetMaximumDiffingTime()
+	//		Purpose: Sets the maximum time that will be spent diffing a file
+	//		Created: 04/19/2005
+	//
+	// --------------------------------------------------------------------------
+	static void SetMaximumDiffingTime(int iSeconds);
+
+	// --------------------------------------------------------------------------
+	//
+	// Function
+	//		Name:    BackupClientContext::SetKeepAliveTime()
+	//		Purpose: Sets the time interval for repetitive keep-alive operation
+	//		Created: 04/19/2005
+	//
+	// --------------------------------------------------------------------------
+	static void SetKeepAliveTime(int iSeconds);
 
+	// --------------------------------------------------------------------------
+	//
+	// Function
+	//		Name:    BackupClientContext::ManageDiffProcess()
+	//		Purpose: Initiates an SSL connection/session keep-alive process
+	//		Created: 04/19/2005
+	//
+	// --------------------------------------------------------------------------
+	void ManageDiffProcess();
+
+	// --------------------------------------------------------------------------
+	//
+	// Function
+	//		Name:    BackupClientContext::UnManageDiffProcess()
+	//		Purpose: Suspends an SSL connection/session keep-alive process
+	//		Created: 04/19/2005
+	//
+	// --------------------------------------------------------------------------
+	void UnManageDiffProcess();
+
+	// --------------------------------------------------------------------------
+	//
+	// Function
+	//		Name:    BackupClientContext::DoKeepAlive()
+	//		Purpose: Does something inconsequential over the SSL link to keep it up
+	//		Created: 04/19/2005
+	//
+	// --------------------------------------------------------------------------
+	void DoKeepAlive();
 private:
 	BackupDaemon &mrDaemon;
 	TLSContext &mrTLSContext;
@@ -187,7 +236,9 @@
 	BackupClientInodeToIDMap *mpNewIDMap;
 	bool mStorageLimitExceeded;
 	ExcludeList *mpExcludeFiles;
-	ExcludeList *mpExcludeDirs;
+	ExcludeList *mpExcludeDirs;
+
+	bool mbIsManaged;
 };
 
 

Modified: box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupClientDirectoryRecord.cpp
===================================================================
--- box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupClientDirectoryRecord.cpp	2006-01-17 22:24:47 UTC (rev 319)
+++ box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupClientDirectoryRecord.cpp	2006-01-17 22:27:22 UTC (rev 320)
@@ -1083,8 +1083,6 @@
 	}
 }
 
-
-
 // --------------------------------------------------------------------------
 //
 // Function
@@ -1118,15 +1116,24 @@
 			{
 				// Found an old version -- get the index
 				std::auto_ptr<IOStream> blockIndexStream(connection.ReceiveStream());
-			
-				// Diff the file
+			
+				//
+				// Diff the file
+				//
+
+				rParams.mrContext.ManageDiffProcess();
+
 				bool isCompletelyDifferent = false;
 				std::auto_ptr<IOStream> patchStream(BackupStoreFile::EncodeFileDiff(rFilename.c_str(),
 						mObjectID,	/* containing directory */
 						rStoreFilename, diffFromID, *blockIndexStream,
 						connection.GetTimeout(), 0 /* not interested in the modification time */, &isCompletelyDifferent));
-	
-				// Upload the patch to the store
+	
+				rParams.mrContext.UnManageDiffProcess();
+
+				//
+				// Upload the patch to the store
+				//
 				std::auto_ptr<BackupProtocolClientSuccess> stored(connection.QueryStoreFile(mObjectID, ModificationTime,
 						AttributesHash, isCompletelyDifferent?(0):(diffFromID), rStoreFilename, *patchStream));
 				
@@ -1151,7 +1158,9 @@
 		}
 	}
 	catch(BoxException &e)
-	{
+	{
+		rParams.mrContext.UnManageDiffProcess();
+
 		if(e.GetType() == ConnectionException::ExceptionType && e.GetSubType() == ConnectionException::Protocol_UnexpectedReply)
 		{
 			// Check and see what error the protocol has -- as it might be an error...
@@ -1163,8 +1172,8 @@
 				// The hard limit was exceeded on the server, notify!
 				rParams.mrDaemon.NotifySysadmin(BackupDaemon::NotifyEvent_StoreFull);
 			}
-		}
-	
+		}
+
 		// Send the error on it's way
 		throw;
 	}
@@ -1233,5 +1242,147 @@
 {
 }
 
-
-
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    BackupClientDirectoryRecord::Deserialize(Archive & rArchive)
+//		Purpose: Deserializes this object instance from a stream of bytes, using an Archive abstraction.
+//
+//		Created: 2005/04/11
+//
+// --------------------------------------------------------------------------
+void BackupClientDirectoryRecord::Deserialize(Archive & rArchive)
+{
+	// Make deletion recursive
+	DeleteSubDirectories();
+
+	// Delete maps
+	if(mpPendingEntries != 0)
+	{
+		delete mpPendingEntries;
+		mpPendingEntries = 0;
+	}
+
+	//
+	//
+	//
+	rArchive >> mObjectID >> mSubDirName >> mInitialSyncDone >> mSyncDone;
+
+	//
+	//
+	//
+	int64_t iCount = 0;
+	rArchive.Get(iCount);
+
+	if (iCount != sizeof(mStateChecksum)/sizeof(mStateChecksum[0]))
+	{
+		// we have some kind of internal system representation change: throw for now
+		THROW_EXCEPTION(CommonException, Internal)
+	}
+
+	for (int v = 0; v < iCount; v++)
+		/**** LOAD ****/ rArchive.Get(mStateChecksum[v]);
+
+	//
+	//
+	//
+	iCount = 0;
+	rArchive.Get(iCount);
+
+	if (iCount > 0)
+	{
+		/**** LOAD ****/ mpPendingEntries = new std::map<std::string, box_time_t>;
+		if (!mpPendingEntries)
+			throw std::bad_alloc();
+
+		for (int v = 0; v < iCount; v++)
+		{
+			std::string strItem;
+			box_time_t btItem;
+
+			rArchive >> strItem >> btItem;
+			(*mpPendingEntries)[strItem] = btItem;
+		}
+	}
+
+	//
+	//
+	//
+	iCount = 0;
+	rArchive.Get(iCount);
+
+	if (iCount > 0)
+	{
+		for (int v = 0; v < iCount; v++)
+		{
+			std::string strItem;
+			rArchive.Get(strItem);
+
+			BackupClientDirectoryRecord* pSubDirRecord = new BackupClientDirectoryRecord(0, ""); // will be deserialized anyway, give it id 0 for now
+			if (!pSubDirRecord)
+				throw std::bad_alloc();
+
+			/***** RECURSE *****/
+			pSubDirRecord->Deserialize(rArchive);
+			mSubDirectories[strItem] = pSubDirRecord;
+		}
+	}
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    BackupClientDirectoryRecord::Serialize(Archive & rArchive)
+//		Purpose: Serializes this object instance into a stream of bytes, using an Archive abstraction.
+//
+//		Created: 2005/04/11
+//
+// --------------------------------------------------------------------------
+void BackupClientDirectoryRecord::Serialize(Archive & rArchive) const
+{
+	//
+	//
+	//
+	rArchive << mObjectID << mSubDirName << mInitialSyncDone << mSyncDone;
+
+	//
+	//
+	//
+	int64_t iCount = 0;
+
+	iCount = sizeof(mStateChecksum)/sizeof(mStateChecksum[0]);
+	rArchive.Add(iCount); // add count to see if we have crossed build-level mods
+	for (int v = 0; v < iCount; v++)
+		rArchive.Add(mStateChecksum[v]);
+
+	//
+	//
+	//
+	if (!mpPendingEntries)
+	{
+		iCount = 0;
+		rArchive.Add(iCount);
+	}
+	else
+	{
+		iCount = mpPendingEntries->size();
+		rArchive.Add(iCount);
+
+		for (std::map<std::string, box_time_t>::const_iterator i = mpPendingEntries->begin(); i != mpPendingEntries->end(); i++)
+			rArchive << (*i).first << (*i).second;
+	}
+	//
+	//
+	//
+	iCount = mSubDirectories.size();
+	rArchive.Add(iCount);
+
+	for (std::map<std::string, BackupClientDirectoryRecord*>::const_iterator i = mSubDirectories.begin(); i != mSubDirectories.end(); i++)
+	{
+		const BackupClientDirectoryRecord* pSubItem = (*i).second;
+		ASSERT(pSubItem);
+
+		rArchive.Add((*i).first);
+		pSubItem->Serialize(rArchive);
+	}
+}

Modified: box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupClientDirectoryRecord.h
===================================================================
--- box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupClientDirectoryRecord.h	2006-01-17 22:24:47 UTC (rev 319)
+++ box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupClientDirectoryRecord.h	2006-01-17 22:27:22 UTC (rev 320)
@@ -56,6 +56,8 @@
 #include "BackupClientFileAttributes.h"
 #include "BackupStoreDirectory.h"
 #include "MD5Digest.h"
+
+#include "Archive.h"
 
 class BackupClientContext;
 class BackupDaemon;
@@ -72,7 +74,10 @@
 {
 public:
 	BackupClientDirectoryRecord(int64_t ObjectID, const std::string &rSubDirName);
-	~BackupClientDirectoryRecord();
+	~BackupClientDirectoryRecord();
+
+	void Deserialize(Archive & rArchive);
+	void Serialize(Archive & rArchive) const;
 private:
 	BackupClientDirectoryRecord(const BackupClientDirectoryRecord &);
 public:

Modified: box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupDaemon.cpp
===================================================================
--- box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupDaemon.cpp	2006-01-17 22:24:47 UTC (rev 319)
+++ box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupDaemon.cpp	2006-01-17 22:27:22 UTC (rev 320)
@@ -97,7 +97,7 @@
 // Make the actual sync period have a little bit of extra time, up to a 64th of the main sync period.
 // This prevents repetative cycles of load on the server
 #define		SYNC_PERIOD_RANDOM_EXTRA_TIME_SHIFT_BY	6
-
+
 // --------------------------------------------------------------------------
 //
 // Function
@@ -309,11 +309,15 @@
 	// Set up the keys for various things
 	BackupClientCryptoKeys_Setup(conf.GetKeyValue("KeysFile").c_str());
 
-	// Set maximum diffing time?
+	// max diffing time, keep-alive time
 	if(conf.KeyExists("MaximumDiffingTime"))
 	{
-		BackupStoreFile::SetMaximumDiffingTime(conf.GetKeyValueInt("MaximumDiffingTime"));
+		BackupClientContext::SetMaximumDiffingTime(conf.GetKeyValueInt("MaximumDiffingTime"));
 	}
+	if(conf.KeyExists("KeepAliveTime"))
+	{
+		BackupClientContext::SetKeepAliveTime(conf.GetKeyValueInt("KeepAliveTime"));
+	}
 
 	// Setup various timings
 	
@@ -336,12 +340,16 @@
 
 	// When the last sync started (only updated if the store was not full when the sync ended)
 	box_time_t lastSyncTime = 0;
-	
+	
+	// And what's the current client store marker?
+	int64_t clientStoreMarker = BackupClientContext::ClientStoreMarker_NotKnown;		// haven't contacted the store yet
+
+	// --------------------------------------------------------------------------------------------
+
+	DeserializeStoreObjectInfo(clientStoreMarker, lastSyncTime, nextSyncTime);
+
 	// --------------------------------------------------------------------------------------------
 
-	// And what's the current client store marker?
-	int64_t clientStoreMarker = BackupClientContext::ClientStoreMarker_NotKnown;		// haven't contacted the store yet
-
 	// Set state
 	SetState(State_Idle);
 
@@ -540,7 +548,14 @@
 				CommitIDMapsAfterSync();
 
 				// Log
-				::syslog(LOG_INFO, "Finished scan of local files");
+				::syslog(LOG_INFO, "Finished scan of local files");
+
+				// --------------------------------------------------------------------------------------------
+
+				// We had a successful backup, save the store info
+				SerializeStoreObjectInfo(clientStoreMarker, lastSyncTime, nextSyncTime);
+
+				// --------------------------------------------------------------------------------------------
 			}
 			catch(BoxException &e)
 			{
@@ -727,7 +742,7 @@
 			{
 #ifdef PLATFORM_CANNOT_FIND_PEER_UID_OF_UNIX_SOCKET
 				bool uidOK = true;
-				::syslog(LOG_ERR, "On this platform, no security check can be made on the credientials of peers connecting to the command socket. (bbackupctl)");
+				::syslog(LOG_WARNING, "On this platform, no security check can be made on the credientials of peers connecting to the command socket. (bbackupctl)");
 #else
 				// Security check -- does the process connecting to this socket have
 				// the same UID as this process?
@@ -1590,6 +1605,18 @@
 	mUnusedRootDirEntries.clear();
 }
 
+// --------------------------------------------------------------------------
+
+typedef struct
+{
+	int32_t mMagicValue;	// also the version number
+	int32_t mNumEntries;
+	int64_t mObjectID;		// this object ID
+	int64_t mContainerID;	// ID of container
+	uint64_t mAttributesModTime;
+	int32_t mOptionsPresent;	// bit mask of optional sections / features present
+
+} loc_StreamFormat;
 
 // --------------------------------------------------------------------------
 //
@@ -1598,7 +1625,8 @@
 //		Purpose: Constructor
 //		Created: 11/11/03
 //
-// --------------------------------------------------------------------------
+// --------------------------------------------------------------------------
+
 BackupDaemon::Location::Location()
 	: mIDMapIndex(0),
 	  mpExcludeFiles(0),
@@ -1628,8 +1656,175 @@
 		mpExcludeFiles = 0;
 	}
 }
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    BackupDaemon::Location::Deserialize(Archive & rArchive)
+//		Purpose: Deserializes this object instance from a stream of bytes, using an Archive abstraction.
+//
+//		Created: 2005/04/11
+//
+// --------------------------------------------------------------------------
+void BackupDaemon::Location::Deserialize(Archive & rArchive)
+{
+	//
+	//
+	//
+	mpDirectoryRecord.reset(NULL);
+	if (mpExcludeFiles)
+	{
+		delete mpExcludeFiles;
+		mpExcludeFiles = NULL;
+	}
+	if (mpExcludeDirs)
+	{
+		delete mpExcludeDirs;
+		mpExcludeDirs = NULL;
+	}
+
+	//
+	//
+	//
+	rArchive >> mName >> mPath >> mIDMapIndex;
+
+	//
+	//
+	//
+	int64_t aMagicMarker = 0;
+	rArchive.Get(aMagicMarker);
+
+	if (aMagicMarker == ARCHIVE_MAGIC_VALUE_NOOP)
+	{
+		// NOOP
+	}
+	else if (aMagicMarker == ARCHIVE_MAGIC_VALUE_RECURSE)
+	{
+		BackupClientDirectoryRecord *pSubRecord = new BackupClientDirectoryRecord(0, "");
+		if (!pSubRecord)
+			throw std::bad_alloc();
+
+		mpDirectoryRecord.reset(pSubRecord);
+		mpDirectoryRecord->Deserialize(rArchive);
+	}
+	else
+	{
+		// there is something going on here
+		THROW_EXCEPTION(CommonException, Internal)
+	}
+
+	//
+	//
+	//
+	rArchive.Get(aMagicMarker);
+
+	if (aMagicMarker == ARCHIVE_MAGIC_VALUE_NOOP)
+	{
+		// NOOP
+	}
+	else if (aMagicMarker == ARCHIVE_MAGIC_VALUE_RECURSE)
+	{
+		mpExcludeFiles = new ExcludeList;
+		if (!mpExcludeFiles)
+			throw std::bad_alloc();
+
+		mpExcludeFiles->Deserialize(rArchive);
+	}
+	else
+	{
+		// there is something going on here
+		THROW_EXCEPTION(CommonException, Internal)
+	}
+
+	//
+	//
+	//
+	rArchive.Get(aMagicMarker);
+
+	if (aMagicMarker == ARCHIVE_MAGIC_VALUE_NOOP)
+	{
+		// NOOP
+	}
+	else if (aMagicMarker == ARCHIVE_MAGIC_VALUE_RECURSE)
+	{
+		mpExcludeDirs = new ExcludeList;
+		if (!mpExcludeDirs)
+			throw std::bad_alloc();
+
+		mpExcludeDirs->Deserialize(rArchive);
+	}
+	else
+	{
+		// there is something going on here
+		THROW_EXCEPTION(CommonException, Internal)
+	}
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    BackupDaemon::Location::Serialize(Archive & rArchive)
+//		Purpose: Serializes this object instance into a stream of bytes, using an Archive abstraction.
+//
+//		Created: 2005/04/11
+//
+// --------------------------------------------------------------------------
+void BackupDaemon::Location::Serialize(Archive & rArchive) const
+{
+	//
+	//
+	//
+	rArchive << mName << mPath << mIDMapIndex;
+
+	//
+	//
+	//
+	if (mpDirectoryRecord.get() == NULL)
+	{
+		int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_NOOP;
+		rArchive.Add(aMagicMarker);
+	}
+	else
+	{
+		int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_RECURSE; // be explicit about whether recursion follows
+		rArchive.Add(aMagicMarker);
+
+		mpDirectoryRecord->Serialize(rArchive);
+	}
+
+	//
+	//
+	//
+	if (!mpExcludeFiles)
+	{
+		int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_NOOP;
+		rArchive.Add(aMagicMarker);
+	}
+	else
+	{
+		int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_RECURSE; // be explicit about whether recursion follows
+		rArchive.Add(aMagicMarker);
+
+		mpExcludeFiles->Serialize(rArchive);
+	}
+
+	//
+	//
+	//
+	if (!mpExcludeDirs)
+	{
+		int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_NOOP;
+		rArchive.Add(aMagicMarker);
+	}
+	else
+	{
+		int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_RECURSE; // be explicit about whether recursion follows
+		rArchive.Add(aMagicMarker);
+
+		mpExcludeDirs->Serialize(rArchive);
+	}
+}
 
-
 // --------------------------------------------------------------------------
 //
 // Function
@@ -1660,4 +1855,192 @@
 		mpGetLine = 0;
 	}
 }
-
+
+// --------------------------------------------------------------------------
+//
+// 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.
+//
+//		Created: 2005/04/11
+//
+// --------------------------------------------------------------------------
+
+static const int STOREOBJECTINFO_MAGIC_ID_VALUE = 0x7777525F;
+static const std::string STOREOBJECTINFO_MAGIC_ID_STRING = "BBACKUPD-STATE";
+static const int STOREOBJECTINFO_VERSION = 1;
+
+void BackupDaemon::SerializeStoreObjectInfo(int64_t aClientStoreMarker, box_time_t theLastSyncTime, box_time_t theNextSyncTime) const
+{
+	if(!GetConfiguration().KeyExists("StoreObjectInfoFile") || GetConfiguration().GetKeyValue("StoreObjectInfoFile").size() <= 0)
+		return;
+
+	try
+	{
+		FileStream aFile(GetConfiguration().GetKeyValue("StoreObjectInfoFile").c_str(), O_WRONLY | O_CREAT | O_TRUNC);
+		IOStreamArchive anArchive(aFile, 0);
+
+		anArchive << STOREOBJECTINFO_MAGIC_ID_VALUE << STOREOBJECTINFO_MAGIC_ID_STRING << STOREOBJECTINFO_VERSION
+					<< GetConfiguration().GetModTime()
+					<< aClientStoreMarker << theLastSyncTime << theNextSyncTime;
+
+		//
+		//
+		//
+		int64_t iCount = mLocations.size();
+		anArchive.Add(iCount);
+
+		for (int v = 0; v < iCount; v++)
+		{
+			ASSERT(mLocations[v]);
+			mLocations[v]->Serialize(anArchive);
+		}
+
+		//
+		//
+		//
+		iCount = mIDMapMounts.size();
+		anArchive.Add(iCount);
+
+		for (int v = 0; v < iCount; v++)
+			anArchive.Add(mIDMapMounts[v]);
+
+		//
+		//
+		//
+		aFile.Close();
+		::syslog(LOG_INFO, "Saved store object info file '%s'", GetConfiguration().GetKeyValue("StoreObjectInfoFile").c_str());
+	}
+	catch (...)
+	{
+		::syslog(LOG_WARNING, "Requested store object info file '%s' not accessible or could not be created", GetConfiguration().GetKeyValue("StoreObjectInfoFile").c_str());
+	}
+}
+
+// --------------------------------------------------------------------------
+//
+// 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.
+//
+//		Created: 2005/04/11
+//
+// --------------------------------------------------------------------------
+void BackupDaemon::DeserializeStoreObjectInfo(int64_t & aClientStoreMarker, box_time_t & theLastSyncTime, box_time_t & theNextSyncTime)
+{
+	//
+	//
+	//
+	DeleteAllLocations();
+
+	//
+	//
+	//
+	if(!GetConfiguration().KeyExists("StoreObjectInfoFile") || GetConfiguration().GetKeyValue("StoreObjectInfoFile").size() <=0)
+		return;
+
+	try
+	{
+		FileStream aFile(GetConfiguration().GetKeyValue("StoreObjectInfoFile").c_str(), O_RDONLY);
+		IOStreamArchive anArchive(aFile, 0);
+
+		//
+		// see if the content has a chance to be what we think it is
+		//
+		int iMagicValue = 0;
+		anArchive.Get(iMagicValue);
+
+		if (iMagicValue != STOREOBJECTINFO_MAGIC_ID_VALUE)
+		{
+			::syslog(LOG_WARNING, "Store object info file '%s' is not what you think it is: no harm done, will re-cache from store", GetConfiguration().GetKeyValue("StoreObjectInfoFile").c_str());
+			return;
+		}
+
+		//
+		// get a bit optimistic and read in a string identifier
+		//
+		std::string strMagicValue;
+		anArchive.Get(strMagicValue);
+
+		if (strMagicValue != STOREOBJECTINFO_MAGIC_ID_STRING)
+		{
+			::syslog(LOG_WARNING, "Store object info file '%s' is not what you think it is: no harm done, will re-cache from store", GetConfiguration().GetKeyValue("StoreObjectInfoFile").c_str());
+			return;
+		}
+
+		//
+		// ok, check if we are not loading some future development version by mistake
+		//
+		int iVersion = 0;
+		anArchive.Get(iVersion);
+
+		if (iVersion != STOREOBJECTINFO_VERSION)
+		{
+			::syslog(LOG_WARNING, "Store object info file '%s' version [%d] unsupported: no harm done, will re-cache from store", GetConfiguration().GetKeyValue("StoreObjectInfoFile").c_str(), iVersion);
+			return;
+		}
+
+		//
+		// check if this state file is even valid for the loaded bbackupd.conf file
+		//
+		box_time_t lastKnownConfigModTime;
+		anArchive.Get(lastKnownConfigModTime);
+
+		if (lastKnownConfigModTime != GetConfiguration().GetModTime())
+		{
+			::syslog(LOG_WARNING, "Store object info file '%s' out of date: no harm done, will re-cache from store", GetConfiguration().GetKeyValue("StoreObjectInfoFile").c_str());
+			return;
+		}
+
+		//
+		// this is it, go at it
+		//
+		anArchive >> aClientStoreMarker >> theLastSyncTime >> theNextSyncTime;
+
+		//
+		//
+		//
+		int64_t iCount = 0;
+		anArchive.Get(iCount);
+
+		for (int v = 0; v < iCount; v++)
+		{
+			Location* pLocation = new Location;
+			if (!pLocation)
+				throw std::bad_alloc();
+
+			pLocation->Deserialize(anArchive);
+			mLocations.push_back(pLocation);
+		}
+
+		//
+		//
+		//
+		iCount = 0;
+		anArchive.Get(iCount);
+
+		for (int v = 0; v < iCount; v++)
+		{
+			std::string strItem;
+			anArchive.Get(strItem);
+
+			mIDMapMounts.push_back(strItem);
+		}
+
+		//
+		//
+		//
+		aFile.Close();
+		::syslog(LOG_INFO, "Loaded store object info file '%s', version [%04d]", GetConfiguration().GetKeyValue("StoreObjectInfoFile").c_str(), iVersion);
+	}
+	catch (...)
+	{
+		DeleteAllLocations();
+
+		aClientStoreMarker = BackupClientContext::ClientStoreMarker_NotKnown;
+		theLastSyncTime = 0;
+		theNextSyncTime = 0;
+
+		::syslog(LOG_WARNING, "Requested store object info file '%s' does not exist, not accessible, or inconsistent: no harm done, will re-cache from store", GetConfiguration().GetKeyValue("StoreObjectInfoFile").c_str());
+	}
+}

Modified: box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupDaemon.h
===================================================================
--- box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupDaemon.h	2006-01-17 22:24:47 UTC (rev 319)
+++ box/gary/boxbackup-0.09-mod/bin/bbackupd/BackupDaemon.h	2006-01-17 22:27:22 UTC (rev 320)
@@ -58,6 +58,8 @@
 #include "Socket.h"
 #include "SocketListen.h"
 #include "SocketStream.h"
+
+#include "Archive.h"
 
 class BackupClientDirectoryRecord;
 class BackupClientContext;
@@ -78,7 +80,11 @@
 {
 public:
 	BackupDaemon();
-	~BackupDaemon();
+	~BackupDaemon();
+
+	// methods below do partial (specialized) serialization of client state only
+	void SerializeStoreObjectInfo(int64_t aClientStoreMarker, box_time_t theLastSyncTime, box_time_t theNextSyncTime) const;
+	void DeserializeStoreObjectInfo(int64_t & aClientStoreMarker, box_time_t & theLastSyncTime, box_time_t & theNextSyncTime);
 private:
 	BackupDaemon(const BackupDaemon &);
 public:
@@ -154,7 +160,10 @@
 	{
 	public:
 		Location();
-		~Location();
+		~Location();
+
+		void Deserialize(Archive & rArchive);
+		void Serialize(Archive & rArchive) const;
 	private:
 		Location(const Location &);	// copy not allowed
 		Location &operator=(const Location &);

Modified: box/gary/boxbackup-0.09-mod/bin/bbstored/BackupCommands.cpp
===================================================================
--- box/gary/boxbackup-0.09-mod/bin/bbstored/BackupCommands.cpp	2006-01-17 22:24:47 UTC (rev 319)
+++ box/gary/boxbackup-0.09-mod/bin/bbstored/BackupCommands.cpp	2006-01-17 22:27:22 UTC (rev 320)
@@ -898,3 +898,20 @@
 	));
 }
 
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    BackupProtocolServerGetIsAlive::DoCommand(BackupProtocolServer &, BackupContext &)
+//		Purpose: Return the amount of disc space used
+//		Created: 19/4/04
+//
+// --------------------------------------------------------------------------
+std::auto_ptr<ProtocolObject> BackupProtocolServerGetIsAlive::DoCommand(BackupProtocolServer &rProtocol, BackupContext &rContext)
+{
+	CHECK_PHASE(Phase_Commands)
+
+	//
+	// NOOP
+	//
+	return std::auto_ptr<ProtocolObject>(new BackupProtocolServerIsAlive());
+}

Modified: box/gary/boxbackup-0.09-mod/bin/bbstored/backupprotocol.txt
===================================================================
--- box/gary/boxbackup-0.09-mod/bin/bbstored/backupprotocol.txt	2006-01-17 22:24:47 UTC (rev 319)
+++ box/gary/boxbackup-0.09-mod/bin/bbstored/backupprotocol.txt	2006-01-17 22:27:22 UTC (rev 320)
@@ -219,3 +219,10 @@
 	int64	BlocksSoftLimit
 	int64	BlocksHardLimit
 	int32	BlockSize
+
+GetIsAlive	42	Command(IsAlive)
+	# no data members
+
+IsAlive	43	Reply
+	# no data members
+

Modified: box/gary/boxbackup-0.09-mod/lib/backupclient/BackupDaemonConfigVerify.cpp
===================================================================
--- box/gary/boxbackup-0.09-mod/lib/backupclient/BackupDaemonConfigVerify.cpp	2006-01-17 22:24:47 UTC (rev 319)
+++ box/gary/boxbackup-0.09-mod/lib/backupclient/BackupDaemonConfigVerify.cpp	2006-01-17 22:27:22 UTC (rev 320)
@@ -123,6 +123,8 @@
 	{"ExtendedLogging",	"no", ConfigTest_IsBool, 0},			// make value "yes" to enable in config file
 
 	{"CommandSocket", 0, 0, 0},				// not compulsory to have this
+	{"StoreObjectInfoFile", 0, 0, 0},				// optional
+	{"KeepAliveTime", 0, ConfigTest_IsInt, 0},				// optional
 
 	{"NotifyScript", 0, 0, 0},				// optional script to run when backup needs attention, eg store full
 	

Modified: box/gary/boxbackup-0.09-mod/lib/backupclient/BackupStoreFile.h
===================================================================
--- box/gary/boxbackup-0.09-mod/lib/backupclient/BackupStoreFile.h	2006-01-17 22:24:47 UTC (rev 319)
+++ box/gary/boxbackup-0.09-mod/lib/backupclient/BackupStoreFile.h	2006-01-17 22:27:22 UTC (rev 320)
@@ -180,8 +180,17 @@
 		free(a);
 	}
 
-	// Limits
-	static void SetMaximumDiffingTime(int Seconds);
+	// --------------------------------------------------------------------------
+	//
+	// Function
+	//		Name:    BackupStoreFile::SuspendFileDiff()
+	//		Purpose: Notifies BackupStoreFile object that a diff operation should be
+	//				 terminated ASAP. Usually called from an external timer.
+	//
+	//		Created: 12/1/04
+	//
+	// --------------------------------------------------------------------------
+	static void SuspendFileDiff();
 
 	// Building blocks
 	class EncodingBuffer

Modified: box/gary/boxbackup-0.09-mod/lib/backupclient/BackupStoreFileDiff.cpp
===================================================================
--- box/gary/boxbackup-0.09-mod/lib/backupclient/BackupStoreFileDiff.cpp	2006-01-17 22:24:47 UTC (rev 319)
+++ box/gary/boxbackup-0.09-mod/lib/backupclient/BackupStoreFileDiff.cpp	2006-01-17 22:27:22 UTC (rev 320)
@@ -50,8 +50,6 @@
 
 #include <new>
 #include <map>
-#include <signal.h>
-#include <sys/time.h>
 
 #include "BackupStoreFile.h"
 #include "BackupStoreFileWire.h"
@@ -60,7 +58,7 @@
 #include "BackupStoreException.h"
 #include "BackupStoreFileEncodeStream.h"
 #include "BackupStoreConstants.h"
-#include "FileStream.h"
+#include "FileStream.h"
 #include "RollingChecksum.h"
 #include "MD5Digest.h"
 #include "CommonException.h"
@@ -82,34 +80,28 @@
 static void SetupHashTable(BlocksAvailableEntry *pIndex, int64_t NumBlocks, int32_t BlockSize, BlocksAvailableEntry **pHashTable);
 static bool SecondStageMatch(BlocksAvailableEntry *pFirstInHashList, uint16_t Hash, uint8_t *pBeginnings, uint8_t *pEndings, int Offset, int32_t BlockSize, int64_t FileBlockNumber, BlocksAvailableEntry *pIndex, std::map<int64_t, int64_t> &rFoundBlocks);
 static void GenerateRecipe(BackupStoreFileEncodeStream::Recipe &rRecipe, BlocksAvailableEntry *pIndex, int64_t NumBlocks, std::map<int64_t, int64_t> &rFoundBlocks, int64_t SizeOfInputFile);
-
-// Avoid running on too long with diffs
-static int sMaximumDiffTime = 10;		// maximum time to spend diffing
+
+// control whether a currently active diff should be terminated ASAP
 static bool sDiffTimedOut = false;
-static bool sSetTimerSignelHandler = false;
-static void TimerSignalHandler(int signal);
-static void StartDiffTimer();
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    BackupStoreFile::SuspendFileDiff()
+//		Purpose: Notifies BackupStoreFile object that a diff operation should be
+//				 terminated ASAP. Usually called from an external timer.
+//
+//		Created: 12/1/04
+//
+// --------------------------------------------------------------------------
+void BackupStoreFile::SuspendFileDiff()
+{
+	sDiffTimedOut = true;
+}
 
-
 // --------------------------------------------------------------------------
 //
 // Function
-//		Name:    BackupStoreFile::SetMaximumDiffingTime(int)
-//		Purpose: Sets the maximum time to spend diffing, in seconds. Time is
-//				 process virutal time.
-//		Created: 19/3/04
-//
-// --------------------------------------------------------------------------
-void BackupStoreFile::SetMaximumDiffingTime(int Seconds)
-{
-	sMaximumDiffTime = Seconds;
-	TRACE1("Set maximum diffing time to %d seconds\n", Seconds);
-}
-
-
-// --------------------------------------------------------------------------
-//
-// Function
 //		Name:    BackupStoreFile::MoveStreamPositionToBlockIndex(IOStream &)
 //		Purpose: Move the file pointer in this stream to just before the block index.
 //				 Assumes that the stream is at the beginning, seekable, and
@@ -214,9 +206,6 @@
 	
 	// Pointer to recipe we're going to create
 	BackupStoreFileEncodeStream::Recipe *precipe = 0;
-
-	// Start the timeout timer, so that the operation doesn't continue for ever
-	StartDiffTimer();
 	
 	try
 	{
@@ -508,7 +497,10 @@
 // --------------------------------------------------------------------------
 static void SearchForMatchingBlocks(IOStream &rFile, std::map<int64_t, int64_t> &rFoundBlocks,
 	BlocksAvailableEntry *pIndex, int64_t NumBlocks, int32_t Sizes[BACKUP_FILE_DIFF_MAX_BLOCK_SIZES])
-{
+{
+	// reset state in case we got timing/call-order mess-up somewhere else
+	sDiffTimedOut = false;
+
 	// Allocate the hash lookup table
 	BlocksAvailableEntry **phashTable = (BlocksAvailableEntry **)::malloc(sizeof(BlocksAvailableEntry *) * (64*1024));
 
@@ -630,7 +622,7 @@
 						if(static_cast<int64_t>(rFoundBlocks.size()) > (NumBlocks * BACKUP_FILE_DIFF_MAX_BLOCK_FIND_MULTIPLE)
 							|| sDiffTimedOut)
 						{
-							abortSearch = true;
+							abortSearch = true;
 							break;
 						}
 					}
@@ -957,56 +949,4 @@
 		TRACE0("======== ========= ========\n");
 	}
 #endif
-}
-
-
-// --------------------------------------------------------------------------
-//
-// Function
-//		Name:    static TimerSignalHandler(int)
-//		Purpose: Signal handler
-//		Created: 19/3/04
-//
-// --------------------------------------------------------------------------
-void TimerSignalHandler(int signal)
-{
-	sDiffTimedOut = true;
-}
-
-
-// --------------------------------------------------------------------------
-//
-// Function
-//		Name:    static StartDiffTimer()
-//		Purpose: Starts the diff timeout timer
-//		Created: 19/3/04
-//
-// --------------------------------------------------------------------------
-void StartDiffTimer()
-{
-	// Set timer signal handler
-	if(!sSetTimerSignelHandler)
-	{
-		::signal(SIGVTALRM, TimerSignalHandler);
-		sSetTimerSignelHandler = true;
-	}
-
-	struct itimerval timeout;
-	// Don't want this to repeat
-	timeout.it_interval.tv_sec = 0;
-	timeout.it_interval.tv_usec = 0;
-	// Single timeout after the specified number of seconds
-	timeout.it_value.tv_sec = sMaximumDiffTime;
-	timeout.it_value.tv_usec = 0;
-	// Set timer
-	if(::setitimer(ITIMER_VIRTUAL, &timeout, NULL) != 0)
-	{
-		TRACE0("WARNING: couldn't set diff timeout\n");
-	}
-	
-	// Unset flag (last thing)
-	sDiffTimedOut = false;
-}
-
-
-
+}

Added: box/gary/boxbackup-0.09-mod/lib/common/Archive.h
===================================================================
--- box/gary/boxbackup-0.09-mod/lib/common/Archive.h	2006-01-17 22:24:47 UTC (rev 319)
+++ box/gary/boxbackup-0.09-mod/lib/common/Archive.h	2006-01-17 22:27:22 UTC (rev 320)
@@ -0,0 +1,243 @@
+// distribution boxbackup-0.09
+// 
+//  
+// Copyright (c) 2003, 2004
+//      Ben Summers.  All rights reserved.
+//  
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the
+//    documentation and/or other materials provided with the distribution.
+// 3. All use of this software and associated advertising materials must 
+//    display the following acknowledgement:
+//        This product includes software developed by Ben Summers.
+// 4. The names of the Authors may not be used to endorse or promote
+//    products derived from this software without specific prior written
+//    permission.
+// 
+// [Where legally impermissible the Authors do not disclaim liability for 
+// direct physical injury or death caused solely by defects in the software 
+// unless it is modified by a third party.]
+// 
+// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//  
+//  
+//  
+// --------------------------------------------------------------------------
+//
+// File
+//		Name:    Archive.h
+//		Purpose: Backup daemon state archive
+//		Created: 2005/04/11
+//
+// --------------------------------------------------------------------------
+
+#ifndef ARCHIVE__H
+#define ARCHIVE__H
+
+#include <vector>
+#include <string>
+#include <memory>
+
+#include "IOStream.h"
+#include "Guards.h"
+
+#define ARCHIVE_GET_SIZE(hdr)		(( ((uint8_t)((hdr)[0])) | ( ((uint8_t)((hdr)[1])) << 8)) >> 2)
+
+#define ARCHIVE_MAGIC_VALUE_RECURSE 0x4449525F
+#define ARCHIVE_MAGIC_VALUE_NOOP 0x5449525F
+
+class Archive
+{
+public:
+	Archive()
+	{
+	}
+	virtual ~Archive()
+	{
+	}
+	//
+	// primitive insertion operations
+	//
+	virtual void Add(bool bItem) = 0;
+	virtual void Add(int iItem) = 0;
+	virtual void Add(int64_t  iItem) = 0;
+	virtual void Add(uint64_t iItem) = 0;
+	virtual void Add(uint8_t iItem) = 0;
+	virtual void Add(const std::string & strItem) = 0;
+	//
+	// chaining support
+	//
+	Archive & operator<<(bool bItem) { Add(bItem); return *this; }
+	Archive & operator<<(int iItem) { Add(iItem); return *this; }
+	Archive & operator<<(int64_t iItem) { Add(iItem); return *this; }
+	Archive & operator<<(uint64_t iItem) { Add(iItem); return *this; }
+	Archive & operator<<(uint8_t iItem) { Add(iItem); return *this; }
+	Archive & operator<<(const std::string & strItem) { Add(strItem); return *this; }
+	//
+	// primitive extraction oprations
+	//
+	virtual void Get(bool & bItem) = 0;
+	virtual void Get(int & iItem) = 0;
+	virtual void Get(int64_t  & iItem) = 0;
+	virtual void Get(uint64_t & iItem) = 0;
+	virtual void Get(uint8_t & iItem) = 0;
+	virtual void Get(std::string & strItem) = 0;
+	//
+	// chaining support
+	//
+	Archive & operator>>(bool & bItem) { Get(bItem); return *this; }
+	Archive & operator>>(int & iItem) { Get(iItem); return *this; }
+	Archive & operator>>(int64_t & iItem) { Get(iItem); return *this; }
+	Archive & operator>>(uint64_t & iItem) { Get(iItem); return *this; }
+	Archive & operator>>(uint8_t & iItem) { Get(iItem); return *this; }
+	Archive & operator>>(std::string & strItem) { Get(strItem); return *this; }
+private:
+	Archive(const Archive &);
+	Archive & operator=(const Archive &);
+};
+
+class IOStreamArchive : public Archive
+{
+public:
+	IOStreamArchive(IOStream & mStream, int Timeout) : mStream(mStream)
+	{
+		mTimeout = Timeout;
+	}
+	virtual ~IOStreamArchive()
+	{
+	}
+	//
+	//
+	//
+	virtual void Add(bool bItem)
+	{
+		Add((int) bItem);
+	}
+	virtual void Add(int iItem)
+	{
+		int32_t privItem = htonl(iItem);
+		mStream.Write(&privItem, sizeof(privItem));
+	}
+	virtual void Add(int64_t  iItem)
+	{
+		int64_t privItem = hton64(iItem);
+		mStream.Write(&privItem, sizeof(privItem));
+	}
+	virtual void Add(uint64_t iItem)
+	{
+		uint64_t privItem = hton64(iItem);
+		mStream.Write(&privItem, sizeof(privItem));
+	}
+	virtual void Add(uint8_t iItem)
+	{
+		int privItem = iItem;
+		Add(privItem);
+	}
+	virtual void Add(const std::string & strItem)
+	{
+		int iSize = strItem.size();
+		Add(iSize);
+		mStream.Write(strItem.c_str(), iSize);
+	}
+	//
+	//
+	//
+	virtual void Get(bool & bItem)
+	{
+		int privItem;
+		Get(privItem);
+
+		if (privItem)
+			bItem = true;
+		else
+			bItem = false;
+	}
+	virtual void Get(int & iItem)
+	{
+		int32_t privItem;
+		if(!mStream.ReadFullBuffer(&privItem, sizeof(privItem), 0 /* not interested in bytes read if this fails */))
+		{
+			THROW_EXCEPTION(CommonException, StreamableMemBlockIncompleteRead)
+		}
+		iItem = ntohl(privItem);
+	}
+	virtual void Get(int64_t  & iItem)
+	{
+		int64_t privItem;
+		if(!mStream.ReadFullBuffer(&privItem, sizeof(privItem), 0 /* not interested in bytes read if this fails */))
+		{
+			THROW_EXCEPTION(CommonException, StreamableMemBlockIncompleteRead)
+		}
+		iItem = ntoh64(privItem);
+	}
+	virtual void Get(uint64_t & iItem)
+	{
+		uint64_t privItem;
+		if(!mStream.ReadFullBuffer(&privItem, sizeof(privItem), 0 /* not interested in bytes read if this fails */))
+		{
+			THROW_EXCEPTION(CommonException, StreamableMemBlockIncompleteRead)
+		}
+		iItem = ntoh64(privItem);
+	}
+	virtual void Get(uint8_t & iItem)
+	{
+		int privItem;
+		Get(privItem);
+		iItem = privItem;
+	}
+	virtual void Get(std::string & strItem)
+	{
+		int iSize;
+		Get(iSize);
+
+		// Assume most strings are relatively small
+		char buf[256];
+		if(iSize < (int) sizeof(buf))
+		{
+			// Fetch rest of pPayload, relying on the Protocol to error on stupidly large sizes for us
+			if(!mStream.ReadFullBuffer(buf, iSize, 0 /* not interested in bytes read if this fails */, mTimeout))
+			{
+				THROW_EXCEPTION(CommonException, StreamableMemBlockIncompleteRead)
+			}
+			// assign to this string, storing the header and the extra pPayload
+			strItem.assign(buf, iSize);
+		}
+		else
+		{
+			// Block of memory to hold it
+			MemoryBlockGuard<char*> dataB(iSize);
+			char *pPayload = dataB;
+
+			// Fetch rest of pPayload, relying on the Protocol to error on stupidly large sizes for us
+			if(!mStream.ReadFullBuffer(pPayload, iSize, 0 /* not interested in bytes read if this fails */, mTimeout))
+			{
+				THROW_EXCEPTION(CommonException, StreamableMemBlockIncompleteRead)
+			}
+			// assign to this string, storing the header and the extra pPayload
+			strItem.assign(pPayload, iSize);
+		}
+	}
+protected:
+	IOStream & mStream;
+	int mTimeout;
+private:
+	IOStreamArchive(const IOStreamArchive &);
+	IOStreamArchive & operator=(const IOStreamArchive &);
+};
+
+#endif // ARCHIVE__H

Modified: box/gary/boxbackup-0.09-mod/lib/common/Configuration.cpp
===================================================================
--- box/gary/boxbackup-0.09-mod/lib/common/Configuration.cpp	2006-01-17 22:24:47 UTC (rev 319)
+++ box/gary/boxbackup-0.09-mod/lib/common/Configuration.cpp	2006-01-17 22:27:22 UTC (rev 320)
@@ -57,6 +57,8 @@
 #include "FdGetLine.h"
 
 #include "MemLeakFindOn.h"
+
+#include "FileModificationTime.h"
 
 // utility whitespace function
 inline bool iw(int c)
@@ -68,25 +70,22 @@
 static const char *sValueBooleanStrings[] = {"yes", "true", "no", "false", 0};
 static const bool sValueBooleanValue[] = {true, true, false, false};
 
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    Configuration::Configuration(const std::string &, box_time_t)
+//		Purpose: Constructor
+//		Created: 2003/07/23
+//
+// --------------------------------------------------------------------------
+Configuration::Configuration(const std::string &rName, box_time_t configModTime)
+	: mName(rName), mConfigModTime(configModTime)
+{
+}
 
-
 // --------------------------------------------------------------------------
 //
 // Function
-//		Name:    Configuration::Configuration(const std::string &)
-//		Purpose: Constructor
-//		Created: 2003/07/23
-//
-// --------------------------------------------------------------------------
-Configuration::Configuration(const std::string &rName)
-	: mName(rName)
-{
-}
-
-
-// --------------------------------------------------------------------------
-//
-// Function
 //		Name:    Configuration::Configuration(const Configuration &)
 //		Purpose: Copy constructor
 //		Created: 2003/07/23
@@ -95,7 +94,8 @@
 Configuration::Configuration(const Configuration &rToCopy)
 	: mName(rToCopy.mName),
 	  mSubConfigurations(rToCopy.mSubConfigurations),
-	  mKeys(rToCopy.mKeys)
+	  mKeys(rToCopy.mKeys),
+	  mConfigModTime(rToCopy.mConfigModTime)
 {
 }
 
@@ -132,7 +132,14 @@
 	
 	// Just to make sure
 	rErrorMsg.erase();
-	
+	
+	// Save modification time to be able to distinguish across configuration sets
+	struct stat st;
+	if(::stat(Filename, &st) != 0)
+	{
+		THROW_EXCEPTION(CommonException, OSFileError)
+	}
+
 	// Open the file
 	FileHandleGuard<O_RDONLY> file(Filename);
 	
@@ -140,7 +147,7 @@
 	FdGetLine getline(file);
 	
 	// Object to create
-	Configuration *pconfig = new Configuration(std::string("<root>"));
+	Configuration *pconfig = new Configuration(std::string("<root>"), FileModificationTime(st));
 	
 	try
 	{
@@ -215,7 +222,7 @@
 			if(startBlockExpected)
 			{
 				// New config object
-				Configuration config(blockName);
+				Configuration config(blockName, rConfig.mConfigModTime);
 				
 				// Continue processing into this block
 				if(!LoadInto(config, rGetLine, rErrorMsg, false))

Modified: box/gary/boxbackup-0.09-mod/lib/common/Configuration.h
===================================================================
--- box/gary/boxbackup-0.09-mod/lib/common/Configuration.h	2006-01-17 22:24:47 UTC (rev 319)
+++ box/gary/boxbackup-0.09-mod/lib/common/Configuration.h	2006-01-17 22:27:22 UTC (rev 320)
@@ -54,6 +54,8 @@
 #include <vector>
 #include <string>
 #include <memory>
+
+#include "BoxTime.h"
 
 // For defining tests
 enum
@@ -97,11 +99,13 @@
 class Configuration
 {
 private:
-	Configuration(const std::string &rName);
+	Configuration(const std::string &rName, box_time_t configModTime);
 public:
 	Configuration(const Configuration &rToCopy);
 	~Configuration();
-	
+	
+	box_time_t GetModTime() const { return mConfigModTime; }
+
 	enum
 	{
 		// The character to separate multi-values
@@ -126,8 +130,9 @@
 	typedef std::list<std::pair<std::string, Configuration> > SubConfigListType;
 	SubConfigListType mSubConfigurations;
 	// Order of keys, not preserved
-	std::map<std::string, std::string> mKeys;
-	
+	std::map<std::string, std::string> mKeys;
+protected:
+	box_time_t mConfigModTime;
 private:
 	static bool LoadInto(Configuration &rConfig, FdGetLine &rGetLine, std::string &rErrorMsg, bool RootLevel);
 	static bool Verify(Configuration &rConfig, const ConfigurationVerify &rVerify, const std::string &rLevel, std::string &rErrorMsg);

Modified: box/gary/boxbackup-0.09-mod/lib/common/ExcludeList.cpp
===================================================================
--- box/gary/boxbackup-0.09-mod/lib/common/ExcludeList.cpp	2006-01-17 22:24:47 UTC (rev 319)
+++ box/gary/boxbackup-0.09-mod/lib/common/ExcludeList.cpp	2006-01-17 22:27:22 UTC (rev 320)
@@ -168,7 +168,9 @@
 				}
 				
 				// Store in list of regular expressions
-				mRegex.push_back(pregex);
+				mRegex.push_back(pregex);
+				// Store in list of regular expression string for Serialize
+				mRegexStr.push_back(i->c_str());
 			}
 			catch(...)
 			{
@@ -251,8 +253,170 @@
 	// Store the pointer
 	mpAlwaysInclude = pAlwaysInclude;
 }
-
-
-	
-
-
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    ExcludeList::Deserialize(Archive & rArchive)
+//		Purpose: Deserializes this object instance from a stream of bytes, using an Archive abstraction.
+//
+//		Created: 2005/04/11
+//
+// --------------------------------------------------------------------------
+void ExcludeList::Deserialize(Archive & rArchive)
+{
+	//
+	//
+	//
+	mDefinite.clear();
+
+#ifndef PLATFORM_REGEX_NOT_SUPPORTED
+	// free regex memory
+	while(mRegex.size() > 0)
+	{
+		regex_t *pregex = mRegex.back();
+		mRegex.pop_back();
+		// Free regex storage, and the structure itself
+		::regfree(pregex);
+		delete pregex;
+	}
+
+	mRegexStr.clear();
+#endif
+
+	// Clean up exceptions list
+	if(mpAlwaysInclude != 0)
+	{
+		delete mpAlwaysInclude;
+		mpAlwaysInclude = 0;
+	}
+
+	//
+	//
+	//
+	int64_t iCount = 0;
+	rArchive.Get(iCount);
+
+	if (iCount > 0)
+	{
+		for (int v = 0; v < iCount; v++)
+		{
+			std::string strItem;
+			rArchive.Get(strItem);
+
+			/**** LOAD ****/ mDefinite.insert(strItem);
+		}
+	}
+
+	//
+	//
+	//
+#ifndef PLATFORM_REGEX_NOT_SUPPORTED
+	rArchive.Get(iCount);
+
+	if (iCount > 0)
+	{
+		for (int v = 0; v < iCount; v++)
+		{
+			std::string strItem;
+			rArchive.Get(strItem);
+
+			// Allocate memory
+			regex_t* pregex = new regex_t;
+			
+			try
+			{
+				// Compile
+				if(::regcomp(pregex, strItem.c_str(), REG_EXTENDED | REG_NOSUB) != 0)
+				{
+					THROW_EXCEPTION(CommonException, BadRegularExpression)
+				}
+				
+				// Store in list of regular expressions
+				/**** LOAD ****/ mRegex.push_back(pregex);
+				// Store in list of regular expression string for Serialize
+				/**** LOAD ****/ mRegexStr.push_back(strItem);
+			}
+			catch(...)
+			{
+				delete pregex;
+				throw;
+			}
+		}
+	}
+#endif // PLATFORM_REGEX_NOT_SUPPORTED
+
+	//
+	//
+	//
+	int64_t aMagicMarker = 0;
+	rArchive.Get(aMagicMarker);
+
+	if (aMagicMarker == ARCHIVE_MAGIC_VALUE_NOOP)
+	{
+		// NOOP
+	}
+	else if (aMagicMarker == ARCHIVE_MAGIC_VALUE_RECURSE)
+	{
+		/**** LOAD ****/ mpAlwaysInclude = new ExcludeList;
+		if (!mpAlwaysInclude)
+			throw std::bad_alloc();
+
+		mpAlwaysInclude->Deserialize(rArchive);
+	}
+	else
+	{
+		// there is something going on here
+		THROW_EXCEPTION(CommonException, Internal)
+	}
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    ExcludeList::Serialize(Archive & rArchive)
+//		Purpose: Serializes this object instance into a stream of bytes, using an Archive abstraction.
+//
+//		Created: 2005/04/11
+//
+// --------------------------------------------------------------------------
+void ExcludeList::Serialize(Archive & rArchive) const
+{
+	//
+	//
+	//
+	int64_t iCount = mDefinite.size();
+	rArchive.Add(iCount);
+
+	for (std::set<std::string>::const_iterator i = mDefinite.begin(); i != mDefinite.end(); i++)
+		rArchive.Add(*i);
+
+	//
+	//
+	//
+#ifndef PLATFORM_REGEX_NOT_SUPPORTED
+	ASSERT(mRegex.size() == mRegexStr.size()); 	// don't even try to save compiled regular expressions - use string copies
+
+	iCount = mRegexStr.size();
+	rArchive.Add(iCount);
+
+	for (std::vector<std::string>::const_iterator i = mRegexStr.begin(); i != mRegexStr.end(); i++)
+		rArchive.Add(*i);
+#endif // PLATFORM_REGEX_NOT_SUPPORTED
+
+	//
+	//
+	//
+	if (!mpAlwaysInclude)
+	{
+		int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_NOOP;
+		rArchive.Add(aMagicMarker);
+	}
+	else
+	{
+		int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_RECURSE; // be explicit about whether recursion follows
+		rArchive.Add(aMagicMarker);
+
+		mpAlwaysInclude->Serialize(rArchive);
+	}
+}

Modified: box/gary/boxbackup-0.09-mod/lib/common/ExcludeList.h
===================================================================
--- box/gary/boxbackup-0.09-mod/lib/common/ExcludeList.h	2006-01-17 22:24:47 UTC (rev 319)
+++ box/gary/boxbackup-0.09-mod/lib/common/ExcludeList.h	2006-01-17 22:27:22 UTC (rev 320)
@@ -57,6 +57,8 @@
 #ifndef EXCLUDELIST_IMPLEMENTATION_REGEX_T_DEFINED
 	typedef int regex_t;
 #endif
+
+#include "Archive.h"
 
 // --------------------------------------------------------------------------
 //
@@ -71,6 +73,9 @@
 public:
 	ExcludeList();
 	~ExcludeList();
+
+	void Deserialize(Archive & rArchive);
+	void Serialize(Archive & rArchive) const;
 
 	void AddDefiniteEntries(const std::string &rEntries);
 	void AddRegexEntries(const std::string &rEntries);
@@ -93,7 +98,8 @@
 private:
 	std::set<std::string> mDefinite;
 #ifndef PLATFORM_REGEX_NOT_SUPPORTED
-	std::vector<regex_t *> mRegex;
+	std::vector<regex_t *> mRegex;
+	std::vector<std::string> mRegexStr;	// save original regular expression string-based source for Serialize
 #endif
 
 	// For exceptions to the excludes