source: box/trunk/lib/backupstore/BackupStoreCheck2.cpp @ 3038

Revision 3038, 26.3 KB checked in by chris, 7 months ago (diff)

Fix spelling error in AddUnattachedObject? method name.

  • Property svn:eol-style set to native
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    BackupStoreCheck2.cpp
5//              Purpose: More backup store checking
6//              Created: 22/4/04
7//
8// --------------------------------------------------------------------------
9
10#include "Box.h"
11
12#include <stdio.h>
13#include <string.h>
14
15#include "BackupStoreCheck.h"
16#include "StoreStructure.h"
17#include "RaidFileRead.h"
18#include "RaidFileWrite.h"
19#include "autogen_BackupStoreException.h"
20#include "BackupStoreObjectMagic.h"
21#include "BackupStoreFile.h"
22#include "BackupStoreFileWire.h"
23#include "BackupStoreDirectory.h"
24#include "BackupStoreConstants.h"
25#include "BackupStoreInfo.h"
26
27#include "MemLeakFindOn.h"
28
29
30// --------------------------------------------------------------------------
31//
32// Function
33//              Name:    BackupStoreCheck::CheckRoot()
34//              Purpose: Check the root directory exists.
35//              Created: 22/4/04
36//
37// --------------------------------------------------------------------------
38void BackupStoreCheck::CheckRoot()
39{
40        int32_t index = 0;
41        IDBlock *pblock = LookupID(BACKUPSTORE_ROOT_DIRECTORY_ID, index);
42       
43        if(pblock != 0)
44        {
45                // Found it. Which is lucky. Mark it as contained.
46                SetFlags(pblock, index, Flags_IsContained);
47        }
48        else
49        {
50                BOX_WARNING("Root directory doesn't exist");
51               
52                ++mNumberErrorsFound;
53               
54                if(mFixErrors)
55                {
56                        // Create a new root directory
57                        CreateBlankDirectory(BACKUPSTORE_ROOT_DIRECTORY_ID, BACKUPSTORE_ROOT_DIRECTORY_ID);
58                }
59        }
60}
61
62
63// --------------------------------------------------------------------------
64//
65// Function
66//              Name:    BackupStoreCheck::CreateBlankDirectory(int64_t, int64_t)
67//              Purpose: Creates a blank directory
68//              Created: 22/4/04
69//
70// --------------------------------------------------------------------------
71void BackupStoreCheck::CreateBlankDirectory(int64_t DirectoryID, int64_t ContainingDirID)
72{
73        if(!mFixErrors)
74        {
75                // Don't do anything if we're not supposed to fix errors
76                return;
77        }
78
79        BackupStoreDirectory dir(DirectoryID, ContainingDirID);
80       
81        // Serialise to disc
82        std::string filename;
83        StoreStructure::MakeObjectFilename(DirectoryID, mStoreRoot, mDiscSetNumber, filename, true /* make sure the dir exists */);
84        RaidFileWrite obj(mDiscSetNumber, filename);
85        obj.Open(false /* don't allow overwriting */);
86        dir.WriteToStream(obj);
87        int64_t size = obj.GetDiscUsageInBlocks();
88        obj.Commit(true /* convert to raid now */);
89       
90        // Record the fact we've done this
91        mDirsAdded.insert(DirectoryID);
92       
93        // Add to sizes
94        mBlocksUsed += size;
95        mBlocksInDirectories += size;
96}
97
98class BackupStoreDirectoryFixer
99{
100        private:
101        BackupStoreDirectory mDirectory;
102        std::string mFilename;
103        std::string mStoreRoot;
104        int mDiscSetNumber;
105
106        public:
107        BackupStoreDirectoryFixer(std::string storeRoot, int discSetNumber,
108                int64_t ID);
109        void InsertObject(int64_t ObjectID, bool IsDirectory,
110                int32_t lostDirNameSerial);
111        ~BackupStoreDirectoryFixer();
112};
113
114// --------------------------------------------------------------------------
115//
116// Function
117//              Name:    BackupStoreCheck::CheckUnattachedObjects()
118//              Purpose: Check for objects which aren't attached to anything
119//              Created: 22/4/04
120//
121// --------------------------------------------------------------------------
122void BackupStoreCheck::CheckUnattachedObjects()
123{
124        typedef std::map<int64_t, BackupStoreDirectoryFixer*> fixers_t;
125        typedef std::pair<int64_t, BackupStoreDirectoryFixer*> fixer_pair_t;
126        fixers_t fixers;
127
128        // Scan all objects, finding ones which have no container
129        for(Info_t::const_iterator i(mInfo.begin()); i != mInfo.end(); ++i)
130        {
131                IDBlock *pblock = i->second;
132                int32_t bentries = (pblock == mpInfoLastBlock)?mInfoLastBlockEntries:BACKUPSTORECHECK_BLOCK_SIZE;
133               
134                for(int e = 0; e < bentries; ++e)
135                {
136                        uint8_t flags = GetFlags(pblock, e);
137                        if((flags & Flags_IsContained) == 0)
138                        {
139                                // Unattached object...
140                                BOX_WARNING("Object " <<
141                                        BOX_FORMAT_OBJECTID(pblock->mID[e]) <<
142                                        " is unattached.");
143                                ++mNumberErrorsFound;
144
145                                // What's to be done?
146                                int64_t putIntoDirectoryID = 0;
147
148                                if((flags & Flags_IsDir) == Flags_IsDir)
149                                {
150                                        // Directory. Just put into lost and found.
151                                        putIntoDirectoryID = GetLostAndFoundDirID();
152                                }
153                                else
154                                {
155                                        // File. Only attempt to attach it somewhere if it isn't a patch
156                                        {
157                                                int64_t diffFromObjectID = 0;
158                                                std::string filename;
159                                                StoreStructure::MakeObjectFilename(pblock->mID[e], mStoreRoot, mDiscSetNumber, filename, false /* don't attempt to make sure the dir exists */);
160
161                                                // Debugging for Sune Molgaard's issue with non-existent files being
162                                                // detected as unattached and crashing later in CheckUnattachedObjects()
163                                                if (pblock->mID[e] == 0x90c1a)
164                                                {
165                                                        BOX_INFO("Trying to open unattached " <<
166                                                                BOX_FORMAT_OBJECTID(pblock->mID[e]) <<
167                                                                " from " << filename << " on " << mDiscSetNumber);
168                                                }
169
170                                                // The easiest way to do this is to verify it again. Not such a bad penalty, because
171                                                // this really shouldn't be done very often.
172                                                {
173                                                        std::auto_ptr<RaidFileRead> file(RaidFileRead::Open(mDiscSetNumber, filename));
174                                                        BackupStoreFile::VerifyEncodedFileFormat(*file, &diffFromObjectID);
175                                                }
176
177                                                // If not zero, then it depends on another file, which may or may not be available.
178                                                // Just delete it to be safe.
179                                                if(diffFromObjectID != 0)
180                                                {
181                                                        BOX_WARNING("Object " << BOX_FORMAT_OBJECTID(pblock->mID[e]) << " is unattached, and is a patch. Deleting, cannot reliably recover.");
182                                               
183                                                        // Delete this object instead
184                                                        if(mFixErrors)
185                                                        {
186                                                                RaidFileWrite del(mDiscSetNumber, filename);
187                                                                del.Delete();
188                                                        }
189                                                       
190                                                        // Move on to next item
191                                                        continue;
192                                                }
193                                        }
194                                       
195                                        // Files contain their original filename, so perhaps the orginal directory still exists,
196                                        // or we can infer the existance of a directory?
197                                        // Look for a matching entry in the mDirsWhichContainLostDirs map.
198                                        // Can't do this with a directory, because the name just wouldn't be known, which is
199                                        // pretty useless as bbackupd would just delete it. So better to put it in lost+found
200                                        // where the admin can do something about it.
201                                        int32_t dirindex;
202                                        IDBlock *pdirblock = LookupID(pblock->mContainer[e], dirindex);
203                                        if(pdirblock != 0)
204                                        {
205                                                // Something with that ID has been found. Is it a directory?
206                                                if(GetFlags(pdirblock, dirindex) & Flags_IsDir)
207                                                {
208                                                        // Directory exists, add to that one
209                                                        putIntoDirectoryID = pblock->mContainer[e];
210                                                }
211                                                else
212                                                {
213                                                        // Not a directory. Use lost and found dir
214                                                        putIntoDirectoryID = GetLostAndFoundDirID();
215                                                }
216                                        }
217                                        else if(mDirsAdded.find(pblock->mContainer[e]) != mDirsAdded.end()
218                                                || TryToRecreateDirectory(pblock->mContainer[e]))
219                                        {
220                                                // The directory reappeared, or was created somehow elsewhere
221                                                putIntoDirectoryID = pblock->mContainer[e];
222                                        }
223                                        else
224                                        {
225                                                putIntoDirectoryID = GetLostAndFoundDirID();
226                                        }
227                                }
228                                ASSERT(putIntoDirectoryID != 0);
229
230                                if (!mFixErrors)
231                                {
232                                        continue;
233                                }
234
235                                BackupStoreDirectoryFixer* pFixer;
236                                fixers_t::iterator fi = 
237                                        fixers.find(putIntoDirectoryID);
238                                if (fi == fixers.end())
239                                {
240                                        // no match, create a new one
241                                        pFixer = new BackupStoreDirectoryFixer(
242                                                mStoreRoot, mDiscSetNumber,
243                                                putIntoDirectoryID);
244                                        fixers.insert(fixer_pair_t(
245                                                putIntoDirectoryID, pFixer));
246                                }
247                                else
248                                {
249                                        pFixer = fi->second;
250                                }
251
252                                int32_t lostDirNameSerial = 0;
253
254                                if(flags & Flags_IsDir)
255                                {
256                                        lostDirNameSerial = mLostDirNameSerial++;
257                                }
258
259                                // Add it to the directory
260                                pFixer->InsertObject(pblock->mID[e],
261                                        ((flags & Flags_IsDir) == Flags_IsDir),
262                                        lostDirNameSerial);
263                        }
264                }
265        }
266
267        // clean up all the fixers. Deleting them commits them automatically.
268        for (fixers_t::iterator i = fixers.begin(); i != fixers.end(); i++)
269        {
270                BackupStoreDirectoryFixer* pFixer = i->second;
271                delete pFixer;
272        }
273}
274
275// --------------------------------------------------------------------------
276//
277// Function
278//              Name:    BackupStoreCheck::TryToRecreateDirectory(int64_t)
279//              Purpose: Recreate a missing directory
280//              Created: 22/4/04
281//
282// --------------------------------------------------------------------------
283bool BackupStoreCheck::TryToRecreateDirectory(int64_t MissingDirectoryID)
284{
285        // During the directory checking phase, a map of "missing directory" to
286        // containing directory was built. If we can find it here, then it's
287        // something which can be recreated!
288        std::map<BackupStoreCheck_ID_t, BackupStoreCheck_ID_t>::iterator missing(
289                mDirsWhichContainLostDirs.find(MissingDirectoryID));
290        if(missing == mDirsWhichContainLostDirs.end())
291        {
292                // Not a missing directory, can't recreate.
293                return false;
294        }
295       
296        // Can recreate this! Wooo!
297        if(!mFixErrors)
298        {
299                BOX_WARNING("Missing directory " << 
300                        BOX_FORMAT_OBJECTID(MissingDirectoryID) <<
301                        " could be recreated.");
302                mDirsAdded.insert(MissingDirectoryID);
303                return true;
304        }
305
306        BOX_WARNING("Recreating missing directory " << 
307                BOX_FORMAT_OBJECTID(MissingDirectoryID));
308       
309        // Create a blank directory
310        BackupStoreDirectory dir(MissingDirectoryID, missing->second /* containing dir ID */);
311        // Note that this directory already contains a directory entry pointing to
312        // this dir, so it doesn't have to be added.
313       
314        // Serialise to disc
315        std::string filename;
316        StoreStructure::MakeObjectFilename(MissingDirectoryID, mStoreRoot, mDiscSetNumber, filename, true /* make sure the dir exists */);
317        RaidFileWrite root(mDiscSetNumber, filename);
318        root.Open(false /* don't allow overwriting */);
319        dir.WriteToStream(root);
320        root.Commit(true /* convert to raid now */);
321       
322        // Record the fact we've done this
323        mDirsAdded.insert(MissingDirectoryID);
324       
325        // Remove the entry from the map, so this doesn't happen again
326        mDirsWhichContainLostDirs.erase(missing);
327
328        return true;
329}
330
331BackupStoreDirectoryFixer::BackupStoreDirectoryFixer(std::string storeRoot,
332        int discSetNumber, int64_t ID)
333: mStoreRoot(storeRoot),
334  mDiscSetNumber(discSetNumber)
335{
336        // Generate filename
337        StoreStructure::MakeObjectFilename(ID, mStoreRoot, mDiscSetNumber,
338                mFilename, false /* don't make sure the dir exists */);
339       
340        // Read it in
341        std::auto_ptr<RaidFileRead> file(
342                RaidFileRead::Open(mDiscSetNumber, mFilename));
343        mDirectory.ReadFromStream(*file, IOStream::TimeOutInfinite);
344}
345
346void BackupStoreDirectoryFixer::InsertObject(int64_t ObjectID, bool IsDirectory,
347        int32_t lostDirNameSerial)
348{
349        // Data for the object
350        BackupStoreFilename objectStoreFilename;
351        int64_t modTime = 100;  // something which isn't zero or a special time
352        int32_t sizeInBlocks = 0; // suitable for directories
353
354        if(IsDirectory)
355        {
356                // Directory -- simply generate a name for it.
357                char name[32];
358                ::sprintf(name, "dir%08x", lostDirNameSerial);
359                objectStoreFilename.SetAsClearFilename(name);
360        }
361        else
362        {
363                // Files require a little more work...
364                // Open file
365                std::string fileFilename;
366                StoreStructure::MakeObjectFilename(ObjectID, mStoreRoot,
367                        mDiscSetNumber, fileFilename,
368                        false /* don't make sure the dir exists */);
369                std::auto_ptr<RaidFileRead> file(
370                        RaidFileRead::Open(mDiscSetNumber, fileFilename));
371
372                // Fill in size information
373                sizeInBlocks = file->GetDiscUsageInBlocks();
374
375                // Read in header
376                file_StreamFormat hdr;
377                if(file->Read(&hdr, sizeof(hdr)) != sizeof(hdr) ||
378                        (ntohl(hdr.mMagicValue) != OBJECTMAGIC_FILE_MAGIC_VALUE_V1
379#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
380                        && ntohl(hdr.mMagicValue) != OBJECTMAGIC_FILE_MAGIC_VALUE_V0
381#endif         
382                        ))
383                {
384                        // This should never happen, everything has been
385                        // checked before.
386                        THROW_EXCEPTION(BackupStoreException, Internal)
387                }
388                // This tells us nice things
389                modTime = box_ntoh64(hdr.mModificationTime);
390                // And the filename comes next
391                objectStoreFilename.ReadFromStream(*file, IOStream::TimeOutInfinite);
392        }
393
394        // Add a new entry in an appropriate place
395        mDirectory.AddUnattachedObject(objectStoreFilename, modTime,
396                ObjectID, sizeInBlocks,
397                IsDirectory?(BackupStoreDirectory::Entry::Flags_Dir):(BackupStoreDirectory::Entry::Flags_File));
398}
399
400BackupStoreDirectoryFixer::~BackupStoreDirectoryFixer()
401{
402        // Fix any flags which have been broken, which there's a good chance of doing
403        mDirectory.CheckAndFix();
404       
405        // Write it out
406        RaidFileWrite root(mDiscSetNumber, mFilename);
407        root.Open(true /* allow overwriting */);
408        mDirectory.WriteToStream(root);
409        root.Commit(true /* convert to raid now */);
410}
411
412// --------------------------------------------------------------------------
413//
414// Function
415//              Name:    BackupStoreCheck::GetLostAndFoundDirID()
416//              Purpose: Returns the ID of the lost and found directory, creating it if necessary
417//              Created: 22/4/04
418//
419// --------------------------------------------------------------------------
420int64_t BackupStoreCheck::GetLostAndFoundDirID()
421{
422        // Already allocated it?
423        if(mLostAndFoundDirectoryID != 0)
424        {
425                return mLostAndFoundDirectoryID;
426        }
427
428        if(!mFixErrors)
429        {
430                // The result will never be used anyway if errors aren't being fixed
431                return 1;
432        }
433
434        // Load up the root directory
435        BackupStoreDirectory dir;
436        std::string filename;
437        StoreStructure::MakeObjectFilename(BACKUPSTORE_ROOT_DIRECTORY_ID, mStoreRoot, mDiscSetNumber, filename, false /* don't make sure the dir exists */);
438        {
439                std::auto_ptr<RaidFileRead> file(RaidFileRead::Open(mDiscSetNumber, filename));
440                dir.ReadFromStream(*file, IOStream::TimeOutInfinite);
441        }
442
443        // Find a suitable name
444        BackupStoreFilename lostAndFound;
445        int n = 0;
446        while(true)
447        {
448                char name[32];
449                ::sprintf(name, "lost+found%d", n++);
450                lostAndFound.SetAsClearFilename(name);
451                if(!dir.NameInUse(lostAndFound))
452                {
453                        // Found a name which can be used
454                        BOX_WARNING("Lost and found dir has name " << name);
455                        break;
456                }
457        }
458
459        // Allocate an ID
460        int64_t id = mLastIDInInfo + 1;
461
462        // Create a blank directory
463        CreateBlankDirectory(id, BACKUPSTORE_ROOT_DIRECTORY_ID);
464       
465        // Add an entry for it
466        dir.AddEntry(lostAndFound, 0, id, 0, BackupStoreDirectory::Entry::Flags_Dir, 0);
467
468        // Write out root dir
469        RaidFileWrite root(mDiscSetNumber, filename);
470        root.Open(true /* allow overwriting */);
471        dir.WriteToStream(root);
472        root.Commit(true /* convert to raid now */);
473       
474        // Store
475        mLostAndFoundDirectoryID = id;
476
477        // Tell caller
478        return mLostAndFoundDirectoryID;
479}
480
481
482// --------------------------------------------------------------------------
483//
484// Function
485//              Name:    BackupStoreCheck::FixDirsWithWrongContainerID()
486//              Purpose: Rewrites container IDs where required
487//              Created: 22/4/04
488//
489// --------------------------------------------------------------------------
490void BackupStoreCheck::FixDirsWithWrongContainerID()
491{
492        if(!mFixErrors)
493        {
494                // Don't do anything if we're not supposed to fix errors
495                return;
496        }
497
498        // Run through things which need fixing
499        for(std::vector<BackupStoreCheck_ID_t>::iterator i(mDirsWithWrongContainerID.begin());
500                        i != mDirsWithWrongContainerID.end(); ++i)
501        {
502                int32_t index = 0;
503                IDBlock *pblock = LookupID(*i, index);
504                if(pblock == 0) continue;
505               
506                // Load in
507                BackupStoreDirectory dir;
508                std::string filename;
509                StoreStructure::MakeObjectFilename(*i, mStoreRoot, mDiscSetNumber, filename, false /* don't make sure the dir exists */);
510                {
511                        std::auto_ptr<RaidFileRead> file(RaidFileRead::Open(mDiscSetNumber, filename));
512                        dir.ReadFromStream(*file, IOStream::TimeOutInfinite);
513                }
514
515                // Adjust container ID
516                dir.SetContainerID(pblock->mContainer[index]);
517               
518                // Write it out
519                RaidFileWrite root(mDiscSetNumber, filename);
520                root.Open(true /* allow overwriting */);
521                dir.WriteToStream(root);
522                root.Commit(true /* convert to raid now */);
523        }
524}
525
526
527// --------------------------------------------------------------------------
528//
529// Function
530//              Name:    BackupStoreCheck::FixDirsWithLostDirs()
531//              Purpose: Fix directories
532//              Created: 22/4/04
533//
534// --------------------------------------------------------------------------
535void BackupStoreCheck::FixDirsWithLostDirs()
536{
537        if(!mFixErrors)
538        {
539                // Don't do anything if we're not supposed to fix errors
540                return;
541        }
542
543        // Run through things which need fixing
544        for(std::map<BackupStoreCheck_ID_t, BackupStoreCheck_ID_t>::iterator i(mDirsWhichContainLostDirs.begin());
545                        i != mDirsWhichContainLostDirs.end(); ++i)
546        {
547                int32_t index = 0;
548                IDBlock *pblock = LookupID(i->second, index);
549                if(pblock == 0) continue;
550               
551                // Load in
552                BackupStoreDirectory dir;
553                std::string filename;
554                StoreStructure::MakeObjectFilename(i->second, mStoreRoot, mDiscSetNumber, filename, false /* don't make sure the dir exists */);
555                {
556                        std::auto_ptr<RaidFileRead> file(RaidFileRead::Open(mDiscSetNumber, filename));
557                        dir.ReadFromStream(*file, IOStream::TimeOutInfinite);
558                }
559
560                // Delete the dodgy entry
561                dir.DeleteEntry(i->first);
562               
563                // Fix it up
564                dir.CheckAndFix();
565               
566                // Write it out
567                RaidFileWrite root(mDiscSetNumber, filename);
568                root.Open(true /* allow overwriting */);
569                dir.WriteToStream(root);
570                root.Commit(true /* convert to raid now */);
571        }
572}
573
574
575// --------------------------------------------------------------------------
576//
577// Function
578//              Name:    BackupStoreCheck::WriteNewStoreInfo()
579//              Purpose: Regenerate store info
580//              Created: 23/4/04
581//
582// --------------------------------------------------------------------------
583void BackupStoreCheck::WriteNewStoreInfo()
584{
585        // Attempt to load the existing store info file
586        std::auto_ptr<BackupStoreInfo> pOldInfo;
587        try
588        {
589                pOldInfo.reset(BackupStoreInfo::Load(mAccountID, mStoreRoot, mDiscSetNumber, true /* read only */).release());
590                mAccountName = pOldInfo->GetAccountName();
591        }
592        catch(...)
593        {
594                BOX_WARNING("Load of existing store info failed, regenerating.");
595                ++mNumberErrorsFound;
596        }
597
598        BOX_NOTICE("Total files: " << mNumFiles << " (of which "
599                "old files: " << mNumOldFiles << ", "
600                "deleted files: " << mNumDeletedFiles << "), "
601                "directories: " << mNumDirectories);
602
603        // Minimum soft and hard limits
604        int64_t minSoft = ((mBlocksUsed * 11) / 10) + 1024;
605        int64_t minHard = ((minSoft * 11) / 10) + 1024;
606
607        // Need to do anything?
608        if(pOldInfo.get() != 0 &&
609                mNumberErrorsFound == 0 &&
610                pOldInfo->GetAccountID() == mAccountID)
611        {
612                // Leave the store info as it is, no need to alter it because nothing really changed,
613                // and the only essential thing was that the account ID was correct, which is was.
614                return;
615        }
616       
617        // NOTE: We will always build a new store info, so the client store marker gets changed.
618
619        // Work out the new limits
620        int64_t softLimit = minSoft;
621        int64_t hardLimit = minHard;
622        if(pOldInfo.get() != 0 && pOldInfo->GetBlocksSoftLimit() > minSoft)
623        {
624                softLimit = pOldInfo->GetBlocksSoftLimit();
625        }
626        else
627        {
628                BOX_WARNING("Soft limit for account changed to ensure "
629                        "housekeeping doesn't delete files on next run.");
630        }
631        if(pOldInfo.get() != 0 && pOldInfo->GetBlocksHardLimit() > minHard)
632        {
633                hardLimit = pOldInfo->GetBlocksHardLimit();
634        }
635        else
636        {
637                BOX_WARNING("Hard limit for account changed to ensure "
638                        "housekeeping doesn't delete files on next run.");
639        }
640       
641        // Object ID
642        int64_t lastObjID = mLastIDInInfo;
643        if(mLostAndFoundDirectoryID != 0)
644        {
645                mLastIDInInfo++;
646        }
647
648        // Build a new store info
649        std::auto_ptr<BackupStoreInfo> info(BackupStoreInfo::CreateForRegeneration(
650                mAccountID,
651                mAccountName,
652                mStoreRoot,
653                mDiscSetNumber,
654                lastObjID,
655                mBlocksUsed,
656                mBlocksInCurrentFiles,
657                mBlocksInOldFiles,
658                mBlocksInDeletedFiles,
659                mBlocksInDirectories,
660                softLimit,
661                hardLimit));
662        info->AdjustNumFiles(mNumFiles);
663        info->AdjustNumOldFiles(mNumOldFiles);
664        info->AdjustNumDeletedFiles(mNumDeletedFiles);
665        info->AdjustNumDirectories(mNumDirectories);
666
667        if(pOldInfo.get())
668        {
669                mNumberErrorsFound += info->ReportChangesTo(*pOldInfo);
670        }
671
672        // Save to disc?
673        if(mFixErrors)
674        {
675                info->Save();
676                BOX_NOTICE("New store info file written successfully.");
677        }
678}
679
680#define FMT_OID(x) BOX_FORMAT_OBJECTID(x)
681#define FMT_i      BOX_FORMAT_OBJECTID((*i)->GetObjectID())
682
683// --------------------------------------------------------------------------
684//
685// Function
686//              Name:    BackupStoreDirectory::CheckAndFix()
687//              Purpose: Check the directory for obvious logical problems, and fix them.
688//                               Return true if the directory was changed.
689//              Created: 22/4/04
690//
691// --------------------------------------------------------------------------
692bool BackupStoreDirectory::CheckAndFix()
693{
694        bool changed = false;
695       
696        // Check that if a file depends on a new version, that version is in this directory
697        {
698                std::vector<Entry*>::iterator i(mEntries.begin());
699                for(; i != mEntries.end(); ++i)
700                {
701                        int64_t dependsNewer = (*i)->GetDependsNewer();
702                        if(dependsNewer != 0)
703                        {
704                                BackupStoreDirectory::Entry *newerEn = FindEntryByID(dependsNewer);
705                                if(newerEn == 0)
706                                {
707                                        // Depends on something, but it isn't there.
708                                        BOX_TRACE("Entry id " << FMT_i <<
709                                                " removed because depends "
710                                                "on newer version " <<
711                                                FMT_OID(dependsNewer) <<
712                                                " which doesn't exist");
713                                       
714                                        // Remove
715                                        delete *i;
716                                        mEntries.erase(i);
717                                       
718                                        // Start again at the beginning of the vector, the iterator is now invalid
719                                        i = mEntries.begin();
720                                       
721                                        // Mark as changed
722                                        changed = true;
723                                }
724                                else
725                                {
726                                        // Check that newerEn has it marked
727                                        if(newerEn->GetDependsOlder() != (*i)->GetObjectID())
728                                        {
729                                                // Wrong entry
730                                                BOX_TRACE("Entry id " <<
731                                                        FMT_OID(dependsNewer) <<
732                                                        ", correcting DependsOlder to " <<
733                                                        FMT_i <<
734                                                        ", was " <<
735                                                        FMT_OID(newerEn->GetDependsOlder()));
736                                                newerEn->SetDependsOlder((*i)->GetObjectID());
737                                                // Mark as changed
738                                                changed = true;
739                                        }
740                                }
741                        }
742                }
743        }
744       
745        // Check that if a file has a dependency marked, it exists, and remove it if it doesn't
746        {
747                std::vector<Entry*>::iterator i(mEntries.begin());
748                for(; i != mEntries.end(); ++i)
749                {
750                        int64_t dependsOlder = (*i)->GetDependsOlder();
751                        if(dependsOlder != 0 && FindEntryByID(dependsOlder) == 0)
752                        {
753                                // Has an older version marked, but this doesn't exist. Remove this mark
754                                BOX_TRACE("Entry id " << FMT_i <<
755                                        " was marked as depended on by " <<
756                                        FMT_OID(dependsOlder) << ", "
757                                        "which doesn't exist, dependency "
758                                        "info cleared");
759
760                                (*i)->SetDependsOlder(0);
761                               
762                                // Mark as changed
763                                changed = true;
764                        }
765                }
766        }
767
768        bool ch = false;
769        do
770        {
771                // Reset change marker
772                ch = false;
773               
774                // Search backwards -- so see newer versions first
775                std::vector<Entry*>::iterator i(mEntries.end());
776                if(i == mEntries.begin())
777                {
778                        // Directory is empty, stop now
779                        return changed; // changed flag
780                }
781
782                // Records of things seen
783                std::set<int64_t> idsEncountered;
784                std::set<std::string> filenamesEncountered;
785
786                do
787                {
788                        // Look at previous
789                        --i;
790
791                        bool removeEntry = false;
792                        if((*i) == 0)
793                        {
794                                BOX_TRACE("Remove because null pointer found");
795                                removeEntry = true;
796                        }
797                        else
798                        {
799                                bool isDir = (((*i)->GetFlags() & Entry::Flags_Dir) == Entry::Flags_Dir);
800                               
801                                // Check mutually exclusive flags
802                                if(isDir && (((*i)->GetFlags() & Entry::Flags_File) == Entry::Flags_File))
803                                {
804                                        // Bad! Unset the file flag
805                                        BOX_TRACE("Entry " << FMT_i <<
806                                                ": File flag and dir flag both set");
807                                        (*i)->RemoveFlags(Entry::Flags_File);
808                                        changed = true;
809                                }
810                       
811                                // Check...
812                                if(idsEncountered.find((*i)->GetObjectID()) != idsEncountered.end())
813                                {
814                                        // ID already seen, or type doesn't match
815                                        BOX_TRACE("Entry " << FMT_i <<
816                                                ": Remove because ID already seen");
817                                        removeEntry = true;
818                                }
819                                else
820                                {
821                                        // Haven't already seen this ID, remember it
822                                        idsEncountered.insert((*i)->GetObjectID());
823                                       
824                                        // Check to see if the name has already been encountered -- if not, then it
825                                        // needs to have the old version flag set
826                                        if(filenamesEncountered.find((*i)->GetName().GetEncodedFilename()) != filenamesEncountered.end())
827                                        {
828                                                // Seen before -- check old version flag set
829                                                if(((*i)->GetFlags() & Entry::Flags_OldVersion) != Entry::Flags_OldVersion
830                                                        && ((*i)->GetFlags() & Entry::Flags_Deleted) == 0)
831                                                {
832                                                        // Not set, set it
833                                                        BOX_TRACE("Entry " << FMT_i <<
834                                                                ": Set old flag");
835                                                        (*i)->AddFlags(Entry::Flags_OldVersion);
836                                                        changed = true;
837                                                }
838                                        }
839                                        else
840                                        {
841                                                // Check old version flag NOT set
842                                                if(((*i)->GetFlags() & Entry::Flags_OldVersion) == Entry::Flags_OldVersion)
843                                                {
844                                                        // Set, unset it
845                                                        BOX_TRACE("Entry " << FMT_i <<
846                                                                ": Old flag unset");
847                                                        (*i)->RemoveFlags(Entry::Flags_OldVersion);
848                                                        changed = true;
849                                                }
850                                               
851                                                // Remember filename
852                                                filenamesEncountered.insert((*i)->GetName().GetEncodedFilename());
853                                        }
854                                }
855                        }
856                       
857                        if(removeEntry)
858                        {
859                                // Mark something as changed, in loop
860                                ch = true;
861                               
862                                // Mark something as globally changed
863                                changed = true;
864                               
865                                // erase the thing from the list
866                                Entry *pentry = (*i);
867                                mEntries.erase(i);
868                               
869                                // And delete the entry object
870                                delete pentry;
871                               
872                                // Stop going around this loop, as the iterator is now invalid
873                                break;
874                        }
875                } while(i != mEntries.begin());
876
877        } while(ch != false);
878       
879        return changed;
880}
881
882
883// --------------------------------------------------------------------------
884//
885// Function
886//              Name:    BackupStoreDirectory::AddUnattachedObject(...)
887//              Purpose: Adds an object which is currently unattached. Assume that CheckAndFix() will be called afterwards.
888//              Created: 22/4/04
889//
890// --------------------------------------------------------------------------
891void BackupStoreDirectory::AddUnattachedObject(const BackupStoreFilename &rName,
892        box_time_t ModificationTime, int64_t ObjectID, int64_t SizeInBlocks, int16_t Flags)
893{
894        Entry *pnew = new Entry(rName, ModificationTime, ObjectID, SizeInBlocks, Flags,
895                        ModificationTime /* use as attr mod time too */);
896        try
897        {
898                // Want to order this just before the first object which has a higher ID,
899                // which is the place it's most likely to be correct.
900                std::vector<Entry*>::iterator i(mEntries.begin());
901                for(; i != mEntries.end(); ++i)
902                {
903                        if((*i)->GetObjectID() > ObjectID)
904                        {
905                                // Found a good place to insert it
906                                break;
907                        }
908                }
909                if(i == mEntries.end())
910                {
911                        mEntries.push_back(pnew);
912                }
913                else
914                {
915                        mEntries.insert(i, 1 /* just the one copy */, pnew);
916                }
917        }
918        catch(...)
919        {
920                delete pnew;
921                throw;
922        }
923}
924
925
926// --------------------------------------------------------------------------
927//
928// Function
929//              Name:    BackupStoreDirectory::NameInUse(const BackupStoreFilename &)
930//              Purpose: Returns true if the name is currently in use in the directory
931//              Created: 22/4/04
932//
933// --------------------------------------------------------------------------
934bool BackupStoreDirectory::NameInUse(const BackupStoreFilename &rName)
935{
936        for(std::vector<Entry*>::iterator i(mEntries.begin()); i != mEntries.end(); ++i)
937        {
938                if((*i)->GetName() == rName)
939                {
940                        return true;
941                }
942        }
943
944        return false;
945}
946
947
Note: See TracBrowser for help on using the repository browser.