source: box/trunk/lib/backupstore/BackupStoreContext.cpp @ 3049

Revision 3049, 50.9 KB checked in by chris, 5 months ago (diff)

Add remote host and port to post-login login message, requested by Pete Jalajas.

  • Property svn:eol-style set to native
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    BackupStoreContext.cpp
5//              Purpose: Context for backup store server
6//              Created: 2003/08/20
7//
8// --------------------------------------------------------------------------
9
10#include "Box.h"
11
12#include <stdio.h>
13
14#include "BackupConstants.h"
15#include "BackupStoreContext.h"
16#include "BackupStoreDirectory.h"
17#include "BackupStoreException.h"
18#include "BackupStoreFile.h"
19#include "BackupStoreInfo.h"
20#include "BackupStoreObjectMagic.h"
21#include "BufferedStream.h"
22#include "BufferedWriteStream.h"
23#include "FileStream.h"
24#include "InvisibleTempFileStream.h"
25#include "RaidFileController.h"
26#include "RaidFileRead.h"
27#include "RaidFileWrite.h"
28#include "StoreStructure.h"
29
30#include "MemLeakFindOn.h"
31
32
33// Maximum number of directories to keep in the cache
34// When the cache is bigger than this, everything gets
35// deleted.
36#ifdef BOX_RELEASE_BUILD
37        #define MAX_CACHE_SIZE  32
38#else
39        #define MAX_CACHE_SIZE  2
40#endif
41
42// Allow the housekeeping process 4 seconds to release an account
43#define MAX_WAIT_FOR_HOUSEKEEPING_TO_RELEASE_ACCOUNT    4
44
45// Maximum amount of store info updates before it's actually saved to disc.
46#define STORE_INFO_SAVE_DELAY   96
47
48// --------------------------------------------------------------------------
49//
50// Function
51//              Name:    BackupStoreContext::BackupStoreContext()
52//              Purpose: Constructor
53//              Created: 2003/08/20
54//
55// --------------------------------------------------------------------------
56BackupStoreContext::BackupStoreContext(int32_t ClientID,
57        HousekeepingInterface &rDaemon, const std::string& rConnectionDetails)
58        : mConnectionDetails(rConnectionDetails),
59          mClientID(ClientID),
60          mrDaemon(rDaemon),
61          mProtocolPhase(Phase_START),
62          mClientHasAccount(false),
63          mStoreDiscSet(-1),
64          mReadOnly(true),
65          mSaveStoreInfoDelay(STORE_INFO_SAVE_DELAY),
66          mpTestHook(NULL)
67{
68}
69
70// --------------------------------------------------------------------------
71//
72// Function
73//              Name:    BackupStoreContext::~BackupStoreContext()
74//              Purpose: Destructor
75//              Created: 2003/08/20
76//
77// --------------------------------------------------------------------------
78BackupStoreContext::~BackupStoreContext()
79{
80        // Delete the objects in the cache
81        for(std::map<int64_t, BackupStoreDirectory*>::iterator i(mDirectoryCache.begin()); i != mDirectoryCache.end(); ++i)
82        {
83                delete (i->second);
84        }
85}
86
87
88// --------------------------------------------------------------------------
89//
90// Function
91//              Name:    BackupStoreContext::CleanUp()
92//              Purpose: Clean up after a connection
93//              Created: 16/12/03
94//
95// --------------------------------------------------------------------------
96void BackupStoreContext::CleanUp()
97{
98        // Make sure the store info is saved, if it has been loaded, isn't read only and has been modified
99        if(mapStoreInfo.get() && !(mapStoreInfo->IsReadOnly()) &&
100                mapStoreInfo->IsModified())
101        {
102                mapStoreInfo->Save();
103        }
104}
105
106// --------------------------------------------------------------------------
107//
108// Function
109//              Name:    BackupStoreContext::ReceivedFinishCommand()
110//              Purpose: Called when the finish command is received by the protocol
111//              Created: 16/12/03
112//
113// --------------------------------------------------------------------------
114void BackupStoreContext::ReceivedFinishCommand()
115{
116        if(!mReadOnly && mapStoreInfo.get())
117        {
118                // Save the store info, not delayed
119                SaveStoreInfo(false);
120        }
121}
122
123
124// --------------------------------------------------------------------------
125//
126// Function
127//              Name:    BackupStoreContext::AttemptToGetWriteLock()
128//              Purpose: Attempt to get a write lock for the store, and if so, unset the read only flags
129//              Created: 2003/09/02
130//
131// --------------------------------------------------------------------------
132bool BackupStoreContext::AttemptToGetWriteLock()
133{
134        // Make the filename of the write lock file
135        std::string writeLockFile;
136        StoreStructure::MakeWriteLockFilename(mStoreRoot, mStoreDiscSet, writeLockFile);
137
138        // Request the lock
139        bool gotLock = mWriteLock.TryAndGetLock(writeLockFile.c_str(), 0600 /* restrictive file permissions */);
140       
141        if(!gotLock)
142        {
143                // The housekeeping process might have the thing open -- ask it to stop
144                char msg[256];
145                int msgLen = sprintf(msg, "r%x\n", mClientID);
146                // Send message
147                mrDaemon.SendMessageToHousekeepingProcess(msg, msgLen);
148               
149                // Then try again a few times
150                int tries = MAX_WAIT_FOR_HOUSEKEEPING_TO_RELEASE_ACCOUNT;
151                do
152                {
153                        ::sleep(1 /* second */);
154                        --tries;
155                        gotLock = mWriteLock.TryAndGetLock(writeLockFile.c_str(), 0600 /* restrictive file permissions */);
156                       
157                } while(!gotLock && tries > 0);
158        }
159       
160        if(gotLock)
161        {
162                // Got the lock, mark as not read only
163                mReadOnly = false;
164        }
165       
166        return gotLock;
167}
168
169
170// --------------------------------------------------------------------------
171//
172// Function
173//              Name:    BackupStoreContext::LoadStoreInfo()
174//              Purpose: Load the store info from disc
175//              Created: 2003/09/03
176//
177// --------------------------------------------------------------------------
178void BackupStoreContext::LoadStoreInfo()
179{
180        if(mapStoreInfo.get() != 0)
181        {
182                THROW_EXCEPTION(BackupStoreException, StoreInfoAlreadyLoaded)
183        }
184       
185        // Load it up!
186        std::auto_ptr<BackupStoreInfo> i(BackupStoreInfo::Load(mClientID, mStoreRoot, mStoreDiscSet, mReadOnly));
187       
188        // Check it
189        if(i->GetAccountID() != mClientID)
190        {
191                THROW_EXCEPTION(BackupStoreException, StoreInfoForWrongAccount)
192        }
193       
194        // Keep the pointer to it
195        mapStoreInfo = i;
196
197        BackupStoreAccountDatabase::Entry account(mClientID, mStoreDiscSet);
198
199        // try to load the reference count database
200        try
201        {
202                mapRefCount = BackupStoreRefCountDatabase::Load(account, false);
203        }
204        catch(BoxException &e)
205        {
206                BOX_WARNING("Reference count database is missing or corrupted, "
207                        "creating a new one, expect housekeeping to find and "
208                        "fix problems with reference counts later.");
209               
210                BackupStoreRefCountDatabase::CreateForRegeneration(account);
211                mapRefCount = BackupStoreRefCountDatabase::Load(account, false);
212        }
213}
214
215
216// --------------------------------------------------------------------------
217//
218// Function
219//              Name:    BackupStoreContext::SaveStoreInfo(bool)
220//              Purpose: Potentially delayed saving of the store info
221//              Created: 16/12/03
222//
223// --------------------------------------------------------------------------
224void BackupStoreContext::SaveStoreInfo(bool AllowDelay)
225{
226        if(mapStoreInfo.get() == 0)
227        {
228                THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded)
229        }
230        if(mReadOnly)
231        {
232                THROW_EXCEPTION(BackupStoreException, ContextIsReadOnly)
233        }
234
235        // Can delay saving it a little while?
236        if(AllowDelay)
237        {
238                --mSaveStoreInfoDelay;
239                if(mSaveStoreInfoDelay > 0)
240                {
241                        return;
242                }
243        }
244
245        // Want to save now     
246        mapStoreInfo->Save();
247
248        // Set count for next delay
249        mSaveStoreInfoDelay = STORE_INFO_SAVE_DELAY;
250}
251
252
253
254// --------------------------------------------------------------------------
255//
256// Function
257//              Name:    BackupStoreContext::MakeObjectFilename(int64_t, std::string &, bool)
258//              Purpose: Create the filename of an object in the store, optionally creating the
259//                               containing directory if it doesn't already exist.
260//              Created: 2003/09/02
261//
262// --------------------------------------------------------------------------
263void BackupStoreContext::MakeObjectFilename(int64_t ObjectID, std::string &rOutput, bool EnsureDirectoryExists)
264{
265        // Delegate to utility function
266        StoreStructure::MakeObjectFilename(ObjectID, mStoreRoot, mStoreDiscSet, rOutput, EnsureDirectoryExists);
267}
268
269
270// --------------------------------------------------------------------------
271//
272// Function
273//              Name:    BackupStoreContext::GetDirectoryInternal(int64_t)
274//              Purpose: Return a reference to a directory. Valid only until the
275//                               next time a function which affects directories is called.
276//                               Mainly this funciton, and creation of files.
277//                               Private version of this, which returns non-const directories.
278//              Created: 2003/09/02
279//
280// --------------------------------------------------------------------------
281BackupStoreDirectory &BackupStoreContext::GetDirectoryInternal(int64_t ObjectID)
282{
283        // Get the filename
284        std::string filename;
285        MakeObjectFilename(ObjectID, filename);
286       
287        // Already in cache?
288        std::map<int64_t, BackupStoreDirectory*>::iterator item(mDirectoryCache.find(ObjectID));
289        if(item != mDirectoryCache.end())
290        {
291                // Check the revision ID of the file -- does it need refreshing?
292                int64_t revID = 0;
293                if(!RaidFileRead::FileExists(mStoreDiscSet, filename, &revID))
294                {
295                        THROW_EXCEPTION(BackupStoreException, DirectoryHasBeenDeleted)
296                }
297       
298                if(revID == item->second->GetRevisionID())
299                {
300                        // Looks good... return the cached object
301                        BOX_TRACE("Returning object " <<
302                                BOX_FORMAT_OBJECTID(ObjectID) <<
303                                " from cache, modtime = " << revID);
304                        return *(item->second);
305                }
306               
307                BOX_TRACE("Refreshing object " <<
308                        BOX_FORMAT_OBJECTID(ObjectID) <<
309                        " in cache, modtime changed from " <<
310                        item->second->GetRevisionID() << " to " << revID);
311
312                // Delete this cached object
313                delete item->second;
314                mDirectoryCache.erase(item);
315        }
316       
317        // Need to load it up
318       
319        // First check to see if the cache is too big
320        if(mDirectoryCache.size() > MAX_CACHE_SIZE)
321        {
322                // Very simple. Just delete everything!
323                for(std::map<int64_t, BackupStoreDirectory*>::iterator i(mDirectoryCache.begin()); i != mDirectoryCache.end(); ++i)
324                {
325                        delete (i->second);
326                }
327                mDirectoryCache.clear();
328        }
329
330        // Get a RaidFileRead to read it
331        int64_t revID = 0;
332        std::auto_ptr<RaidFileRead> objectFile(RaidFileRead::Open(mStoreDiscSet, filename, &revID));
333        ASSERT(revID != 0);
334       
335        // New directory object
336        std::auto_ptr<BackupStoreDirectory> dir(new BackupStoreDirectory);
337       
338        // Read it from the stream, then set it's revision ID
339        BufferedStream buf(*objectFile);
340        dir->ReadFromStream(buf, IOStream::TimeOutInfinite);
341        dir->SetRevisionID(revID);
342                       
343        // Make sure the size of the directory is available for writing the dir back
344        int64_t dirSize = objectFile->GetDiscUsageInBlocks();
345        ASSERT(dirSize > 0);
346        dir->SetUserInfo1_SizeInBlocks(dirSize);
347
348        // Store in cache
349        BackupStoreDirectory *pdir = dir.release();
350        try
351        {       
352                mDirectoryCache[ObjectID] = pdir;
353        }
354        catch(...)
355        {
356                delete pdir;
357                throw;
358        }
359       
360        // Return it
361        return *pdir;
362}
363
364// --------------------------------------------------------------------------
365//
366// Function
367//              Name:    BackupStoreContext::AllocateObjectID()
368//              Purpose: Allocate a new object ID, tolerant of failures to save store info
369//              Created: 16/12/03
370//
371// --------------------------------------------------------------------------
372int64_t BackupStoreContext::AllocateObjectID()
373{
374        if(mapStoreInfo.get() == 0)
375        {
376                THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded)
377        }
378
379        // Given that the store info may not be saved for STORE_INFO_SAVE_DELAY
380        // times after it has been updated, this is a reasonable number of times
381        // to try for finding an unused ID.
382        // (Sizes used in the store info are fixed by the housekeeping process)
383        int retryLimit = (STORE_INFO_SAVE_DELAY * 2);
384       
385        while(retryLimit > 0)
386        {
387                // Attempt to allocate an ID from the store
388                int64_t id = mapStoreInfo->AllocateObjectID();
389               
390                // Generate filename
391                std::string filename;
392                MakeObjectFilename(id, filename);
393                // Check it doesn't exist
394                if(!RaidFileRead::FileExists(mStoreDiscSet, filename))
395                {
396                        // Success!
397                        return id;
398                }
399               
400                // Decrement retry count, and try again
401                --retryLimit;
402               
403                // Mark that the store info should be saved as soon as possible
404                mSaveStoreInfoDelay = 0;
405               
406                BOX_WARNING("When allocating object ID, found that " <<
407                        BOX_FORMAT_OBJECTID(id) << " is already in use");
408        }
409       
410        THROW_EXCEPTION(BackupStoreException, CouldNotFindUnusedIDDuringAllocation)
411}
412
413
414// --------------------------------------------------------------------------
415//
416// Function
417//              Name:    BackupStoreContext::AddFile(IOStream &, int64_t,
418//                       int64_t, int64_t, const BackupStoreFilename &, bool)
419//              Purpose: Add a file to the store, from a given stream, into
420//                       a specified directory. Returns object ID of the new
421//                       file.
422//              Created: 2003/09/03
423//
424// --------------------------------------------------------------------------
425int64_t BackupStoreContext::AddFile(IOStream &rFile, int64_t InDirectory,
426        int64_t ModificationTime, int64_t AttributesHash,
427        int64_t DiffFromFileID, const BackupStoreFilename &rFilename,
428        bool MarkFileWithSameNameAsOldVersions)
429{
430        if(mapStoreInfo.get() == 0)
431        {
432                THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded)
433        }
434        if(mReadOnly)
435        {
436                THROW_EXCEPTION(BackupStoreException, ContextIsReadOnly)
437        }
438       
439        // This is going to be a bit complex to make sure it copes OK
440        // with things going wrong.
441        // The only thing which isn't safe is incrementing the object ID
442        // and keeping the blocks used entirely accurate -- but these
443        // aren't big problems if they go horribly wrong. The sizes will
444        // be corrected the next time the account has a housekeeping run,
445        // and the object ID allocation code is tolerant of missed IDs.
446        // (the info is written lazily, so these are necessary)
447       
448        // Get the directory we want to modify
449        BackupStoreDirectory &dir(GetDirectoryInternal(InDirectory));
450       
451        // Allocate the next ID
452        int64_t id = AllocateObjectID();
453       
454        // Stream the file to disc
455        std::string fn;
456        MakeObjectFilename(id, fn, true /* make sure the directory it's in exists */);
457        int64_t newObjectBlocksUsed = 0;
458        RaidFileWrite *ppreviousVerStoreFile = 0;
459        bool reversedDiffIsCompletelyDifferent = false;
460        int64_t oldVersionNewBlocksUsed = 0;
461        try
462        {
463                RaidFileWrite storeFile(mStoreDiscSet, fn);
464                storeFile.Open(false /* no overwriting */);
465
466                // size adjustment from use of patch in old file
467                int64_t spaceSavedByConversionToPatch = 0;
468
469                // Diff or full file?
470                if(DiffFromFileID == 0)
471                {
472                        // A full file, just store to disc
473                        if(!rFile.CopyStreamTo(storeFile, BACKUP_STORE_TIMEOUT))
474                        {
475                                THROW_EXCEPTION(BackupStoreException, ReadFileFromStreamTimedOut)
476                        }
477                }
478                else
479                {
480                        // Check that the diffed from ID actually exists in the directory
481                        if(dir.FindEntryByID(DiffFromFileID) == 0)
482                        {
483                                THROW_EXCEPTION(BackupStoreException, DiffFromIDNotFoundInDirectory)
484                        }
485               
486                        // Diff file, needs to be recreated.
487                        // Choose a temporary filename.
488                        std::string tempFn(RaidFileController::DiscSetPathToFileSystemPath(mStoreDiscSet, fn + ".difftemp",
489                                1 /* NOT the same disc as the write file, to avoid using lots of space on the same disc unnecessarily */));
490                       
491                        try
492                        {
493                                // Open it twice
494#ifdef WIN32
495                                InvisibleTempFileStream diff(tempFn.c_str(), 
496                                        O_RDWR | O_CREAT | O_BINARY);
497                                InvisibleTempFileStream diff2(tempFn.c_str(), 
498                                        O_RDWR | O_BINARY);
499#else
500                                FileStream diff(tempFn.c_str(), O_RDWR | O_CREAT | O_EXCL);
501                                FileStream diff2(tempFn.c_str(), O_RDONLY);
502
503                                // Unlink it immediately, so it definitely goes away
504                                if(::unlink(tempFn.c_str()) != 0)
505                                {
506                                        THROW_EXCEPTION(CommonException, OSFileError);
507                                }
508#endif
509                               
510                                // Stream the incoming diff to this temporary file
511                                if(!rFile.CopyStreamTo(diff, BACKUP_STORE_TIMEOUT))
512                                {
513                                        THROW_EXCEPTION(BackupStoreException, ReadFileFromStreamTimedOut)
514                                }
515                               
516                                // Verify the diff
517                                diff.Seek(0, IOStream::SeekType_Absolute);
518                                if(!BackupStoreFile::VerifyEncodedFileFormat(diff))
519                                {
520                                        THROW_EXCEPTION(BackupStoreException, AddedFileDoesNotVerify)
521                                }
522
523                                // Seek to beginning of diff file
524                                diff.Seek(0, IOStream::SeekType_Absolute);
525
526                                // Filename of the old version
527                                std::string oldVersionFilename;
528                                MakeObjectFilename(DiffFromFileID, oldVersionFilename, false /* no need to make sure the directory it's in exists */);
529                               
530                                // Reassemble that diff -- open previous file, and combine the patch and file
531                                std::auto_ptr<RaidFileRead> from(RaidFileRead::Open(mStoreDiscSet, oldVersionFilename));
532                                BackupStoreFile::CombineFile(diff, diff2, *from, storeFile);
533
534                                // Then... reverse the patch back (open the from file again, and create a write file to overwrite it)
535                                std::auto_ptr<RaidFileRead> from2(RaidFileRead::Open(mStoreDiscSet, oldVersionFilename));
536                                ppreviousVerStoreFile = new RaidFileWrite(mStoreDiscSet, oldVersionFilename);
537                                ppreviousVerStoreFile->Open(true /* allow overwriting */);
538                                from->Seek(0, IOStream::SeekType_Absolute);
539                                diff.Seek(0, IOStream::SeekType_Absolute);
540                                BackupStoreFile::ReverseDiffFile(diff, *from, *from2, *ppreviousVerStoreFile,
541                                                DiffFromFileID, &reversedDiffIsCompletelyDifferent);
542                               
543                                // Store disc space used
544                                oldVersionNewBlocksUsed = ppreviousVerStoreFile->GetDiscUsageInBlocks();
545                               
546                                // And make a space adjustment for the size calculation
547                                spaceSavedByConversionToPatch =
548                                        from->GetDiscUsageInBlocks() - 
549                                        oldVersionNewBlocksUsed;
550
551                                // Everything cleans up here...
552                        }
553                        catch(...)
554                        {
555                                // Be very paranoid about deleting this temp file -- we could only leave a zero byte file anyway
556                                ::unlink(tempFn.c_str());
557                                throw;
558                        }
559                }
560               
561                // Get the blocks used
562                newObjectBlocksUsed = storeFile.GetDiscUsageInBlocks();
563               
564                // Exceeds the hard limit?
565                int64_t newBlocksUsed = mapStoreInfo->GetBlocksUsed() + 
566                        newObjectBlocksUsed - spaceSavedByConversionToPatch;
567                if(newBlocksUsed > mapStoreInfo->GetBlocksHardLimit())
568                {
569                        THROW_EXCEPTION(BackupStoreException, AddedFileExceedsStorageLimit)
570                        // The store file will be deleted automatically by the RaidFile object
571                }
572
573                // Commit the file
574                storeFile.Commit(BACKUP_STORE_CONVERT_TO_RAID_IMMEDIATELY);
575        }
576        catch(...)
577        {
578                // Delete any previous version store file
579                if(ppreviousVerStoreFile != 0)
580                {
581                        delete ppreviousVerStoreFile;
582                        ppreviousVerStoreFile = 0;
583                }
584               
585                throw;
586        }
587
588        // Verify the file -- only necessary for non-diffed versions
589        // NOTE: No need to catch exceptions and delete ppreviousVerStoreFile, because
590        // in the non-diffed code path it's never allocated.
591        if(DiffFromFileID == 0)
592        {
593                std::auto_ptr<RaidFileRead> checkFile(RaidFileRead::Open(mStoreDiscSet, fn));
594                if(!BackupStoreFile::VerifyEncodedFileFormat(*checkFile))
595                {
596                        // Error! Delete the file
597                        RaidFileWrite del(mStoreDiscSet, fn);
598                        del.Delete();
599                       
600                        // Exception
601                        THROW_EXCEPTION(BackupStoreException, AddedFileDoesNotVerify)
602                }
603        }                       
604       
605        // Modify the directory -- first make all files with the same name
606        // marked as an old version
607        int64_t blocksInOldFiles = 0;
608        try
609        {
610                if(MarkFileWithSameNameAsOldVersions)
611                {
612                        BackupStoreDirectory::Iterator i(dir);
613
614                        BackupStoreDirectory::Entry *e = 0;
615                        while((e = i.Next()) != 0)
616                        {
617                                // First, check it's not an old version (cheaper comparison)
618                                if(! e->IsOld())
619                                {
620                                        // Compare name
621                                        if(e->GetName() == rFilename)
622                                        {
623                                                // Check that it's definately not an old version
624                                                ASSERT((e->GetFlags() & BackupStoreDirectory::Entry::Flags_OldVersion) == 0);
625                                                // Set old version flag
626                                                e->AddFlags(BackupStoreDirectory::Entry::Flags_OldVersion);
627                                                // Can safely do this, because we know we won't be here if it's already
628                                                // an old version
629                                                blocksInOldFiles += e->GetSizeInBlocks();
630                                        }
631                                }
632                        }
633                }
634               
635                // Then the new entry
636                BackupStoreDirectory::Entry *pnewEntry = dir.AddEntry(rFilename,
637                                ModificationTime, id, newObjectBlocksUsed,
638                                BackupStoreDirectory::Entry::Flags_File,
639                                AttributesHash);
640
641                // Adjust for the patch back stuff?
642                if(DiffFromFileID != 0)
643                {
644                        // Get old version entry
645                        BackupStoreDirectory::Entry *poldEntry = dir.FindEntryByID(DiffFromFileID);
646                        ASSERT(poldEntry != 0);
647               
648                        // Adjust dependency info of file?
649                        if(!reversedDiffIsCompletelyDifferent)
650                        {
651                                poldEntry->SetDependsNewer(id);
652                                pnewEntry->SetDependsOlder(DiffFromFileID);
653                        }
654                       
655                        // Adjust size of old entry
656                        int64_t oldSize = poldEntry->GetSizeInBlocks();
657                        poldEntry->SetSizeInBlocks(oldVersionNewBlocksUsed);
658                       
659                        // And adjust blocks used count, for later adjustment
660                        newObjectBlocksUsed += (oldVersionNewBlocksUsed - oldSize);
661                        blocksInOldFiles += (oldVersionNewBlocksUsed - oldSize);
662                }
663
664                // Write the directory back to disc
665                SaveDirectory(dir, InDirectory);
666
667                // Commit the old version's new patched version, now that the directory safely reflects
668                // the state of the files on disc.
669                if(ppreviousVerStoreFile != 0)
670                {
671                        ppreviousVerStoreFile->Commit(BACKUP_STORE_CONVERT_TO_RAID_IMMEDIATELY);
672                        delete ppreviousVerStoreFile;
673                        ppreviousVerStoreFile = 0;
674                }
675        }
676        catch(...)
677        {
678                // Back out on adding that file
679                RaidFileWrite del(mStoreDiscSet, fn);
680                del.Delete();
681               
682                // Remove this entry from the cache
683                RemoveDirectoryFromCache(InDirectory);
684               
685                // Delete any previous version store file
686                if(ppreviousVerStoreFile != 0)
687                {
688                        delete ppreviousVerStoreFile;
689                        ppreviousVerStoreFile = 0;
690                }
691               
692                // Don't worry about the incremented number in the store info
693                throw;
694        }
695       
696        // Check logic
697        ASSERT(ppreviousVerStoreFile == 0);
698       
699        // Modify the store info
700
701        if(DiffFromFileID == 0)
702        {
703                mapStoreInfo->AdjustNumFiles(1);
704        }
705        else
706        {
707                mapStoreInfo->AdjustNumOldFiles(1);
708        }
709       
710        mapStoreInfo->ChangeBlocksUsed(newObjectBlocksUsed);
711        mapStoreInfo->ChangeBlocksInCurrentFiles(newObjectBlocksUsed -
712                blocksInOldFiles);
713        mapStoreInfo->ChangeBlocksInOldFiles(blocksInOldFiles);
714       
715        // Increment reference count on the new directory to one
716        mapRefCount->AddReference(id);
717       
718        // Save the store info -- can cope if this exceptions because infomation
719        // will be rebuilt by housekeeping, and ID allocation can recover.
720        SaveStoreInfo(false);
721       
722        // Return the ID to the caller
723        return id;
724}
725
726
727
728// --------------------------------------------------------------------------
729//
730// Function
731//              Name:    BackupStoreContext::DeleteFile(const BackupStoreFilename &, int64_t, int64_t &)
732//              Purpose: Deletes a file, returning true if the file existed. Object ID returned too, set to zero if not found.
733//              Created: 2003/10/21
734//
735// --------------------------------------------------------------------------
736bool BackupStoreContext::DeleteFile(const BackupStoreFilename &rFilename, int64_t InDirectory, int64_t &rObjectIDOut)
737{
738        // Essential checks!
739        if(mapStoreInfo.get() == 0)
740        {
741                THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded)
742        }
743
744        if(mReadOnly)
745        {
746                THROW_EXCEPTION(BackupStoreException, ContextIsReadOnly)
747        }
748
749        // Find the directory the file is in (will exception if it fails)
750        BackupStoreDirectory &dir(GetDirectoryInternal(InDirectory));
751
752        // Setup flags
753        bool fileExisted = false;
754        bool madeChanges = false;
755        rObjectIDOut = 0;               // not found
756
757        // Count of deleted blocks
758        int64_t blocksDel = 0;
759
760        try
761        {
762                // Iterate through directory, only looking at files which haven't been deleted
763                BackupStoreDirectory::Iterator i(dir);
764                BackupStoreDirectory::Entry *e = 0;
765                while((e = i.Next(BackupStoreDirectory::Entry::Flags_File,
766                        BackupStoreDirectory::Entry::Flags_Deleted)) != 0)
767                {
768                        // Compare name
769                        if(e->GetName() == rFilename)
770                        {
771                                // Check that it's definately not already deleted
772                                ASSERT((e->GetFlags() & BackupStoreDirectory::Entry::Flags_Deleted) == 0);
773                                // Set deleted flag
774                                e->AddFlags(BackupStoreDirectory::Entry::Flags_Deleted);
775                                // Mark as made a change
776                                madeChanges = true;
777                                // Can safely do this, because we know we won't be here if it's already
778                                // an old version
779                                blocksDel += e->GetSizeInBlocks();
780                                // Is this the last version?
781                                if((e->GetFlags() & BackupStoreDirectory::Entry::Flags_OldVersion) == 0)
782                                {
783                                        // Yes. It's been found.
784                                        rObjectIDOut = e->GetObjectID();
785                                        fileExisted = true;
786                                }
787                        }
788                }
789               
790                // Save changes?
791                if(madeChanges)
792                {
793                        // Save the directory back
794                        SaveDirectory(dir, InDirectory);
795                       
796                        // Modify the store info, and write
797                        // It definitely wasn't an old or deleted version
798                        mapStoreInfo->AdjustNumFiles(-1);
799                        mapStoreInfo->AdjustNumDeletedFiles(1);
800                        mapStoreInfo->ChangeBlocksInDeletedFiles(blocksDel);
801                       
802                        SaveStoreInfo(false);
803                }
804        }
805        catch(...)
806        {
807                RemoveDirectoryFromCache(InDirectory);
808                throw;
809        }
810
811        return fileExisted;
812}
813
814
815// --------------------------------------------------------------------------
816//
817// Function
818//              Name:    BackupStoreContext::UndeleteFile(int64_t, int64_t)
819//              Purpose: Undeletes a file, if it exists, returning true if
820//                       the file existed.
821//              Created: 2003/10/21
822//
823// --------------------------------------------------------------------------
824bool BackupStoreContext::UndeleteFile(int64_t ObjectID, int64_t InDirectory)
825{
826        // Essential checks!
827        if(mapStoreInfo.get() == 0)
828        {
829                THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded)
830        }
831
832        if(mReadOnly)
833        {
834                THROW_EXCEPTION(BackupStoreException, ContextIsReadOnly)
835        }
836
837        // Find the directory the file is in (will exception if it fails)
838        BackupStoreDirectory &dir(GetDirectoryInternal(InDirectory));
839
840        // Setup flags
841        bool fileExisted = false;
842        bool madeChanges = false;
843
844        // Count of deleted blocks
845        int64_t blocksDel = 0;
846
847        try
848        {
849                // Iterate through directory, only looking at files which have been deleted
850                BackupStoreDirectory::Iterator i(dir);
851                BackupStoreDirectory::Entry *e = 0;
852                while((e = i.Next(BackupStoreDirectory::Entry::Flags_File |
853                        BackupStoreDirectory::Entry::Flags_Deleted, 0)) != 0)
854                {
855                        // Compare name
856                        if(e->GetObjectID() == ObjectID)
857                        {
858                                // Check that it's definitely already deleted
859                                ASSERT((e->GetFlags() & BackupStoreDirectory::Entry::Flags_Deleted) != 0);
860                                // Clear deleted flag
861                                e->RemoveFlags(BackupStoreDirectory::Entry::Flags_Deleted);
862                                // Mark as made a change
863                                madeChanges = true;
864                                blocksDel -= e->GetSizeInBlocks();
865
866                                // Is this the last version?
867                                if((e->GetFlags() & BackupStoreDirectory::Entry::Flags_OldVersion) == 0)
868                                {
869                                        // Yes. It's been found.
870                                        fileExisted = true;
871                                }
872                        }
873                }
874               
875                // Save changes?
876                if(madeChanges)
877                {
878                        // Save the directory back
879                        SaveDirectory(dir, InDirectory);
880                       
881                        // Modify the store info, and write
882                        mapStoreInfo->ChangeBlocksInDeletedFiles(blocksDel);
883                       
884                        // Maybe postponed save of store info
885                        SaveStoreInfo();
886                }
887        }
888        catch(...)
889        {
890                RemoveDirectoryFromCache(InDirectory);
891                throw;
892        }
893
894        return fileExisted;
895}
896
897
898// --------------------------------------------------------------------------
899//
900// Function
901//              Name:    BackupStoreContext::RemoveDirectoryFromCache(int64_t)
902//              Purpose: Remove directory from cache
903//              Created: 2003/09/04
904//
905// --------------------------------------------------------------------------
906void BackupStoreContext::RemoveDirectoryFromCache(int64_t ObjectID)
907{
908        std::map<int64_t, BackupStoreDirectory*>::iterator item(mDirectoryCache.find(ObjectID));
909        if(item != mDirectoryCache.end())
910        {
911                // Delete this cached object
912                delete item->second;
913                // Erase the entry form the map
914                mDirectoryCache.erase(item);
915        }
916}
917
918
919// --------------------------------------------------------------------------
920//
921// Function
922//              Name:    BackupStoreContext::SaveDirectory(BackupStoreDirectory &, int64_t)
923//              Purpose: Save directory back to disc, update time in cache
924//              Created: 2003/09/04
925//
926// --------------------------------------------------------------------------
927void BackupStoreContext::SaveDirectory(BackupStoreDirectory &rDir, int64_t ObjectID)
928{
929        if(mapStoreInfo.get() == 0)
930        {
931                THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded)
932        }
933        if(rDir.GetObjectID() != ObjectID)
934        {
935                THROW_EXCEPTION(BackupStoreException, Internal)
936        }
937
938        try
939        {
940                // Write to disc, adjust size in store info
941                std::string dirfn;
942                MakeObjectFilename(ObjectID, dirfn);
943                {
944                        RaidFileWrite writeDir(mStoreDiscSet, dirfn);
945                        writeDir.Open(true /* allow overwriting */);
946
947                        BufferedWriteStream buffer(writeDir);
948                        rDir.WriteToStream(buffer);
949                        buffer.Flush();
950
951                        // get the disc usage (must do this before commiting it)
952                        int64_t dirSize = writeDir.GetDiscUsageInBlocks();
953
954                        // Commit directory
955                        writeDir.Commit(BACKUP_STORE_CONVERT_TO_RAID_IMMEDIATELY);
956                       
957                        // Make sure the size of the directory is available for writing the dir back
958                        ASSERT(dirSize > 0);
959                        int64_t sizeAdjustment = dirSize - rDir.GetUserInfo1_SizeInBlocks();
960                        mapStoreInfo->ChangeBlocksUsed(sizeAdjustment);
961                        mapStoreInfo->ChangeBlocksInDirectories(sizeAdjustment);
962                        // Update size stored in directory
963                        rDir.SetUserInfo1_SizeInBlocks(dirSize);
964                }
965                // Refresh revision ID in cache
966                {
967                        int64_t revid = 0;
968                        if(!RaidFileRead::FileExists(mStoreDiscSet, dirfn, &revid))
969                        {
970                                THROW_EXCEPTION(BackupStoreException, Internal)
971                        }
972                        rDir.SetRevisionID(revid);
973                }
974        }
975        catch(...)
976        {
977                // Remove it from the cache if anything went wrong
978                RemoveDirectoryFromCache(ObjectID);
979                throw;
980        }
981}
982
983
984// --------------------------------------------------------------------------
985//
986// Function
987//              Name:    BackupStoreContext::AddDirectory(int64_t,
988//                       const BackupStoreFilename &, bool &)
989//              Purpose: Creates a directory (or just returns the ID of an
990//                       existing one). rAlreadyExists set appropraitely.
991//              Created: 2003/09/04
992//
993// --------------------------------------------------------------------------
994int64_t BackupStoreContext::AddDirectory(int64_t InDirectory, const BackupStoreFilename &rFilename, const StreamableMemBlock &Attributes, int64_t AttributesModTime, bool &rAlreadyExists)
995{
996        if(mapStoreInfo.get() == 0)
997        {
998                THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded)
999        }
1000        if(mReadOnly)
1001        {
1002                THROW_EXCEPTION(BackupStoreException, ContextIsReadOnly)
1003        }
1004       
1005        // Flags as not already existing
1006        rAlreadyExists = false;
1007       
1008        // Get the directory we want to modify
1009        BackupStoreDirectory &dir(GetDirectoryInternal(InDirectory));
1010
1011        // Scan the directory for the name (only looking for directories which already exist)
1012        {
1013                BackupStoreDirectory::Iterator i(dir);
1014                BackupStoreDirectory::Entry *en = 0;
1015                while((en = i.Next(BackupStoreDirectory::Entry::Flags_INCLUDE_EVERYTHING,
1016                        BackupStoreDirectory::Entry::Flags_Deleted | BackupStoreDirectory::Entry::Flags_OldVersion)) != 0)      // Ignore deleted and old directories
1017                {
1018                        if(en->GetName() == rFilename)
1019                        {
1020                                // Already exists
1021                                rAlreadyExists = true;
1022                                return en->GetObjectID();
1023                        }
1024                }
1025        }
1026
1027        // Allocate the next ID
1028        int64_t id = AllocateObjectID();
1029
1030        // Create an empty directory with the given attributes on disc
1031        std::string fn;
1032        MakeObjectFilename(id, fn, true /* make sure the directory it's in exists */);
1033        {
1034                BackupStoreDirectory emptyDir(id, InDirectory);
1035                // add the atttribues
1036                emptyDir.SetAttributes(Attributes, AttributesModTime);
1037               
1038                // Write...
1039                RaidFileWrite dirFile(mStoreDiscSet, fn);
1040                dirFile.Open(false /* no overwriting */);
1041                emptyDir.WriteToStream(dirFile);
1042                // Get disc usage, before it's commited
1043                int64_t dirSize = dirFile.GetDiscUsageInBlocks();
1044                // Commit the file
1045                dirFile.Commit(BACKUP_STORE_CONVERT_TO_RAID_IMMEDIATELY);               
1046
1047                // Make sure the size of the directory is added to the usage counts in the info
1048                ASSERT(dirSize > 0);
1049                mapStoreInfo->ChangeBlocksUsed(dirSize);
1050                mapStoreInfo->ChangeBlocksInDirectories(dirSize);
1051                // Not added to cache, so don't set the size in the directory
1052        }
1053       
1054        // Then add it into the parent directory
1055        try
1056        {
1057                dir.AddEntry(rFilename, 0 /* modification time */, id, 0 /* blocks used */, BackupStoreDirectory::Entry::Flags_Dir, 0 /* attributes mod time */);
1058                SaveDirectory(dir, InDirectory);
1059
1060                // Increment reference count on the new directory to one
1061                mapRefCount->AddReference(id);
1062        }
1063        catch(...)
1064        {
1065                // Back out on adding that directory
1066                RaidFileWrite del(mStoreDiscSet, fn);
1067                del.Delete();
1068               
1069                // Remove this entry from the cache
1070                RemoveDirectoryFromCache(InDirectory);
1071               
1072                // Don't worry about the incremented number in the store info
1073                throw; 
1074        }
1075
1076        // Save the store info (may not be postponed)
1077        mapStoreInfo->AdjustNumDirectories(1);
1078        SaveStoreInfo(false);
1079
1080        // tell caller what the ID was
1081        return id;
1082}
1083
1084// --------------------------------------------------------------------------
1085//
1086// Function
1087//              Name:    BackupStoreContext::DeleteFile(const BackupStoreFilename &, int64_t, int64_t &, bool)
1088//              Purpose: Recusively deletes a directory (or undeletes if Undelete = true)
1089//              Created: 2003/10/21
1090//
1091// --------------------------------------------------------------------------
1092void BackupStoreContext::DeleteDirectory(int64_t ObjectID, bool Undelete)
1093{
1094        // Essential checks!
1095        if(mapStoreInfo.get() == 0)
1096        {
1097                THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded)
1098        }
1099        if(mReadOnly)
1100        {
1101                THROW_EXCEPTION(BackupStoreException, ContextIsReadOnly)
1102        }
1103
1104        // Containing directory
1105        int64_t InDirectory = 0;
1106       
1107        // Count of blocks deleted
1108        int64_t blocksDeleted = 0;
1109
1110        try
1111        {
1112                // Get the directory that's to be deleted
1113                {
1114                        // In block, because dir may not be valid after the delete directory call
1115                        BackupStoreDirectory &dir(GetDirectoryInternal(ObjectID));
1116                       
1117                        // Store the directory it's in for later
1118                        InDirectory = dir.GetContainerID();
1119               
1120                        // Depth first delete of contents
1121                        DeleteDirectoryRecurse(ObjectID, blocksDeleted, Undelete);
1122                }
1123               
1124                // Remove the entry from the directory it's in
1125                ASSERT(InDirectory != 0);
1126                BackupStoreDirectory &parentDir(GetDirectoryInternal(InDirectory));
1127               
1128                BackupStoreDirectory::Iterator i(parentDir);
1129                BackupStoreDirectory::Entry *en = 0;
1130                while((en = i.Next(Undelete?(BackupStoreDirectory::Entry::Flags_Deleted):(BackupStoreDirectory::Entry::Flags_INCLUDE_EVERYTHING),
1131                        Undelete?(0):(BackupStoreDirectory::Entry::Flags_Deleted))) != 0)       // Ignore deleted directories (or not deleted if Undelete)
1132                {
1133                        if(en->GetObjectID() == ObjectID)
1134                        {
1135                                // This is the one to delete
1136                                if(Undelete)
1137                                {
1138                                        en->RemoveFlags(BackupStoreDirectory::Entry::Flags_Deleted);
1139                                }
1140                                else
1141                                {
1142                                        en->AddFlags(BackupStoreDirectory::Entry::Flags_Deleted);
1143                                }
1144                                                       
1145                                // Save it
1146                                SaveDirectory(parentDir, InDirectory);
1147                               
1148                                // Done
1149                                break;
1150                        }
1151                }
1152               
1153                // Update blocks deleted count
1154                mapStoreInfo->ChangeBlocksInDeletedFiles(Undelete?(0 - blocksDeleted):(blocksDeleted));
1155                mapStoreInfo->AdjustNumDirectories(-1);
1156                SaveStoreInfo(false);
1157        }
1158        catch(...)
1159        {
1160                RemoveDirectoryFromCache(InDirectory);
1161                throw;
1162        }
1163}
1164
1165// --------------------------------------------------------------------------
1166//
1167// Function
1168//              Name:    BackupStoreContext::DeleteDirectoryRecurse(BackupStoreDirectory &, int64_t)
1169//              Purpose: Private. Deletes a directory depth-first recusively.
1170//              Created: 2003/10/21
1171//
1172// --------------------------------------------------------------------------
1173void BackupStoreContext::DeleteDirectoryRecurse(int64_t ObjectID, int64_t &rBlocksDeletedOut, bool Undelete)
1174{
1175        try
1176        {
1177                // Does things carefully to avoid using a directory in the cache after recursive call
1178                // because it may have been deleted.
1179               
1180                // Do sub directories
1181                {
1182                        // Get the directory...
1183                        BackupStoreDirectory &dir(GetDirectoryInternal(ObjectID));
1184                       
1185                        // Then scan it for directories
1186                        std::vector<int64_t> subDirs;
1187                        BackupStoreDirectory::Iterator i(dir);
1188                        BackupStoreDirectory::Entry *en = 0;
1189                        if(Undelete)
1190                        {
1191                                while((en = i.Next(BackupStoreDirectory::Entry::Flags_Dir | BackupStoreDirectory::Entry::Flags_Deleted, // deleted dirs
1192                                        BackupStoreDirectory::Entry::Flags_EXCLUDE_NOTHING)) != 0)
1193                                {
1194                                        // Store the directory ID.
1195                                        subDirs.push_back(en->GetObjectID());
1196                                }
1197                        }
1198                        else
1199                        {
1200                                while((en = i.Next(BackupStoreDirectory::Entry::Flags_Dir,      // dirs only
1201                                        BackupStoreDirectory::Entry::Flags_Deleted)) != 0)              // but not deleted ones
1202                                {
1203                                        // Store the directory ID.
1204                                        subDirs.push_back(en->GetObjectID());
1205                                }
1206                        }
1207                       
1208                        // Done with the directory for now. Recurse to sub directories
1209                        for(std::vector<int64_t>::const_iterator i = subDirs.begin(); i != subDirs.end(); ++i)
1210                        {
1211                                DeleteDirectoryRecurse((*i), rBlocksDeletedOut, Undelete);     
1212                        }
1213                }
1214               
1215                // Then, delete the files. Will need to load the directory again because it might have
1216                // been removed from the cache.
1217                {
1218                        // Get the directory...
1219                        BackupStoreDirectory &dir(GetDirectoryInternal(ObjectID));
1220       
1221                        // Changes made?
1222                        bool changesMade = false;
1223       
1224                        // Run through files           
1225                        BackupStoreDirectory::Iterator i(dir);
1226                        BackupStoreDirectory::Entry *en = 0;
1227
1228                        while((en = i.Next(Undelete?(BackupStoreDirectory::Entry::Flags_Deleted):(BackupStoreDirectory::Entry::Flags_INCLUDE_EVERYTHING),
1229                                Undelete?(0):(BackupStoreDirectory::Entry::Flags_Deleted))) != 0)       // Ignore deleted directories (or not deleted if Undelete)
1230                        {
1231                                // Add/remove the deleted flags
1232                                if(Undelete)
1233                                {
1234                                        en->RemoveFlags(BackupStoreDirectory::Entry::Flags_Deleted);
1235                                }
1236                                else
1237                                {
1238                                        en->AddFlags(BackupStoreDirectory::Entry::Flags_Deleted);
1239                                }
1240                                                       
1241                                // Keep count of the deleted blocks
1242                                if((en->GetFlags() & BackupStoreDirectory::Entry::Flags_File) != 0)
1243                                {
1244                                        rBlocksDeletedOut += en->GetSizeInBlocks();
1245                                }
1246                               
1247                                // Did something
1248                                changesMade = true;
1249                        }
1250                       
1251                        // Save the directory
1252                        if(changesMade)
1253                        {
1254                                SaveDirectory(dir, ObjectID);
1255                        }
1256                }
1257        }
1258        catch(...)
1259        {
1260                RemoveDirectoryFromCache(ObjectID);
1261                throw;
1262        }
1263}
1264
1265
1266
1267// --------------------------------------------------------------------------
1268//
1269// Function
1270//              Name:    BackupStoreContext::ChangeDirAttributes(int64_t, const StreamableMemBlock &, int64_t)
1271//              Purpose: Change the attributes of a directory
1272//              Created: 2003/09/06
1273//
1274// --------------------------------------------------------------------------
1275void BackupStoreContext::ChangeDirAttributes(int64_t Directory, const StreamableMemBlock &Attributes, int64_t AttributesModTime)
1276{
1277        if(mapStoreInfo.get() == 0)
1278        {
1279                THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded)
1280        }
1281        if(mReadOnly)
1282        {
1283                THROW_EXCEPTION(BackupStoreException, ContextIsReadOnly)
1284        }
1285
1286        try
1287        {       
1288                // Get the directory we want to modify
1289                BackupStoreDirectory &dir(GetDirectoryInternal(Directory));
1290       
1291                // Set attributes
1292                dir.SetAttributes(Attributes, AttributesModTime);
1293               
1294                // Save back
1295                SaveDirectory(dir, Directory);
1296        }
1297        catch(...)
1298        {
1299                RemoveDirectoryFromCache(Directory);
1300                throw;
1301        }
1302}
1303
1304// --------------------------------------------------------------------------
1305//
1306// Function
1307//              Name:    BackupStoreContext::ChangeFileAttributes(int64_t, int64_t, const StreamableMemBlock &, int64_t)
1308//              Purpose: Sets the attributes on a directory entry. Returns true if the object existed, false if it didn't.
1309//              Created: 2003/09/06
1310//
1311// --------------------------------------------------------------------------
1312bool BackupStoreContext::ChangeFileAttributes(const BackupStoreFilename &rFilename, int64_t InDirectory, const StreamableMemBlock &Attributes, int64_t AttributesHash, int64_t &rObjectIDOut)
1313{
1314        if(mapStoreInfo.get() == 0)
1315        {
1316                THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded)
1317        }
1318        if(mReadOnly)
1319        {
1320                THROW_EXCEPTION(BackupStoreException, ContextIsReadOnly)
1321        }
1322       
1323        try
1324        {
1325                // Get the directory we want to modify
1326                BackupStoreDirectory &dir(GetDirectoryInternal(InDirectory));
1327       
1328                // Find the file entry
1329                BackupStoreDirectory::Entry *en = 0;
1330                // Iterate through current versions of files, only
1331                BackupStoreDirectory::Iterator i(dir);
1332                while((en = i.Next(
1333                        BackupStoreDirectory::Entry::Flags_File,
1334                        BackupStoreDirectory::Entry::Flags_Deleted | BackupStoreDirectory::Entry::Flags_OldVersion)
1335                        ) != 0)
1336                {
1337                        if(en->GetName() == rFilename)
1338                        {
1339                                // Set attributes
1340                                en->SetAttributes(Attributes, AttributesHash);
1341                               
1342                                // Tell caller the object ID
1343                                rObjectIDOut = en->GetObjectID();
1344                               
1345                                // Done
1346                                break;
1347                        }
1348                }
1349                if(en == 0)
1350                {
1351                        // Didn't find it
1352                        return false;
1353                }
1354       
1355                // Save back
1356                SaveDirectory(dir, InDirectory);
1357        }
1358        catch(...)
1359        {
1360                RemoveDirectoryFromCache(InDirectory);
1361                throw;
1362        }
1363       
1364        // Changed, everything OK
1365        return true;
1366}
1367
1368
1369// --------------------------------------------------------------------------
1370//
1371// Function
1372//              Name:    BackupStoreContext::ObjectExists(int64_t)
1373//              Purpose: Test to see if an object of this ID exists in the store
1374//              Created: 2003/09/03
1375//
1376// --------------------------------------------------------------------------
1377bool BackupStoreContext::ObjectExists(int64_t ObjectID, int MustBe)
1378{
1379        if(mapStoreInfo.get() == 0)
1380        {
1381                THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded)
1382        }
1383       
1384        // Note that we need to allow object IDs a little bit greater than the last one in the store info,
1385        // because the store info may not have got saved in an error condition. Max greater ID is
1386        // STORE_INFO_SAVE_DELAY in this case, *2 to be safe.
1387        if(ObjectID <= 0 || ObjectID > (mapStoreInfo->GetLastObjectIDUsed() + (STORE_INFO_SAVE_DELAY * 2)))
1388        {
1389                // Obviously bad object ID
1390                return false;
1391        }
1392       
1393        // Test to see if it exists on the disc
1394        std::string filename;
1395        MakeObjectFilename(ObjectID, filename);
1396        if(!RaidFileRead::FileExists(mStoreDiscSet, filename))
1397        {
1398                // RaidFile reports no file there
1399                return false;
1400        }
1401       
1402        // Do we need to be more specific?
1403        if(MustBe != ObjectExists_Anything)
1404        {
1405                // Open the file
1406                std::auto_ptr<RaidFileRead> objectFile(RaidFileRead::Open(mStoreDiscSet, filename));
1407
1408                // Read the first integer
1409                u_int32_t magic;
1410                if(!objectFile->ReadFullBuffer(&magic, sizeof(magic), 0 /* not interested in how many read if failure */))
1411                {
1412                        // Failed to get any bytes, must have failed
1413                        return false;
1414                }
1415
1416#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
1417                if(MustBe == ObjectExists_File && ntohl(magic) == OBJECTMAGIC_FILE_MAGIC_VALUE_V0)
1418                {
1419                        // Old version detected
1420                        return true;
1421                }
1422#endif
1423
1424                // Right one?
1425                u_int32_t requiredMagic = (MustBe == ObjectExists_File)?OBJECTMAGIC_FILE_MAGIC_VALUE_V1:OBJECTMAGIC_DIR_MAGIC_VALUE;
1426       
1427                // Check
1428                if(ntohl(magic) != requiredMagic)
1429                {
1430                        return false;
1431                }
1432               
1433                // File is implicitly closed
1434        }
1435       
1436        return true;
1437}
1438
1439
1440// --------------------------------------------------------------------------
1441//
1442// Function
1443//              Name:    BackupStoreContext::OpenObject(int64_t)
1444//              Purpose: Opens an object
1445//              Created: 2003/09/03
1446//
1447// --------------------------------------------------------------------------
1448std::auto_ptr<IOStream> BackupStoreContext::OpenObject(int64_t ObjectID)
1449{
1450        if(mapStoreInfo.get() == 0)
1451        {
1452                THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded)
1453        }
1454       
1455        // Attempt to open the file
1456        std::string fn;
1457        MakeObjectFilename(ObjectID, fn);
1458        return std::auto_ptr<IOStream>(RaidFileRead::Open(mStoreDiscSet, fn).release());
1459}
1460
1461
1462// --------------------------------------------------------------------------
1463//
1464// Function
1465//              Name:    BackupStoreContext::GetClientStoreMarker()
1466//              Purpose: Retrieve the client store marker
1467//              Created: 2003/10/29
1468//
1469// --------------------------------------------------------------------------
1470int64_t BackupStoreContext::GetClientStoreMarker()
1471{
1472        if(mapStoreInfo.get() == 0)
1473        {
1474                THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded)
1475        }
1476       
1477        return mapStoreInfo->GetClientStoreMarker();
1478}
1479
1480
1481// --------------------------------------------------------------------------
1482//
1483// Function
1484//              Name:    BackupStoreContext::GetStoreDiscUsageInfo(int64_t &, int64_t &, int64_t &)
1485//              Purpose: Get disc usage info from store info
1486//              Created: 1/1/04
1487//
1488// --------------------------------------------------------------------------
1489void BackupStoreContext::GetStoreDiscUsageInfo(int64_t &rBlocksUsed, int64_t &rBlocksSoftLimit, int64_t &rBlocksHardLimit)
1490{
1491        if(mapStoreInfo.get() == 0)
1492        {
1493                THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded)
1494        }
1495
1496        rBlocksUsed = mapStoreInfo->GetBlocksUsed();
1497        rBlocksSoftLimit = mapStoreInfo->GetBlocksSoftLimit();
1498        rBlocksHardLimit = mapStoreInfo->GetBlocksHardLimit();
1499}
1500
1501
1502// --------------------------------------------------------------------------
1503//
1504// Function
1505//              Name:    BackupStoreContext::HardLimitExceeded()
1506//              Purpose: Returns true if the hard limit has been exceeded
1507//              Created: 1/1/04
1508//
1509// --------------------------------------------------------------------------
1510bool BackupStoreContext::HardLimitExceeded()
1511{
1512        if(mapStoreInfo.get() == 0)
1513        {
1514                THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded)
1515        }
1516
1517        return mapStoreInfo->GetBlocksUsed() > mapStoreInfo->GetBlocksHardLimit();
1518}
1519
1520
1521// --------------------------------------------------------------------------
1522//
1523// Function
1524//              Name:    BackupStoreContext::SetClientStoreMarker(int64_t)
1525//              Purpose: Sets the client store marker, and commits it to disc
1526//              Created: 2003/10/29
1527//
1528// --------------------------------------------------------------------------
1529void BackupStoreContext::SetClientStoreMarker(int64_t ClientStoreMarker)
1530{
1531        if(mapStoreInfo.get() == 0)
1532        {
1533                THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded)
1534        }
1535        if(mReadOnly)
1536        {
1537                THROW_EXCEPTION(BackupStoreException, ContextIsReadOnly)
1538        }
1539       
1540        mapStoreInfo->SetClientStoreMarker(ClientStoreMarker);
1541        SaveStoreInfo(false /* don't delay saving this */);
1542}
1543
1544
1545// --------------------------------------------------------------------------
1546//
1547// Function
1548//              Name:    BackupStoreContext::MoveObject(int64_t, int64_t, int64_t, const BackupStoreFilename &, bool)
1549//              Purpose: Move an object (and all objects with the same name) from one directory to another
1550//              Created: 12/11/03
1551//
1552// --------------------------------------------------------------------------
1553void BackupStoreContext::MoveObject(int64_t ObjectID, int64_t MoveFromDirectory, int64_t MoveToDirectory, const BackupStoreFilename &rNewFilename, bool MoveAllWithSameName, bool AllowMoveOverDeletedObject)
1554{
1555        if(mReadOnly)
1556        {
1557                THROW_EXCEPTION(BackupStoreException, ContextIsReadOnly)
1558        }
1559
1560        // Should deleted files be excluded when checking for the existance of objects with the target name?
1561        int64_t targetSearchExcludeFlags = (AllowMoveOverDeletedObject)
1562                ?(BackupStoreDirectory::Entry::Flags_Deleted)
1563                :(BackupStoreDirectory::Entry::Flags_EXCLUDE_NOTHING);
1564       
1565        // Special case if the directories are the same...
1566        if(MoveFromDirectory == MoveToDirectory)
1567        {
1568                try
1569                {
1570                        // Get the first directory
1571                        BackupStoreDirectory &dir(GetDirectoryInternal(MoveFromDirectory));
1572               
1573                        // Find the file entry
1574                        BackupStoreDirectory::Entry *en = dir.FindEntryByID(ObjectID);
1575       
1576                        // Error if not found
1577                        if(en == 0)
1578                        {
1579                                THROW_EXCEPTION(BackupStoreException, CouldNotFindEntryInDirectory)
1580                        }
1581                       
1582                        // Check the new name doens't already exist (optionally ignoring deleted files)
1583                        {
1584                                BackupStoreDirectory::Iterator i(dir);
1585                                BackupStoreDirectory::Entry *c = 0;
1586                                while((c = i.Next(BackupStoreDirectory::Entry::Flags_INCLUDE_EVERYTHING, targetSearchExcludeFlags)) != 0)
1587                                {
1588                                        if(c->GetName() == rNewFilename)
1589                                        {
1590                                                THROW_EXCEPTION(BackupStoreException, NameAlreadyExistsInDirectory)
1591                                        }
1592                                }
1593                        }
1594                       
1595                        // Need to get all the entries with the same name?
1596                        if(MoveAllWithSameName)
1597                        {
1598                                // Iterate through the directory, copying all with matching names
1599                                BackupStoreDirectory::Iterator i(dir);
1600                                BackupStoreDirectory::Entry *c = 0;
1601                                while((c = i.Next()) != 0)
1602                                {
1603                                        if(c->GetName() == en->GetName())
1604                                        {
1605                                                // Rename this one
1606                                                c->SetName(rNewFilename);
1607                                        }
1608                                }
1609                        }
1610                        else
1611                        {
1612                                // Just copy this one
1613                                en->SetName(rNewFilename);
1614                        }
1615                       
1616                        // Save the directory back
1617                        SaveDirectory(dir, MoveFromDirectory);
1618                }
1619                catch(...)
1620                {
1621                        RemoveDirectoryFromCache(MoveToDirectory); // either will do, as they're the same
1622                        throw;
1623                }
1624       
1625                return;
1626        }
1627
1628        // Got to be careful how this is written, as we can't guarentte that if we have two
1629        // directories open, the first won't be deleted as the second is opened. (cache)
1630
1631        // List of entries to move
1632        std::vector<BackupStoreDirectory::Entry *> moving;
1633       
1634        // list of directory IDs which need to have containing dir id changed
1635        std::vector<int64_t> dirsToChangeContainingID;
1636
1637        try
1638        {
1639                // First of all, get copies of the entries to move to the to directory.
1640               
1641                {
1642                        // Get the first directory
1643                        BackupStoreDirectory &from(GetDirectoryInternal(MoveFromDirectory));
1644               
1645                        // Find the file entry
1646                        BackupStoreDirectory::Entry *en = from.FindEntryByID(ObjectID);
1647       
1648                        // Error if not found
1649                        if(en == 0)
1650                        {
1651                                THROW_EXCEPTION(BackupStoreException, CouldNotFindEntryInDirectory)
1652                        }
1653                       
1654                        // Need to get all the entries with the same name?
1655                        if(MoveAllWithSameName)
1656                        {
1657                                // Iterate through the directory, copying all with matching names
1658                                BackupStoreDirectory::Iterator i(from);
1659                                BackupStoreDirectory::Entry *c = 0;
1660                                while((c = i.Next()) != 0)
1661                                {
1662                                        if(c->GetName() == en->GetName())
1663                                        {
1664                                                // Copy
1665                                                moving.push_back(new BackupStoreDirectory::Entry(*c));
1666                                               
1667                                                // Check for containing directory correction
1668                                                if(c->GetFlags() & BackupStoreDirectory::Entry::Flags_Dir) dirsToChangeContainingID.push_back(c->GetObjectID());
1669                                        }
1670                                }
1671                                ASSERT(!moving.empty());
1672                        }
1673                        else
1674                        {
1675                                // Just copy this one
1676                                moving.push_back(new BackupStoreDirectory::Entry(*en));
1677
1678                                // Check for containing directory correction
1679                                if(en->GetFlags() & BackupStoreDirectory::Entry::Flags_Dir) dirsToChangeContainingID.push_back(en->GetObjectID());
1680                        }
1681                }
1682               
1683                // Secondly, insert them into the to directory, and save it
1684               
1685                {
1686                        // To directory
1687                        BackupStoreDirectory &to(GetDirectoryInternal(MoveToDirectory));
1688       
1689                        // Check the new name doens't already exist
1690                        {
1691                                BackupStoreDirectory::Iterator i(to);
1692                                BackupStoreDirectory::Entry *c = 0;
1693                                while((c = i.Next(BackupStoreDirectory::Entry::Flags_INCLUDE_EVERYTHING, targetSearchExcludeFlags)) != 0)
1694                                {
1695                                        if(c->GetName() == rNewFilename)
1696                                        {
1697                                                THROW_EXCEPTION(BackupStoreException, NameAlreadyExistsInDirectory)
1698                                        }
1699                                }
1700                        }
1701                       
1702                        // Copy the entries into it, changing the name as we go
1703                        for(std::vector<BackupStoreDirectory::Entry *>::iterator i(moving.begin()); i != moving.end(); ++i)
1704                        {
1705                                BackupStoreDirectory::Entry *en = (*i);
1706                                en->SetName(rNewFilename);
1707                                to.AddEntry(*en);       // adds copy
1708                        }
1709       
1710                        // Save back
1711                        SaveDirectory(to, MoveToDirectory);
1712                }
1713
1714                // Thirdly... remove them from the first directory -- but if it fails, attempt to delete them from the to directory
1715                try
1716                {
1717                        // Get directory
1718                        BackupStoreDirectory &from(GetDirectoryInternal(MoveFromDirectory));
1719               
1720                        // Delete each one
1721                        for(std::vector<BackupStoreDirectory::Entry *>::iterator i(moving.begin()); i != moving.end(); ++i)
1722                        {
1723                                from.DeleteEntry((*i)->GetObjectID());
1724                        }
1725       
1726                        // Save back
1727                        SaveDirectory(from, MoveFromDirectory);         
1728                }
1729                catch(...)
1730                {
1731                        // UNDO modification to To directory
1732                                       
1733                        // Get directory
1734                        BackupStoreDirectory &to(GetDirectoryInternal(MoveToDirectory));
1735               
1736                        // Delete each one
1737                        for(std::vector<BackupStoreDirectory::Entry *>::iterator i(moving.begin()); i != moving.end(); ++i)
1738                        {
1739                                to.DeleteEntry((*i)->GetObjectID());
1740                        }
1741       
1742                        // Save back
1743                        SaveDirectory(to, MoveToDirectory);
1744
1745                        // Throw the error
1746                        throw;
1747                }
1748               
1749                // Finally... for all the directories we moved, modify their containing directory ID
1750                for(std::vector<int64_t>::iterator i(dirsToChangeContainingID.begin()); i != dirsToChangeContainingID.end(); ++i)
1751                {
1752                        // Load the directory
1753                        BackupStoreDirectory &change(GetDirectoryInternal(*i));
1754                       
1755                        // Modify containing dir ID
1756                        change.SetContainerID(MoveToDirectory);
1757                       
1758                        // Save it back
1759                        SaveDirectory(change, *i);
1760                }
1761        }
1762        catch(...)
1763        {
1764                // Make sure directories aren't in the cache, as they may have been modified           
1765                RemoveDirectoryFromCache(MoveToDirectory);
1766                RemoveDirectoryFromCache(MoveFromDirectory);
1767                for(std::vector<int64_t>::iterator i(dirsToChangeContainingID.begin()); i != dirsToChangeContainingID.end(); ++i)
1768                {
1769                        RemoveDirectoryFromCache(*i);                   
1770                }
1771
1772                while(!moving.empty())
1773                {
1774                        delete moving.back();
1775                        moving.pop_back();
1776                }
1777                throw;
1778        }       
1779
1780        // Clean up
1781        while(!moving.empty())
1782        {
1783                delete moving.back();
1784                moving.pop_back();
1785        }
1786}
1787
1788
1789
1790// --------------------------------------------------------------------------
1791//
1792// Function
1793//              Name:    BackupStoreContext::GetBackupStoreInfo()
1794//              Purpose: Return the backup store info object, exception if it isn't loaded
1795//              Created: 19/4/04
1796//
1797// --------------------------------------------------------------------------
1798const BackupStoreInfo &BackupStoreContext::GetBackupStoreInfo() const
1799{
1800        if(mapStoreInfo.get() == 0)
1801        {
1802                THROW_EXCEPTION(BackupStoreException, StoreInfoNotLoaded)
1803        }
1804       
1805        return *(mapStoreInfo.get());
1806}
1807
1808
Note: See TracBrowser for help on using the repository browser.