source: box/trunk/bin/bbackupd/BackupClientDirectoryRecord.cpp @ 3092

Revision 3092, 64.2 KB checked in by chris, 5 weeks ago (diff)

Treat corrupt filenames (not decryptable) as not existing, so that
the client will flag them for deletion, and the store will eventually
prune them. We could probably recover better by flagging them for
immediate deletion (Remove_ASAP) but this is a better-tested code path.

Remove unused variable hasMultipleHardLinks.

  • Property svn:eol-style set to native
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    BackupClientDirectoryRecord.cpp
5//              Purpose: Implementation of record about directory for
6//                       backup client
7//              Created: 2003/10/08
8//
9// --------------------------------------------------------------------------
10
11#include "Box.h"
12
13#ifdef HAVE_DIRENT_H
14        #include <dirent.h>
15#endif
16
17#include <errno.h>
18#include <string.h>
19
20#include "autogen_BackupProtocol.h"
21#include "autogen_CipherException.h"
22#include "autogen_ClientException.h"
23#include "Archive.h"
24#include "BackupClientContext.h"
25#include "BackupClientDirectoryRecord.h"
26#include "BackupClientInodeToIDMap.h"
27#include "BackupDaemon.h"
28#include "BackupStoreException.h"
29#include "BackupStoreFile.h"
30#include "BackupStoreFileEncodeStream.h"
31#include "CommonException.h"
32#include "CollectInBufferStream.h"
33#include "FileModificationTime.h"
34#include "IOStream.h"
35#include "Logging.h"
36#include "MemBlockStream.h"
37#include "PathUtils.h"
38#include "RateLimitingStream.h"
39#include "ReadLoggingStream.h"
40
41#include "MemLeakFindOn.h"
42
43typedef std::map<std::string, BackupStoreDirectory::Entry *> DecryptedEntriesMap_t;
44
45// --------------------------------------------------------------------------
46//
47// Function
48//              Name:    BackupClientDirectoryRecord::BackupClientDirectoryRecord()
49//              Purpose: Constructor
50//              Created: 2003/10/08
51//
52// --------------------------------------------------------------------------
53BackupClientDirectoryRecord::BackupClientDirectoryRecord(int64_t ObjectID, const std::string &rSubDirName)
54        : mObjectID(ObjectID),
55          mSubDirName(rSubDirName),
56          mInitialSyncDone(false),
57          mSyncDone(false),
58          mSuppressMultipleLinksWarning(false),
59          mpPendingEntries(0)
60{
61        ::memset(mStateChecksum, 0, sizeof(mStateChecksum));
62}
63
64// --------------------------------------------------------------------------
65//
66// Function
67//              Name:    BackupClientDirectoryRecord::~BackupClientDirectoryRecord()
68//              Purpose: Destructor
69//              Created: 2003/10/08
70//
71// --------------------------------------------------------------------------
72BackupClientDirectoryRecord::~BackupClientDirectoryRecord()
73{
74        // Make deletion recursive
75        DeleteSubDirectories();
76       
77        // Delete maps
78        if(mpPendingEntries != 0)
79        {
80                delete mpPendingEntries;
81                mpPendingEntries = 0;
82        }
83}
84
85// --------------------------------------------------------------------------
86//
87// Function
88//              Name:    BackupClientDirectoryRecord::DeleteSubDirectories();
89//              Purpose: Delete all sub directory entries
90//              Created: 2003/10/09
91//
92// --------------------------------------------------------------------------
93void BackupClientDirectoryRecord::DeleteSubDirectories()
94{
95        // Delete all pointers
96        for(std::map<std::string, BackupClientDirectoryRecord *>::iterator i = mSubDirectories.begin();
97                i != mSubDirectories.end(); ++i)
98        {
99                delete i->second;
100        }
101       
102        // Empty list
103        mSubDirectories.clear();
104}
105
106std::string BackupClientDirectoryRecord::ConvertVssPathToRealPath(
107        const std::string &rVssPath,
108        const Location& rBackupLocation)
109{
110#ifdef ENABLE_VSS
111        BOX_TRACE("VSS: ConvertVssPathToRealPath: mIsSnapshotCreated = " <<
112                rBackupLocation.mIsSnapshotCreated);
113        BOX_TRACE("VSS: ConvertVssPathToRealPath: File/Directory Path = " <<
114                rVssPath.substr(0, rBackupLocation.mSnapshotPath.length()));
115        BOX_TRACE("VSS: ConvertVssPathToRealPath: Snapshot Path = " <<
116                rBackupLocation.mSnapshotPath);
117        if (rBackupLocation.mIsSnapshotCreated &&
118                rVssPath.substr(0, rBackupLocation.mSnapshotPath.length()) ==
119                rBackupLocation.mSnapshotPath)
120        {
121                std::string convertedPath = rBackupLocation.mPath +
122                        rVssPath.substr(rBackupLocation.mSnapshotPath.length());
123                BOX_TRACE("VSS: ConvertVssPathToRealPath: Converted Path = " <<
124                        convertedPath);
125                return convertedPath;
126        }
127#endif
128
129        return rVssPath;
130}
131
132// --------------------------------------------------------------------------
133//
134// Function
135//              Name:    BackupClientDirectoryRecord::SyncDirectory(i
136//                       BackupClientDirectoryRecord::SyncParams &,
137//                       int64_t, const std::string &,
138//                       const std::string &, bool)
139//              Purpose: Recursively synchronise a local directory
140//                       with the server.
141//              Created: 2003/10/08
142//
143// --------------------------------------------------------------------------
144void BackupClientDirectoryRecord::SyncDirectory(
145        BackupClientDirectoryRecord::SyncParams &rParams,
146        int64_t ContainingDirectoryID,
147        const std::string &rLocalPath,
148        const std::string &rRemotePath,
149        const Location& rBackupLocation,
150        bool ThisDirHasJustBeenCreated)
151{
152        BackupClientContext& rContext(rParams.mrContext);
153        ProgressNotifier& rNotifier(rContext.GetProgressNotifier());
154
155        // Signal received by daemon?
156        if(rParams.mrRunStatusProvider.StopRun())
157        {
158                // Yes. Stop now.
159                THROW_EXCEPTION(BackupStoreException, SignalReceived)
160        }
161
162        // Start by making some flag changes, marking this sync as not done,
163        // and on the immediate sub directories.
164        mSyncDone = false;
165        for(std::map<std::string, BackupClientDirectoryRecord *>::iterator
166                i  = mSubDirectories.begin();
167                i != mSubDirectories.end(); ++i)
168        {
169                i->second->mSyncDone = false;
170        }
171
172        // Work out the time in the future after which the file should
173        // be uploaded regardless. This is a simple way to avoid having
174        // too many problems with file servers when they have clients
175        // with badly out of sync clocks.
176        rParams.mUploadAfterThisTimeInTheFuture = GetCurrentBoxTime() +
177                rParams.mMaxFileTimeInFuture;
178       
179        // Build the current state checksum to compare against while
180        // getting info from dirs. Note checksum is used locally only,
181        // so byte order isn't considered.
182        MD5Digest currentStateChecksum;
183       
184        EMU_STRUCT_STAT dest_st;
185        // Stat the directory, to get attribute info
186        // If it's a symbolic link, we want the link target here
187        // (as we're about to back up the contents of the directory)
188        {
189                if(EMU_STAT(rLocalPath.c_str(), &dest_st) != 0)
190                {
191                        // The directory has probably been deleted, so
192                        // just ignore this error. In a future scan, this
193                        // deletion will be noticed, deleted from server,
194                        // and this object deleted.
195                        rNotifier.NotifyDirStatFailed(this,
196                                ConvertVssPathToRealPath(rLocalPath, rBackupLocation),
197                                strerror(errno));
198                        return;
199                }
200
201                BOX_TRACE("Stat dir '" << rLocalPath << "' "
202                        "found device/inode " <<
203                        dest_st.st_dev << "/" << dest_st.st_ino);
204
205                // Store inode number in map so directories are tracked
206                // in case they're renamed
207                {
208                        BackupClientInodeToIDMap &idMap(
209                                rParams.mrContext.GetNewIDMap());
210                        idMap.AddToMap(dest_st.st_ino, mObjectID,
211                                ContainingDirectoryID);
212                }
213                // Add attributes to checksum
214                currentStateChecksum.Add(&dest_st.st_mode,
215                        sizeof(dest_st.st_mode));
216                currentStateChecksum.Add(&dest_st.st_uid,
217                        sizeof(dest_st.st_uid));
218                currentStateChecksum.Add(&dest_st.st_gid,
219                        sizeof(dest_st.st_gid));
220                // Inode to be paranoid about things moving around
221                currentStateChecksum.Add(&dest_st.st_ino,
222                        sizeof(dest_st.st_ino));
223#ifdef HAVE_STRUCT_STAT_ST_FLAGS
224                currentStateChecksum.Add(&dest_st.st_flags,
225                        sizeof(dest_st.st_flags));
226#endif
227
228                StreamableMemBlock xattr;
229                BackupClientFileAttributes::FillExtendedAttr(xattr,
230                        rLocalPath.c_str());
231                currentStateChecksum.Add(xattr.GetBuffer(), xattr.GetSize());
232        }
233       
234        // Read directory entries, building arrays of names
235        // First, need to read the contents of the directory.
236        std::vector<std::string> dirs;
237        std::vector<std::string> files;
238        bool downloadDirectoryRecordBecauseOfFutureFiles = false;
239
240        EMU_STRUCT_STAT link_st;
241        if(EMU_LSTAT(rLocalPath.c_str(), &link_st) != 0)
242        {
243                // Report the error (logs and
244                // eventual email to administrator)
245                rNotifier.NotifyFileStatFailed(this, 
246                        ConvertVssPathToRealPath(rLocalPath, rBackupLocation),
247                        strerror(errno));
248               
249                // FIXME move to NotifyFileStatFailed()
250                SetErrorWhenReadingFilesystemObject(rParams, rLocalPath);
251               
252                // This shouldn't happen, so we'd better not continue
253                THROW_EXCEPTION(CommonException, OSFileError)
254        }
255
256        // BLOCK
257        {               
258                // read the contents...
259                DIR *dirHandle = 0;
260                try
261                {
262                        std::string nonVssDirPath = ConvertVssPathToRealPath(rLocalPath,
263                                        rBackupLocation);
264                        rNotifier.NotifyScanDirectory(this, nonVssDirPath);
265
266                        dirHandle = ::opendir(rLocalPath.c_str());
267                        if(dirHandle == 0)
268                        {
269                                // Report the error (logs and
270                                // eventual email to administrator)
271                                if (errno == EACCES)
272                                {
273                                        rNotifier.NotifyDirListFailed(this,
274                                                nonVssDirPath,
275                                                "Access denied");
276                                }
277                                else
278                                {
279                                        rNotifier.NotifyDirListFailed(this, 
280                                                nonVssDirPath,
281                                                strerror(errno));
282                                }
283                               
284                                // Report the error (logs and eventual email
285                                // to administrator)
286                                SetErrorWhenReadingFilesystemObject(rParams,
287                                        nonVssDirPath);
288                                // Ignore this directory for now.
289                                return;
290                        }
291                       
292                        // Basic structure for checksum info
293                        struct {
294                                box_time_t mModificationTime;
295                                box_time_t mAttributeModificationTime;
296                                int64_t mSize;
297                                // And then the name follows
298                        } checksum_info;
299                        // Be paranoid about structure packing
300                        ::memset(&checksum_info, 0, sizeof(checksum_info));
301       
302                        struct dirent *en = 0;
303                        EMU_STRUCT_STAT file_st;
304                        std::string filename;
305                        while((en = ::readdir(dirHandle)) != 0)
306                        {
307                                rParams.mrContext.DoKeepAlive();
308                               
309                                // Don't need to use
310                                // LinuxWorkaround_FinishDirentStruct(en,
311                                // rLocalPath.c_str());
312                                // on Linux, as a stat is performed to
313                                // get all this info
314
315                                if(en->d_name[0] == '.' && 
316                                        (en->d_name[1] == '\0' || (en->d_name[1] == '.' && en->d_name[2] == '\0')))
317                                {
318                                        // ignore, it's . or ..
319                                        continue;
320                                }
321
322                                // Stat file to get info
323                                filename = MakeFullPath(rLocalPath, en->d_name);
324                                std::string realFileName = ConvertVssPathToRealPath(filename,
325                                        rBackupLocation);
326
327                                #ifdef WIN32
328                                // Don't stat the file just yet, to ensure
329                                // that users can exclude unreadable files
330                                // to suppress warnings that they are
331                                // not accessible.
332                                //
333                                // Our emulated readdir() abuses en->d_type,
334                                // which would normally contain DT_REG,
335                                // DT_DIR, etc, but we only use it here and
336                                // prefer to have the full file attributes.
337                                int type;
338                                if (en->d_type & FILE_ATTRIBUTE_DIRECTORY)
339                                {
340                                        type = S_IFDIR;
341                                }
342                                else
343                                {
344                                        type = S_IFREG;
345                                }
346
347                                #else // !WIN32
348                                if(EMU_LSTAT(filename.c_str(), &file_st) != 0)
349                                {
350                                        if(!(rParams.mrContext.ExcludeDir(
351                                                filename)))
352                                        {
353                                                // Report the error (logs and
354                                                // eventual email to
355                                                // administrator)
356                                                rNotifier.NotifyFileStatFailed(
357                                                        this, filename,
358                                                        strerror(errno));
359                                       
360                                                // FIXME move to
361                                                // NotifyFileStatFailed()
362                                                SetErrorWhenReadingFilesystemObject(rParams, filename);
363                                        }
364
365                                        // Ignore this entry for now.
366                                        continue;
367                                }
368
369                                int type = file_st.st_mode & S_IFMT;
370
371                                // ecryptfs reports nlink > 1 for directories
372                                // with contents, but no filesystem supports
373                                // hardlinking directories? so we can ignore
374                                // this if the entry is a directory.
375                                if(file_st.st_nlink != 1 && type == S_IFDIR)
376                                {
377                                        BOX_INFO("Ignoring apparent hard link "
378                                                "count on directory: " <<
379                                                filename << ", nlink=" <<
380                                                file_st.st_nlink);
381                                }
382                                else if(file_st.st_nlink > 1)
383                                {
384                                        if(!mSuppressMultipleLinksWarning)
385                                        {
386                                                BOX_WARNING("File is hard linked, this may "
387                                                        "cause rename tracking to fail and "
388                                                        "move files incorrectly in your "
389                                                        "backup! " << filename << 
390                                                        ", nlink=" << file_st.st_nlink <<
391                                                        " (suppressing further warnings)");
392                                                mSuppressMultipleLinksWarning = true;
393                                        }
394                                        SetErrorWhenReadingFilesystemObject(rParams, filename);
395                                }
396
397                                BOX_TRACE("Stat entry '" << filename << "' "
398                                        "found device/inode " <<
399                                        file_st.st_dev << "/" <<
400                                        file_st.st_ino);
401
402                                /* Workaround for apparent btrfs bug, where
403                                symlinks appear to be on a different filesystem
404                                than their containing directory, thanks to
405                                Toke Hoiland-Jorgensen */
406                                if(type == S_IFDIR &&
407                                        file_st.st_dev != dest_st.st_dev)
408                                {
409                                        if(!(rParams.mrContext.ExcludeDir(
410                                                filename)))
411                                        {
412                                                rNotifier.NotifyMountPointSkipped(
413                                                        this, filename);
414                                        }
415                                        continue;
416                                }
417                                #endif
418
419                                if(type == S_IFREG || type == S_IFLNK)
420                                {
421                                        // File or symbolic link
422
423                                        // Exclude it?
424                                        if(rParams.mrContext.ExcludeFile(realFileName))
425                                        {
426                                                rNotifier.NotifyFileExcluded(this, realFileName);
427                                                // Next item!
428                                                continue;
429                                        }
430
431                                        // Store on list
432                                        files.push_back(std::string(en->d_name));
433                                }
434                                else if(type == S_IFDIR)
435                                {
436                                        // Directory
437
438                                        // Exclude it?
439                                        if(rParams.mrContext.ExcludeDir(realFileName))
440                                        {
441                                                rNotifier.NotifyDirExcluded(this, realFileName);
442
443                                                // Next item!
444                                                continue;
445                                        }
446
447                                        #ifdef WIN32
448                                        // exclude reparse points, as Application Data points to the
449                                        // parent directory under Vista and later, and causes an
450                                        // infinite loop:
451                                        // http://social.msdn.microsoft.com/forums/en-US/windowscompatibility/thread/05d14368-25dd-41c8-bdba-5590bf762a68/
452                                        if (en->d_type & FILE_ATTRIBUTE_REPARSE_POINT)
453                                        {
454                                                rNotifier.NotifyMountPointSkipped(this, realFileName);
455                                                continue;
456                                        }
457                                        #endif
458
459                                        // Store on list
460                                        dirs.push_back(std::string(en->d_name));
461                                }
462                                else // not a file or directory, what is it?
463                                {
464                                        if (type == S_IFSOCK
465#                                               ifndef WIN32
466                                                || type == S_IFIFO
467#                                               endif
468                                                )
469                                        {
470                                                // removed notification for these types
471                                                // see Debian bug 479145, no objections
472                                        }
473                                        else if(rParams.mrContext.ExcludeFile(realFileName))
474                                        {
475                                                rNotifier.NotifyFileExcluded(this, realFileName);
476                                        }
477                                        else
478                                        {
479                                                rNotifier.NotifyUnsupportedFileType(this,
480                                                        realFileName);
481                                                SetErrorWhenReadingFilesystemObject(rParams,
482                                                        realFileName);
483                                        }
484
485                                        continue;
486                                }
487                               
488                                // Here if the object is something to back up (file, symlink or dir, not excluded)
489                                // So make the information for adding to the checksum
490                               
491                                #ifdef WIN32
492                                // We didn't stat the file before,
493                                // but now we need the information.
494                                if(emu_stat(filename.c_str(), &file_st) != 0)
495                                {
496                                        rNotifier.NotifyFileStatFailed(this, 
497                                                        ConvertVssPathToRealPath(filename, rBackupLocation),
498                                                        strerror(errno));
499                                       
500                                        // Report the error (logs and
501                                        // eventual email to administrator)
502                                        SetErrorWhenReadingFilesystemObject(rParams, filename);
503
504                                        // Ignore this entry for now.
505                                        continue;
506                                }
507
508                                if(file_st.st_dev != link_st.st_dev)
509                                {
510                                        rNotifier.NotifyMountPointSkipped(this, 
511                                                ConvertVssPathToRealPath(filename, rBackupLocation));
512                                        continue;
513                                }
514                                #endif
515
516                                checksum_info.mModificationTime = FileModificationTime(file_st);
517                                checksum_info.mAttributeModificationTime = FileAttrModificationTime(file_st);
518                                checksum_info.mSize = file_st.st_size;
519                                currentStateChecksum.Add(&checksum_info, sizeof(checksum_info));
520                                currentStateChecksum.Add(en->d_name, strlen(en->d_name));
521                               
522                                // If the file has been modified madly into the future, download the
523                                // directory record anyway to ensure that it doesn't get uploaded
524                                // every single time the disc is scanned.
525                                if(checksum_info.mModificationTime > rParams.mUploadAfterThisTimeInTheFuture)
526                                {
527                                        downloadDirectoryRecordBecauseOfFutureFiles = true;
528                                        // Log that this has happened
529                                        if(!rParams.mHaveLoggedWarningAboutFutureFileTimes)
530                                        {
531                                                rNotifier.NotifyFileModifiedInFuture(this,
532                                                        ConvertVssPathToRealPath(filename, rBackupLocation));
533                                                rParams.mHaveLoggedWarningAboutFutureFileTimes = true;
534                                        }
535                                }
536                        }
537       
538                        if(::closedir(dirHandle) != 0)
539                        {
540                                THROW_EXCEPTION(CommonException, OSFileError)
541                        }
542                        dirHandle = 0;
543                }
544                catch(...)
545                {
546                        if(dirHandle != 0)
547                        {
548                                ::closedir(dirHandle);
549                        }
550                        throw;
551                }
552        }
553
554        // Finish off the checksum, and compare with the one currently stored
555        bool checksumDifferent = true;
556        currentStateChecksum.Finish();
557        if(mInitialSyncDone && currentStateChecksum.DigestMatches(mStateChecksum))
558        {
559                // The checksum is the same, and there was one to compare with
560                checksumDifferent = false;
561        }
562
563        // Pointer to potentially downloaded store directory info
564        BackupStoreDirectory *pdirOnStore = 0;
565       
566        try
567        {
568                // Want to get the directory listing?
569                if(ThisDirHasJustBeenCreated)
570                {
571                        // Avoid sending another command to the server when we know it's empty
572                        pdirOnStore = new BackupStoreDirectory(mObjectID, ContainingDirectoryID);
573                }
574                else
575                {
576                        // Consider asking the store for it
577                        if(!mInitialSyncDone || checksumDifferent || downloadDirectoryRecordBecauseOfFutureFiles)
578                        {
579                                pdirOnStore = FetchDirectoryListing(rParams);
580                        }
581                }
582                               
583                // Make sure the attributes are up to date -- if there's space on the server
584                // and this directory has not just been created (because it's attributes will be correct in this case)
585                // and the checksum is different, implying they *MIGHT* be different.
586                if((!ThisDirHasJustBeenCreated) && checksumDifferent && (!rParams.mrContext.StorageLimitExceeded()))
587                {
588                        UpdateAttributes(rParams, pdirOnStore, rLocalPath);
589                }
590               
591                // Create the list of pointers to directory entries
592                std::vector<BackupStoreDirectory::Entry *> entriesLeftOver;
593                if(pdirOnStore)
594                {
595                        entriesLeftOver.resize(pdirOnStore->GetNumberOfEntries(), 0);
596                        BackupStoreDirectory::Iterator i(*pdirOnStore);
597                        // Copy in pointers to all the entries
598                        for(unsigned int l = 0; l < pdirOnStore->GetNumberOfEntries(); ++l)
599                        {
600                                entriesLeftOver[l] = i.Next();
601                        }
602                }
603               
604                // Do the directory reading
605                bool updateCompleteSuccess = UpdateItems(rParams, rLocalPath,
606                        rRemotePath, rBackupLocation, pdirOnStore, entriesLeftOver, files, dirs);
607               
608                // LAST THING! (think exception safety)
609                // Store the new checksum -- don't fetch things unnecessarily in the future
610                // But... only if 1) the storage limit isn't exceeded -- make sure things are done again if
611                // the directory is modified later
612                // and 2) All the objects within the directory were stored successfully.
613                if(!rParams.mrContext.StorageLimitExceeded() && updateCompleteSuccess)
614                {
615                        currentStateChecksum.CopyDigestTo(mStateChecksum);
616                }
617        }
618        catch(...)
619        {
620                // Bad things have happened -- clean up
621                if(pdirOnStore != 0)
622                {
623                        delete pdirOnStore;
624                        pdirOnStore = 0;
625                }
626               
627                // Set things so that we get a full go at stuff later
628                ::memset(mStateChecksum, 0, sizeof(mStateChecksum));
629               
630                throw;
631        }
632       
633        // Clean up directory on store
634        if(pdirOnStore != 0)
635        {
636                delete pdirOnStore;
637                pdirOnStore = 0;
638        }
639       
640        // Flag things as having happened.
641        mInitialSyncDone = true;
642        mSyncDone = true;
643}
644
645// --------------------------------------------------------------------------
646//
647// Function
648//              Name:    BackupClientDirectoryRecord::FetchDirectoryListing(BackupClientDirectoryRecord::SyncParams &)
649//              Purpose: Fetch the directory listing of this directory from the store.
650//              Created: 2003/10/09
651//
652// --------------------------------------------------------------------------
653BackupStoreDirectory *BackupClientDirectoryRecord::FetchDirectoryListing(BackupClientDirectoryRecord::SyncParams &rParams)
654{
655        BackupStoreDirectory *pdir = 0;
656       
657        try
658        {
659                // Get connection to store
660                BackupProtocolClient &connection(rParams.mrContext.GetConnection());
661
662                // Query the directory
663                std::auto_ptr<BackupProtocolSuccess> dirreply(connection.QueryListDirectory(
664                                mObjectID,
665                                BackupProtocolListDirectory::Flags_INCLUDE_EVERYTHING,  // both files and directories
666                                BackupProtocolListDirectory::Flags_Deleted | 
667                                BackupProtocolListDirectory::Flags_OldVersion, // exclude old/deleted stuff
668                                true /* want attributes */));
669
670                // Retrieve the directory from the stream following
671                pdir = new BackupStoreDirectory;
672                ASSERT(pdir != 0);
673                std::auto_ptr<IOStream> dirstream(connection.ReceiveStream());
674                pdir->ReadFromStream(*dirstream, connection.GetTimeout());
675        }
676        catch(...)
677        {
678                delete pdir;
679                pdir = 0;
680                throw;
681        }
682       
683        return pdir;
684}
685
686
687// --------------------------------------------------------------------------
688//
689// Function
690//              Name:    BackupClientDirectoryRecord::UpdateAttributes(BackupClientDirectoryRecord::SyncParams &, const std::string &)
691//              Purpose: Sets the attributes of the directory on the store, if necessary
692//              Created: 2003/10/09
693//
694// --------------------------------------------------------------------------
695void BackupClientDirectoryRecord::UpdateAttributes(BackupClientDirectoryRecord::SyncParams &rParams, BackupStoreDirectory *pDirOnStore, const std::string &rLocalPath)
696{
697        // Get attributes for the directory
698        BackupClientFileAttributes attr;
699        box_time_t attrModTime = 0;
700        attr.ReadAttributes(rLocalPath.c_str(), true /* directories have zero mod times */,
701                0 /* no modification time */, &attrModTime);
702
703        // Assume attributes need updating, unless proved otherwise
704        bool updateAttr = true;
705
706        // Got a listing to compare with?
707        ASSERT(pDirOnStore == 0 || (pDirOnStore != 0 && pDirOnStore->HasAttributes()));
708        if(pDirOnStore != 0 && pDirOnStore->HasAttributes())
709        {
710                const StreamableMemBlock &storeAttrEnc(pDirOnStore->GetAttributes());
711                // Explict decryption
712                BackupClientFileAttributes storeAttr(storeAttrEnc);
713               
714                // Compare the attributes
715                if(attr.Compare(storeAttr, true,
716                        true /* ignore both modification times */))
717                {
718                        // No update necessary
719                        updateAttr = false;
720                }
721        }
722
723        // Update them?
724        if(updateAttr)
725        {
726                // Get connection to store
727                BackupProtocolClient &connection(rParams.mrContext.GetConnection());
728
729                // Exception thrown if this doesn't work
730                MemBlockStream attrStream(attr);
731                connection.QueryChangeDirAttributes(mObjectID, attrModTime, attrStream);
732        }
733}
734
735std::string BackupClientDirectoryRecord::DecryptFilename(
736        BackupStoreDirectory::Entry *en,
737        const std::string& rRemoteDirectoryPath)
738{
739        BackupStoreFilenameClear fn(en->GetName());
740        return DecryptFilename(fn, en->GetObjectID(), rRemoteDirectoryPath);
741}
742
743std::string BackupClientDirectoryRecord::DecryptFilename(
744        BackupStoreFilenameClear fn, int64_t filenameObjectID,
745        const std::string& rRemoteDirectoryPath)
746{
747        std::string filenameClear;
748        try
749        {
750                filenameClear = fn.GetClearFilename();
751        }
752        catch(BoxException &e)
753        {
754                BOX_ERROR("Failed to decrypt filename for object " << 
755                        BOX_FORMAT_OBJECTID(filenameObjectID) << " in "
756                        "directory " << BOX_FORMAT_OBJECTID(mObjectID) <<
757                        " (" << rRemoteDirectoryPath << ")");
758                throw;
759        }
760        return filenameClear;
761}
762
763// --------------------------------------------------------------------------
764//
765// Function
766//              Name:    BackupClientDirectoryRecord::UpdateItems(BackupClientDirectoryRecord::SyncParams &, const std::string &, BackupStoreDirectory *, std::vector<BackupStoreDirectory::Entry *> &)
767//              Purpose: Update the items stored on the server. The rFiles vector will be erased after it's used to save space.
768//                               Returns true if all items were updated successfully. (If not, the failures will have been logged).
769//              Created: 2003/10/09
770//
771// --------------------------------------------------------------------------
772bool BackupClientDirectoryRecord::UpdateItems(
773        BackupClientDirectoryRecord::SyncParams &rParams,
774        const std::string &rLocalPath,
775        const std::string &rRemotePath,
776        const Location& rBackupLocation,
777        BackupStoreDirectory *pDirOnStore,
778        std::vector<BackupStoreDirectory::Entry *> &rEntriesLeftOver,
779        std::vector<std::string> &rFiles,
780        const std::vector<std::string> &rDirs)
781{
782        BackupClientContext& rContext(rParams.mrContext);
783        ProgressNotifier& rNotifier(rContext.GetProgressNotifier());
784
785        bool allUpdatedSuccessfully = true;
786
787        // Decrypt all the directory entries.
788        // It would be nice to be able to just compare the encrypted versions, however this doesn't work
789        // in practise because there can be multiple encodings of the same filename using different
790        // methods (although each method will result in the same string for the same filename.) This
791        // happens when the server fixes a broken store, and gives plain text generated filenames.
792        // So if we didn't do things like this, then you wouldn't be able to recover from bad things
793        // happening with the server.
794        DecryptedEntriesMap_t decryptedEntries;
795        if(pDirOnStore != 0)
796        {
797                BackupStoreDirectory::Iterator i(*pDirOnStore);
798                BackupStoreDirectory::Entry *en = 0;
799                while((en = i.Next()) != 0)
800                {
801                        std::string filenameClear;
802                        try
803                        {
804                                filenameClear = DecryptFilename(en,
805                                        rRemotePath);
806                                decryptedEntries[filenameClear] = en;
807                        }
808                        catch (CipherException &e)
809                        {
810                                BOX_ERROR("Failed to decrypt a filename, "
811                                        "pretending that the file doesn't "
812                                        "exist");
813                        }
814                }
815        }
816
817        // Do files
818        for(std::vector<std::string>::const_iterator f = rFiles.begin();
819                f != rFiles.end(); ++f)
820        {
821                // Send keep-alive message if needed
822                rContext.DoKeepAlive();
823               
824                // Filename of this file
825                std::string filename(MakeFullPath(rLocalPath, *f));
826                std::string nonVssFilePath = ConvertVssPathToRealPath(filename,
827                        rBackupLocation);
828
829                // Get relevant info about file
830                box_time_t modTime = 0;
831                uint64_t attributesHash = 0;
832                int64_t fileSize = 0;
833                InodeRefType inodeNum = 0;
834                // BLOCK
835                {
836                        // Stat the file
837                        EMU_STRUCT_STAT st;
838                        if(EMU_LSTAT(filename.c_str(), &st) != 0)
839                        {
840                                rNotifier.NotifyFileStatFailed(this, nonVssFilePath,
841                                        strerror(errno));
842
843                                // Report the error (logs and
844                                // eventual email to administrator)
845                                SetErrorWhenReadingFilesystemObject(rParams, nonVssFilePath);
846
847                                // Ignore this entry for now.
848                                continue;
849                        }
850                       
851                        // Extract required data
852                        modTime = FileModificationTime(st);
853                        fileSize = st.st_size;
854                        inodeNum = st.st_ino;
855                        attributesHash = BackupClientFileAttributes::GenerateAttributeHash(st, filename, *f);
856                }
857
858                // See if it's in the listing (if we have one)
859                BackupStoreFilenameClear storeFilename(*f);
860                BackupStoreDirectory::Entry *en = 0;
861                int64_t latestObjectID = 0;
862                if(pDirOnStore != 0)
863                {
864                        DecryptedEntriesMap_t::iterator i(decryptedEntries.find(*f));
865                        if(i != decryptedEntries.end())
866                        {
867                                en = i->second;
868                                latestObjectID = en->GetObjectID();
869                        }
870                }
871
872                // Check that the entry which might have been found is in fact a file
873                if((en != 0) && !(en->IsFile()))
874                {
875                        // Directory exists in the place of this file -- sort it out
876                        RemoveDirectoryInPlaceOfFile(rParams, pDirOnStore,
877                                en, *f);
878                        en = 0;
879                }
880               
881                // Check for renaming?
882                if(pDirOnStore != 0 && en == 0)
883                {
884                        // We now know...
885                        // 1) File has just been added
886                        // 2) It's not in the store
887                       
888                        // Do we know about the inode number?
889                        const BackupClientInodeToIDMap &idMap(rContext.GetCurrentIDMap());
890                        int64_t renameObjectID = 0, renameInDirectory = 0;
891                        if(idMap.Lookup(inodeNum, renameObjectID, renameInDirectory))
892                        {
893                                // Look up on the server to get the name, to build the local filename
894                                std::string localPotentialOldName;
895                                bool isDir = false;
896                                bool isCurrentVersion = false;
897                                box_time_t srvModTime = 0, srvAttributesHash = 0;
898                                BackupStoreFilenameClear oldLeafname;
899                                if(rContext.FindFilename(renameObjectID, renameInDirectory,
900                                        localPotentialOldName, isDir, isCurrentVersion,
901                                        &srvModTime, &srvAttributesHash, &oldLeafname))
902                                {       
903                                        // Only interested if it's a file and the latest version
904                                        if(!isDir && isCurrentVersion)
905                                        {
906                                                // Check that the object we found in the ID map doesn't exist on disc
907                                                EMU_STRUCT_STAT st;
908                                                if(EMU_STAT(localPotentialOldName.c_str(), &st) != 0 && errno == ENOENT)
909                                                {
910                                                        // Doesn't exist locally, but does exist on the server.
911                                                        // Therefore we can safely rename it to this new file.
912
913                                                        // Get the connection to the server
914                                                        BackupProtocolClient &connection(rContext.GetConnection());
915
916                                                        // Only do this step if there is room on the server.
917                                                        // This step will be repeated later when there is space available
918                                                        if(!rContext.StorageLimitExceeded())
919                                                        {
920                                                                // Rename the existing files (ie include old versions) on the server
921                                                                connection.QueryMoveObject(renameObjectID,
922                                                                        renameInDirectory,
923                                                                        mObjectID /* move to this directory */,
924                                                                        BackupProtocolMoveObject::Flags_MoveAllWithSameName | 
925                                                                        BackupProtocolMoveObject::Flags_AllowMoveOverDeletedObject,
926                                                                        storeFilename);
927                                                                       
928                                                                // Stop the attempt to delete the file in the original location
929                                                                BackupClientDeleteList &rdelList(rContext.GetDeleteList());
930                                                                rdelList.StopFileDeletion(renameInDirectory, oldLeafname);
931                                                               
932                                                                // Create new entry in the directory for it
933                                                                // -- will be near enough what's actually on the server for the rest to work.
934                                                                en = pDirOnStore->AddEntry(storeFilename,
935                                                                        srvModTime, renameObjectID,
936                                                                        0 /* size in blocks unknown, but not needed */,
937                                                                        BackupStoreDirectory::Entry::Flags_File,
938                                                                        srvAttributesHash);
939                                                       
940                                                                // Store the object ID for the inode lookup map later
941                                                                latestObjectID = renameObjectID;
942                                                        }
943                                                }
944                                        }
945                                }
946                        }
947                }
948               
949                // Is it in the mPendingEntries list?
950                box_time_t pendingFirstSeenTime = 0;            // ie not seen
951                if(mpPendingEntries != 0)
952                {
953                        std::map<std::string, box_time_t>::const_iterator i(mpPendingEntries->find(*f));
954                        if(i != mpPendingEntries->end())
955                        {
956                                // found it -- set flag
957                                pendingFirstSeenTime = i->second;
958                        }
959                }
960               
961                // If pDirOnStore == 0, then this must have been after an initial sync:
962                ASSERT(pDirOnStore != 0 || mInitialSyncDone);
963                // So, if pDirOnStore == 0, then we know that everything before syncPeriodStart
964                // is either on the server, or in the toupload list. If the directory had changed,
965                // we'd have got a directory listing.
966                //
967                // At this point, if (pDirOnStore == 0 && en == 0), we can assume it's on the server with a
968                // mod time < syncPeriodStart, or didn't exist before that time.
969                //
970                // But if en != 0, then we need to compare modification times to avoid uploading it again.
971
972                // Need to update?
973                //
974                // Condition for upload:
975                //    modification time within sync period
976                //    if it's been seen before but not uploaded, is the time from this first sight longer than the MaxUploadWait
977                //        and if we know about it from a directory listing, that it hasn't got the same upload time as on the store
978
979                bool doUpload = false;
980                std::string decisionReason = "unknown";
981
982                // Only upload a file if the mod time locally is
983                // different to that on the server.
984
985                if (en == 0 || en->GetModificationTime() != modTime)
986                {
987                        // Check the file modified within the acceptable time period we're checking
988                        // If the file isn't on the server, the acceptable time starts at zero.
989                        // Check pDirOnStore and en, because if we didn't download a directory listing,
990                        // pDirOnStore will be zero, but we know it's on the server.
991                        if (modTime < rParams.mSyncPeriodEnd)
992                        {
993                                if (pDirOnStore != 0 && en == 0)
994                                {
995                                        doUpload = true;
996                                        decisionReason = "not on server";
997                                }
998                                else if (modTime >= rParams.mSyncPeriodStart)
999                                {
1000                                        doUpload = true;
1001                                        decisionReason = "modified since last sync";
1002                                }
1003                        }
1004
1005                        // However, just in case things are continually
1006                        // modified, we check the first seen time.
1007                        // The two compares of syncPeriodEnd and
1008                        // pendingFirstSeenTime are because the values
1009                        // are unsigned.
1010
1011                        if (!doUpload && 
1012                                pendingFirstSeenTime != 0 &&
1013                                rParams.mSyncPeriodEnd > pendingFirstSeenTime &&
1014                                (rParams.mSyncPeriodEnd - pendingFirstSeenTime) 
1015                                > rParams.mMaxUploadWait)
1016                        {
1017                                doUpload = true;
1018                                decisionReason = "continually modified";
1019                        }
1020
1021                        // Then make sure that if files are added with a
1022                        // time less than the sync period start
1023                        // (which can easily happen on file server), it
1024                        // gets uploaded. The directory contents checksum
1025                        // will pick up the fact it has been added, so the
1026                        // store listing will be available when this happens.
1027
1028                        if (!doUpload &&
1029                                modTime <= rParams.mSyncPeriodStart && 
1030                                en != 0 && 
1031                                en->GetModificationTime() != modTime)
1032                        {
1033                                doUpload = true;
1034                                decisionReason = "mod time changed";
1035                        }
1036
1037                        // And just to catch really badly off clocks in
1038                        // the future for file server clients,
1039                        // just upload the file if it's madly in the future.
1040
1041                        if (!doUpload && modTime > 
1042                                rParams.mUploadAfterThisTimeInTheFuture)
1043                        {
1044                                doUpload = true;
1045                                decisionReason = "mod time in the future";
1046                        }
1047                }
1048       
1049                if (en != 0 && en->GetModificationTime() == modTime)
1050                {
1051                        doUpload = false;
1052                        decisionReason = "not modified since last upload";
1053                }
1054                else if (!doUpload)
1055                {
1056                        if (modTime > rParams.mSyncPeriodEnd)
1057                        {
1058                                box_time_t now = GetCurrentBoxTime();
1059                                int age = BoxTimeToSeconds(now -
1060                                        modTime);
1061                                std::ostringstream s;
1062                                s << "modified too recently: "
1063                                        "only " << age << " seconds ago";
1064                                decisionReason = s.str();
1065                        }
1066                        else
1067                        {
1068                                std::ostringstream s;
1069                                s << "mod time is " << modTime << 
1070                                        " which is outside sync window, "
1071                                        << rParams.mSyncPeriodStart << " to "
1072                                        << rParams.mSyncPeriodEnd;
1073                                decisionReason = s.str();
1074                        }
1075                }
1076
1077                BOX_TRACE("Upload decision: " << nonVssFilePath << ": " <<
1078                        (doUpload ? "will upload" : "will not upload") <<
1079                        " (" << decisionReason << ")");
1080
1081                bool fileSynced = true;
1082
1083                if (doUpload)
1084                {
1085                        // Upload needed, don't mark sync success until
1086                        // we've actually done it
1087                        fileSynced = false;
1088
1089                        // Make sure we're connected -- must connect here so we know whether
1090                        // the storage limit has been exceeded, and hence whether or not
1091                        // to actually upload the file.
1092                        rContext.GetConnection();
1093
1094                        // Only do this step if there is room on the server.
1095                        // This step will be repeated later when there is space available
1096                        if(!rContext.StorageLimitExceeded())
1097                        {
1098                                // Upload the file to the server, recording the
1099                                // object ID it returns
1100                                bool noPreviousVersionOnServer = 
1101                                        ((pDirOnStore != 0) && (en == 0));
1102                               
1103                                // Surround this in a try/catch block, to
1104                                // catch errors, but still continue
1105                                bool uploadSuccess = false;
1106                                try
1107                                {
1108                                        latestObjectID = UploadFile(rParams,
1109                                                filename,
1110                                                nonVssFilePath,
1111                                                storeFilename,
1112                                                fileSize, modTime,
1113                                                attributesHash,
1114                                                noPreviousVersionOnServer);
1115
1116                                        if (latestObjectID == 0)
1117                                        {
1118                                                // storage limit exceeded
1119                                                rParams.mrContext.SetStorageLimitExceeded();
1120                                                uploadSuccess = false;
1121                                                allUpdatedSuccessfully = false;
1122                                        }
1123                                        else
1124                                        {
1125                                                uploadSuccess = true;
1126                                        }
1127                                }
1128                                catch(ConnectionException &e)
1129                                {
1130                                        // Connection errors should just be
1131                                        // passed on to the main handler,
1132                                        // retries would probably just cause
1133                                        // more problems.
1134                                        rNotifier.NotifyFileUploadException(
1135                                                this, nonVssFilePath, e);
1136                                        throw;
1137                                }
1138                                catch(BoxException &e)
1139                                {
1140                                        if (e.GetType() == BackupStoreException::ExceptionType &&
1141                                                e.GetSubType() == BackupStoreException::SignalReceived)
1142                                        {
1143                                                // abort requested, pass the
1144                                                // exception on up.
1145                                                throw;
1146                                        }
1147                                       
1148                                        // an error occured -- make return
1149                                        // code false, to show error in directory
1150                                        allUpdatedSuccessfully = false;
1151                                        // Log it.
1152                                        SetErrorWhenReadingFilesystemObject(rParams,
1153                                                nonVssFilePath);
1154                                        rNotifier.NotifyFileUploadException(this,
1155                                                nonVssFilePath, e);
1156                                }
1157
1158                                // Update structures if the file was uploaded
1159                                // successfully.
1160                                if(uploadSuccess)
1161                                {
1162                                        fileSynced = true;
1163
1164                                        // delete from pending entries
1165                                        if(pendingFirstSeenTime != 0 && mpPendingEntries != 0)
1166                                        {
1167                                                mpPendingEntries->erase(*f);
1168                                        }
1169                                }
1170                        }
1171                        else
1172                        {
1173                                rNotifier.NotifyFileSkippedServerFull(this, nonVssFilePath);
1174                        }
1175                }
1176                else if(en != 0 && en->GetAttributesHash() != attributesHash)
1177                {
1178                        // Attributes have probably changed, upload them again.
1179                        // If the attributes have changed enough, the directory
1180                        // hash will have changed too, and so the dir will have
1181                        // been downloaded, and the entry will be available.
1182
1183                        // Get connection
1184                        BackupProtocolClient &connection(rContext.GetConnection());
1185
1186                        // Only do this step if there is room on the server.
1187                        // This step will be repeated later when there is
1188                        // space available
1189                        if(!rContext.StorageLimitExceeded())
1190                        {
1191                                try
1192                                {
1193                                        rNotifier.NotifyFileUploadingAttributes(this,
1194                                                nonVssFilePath);
1195                                       
1196                                        // Update store
1197                                        BackupClientFileAttributes attr;
1198                                        attr.ReadAttributes(filename.c_str(), false /* put mod times in the attributes, please */);
1199                                        MemBlockStream attrStream(attr);
1200                                        connection.QuerySetReplacementFileAttributes(mObjectID, attributesHash, storeFilename, attrStream);
1201                                        fileSynced = true;
1202                                }
1203                                catch (BoxException &e)
1204                                {
1205                                        BOX_ERROR("Failed to read or store file attributes " 
1206                                                "for '" << nonVssFilePath << "', will try again "
1207                                                "later");
1208                                }
1209                        }
1210                }
1211
1212                if(modTime >= rParams.mSyncPeriodEnd)
1213                {
1214                        // Allocate?
1215                        if(mpPendingEntries == 0)
1216                        {
1217                                mpPendingEntries = new std::map<std::string, box_time_t>;
1218                        }
1219                        // Adding to mPendingEntries list
1220                        if(pendingFirstSeenTime == 0)
1221                        {
1222                                // Haven't seen this before -- add to list!
1223                                (*mpPendingEntries)[*f] = modTime;
1224                        }
1225                }
1226               
1227                // Zero pointer in rEntriesLeftOver, if we have a pointer to zero
1228                if(en != 0)
1229                {
1230                        for(unsigned int l = 0; l < rEntriesLeftOver.size(); ++l)
1231                        {
1232                                if(rEntriesLeftOver[l] == en)
1233                                {
1234                                        rEntriesLeftOver[l] = 0;
1235                                        break;
1236                                }
1237                        }
1238                }
1239               
1240                // Does this file need an entry in the ID map?
1241                if(fileSize >= rParams.mFileTrackingSizeThreshold)
1242                {
1243                        // Get the map
1244                        BackupClientInodeToIDMap &idMap(rContext.GetNewIDMap());
1245               
1246                        // Need to get an ID from somewhere...
1247                        if(latestObjectID != 0)
1248                        {
1249                                // Use this one
1250                                BOX_TRACE("Storing uploaded file ID " <<
1251                                        inodeNum << " (" << nonVssFilePath << ") "
1252                                        "in ID map as object " <<
1253                                        latestObjectID << " with parent " <<
1254                                        mObjectID);
1255                                idMap.AddToMap(inodeNum, latestObjectID, mObjectID /* containing directory */);
1256                        }
1257                        else
1258                        {
1259                                // Don't know it -- haven't sent anything to the store, and didn't get a listing.
1260                                // Look it up in the current map, and if it's there, use that.
1261                                const BackupClientInodeToIDMap &currentIDMap(rContext.GetCurrentIDMap());
1262                                int64_t objid = 0, dirid = 0;
1263                                if(currentIDMap.Lookup(inodeNum, objid, dirid))
1264                                {
1265                                        // Found
1266                                        if (dirid != mObjectID)
1267                                        {
1268                                                BOX_WARNING("Found conflicting parent ID for "
1269                                                        "file ID " << inodeNum << " (" <<
1270                                                        nonVssFilePath << "): expected " <<
1271                                                        mObjectID << " but found " << dirid <<
1272                                                        " (same directory used in two different "
1273                                                        "locations?)");
1274                                        }
1275
1276                                        ASSERT(dirid == mObjectID);
1277
1278                                        // NOTE: If the above assert fails, an inode number has been reused by the OS,
1279                                        // or there is a problem somewhere. If this happened on a short test run, look
1280                                        // into it. However, in a long running process this may happen occasionally and
1281                                        // not indicate anything wrong.
1282                                        // Run the release version for real life use, where this check is not made.
1283                                        BOX_TRACE("Storing found file ID " << inodeNum <<
1284                                                " (" << nonVssFilePath << ") in ID map as "
1285                                                "object " << objid << " with parent " << mObjectID);
1286                                        idMap.AddToMap(inodeNum, objid,
1287                                                mObjectID /* containing directory */);
1288                                }
1289                        }
1290                }
1291               
1292                if (fileSynced)
1293                {
1294                        rNotifier.NotifyFileSynchronised(this, nonVssFilePath,
1295                                fileSize);
1296                }
1297        }
1298
1299        // Erase contents of files to save space when recursing
1300        rFiles.clear();
1301
1302        // Delete the pending entries, if the map is entry
1303        if(mpPendingEntries != 0 && mpPendingEntries->size() == 0)
1304        {
1305                BOX_TRACE("Deleting mpPendingEntries from dir ID " <<
1306                        BOX_FORMAT_OBJECTID(mObjectID));
1307                delete mpPendingEntries;
1308                mpPendingEntries = 0;
1309        }
1310       
1311        // Do directories
1312        for(std::vector<std::string>::const_iterator d = rDirs.begin();
1313                d != rDirs.end(); ++d)
1314        {
1315                // Send keep-alive message if needed
1316                rContext.DoKeepAlive();
1317               
1318                // Get the local filename
1319                std::string dirname(MakeFullPath(rLocalPath, *d));
1320                std::string nonVssDirPath = ConvertVssPathToRealPath(dirname,
1321                        rBackupLocation);
1322       
1323                // See if it's in the listing (if we have one)
1324                BackupStoreFilenameClear storeFilename(*d);
1325                BackupStoreDirectory::Entry *en = 0;
1326                if(pDirOnStore != 0)
1327                {
1328                        DecryptedEntriesMap_t::iterator i(decryptedEntries.find(*d));
1329                        if(i != decryptedEntries.end())
1330                        {
1331                                en = i->second;
1332                        }
1333                }
1334               
1335                // Check that the entry which might have been found is in fact a directory
1336                if((en != 0) && !(en->IsDir()))
1337                {
1338                        // Entry exists, but is not a directory. Bad.
1339                        // Get rid of it.
1340                        BackupProtocolClient &connection(rContext.GetConnection());
1341                        connection.QueryDeleteFile(mObjectID /* in directory */, storeFilename);
1342
1343                        std::string filenameClear = DecryptFilename(en,
1344                                rRemotePath);
1345                        rNotifier.NotifyFileDeleted(en->GetObjectID(),
1346                                filenameClear);
1347                       
1348                        // Nothing found
1349                        en = 0;
1350                }
1351
1352                // Flag for having created directory, so can optimise the
1353                // recursive call not to read it again, because we know
1354                // it's empty.
1355                bool haveJustCreatedDirOnServer = false;
1356
1357                // Next, see if it's in the list of sub directories
1358                BackupClientDirectoryRecord *psubDirRecord = 0;
1359                std::map<std::string, BackupClientDirectoryRecord *>::iterator
1360                        e(mSubDirectories.find(*d));
1361
1362                if(e != mSubDirectories.end())
1363                {
1364                        // In the list, just use this pointer
1365                        psubDirRecord = e->second;
1366                }
1367                else 
1368                {
1369                        // Note: if we have exceeded our storage limit, then
1370                        // we should not upload any more data, nor create any
1371                        // DirectoryRecord representing data that would have
1372                        // been uploaded. This step will be repeated when
1373                        // there is some space available.
1374                        bool doCreateDirectoryRecord = true;
1375                       
1376                        // Need to create the record. But do we need to create the directory on the server?
1377                        int64_t subDirObjectID = 0;
1378                        if(en != 0)
1379                        {
1380                                // No. Exists on the server, and we know about it from the listing.
1381                                subDirObjectID = en->GetObjectID();
1382                        }
1383                        else if(rContext.StorageLimitExceeded())       
1384                        // know we've got a connection if we get this far,
1385                        // as dir will have been modified.
1386                        {
1387                                doCreateDirectoryRecord = false;
1388                        }                               
1389                        else
1390                        {
1391                                // Yes, creation required!
1392                                // It is known that the it doesn't exist:
1393                                //   if pDirOnStore == 0, then the directory has had an initial sync, and hasn't been modified.
1394                                //       so it has definately been created already.
1395                                //   if en == 0 but pDirOnStore != 0, well... obviously it doesn't exist.
1396
1397                                // Get attributes
1398                                box_time_t attrModTime = 0;
1399                                InodeRefType inodeNum = 0;
1400                                BackupClientFileAttributes attr;
1401                                bool failedToReadAttributes = false;
1402
1403                                try
1404                                {
1405                                        attr.ReadAttributes(dirname.c_str(),
1406                                                true /* directories have zero mod times */,
1407                                                0 /* not interested in mod time */,
1408                                                &attrModTime, 0 /* not file size */,
1409                                                &inodeNum);
1410                                }
1411                                catch (BoxException &e)
1412                                {
1413                                        BOX_WARNING("Failed to read attributes "
1414                                                "of directory, cannot check "
1415                                                "for rename, assuming new: '"
1416                                                << nonVssDirPath << "'");
1417                                        failedToReadAttributes = true;
1418                                }
1419
1420                                // Check to see if the directory been renamed
1421                                // First, do we have a record in the ID map?
1422                                int64_t renameObjectID = 0, renameInDirectory = 0;
1423                                bool renameDir = false;
1424                                const BackupClientInodeToIDMap &idMap(
1425                                        rContext.GetCurrentIDMap());
1426
1427                                if(!failedToReadAttributes && idMap.Lookup(inodeNum,
1428                                        renameObjectID, renameInDirectory))
1429                                {
1430                                        // Look up on the server to get the name, to build the local filename
1431                                        std::string localPotentialOldName;
1432                                        bool isDir = false;
1433                                        bool isCurrentVersion = false;
1434                                        if(rContext.FindFilename(renameObjectID,
1435                                                renameInDirectory, localPotentialOldName,
1436                                                isDir, isCurrentVersion))
1437                                        {       
1438                                                // Only interested if it's a directory
1439                                                if(isDir && isCurrentVersion)
1440                                                {
1441                                                        // Check that the object doesn't exist already
1442                                                        EMU_STRUCT_STAT st;
1443                                                        if(EMU_STAT(localPotentialOldName.c_str(), &st) != 0 && errno == ENOENT)
1444                                                        {
1445                                                                // Doesn't exist locally, but does exist on the server.
1446                                                                // Therefore we can safely rename it.
1447                                                                renameDir = true;
1448                                                        }
1449                                                }
1450                                        }
1451                                }
1452
1453                                // Get connection
1454                                BackupProtocolClient &connection(rContext.GetConnection());
1455                               
1456                                // Don't do a check for storage limit exceeded here, because if we get to this
1457                                // stage, a connection will have been opened, and the status known, so the check
1458                                // in the else if(...) above will be correct.
1459
1460                                // Build attribute stream for sending
1461                                MemBlockStream attrStream(attr);
1462
1463                                if(renameDir)
1464                                {
1465                                        // Rename the existing directory on the server
1466                                        connection.QueryMoveObject(renameObjectID,
1467                                                renameInDirectory,
1468                                                mObjectID /* move to this directory */,
1469                                                BackupProtocolMoveObject::Flags_MoveAllWithSameName | 
1470                                                BackupProtocolMoveObject::Flags_AllowMoveOverDeletedObject,
1471                                                storeFilename);
1472                                               
1473                                        // Put the latest attributes on it
1474                                        connection.QueryChangeDirAttributes(renameObjectID, attrModTime, attrStream);
1475
1476                                        // Stop it being deleted later
1477                                        BackupClientDeleteList &rdelList(
1478                                                rContext.GetDeleteList());
1479                                        rdelList.StopDirectoryDeletion(renameObjectID);
1480
1481                                        // This is the ID for the renamed directory
1482                                        subDirObjectID = renameObjectID;
1483                                }
1484                                else
1485                                {
1486                                        // Create a new directory
1487                                        std::auto_ptr<BackupProtocolSuccess> dirCreate(
1488                                                connection.QueryCreateDirectory(
1489                                                        mObjectID, attrModTime,
1490                                                        storeFilename, attrStream));
1491                                        subDirObjectID = dirCreate->GetObjectID(); 
1492                                       
1493                                        // Flag as having done this for optimisation later
1494                                        haveJustCreatedDirOnServer = true;
1495
1496                                        std::string filenameClear = 
1497                                                DecryptFilename(storeFilename,
1498                                                        subDirObjectID, 
1499                                                        rRemotePath);
1500                                        rNotifier.NotifyDirectoryCreated(
1501                                                subDirObjectID, filenameClear,
1502                                                nonVssDirPath);
1503                                }
1504                        }
1505
1506                        if (doCreateDirectoryRecord)
1507                        {                               
1508                                // New an object for this
1509                                psubDirRecord = new BackupClientDirectoryRecord(subDirObjectID, *d);
1510                               
1511                                // Store in list
1512                                try
1513                                {
1514                                        mSubDirectories[*d] = psubDirRecord;
1515                                }
1516                                catch(...)
1517                                {
1518                                        delete psubDirRecord;
1519                                        psubDirRecord = 0;
1520                                        throw;
1521                                }
1522                        }
1523                }
1524               
1525                ASSERT(psubDirRecord != 0 || rContext.StorageLimitExceeded());
1526               
1527                if(psubDirRecord)
1528                {
1529                        // Sync this sub directory too
1530                        psubDirRecord->SyncDirectory(rParams, mObjectID, dirname,
1531                                rRemotePath + "/" + *d, rBackupLocation,
1532                                haveJustCreatedDirOnServer);
1533                }
1534
1535                // Zero pointer in rEntriesLeftOver, if we have a pointer to zero
1536                if(en != 0)
1537                {
1538                        for(unsigned int l = 0; l < rEntriesLeftOver.size(); ++l)
1539                        {
1540                                if(rEntriesLeftOver[l] == en)
1541                                {
1542                                        rEntriesLeftOver[l] = 0;
1543                                        break;
1544                                }
1545                        }
1546                }
1547        }
1548       
1549        // Delete everything which is on the store, but not on disc
1550        for(unsigned int l = 0; l < rEntriesLeftOver.size(); ++l)
1551        {
1552                if(rEntriesLeftOver[l] != 0)
1553                {
1554                        BackupStoreDirectory::Entry *en = rEntriesLeftOver[l];
1555               
1556                        // These entries can't be deleted immediately, as it would prevent
1557                        // renaming and moving of objects working properly. So we add them
1558                        // to a list, which is actually deleted at the very end of the session.
1559                        // If there's an error during the process, it doesn't matter if things
1560                        // aren't actually deleted, as the whole state will be reset anyway.
1561                        BackupClientDeleteList &rdel(rContext.GetDeleteList());
1562                        std::string filenameClear;
1563                        bool isCorruptFilename = false;
1564
1565                        try
1566                        {
1567                                filenameClear = DecryptFilename(en,
1568                                        rRemotePath);
1569                        }
1570                        catch (CipherException &e)
1571                        {
1572                                BOX_ERROR("Failed to decrypt a filename, "
1573                                        "scheduling that file for deletion");
1574                                filenameClear = "<corrupt filename>";
1575                                isCorruptFilename = true;
1576                        }
1577
1578                        std::string localName = MakeFullPath(rLocalPath,
1579                                filenameClear);
1580                        std::string nonVssLocalName = ConvertVssPathToRealPath(localName,
1581                                rBackupLocation);
1582                       
1583                        // Delete this entry -- file or directory?
1584                        if((en->GetFlags() & BackupStoreDirectory::Entry::Flags_File) != 0)
1585                        {
1586                                // Set a pending deletion for the file
1587                                rdel.AddFileDelete(mObjectID, en->GetName(),
1588                                        localName);
1589                        }
1590                        else if((en->GetFlags() & BackupStoreDirectory::Entry::Flags_Dir) != 0)
1591                        {
1592                                // Set as a pending deletion for the directory
1593                                rdel.AddDirectoryDelete(en->GetObjectID(),
1594                                        localName);
1595                               
1596                                // If there's a directory record for it in
1597                                // the sub directory map, delete it now
1598                                BackupStoreFilenameClear dirname(en->GetName());
1599                                std::map<std::string, BackupClientDirectoryRecord *>::iterator
1600                                        e(mSubDirectories.find(filenameClear));
1601                                if(e != mSubDirectories.end() && !isCorruptFilename)
1602                                {
1603                                        // Carefully delete the entry from the map
1604                                        BackupClientDirectoryRecord *rec = e->second;
1605                                        mSubDirectories.erase(e);
1606                                        delete rec;
1607
1608                                        BOX_TRACE("Deleted directory record for " << 
1609                                                nonVssLocalName);
1610                                }                               
1611                        }
1612                }
1613        }
1614
1615        // Return success flag (will be false if some files failed)
1616        return allUpdatedSuccessfully;
1617}
1618
1619
1620// --------------------------------------------------------------------------
1621//
1622// Function
1623//              Name:    BackupClientDirectoryRecord::RemoveDirectoryInPlaceOfFile(SyncParams &, BackupStoreDirectory *, int64_t, const std::string &)
1624//              Purpose: Called to resolve difficulties when a directory is found on the
1625//                               store where a file is to be uploaded.
1626//              Created: 9/7/04
1627//
1628// --------------------------------------------------------------------------
1629void BackupClientDirectoryRecord::RemoveDirectoryInPlaceOfFile(
1630        SyncParams &rParams,
1631        BackupStoreDirectory* pDirOnStore,
1632        BackupStoreDirectory::Entry* pEntry,
1633        const std::string &rFilename)
1634{
1635        // First, delete the directory
1636        BackupProtocolClient &connection(rParams.mrContext.GetConnection());
1637        connection.QueryDeleteDirectory(pEntry->GetObjectID());
1638
1639        BackupStoreFilenameClear clear(pEntry->GetName());
1640        rParams.mrContext.GetProgressNotifier().NotifyDirectoryDeleted(
1641                pEntry->GetObjectID(), clear.GetClearFilename());
1642
1643        // Then, delete any directory record
1644        std::map<std::string, BackupClientDirectoryRecord *>::iterator
1645                e(mSubDirectories.find(rFilename));
1646
1647        if(e != mSubDirectories.end())
1648        {
1649                // A record exists for this, remove it
1650                BackupClientDirectoryRecord *psubDirRecord = e->second;
1651                mSubDirectories.erase(e);
1652
1653                // And delete the object
1654                delete psubDirRecord;
1655        }
1656}
1657
1658
1659
1660// --------------------------------------------------------------------------
1661//
1662// Function
1663//              Name:    BackupClientDirectoryRecord::UploadFile(
1664//                       BackupClientDirectoryRecord::SyncParams &,
1665//                       const std::string &,
1666//                       const BackupStoreFilename &,
1667//                       int64_t, box_time_t, box_time_t, bool)
1668//              Purpose: Private. Upload a file to the server. May send
1669//                       a patch instead of the whole thing
1670//              Created: 20/1/04
1671//
1672// --------------------------------------------------------------------------
1673int64_t BackupClientDirectoryRecord::UploadFile(
1674        BackupClientDirectoryRecord::SyncParams &rParams,
1675        const std::string &rFilename,
1676        const std::string &rNonVssFilePath,
1677        const BackupStoreFilename &rStoreFilename,
1678        int64_t FileSize,
1679        box_time_t ModificationTime,
1680        box_time_t AttributesHash,
1681        bool NoPreviousVersionOnServer)
1682{
1683        BackupClientContext& rContext(rParams.mrContext);
1684        ProgressNotifier& rNotifier(rContext.GetProgressNotifier());
1685
1686        // Get the connection
1687        BackupProtocolClient &connection(rContext.GetConnection());
1688
1689        // Info
1690        int64_t objID = 0;
1691        bool doNormalUpload = true;
1692        int64_t uploadedSize = -1;
1693       
1694        // Use a try block to catch store full errors
1695        try
1696        {
1697                // Might an old version be on the server, and is the file
1698                // size over the diffing threshold?
1699                if(!NoPreviousVersionOnServer &&
1700                        FileSize >= rParams.mDiffingUploadSizeThreshold)
1701                {
1702                        // YES -- try to do diff, if possible
1703                        // First, query the server to see if there's an old version available
1704                        std::auto_ptr<BackupProtocolSuccess> getBlockIndex(connection.QueryGetBlockIndexByName(mObjectID, rStoreFilename));
1705                        int64_t diffFromID = getBlockIndex->GetObjectID();
1706                       
1707                        if(diffFromID != 0)
1708                        {
1709                                // Found an old version
1710                                rNotifier.NotifyFileUploadingPatch(this,
1711                                        rNonVssFilePath);
1712
1713                                // Get the index
1714                                std::auto_ptr<IOStream> blockIndexStream(connection.ReceiveStream());
1715                       
1716                                //
1717                                // Diff the file
1718                                //
1719
1720                                rContext.ManageDiffProcess();
1721
1722                                bool isCompletelyDifferent = false;
1723
1724                                std::auto_ptr<IOStream> patchStream(
1725                                        BackupStoreFile::EncodeFileDiff(
1726                                                rFilename.c_str(),
1727                                                mObjectID,      /* containing directory */
1728                                                rStoreFilename, diffFromID, *blockIndexStream,
1729                                                connection.GetTimeout(), 
1730                                                &rContext, // DiffTimer implementation
1731                                                0 /* not interested in the modification time */, 
1732                                                &isCompletelyDifferent));
1733       
1734                                rContext.UnManageDiffProcess();
1735                                rContext.SetNiceMode(true);
1736
1737                                RateLimitingStream rateLimit(*patchStream,
1738                                        rParams.mMaxUploadRate);
1739                                IOStream* pStreamToUpload;
1740
1741                                if(rParams.mMaxUploadRate > 0)
1742                                {
1743                                        pStreamToUpload = &rateLimit;
1744                                }
1745                                else
1746                                {
1747                                        pStreamToUpload = patchStream.get();
1748                                }
1749
1750                                //
1751                                // Upload the patch to the store
1752                                //
1753                                std::auto_ptr<BackupProtocolSuccess> stored(connection.QueryStoreFile(mObjectID, ModificationTime,
1754                                                AttributesHash, isCompletelyDifferent?(0):(diffFromID), rStoreFilename, *pStreamToUpload));
1755
1756                                rContext.SetNiceMode(false);
1757                               
1758                                // Get object ID from the result               
1759                                objID = stored->GetObjectID();
1760
1761                                // Don't attempt to upload it again!
1762                                doNormalUpload = false;
1763
1764                                // Capture number of bytes sent
1765                                uploadedSize = ((BackupStoreFileEncodeStream &)
1766                                        *patchStream).GetTotalBytesSent();
1767                        } 
1768                }
1769       
1770                if(doNormalUpload)
1771                {
1772                        // below threshold or nothing to diff from, so upload whole
1773                        rNotifier.NotifyFileUploading(this, rNonVssFilePath);
1774                       
1775                        // Prepare to upload, getting a stream which will encode the file as we go along
1776                        std::auto_ptr<IOStream> upload(
1777                                BackupStoreFile::EncodeFile(rFilename.c_str(),
1778                                        mObjectID, rStoreFilename, NULL,
1779                                        &rParams,
1780                                        &(rParams.mrRunStatusProvider)));
1781
1782                        rContext.SetNiceMode(true);
1783
1784                        RateLimitingStream rateLimit(*upload,
1785                                rParams.mMaxUploadRate);
1786                        IOStream* pStreamToUpload;
1787
1788                        if(rParams.mMaxUploadRate > 0)
1789                        {
1790                                pStreamToUpload = &rateLimit;
1791                        }
1792                        else
1793                        {
1794                                pStreamToUpload = upload.get();
1795                        }
1796       
1797                        // Send to store
1798                        std::auto_ptr<BackupProtocolSuccess> stored(
1799                                connection.QueryStoreFile(
1800                                        mObjectID, ModificationTime,
1801                                        AttributesHash, 
1802                                        0 /* no diff from file ID */, 
1803                                        rStoreFilename, *pStreamToUpload));
1804
1805                        rContext.SetNiceMode(false);
1806       
1807                        // Get object ID from the result               
1808                        objID = stored->GetObjectID();
1809
1810                        uploadedSize = ((BackupStoreFileEncodeStream &)
1811                                *upload).GetTotalBytesSent();
1812                }
1813        }
1814        catch(BoxException &e)
1815        {
1816                rContext.UnManageDiffProcess();
1817
1818                if(e.GetType() == ConnectionException::ExceptionType &&
1819                        e.GetSubType() == ConnectionException::Protocol_UnexpectedReply)
1820                {
1821                        // Check and see what error the protocol has,
1822                        // this is more useful to users than the exception.
1823                        int type, subtype;
1824                        if(connection.GetLastError(type, subtype))
1825                        {
1826                                if(type == BackupProtocolError::ErrorType
1827                                && subtype == BackupProtocolError::Err_StorageLimitExceeded)
1828                                {
1829                                        // The hard limit was exceeded on the server, notify!
1830                                        rParams.mrSysadminNotifier.NotifySysadmin(
1831                                                SysadminNotifier::StoreFull);
1832                                        // return an error code instead of
1833                                        // throwing an exception that we
1834                                        // can't debug.
1835                                        return 0;
1836                                }
1837                                rNotifier.NotifyFileUploadServerError(this,
1838                                        rNonVssFilePath, type, subtype);
1839                        }
1840                }
1841               
1842                // Send the error on it's way
1843                throw;
1844        }
1845
1846        rNotifier.NotifyFileUploaded(this, rNonVssFilePath, FileSize,
1847                uploadedSize);
1848
1849        // Return the new object ID of this file
1850        return objID;
1851}
1852
1853
1854// --------------------------------------------------------------------------
1855//
1856// Function
1857//              Name:    BackupClientDirectoryRecord::SetErrorWhenReadingFilesystemObject(SyncParams &, const char *)
1858//              Purpose: Sets the error state when there were problems reading an object
1859//                               from the filesystem.
1860//              Created: 29/3/04
1861//
1862// --------------------------------------------------------------------------
1863void BackupClientDirectoryRecord::SetErrorWhenReadingFilesystemObject(
1864        BackupClientDirectoryRecord::SyncParams &rParams,
1865        const std::string& rFilename)
1866{
1867        // Zero hash, so it gets synced properly next time round.
1868        ::memset(mStateChecksum, 0, sizeof(mStateChecksum));
1869
1870        // Log the error - already done by caller
1871        /*
1872        rParams.GetProgressNotifier().NotifyFileReadFailed(this,
1873                Filename, strerror(errno));
1874        */
1875
1876        // Mark that an error occured in the parameters object
1877        rParams.mReadErrorsOnFilesystemObjects = true;
1878}
1879
1880
1881
1882// --------------------------------------------------------------------------
1883//
1884// Function
1885//              Name:    BackupClientDirectoryRecord::SyncParams::SyncParams(BackupClientContext &)
1886//              Purpose: Constructor
1887//              Created: 8/3/04
1888//
1889// --------------------------------------------------------------------------
1890BackupClientDirectoryRecord::SyncParams::SyncParams(
1891        RunStatusProvider &rRunStatusProvider,
1892        SysadminNotifier &rSysadminNotifier,
1893        ProgressNotifier &rProgressNotifier,
1894        BackupClientContext &rContext)
1895: mSyncPeriodStart(0),
1896  mSyncPeriodEnd(0),
1897  mMaxUploadWait(0),
1898  mMaxFileTimeInFuture(99999999999999999LL),
1899  mFileTrackingSizeThreshold(16*1024),
1900  mDiffingUploadSizeThreshold(16*1024),
1901  mrRunStatusProvider(rRunStatusProvider),
1902  mrSysadminNotifier(rSysadminNotifier),
1903  mrProgressNotifier(rProgressNotifier),
1904  mrContext(rContext),
1905  mReadErrorsOnFilesystemObjects(false),
1906  mMaxUploadRate(0),
1907  mUploadAfterThisTimeInTheFuture(99999999999999999LL),
1908  mHaveLoggedWarningAboutFutureFileTimes(false)
1909{
1910}
1911
1912
1913// --------------------------------------------------------------------------
1914//
1915// Function
1916//              Name:    BackupClientDirectoryRecord::SyncParams::~SyncParams()
1917//              Purpose: Destructor
1918//              Created: 8/3/04
1919//
1920// --------------------------------------------------------------------------
1921BackupClientDirectoryRecord::SyncParams::~SyncParams()
1922{
1923}
1924
1925// --------------------------------------------------------------------------
1926//
1927// Function
1928//              Name:    BackupClientDirectoryRecord::Deserialize(Archive & rArchive)
1929//              Purpose: Deserializes this object instance from a stream of bytes, using an Archive abstraction.
1930//
1931//              Created: 2005/04/11
1932//
1933// --------------------------------------------------------------------------
1934void BackupClientDirectoryRecord::Deserialize(Archive & rArchive)
1935{
1936        // Make deletion recursive
1937        DeleteSubDirectories();
1938
1939        // Delete maps
1940        if(mpPendingEntries != 0)
1941        {
1942                delete mpPendingEntries;
1943                mpPendingEntries = 0;
1944        }
1945
1946        //
1947        //
1948        //
1949        rArchive.Read(mObjectID);
1950        rArchive.Read(mSubDirName);
1951        rArchive.Read(mInitialSyncDone);
1952        rArchive.Read(mSyncDone);
1953
1954        //
1955        //
1956        //
1957        int64_t iCount = 0;
1958        rArchive.Read(iCount);
1959
1960        if (iCount != sizeof(mStateChecksum)/sizeof(mStateChecksum[0]))
1961        {
1962                // we have some kind of internal system representation change: throw for now
1963                THROW_EXCEPTION(CommonException, Internal)
1964        }
1965
1966        for (int v = 0; v < iCount; v++)
1967        {
1968                // Load each checksum entry
1969                rArchive.Read(mStateChecksum[v]);
1970        }
1971
1972        //
1973        //
1974        //
1975        iCount = 0;
1976        rArchive.Read(iCount);
1977
1978        if (iCount > 0)
1979        {
1980                // load each pending entry
1981                mpPendingEntries = new std::map<std::string, box_time_t>;
1982                if (!mpPendingEntries)
1983                {
1984                        throw std::bad_alloc();
1985                }
1986
1987                for (int v = 0; v < iCount; v++)
1988                {
1989                        std::string strItem;
1990                        box_time_t btItem;
1991
1992                        rArchive.Read(strItem);
1993                        rArchive.Read(btItem);
1994                        (*mpPendingEntries)[strItem] = btItem;
1995                }
1996        }
1997
1998        //
1999        //
2000        //
2001        iCount = 0;
2002        rArchive.Read(iCount);
2003
2004        if (iCount > 0)
2005        {
2006                for (int v = 0; v < iCount; v++)
2007                {
2008                        std::string strItem;
2009                        rArchive.Read(strItem);
2010
2011                        BackupClientDirectoryRecord* pSubDirRecord = 
2012                                new BackupClientDirectoryRecord(0, ""); 
2013                        // will be deserialized anyway, give it id 0 for now
2014
2015                        if (!pSubDirRecord)
2016                        {
2017                                throw std::bad_alloc();
2018                        }
2019
2020                        /***** RECURSE *****/
2021                        pSubDirRecord->Deserialize(rArchive);
2022                        mSubDirectories[strItem] = pSubDirRecord;
2023                }
2024        }
2025}
2026
2027// --------------------------------------------------------------------------
2028//
2029// Function
2030//              Name:    BackupClientDirectoryRecord::Serialize(Archive & rArchive)
2031//              Purpose: Serializes this object instance into a stream of bytes, using an Archive abstraction.
2032//
2033//              Created: 2005/04/11
2034//
2035// --------------------------------------------------------------------------
2036void BackupClientDirectoryRecord::Serialize(Archive & rArchive) const
2037{
2038        //
2039        //
2040        //
2041        rArchive.Write(mObjectID);
2042        rArchive.Write(mSubDirName);
2043        rArchive.Write(mInitialSyncDone);
2044        rArchive.Write(mSyncDone);
2045
2046        //
2047        //
2048        //
2049        int64_t iCount = 0;
2050
2051        // when reading back the archive, we will
2052        // need to know how many items there are.
2053        iCount = sizeof(mStateChecksum) / sizeof(mStateChecksum[0]);
2054        rArchive.Write(iCount); 
2055
2056        for (int v = 0; v < iCount; v++)
2057        {
2058                rArchive.Write(mStateChecksum[v]);
2059        }
2060
2061        //
2062        //
2063        //
2064        if (!mpPendingEntries)
2065        {
2066                iCount = 0;
2067                rArchive.Write(iCount);
2068        }
2069        else
2070        {
2071                iCount = mpPendingEntries->size();
2072                rArchive.Write(iCount);
2073
2074                for (std::map<std::string, box_time_t>::const_iterator
2075                        i =  mpPendingEntries->begin(); 
2076                        i != mpPendingEntries->end(); i++)
2077                {
2078                        rArchive.Write(i->first);
2079                        rArchive.Write(i->second);
2080                }
2081        }
2082        //
2083        //
2084        //
2085        iCount = mSubDirectories.size();
2086        rArchive.Write(iCount);
2087
2088        for (std::map<std::string, BackupClientDirectoryRecord*>::const_iterator
2089                i =  mSubDirectories.begin(); 
2090                i != mSubDirectories.end(); i++)
2091        {
2092                const BackupClientDirectoryRecord* pSubItem = i->second;
2093                ASSERT(pSubItem);
2094
2095                rArchive.Write(i->first);
2096                pSubItem->Serialize(rArchive);
2097        }
2098}
2099
2100// --------------------------------------------------------------------------
2101//
2102// Function
2103//              Name:    Location::Location()
2104//              Purpose: Constructor
2105//              Created: 11/11/03
2106//
2107// --------------------------------------------------------------------------
2108Location::Location()
2109        : mIDMapIndex(0),
2110          mpExcludeFiles(0),
2111          mpExcludeDirs(0)
2112{
2113}
2114
2115// --------------------------------------------------------------------------
2116//
2117// Function
2118//              Name:    Location::~Location()
2119//              Purpose: Destructor
2120//              Created: 11/11/03
2121//
2122// --------------------------------------------------------------------------
2123Location::~Location()
2124{
2125        // Clean up exclude locations
2126        if(mpExcludeDirs != 0)
2127        {
2128                delete mpExcludeDirs;
2129                mpExcludeDirs = 0;
2130        }
2131        if(mpExcludeFiles != 0)
2132        {
2133                delete mpExcludeFiles;
2134                mpExcludeFiles = 0;
2135        }
2136}
2137
2138// --------------------------------------------------------------------------
2139//
2140// Function
2141//              Name:    Location::Serialize(Archive & rArchive)
2142//              Purpose: Serializes this object instance into a stream of bytes,
2143//               using an Archive abstraction.
2144//
2145//              Created: 2005/04/11
2146//
2147// --------------------------------------------------------------------------
2148void Location::Serialize(Archive & rArchive) const
2149{
2150        //
2151        //
2152        //
2153        rArchive.Write(mName);
2154        rArchive.Write(mPath);
2155        rArchive.Write(mIDMapIndex);
2156
2157        //
2158        //
2159        //
2160        if(mpDirectoryRecord.get() == NULL)
2161        {
2162                int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_NOOP;
2163                rArchive.Write(aMagicMarker);
2164        }
2165        else
2166        {
2167                int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_RECURSE; // be explicit about whether recursion follows
2168                rArchive.Write(aMagicMarker);
2169
2170                mpDirectoryRecord->Serialize(rArchive);
2171        }
2172
2173        //
2174        //
2175        //
2176        if(!mpExcludeFiles)
2177        {
2178                int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_NOOP;
2179                rArchive.Write(aMagicMarker);
2180        }
2181        else
2182        {
2183                int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_RECURSE; // be explicit about whether recursion follows
2184                rArchive.Write(aMagicMarker);
2185
2186                mpExcludeFiles->Serialize(rArchive);
2187        }
2188
2189        //
2190        //
2191        //
2192        if(!mpExcludeDirs)
2193        {
2194                int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_NOOP;
2195                rArchive.Write(aMagicMarker);
2196        }
2197        else
2198        {
2199                int64_t aMagicMarker = ARCHIVE_MAGIC_VALUE_RECURSE; // be explicit about whether recursion follows
2200                rArchive.Write(aMagicMarker);
2201
2202                mpExcludeDirs->Serialize(rArchive);
2203        }
2204}
2205
2206// --------------------------------------------------------------------------
2207//
2208// Function
2209//              Name:    Location::Deserialize(Archive & rArchive)
2210//              Purpose: Deserializes this object instance from a stream of bytes, using an Archive abstraction.
2211//
2212//              Created: 2005/04/11
2213//
2214// --------------------------------------------------------------------------
2215void Location::Deserialize(Archive &rArchive)
2216{
2217        //
2218        //
2219        //
2220        mpDirectoryRecord.reset(NULL);
2221        if(mpExcludeFiles)
2222        {
2223                delete mpExcludeFiles;
2224                mpExcludeFiles = NULL;
2225        }
2226        if(mpExcludeDirs)
2227        {
2228                delete mpExcludeDirs;
2229                mpExcludeDirs = NULL;
2230        }
2231
2232        //
2233        //
2234        //
2235        rArchive.Read(mName);
2236        rArchive.Read(mPath);
2237        rArchive.Read(mIDMapIndex);
2238
2239        //
2240        //
2241        //
2242        int64_t aMagicMarker = 0;
2243        rArchive.Read(aMagicMarker);
2244
2245        if(aMagicMarker == ARCHIVE_MAGIC_VALUE_NOOP)
2246        {
2247                // NOOP
2248        }
2249        else if(aMagicMarker == ARCHIVE_MAGIC_VALUE_RECURSE)
2250        {
2251                BackupClientDirectoryRecord *pSubRecord = new BackupClientDirectoryRecord(0, "");
2252                if(!pSubRecord)
2253                {
2254                        throw std::bad_alloc();
2255                }
2256
2257                mpDirectoryRecord.reset(pSubRecord);
2258                mpDirectoryRecord->Deserialize(rArchive);
2259        }
2260        else
2261        {
2262                // there is something going on here
2263                THROW_EXCEPTION(ClientException, CorruptStoreObjectInfoFile);
2264        }
2265
2266        //
2267        //
2268        //
2269        rArchive.Read(aMagicMarker);
2270
2271        if(aMagicMarker == ARCHIVE_MAGIC_VALUE_NOOP)
2272        {
2273                // NOOP
2274        }
2275        else if(aMagicMarker == ARCHIVE_MAGIC_VALUE_RECURSE)
2276        {
2277                mpExcludeFiles = new ExcludeList;
2278                if(!mpExcludeFiles)
2279                {
2280                        throw std::bad_alloc();
2281                }
2282
2283                mpExcludeFiles->Deserialize(rArchive);
2284        }
2285        else
2286        {
2287                // there is something going on here
2288                THROW_EXCEPTION(ClientException, CorruptStoreObjectInfoFile);
2289        }
2290
2291        //
2292        //
2293        //
2294        rArchive.Read(aMagicMarker);
2295
2296        if(aMagicMarker == ARCHIVE_MAGIC_VALUE_NOOP)
2297        {
2298                // NOOP
2299        }
2300        else if(aMagicMarker == ARCHIVE_MAGIC_VALUE_RECURSE)
2301        {
2302                mpExcludeDirs = new ExcludeList;
2303                if(!mpExcludeDirs)
2304                {
2305                        throw std::bad_alloc();
2306                }
2307
2308                mpExcludeDirs->Deserialize(rArchive);
2309        }
2310        else
2311        {
2312                // there is something going on here
2313                THROW_EXCEPTION(ClientException, CorruptStoreObjectInfoFile);
2314        }
2315}
Note: See TracBrowser for help on using the repository browser.