Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

SMultiPKFile.C

Go to the documentation of this file.
00001 // Copyright (C) 2001, Compaq Computer Corporation
00002 // 
00003 // This file is part of Vesta.
00004 // 
00005 // Vesta is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU Lesser General Public
00007 // License as published by the Free Software Foundation; either
00008 // version 2.1 of the License, or (at your option) any later version.
00009 // 
00010 // Vesta is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 // Lesser General Public License for more details.
00014 // 
00015 // You should have received a copy of the GNU Lesser General Public
00016 // License along with Vesta; if not, write to the Free Software
00017 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 
00019 // Last modified on Mon May 23 22:49:18 EDT 2005 by ken@xorian.net 
00020 //      modified on Sat Feb 12 13:06:19 PST 2000 by mann 
00021 //      modified on Wed Feb 23 18:35:17 PST 2000 by mann  
00022 //      modified on Tue May  4 11:16:46 PDT 1999 by heydon
00023 
00024 #include <sys/stat.h>         // for mkdir(2)
00025 #if defined(__digital__)
00026 #include <sys/mode.h>         // permissions for newly-created directory modes
00027 #endif
00028 
00029 // vesta/basics
00030 #include <Basics.H>
00031 #include <FS.H>
00032 #include <Table.H>
00033 #include <Sequence.H>
00034 #include <VestaLog.H>
00035 #include <AtomicFile.H>
00036 
00037 // vesta/fp
00038 #include <FP.H>
00039 
00040 // vesta/cache-common
00041 #include <BitVector.H>
00042 #include <CacheConfig.H>
00043 #include <CacheState.H>
00044 #include <Debug.H>
00045 #include <PKPrefix.H>
00046 
00047 // vesta/cache-server
00048 #include "CacheConfigServer.H"
00049 #include "EmptyPKLog.H"
00050 #include "SPKFileRep.H"       // for SPKFileRep::*Version
00051 #include "CacheEntry.H"       // for CE::List*
00052 #include "SPKFile.H"          // for SPKFile::CFPEntryMap
00053 #include "SMultiPKFileRep.H"
00054 #include "VPKFile.H"
00055 #include "SMultiPKFile.H"
00056 
00057 using std::ios;
00058 using std::streampos;
00059 using std::ifstream;
00060 using std::cerr;
00061 using std::cout;
00062 using std::endl;
00063 
00064 // KCS, 2003-01-29:
00065 
00066 // I have reason to suspect that there's a bug which, when rewriting a
00067 // MultiPKfile but only modifying a subset of its PKFiles, can copy
00068 // either too many bytes or too few bytes for the unmodified PKFiles.
00069 // This macro changes SMultiPKFile::Rewrite to check for the condition
00070 // that would cause this and to always rewrite all PKFiles from memory
00071 // rather than copying unmodified ones as sequences of bytes.
00072 #define MISTRUST_PKLEN
00073 
00074 // Number of bits used for each arc of the MultiPKFile pathname. The code
00075 // requires that ArcBits <= 8*sizeof(Word) + 1 (that is, that a single arc
00076 // cannot span more than one word boundary).
00077 //
00078 // static const int ArcBits = 12; // at most 4K elements per directory
00079 static const int ArcBits = 8; // at most 256 elements per directory
00080 
00081 // Lookup Methods -------------------------------------------------------------
00082 
00083 void SMultiPKFile::OpenRead(const PKPrefix::T &pfx, /*OUT*/ ifstream &ifs)
00084   throw (SMultiPKFileRep::NotFound, FS::Failure)
00085 {
00086     try {
00087         // Try to open the file for reading
00088         Text fpath(FullName(pfx));
00089         FS::OpenReadOnly(fpath, /*OUT*/ ifs);
00090     }
00091     catch (FS::DoesNotExist) {
00092         throw SMultiPKFileRep::NotFound();
00093     }
00094 }
00095 
00096 streampos SMultiPKFile::SeekToPKFile(ifstream &ifs, const FP::Tag &pk,
00097   /*OUT*/ int &version) throw (SMultiPKFileRep::NotFound, FS::Failure)
00098 {
00099     try {
00100         // read version and header type
00101         SMultiPKFileRep::Header hdr(ifs);
00102 
00103         // search and seek to result
00104         streampos seek;
00105         switch (hdr.type) {
00106           case SMultiPKFileRep::HT_List:
00107             if (hdr.version <= SPKFileRep::SourceFuncVersion) {
00108                 seek = SeekInListV1(ifs, hdr.num, pk);
00109             } else {
00110                 // no support for other versions
00111                 assert(false);
00112             }
00113             break;
00114           case SMultiPKFileRep::HT_SortedList:
00115             if (hdr.version <= SPKFileRep::SourceFuncVersion) {
00116                 seek = SeekInSortedListV1(ifs, hdr.num, pk);
00117             } else {
00118                 // no support for other versions
00119                 assert(false);
00120             }
00121             break;
00122           case SMultiPKFileRep::HT_HashTable:
00123             /*** NYI ***/
00124             assert(false);
00125           default:
00126             assert(false);
00127         }
00128         FS::Seek(ifs, seek);
00129         version = hdr.version;
00130         return seek;
00131     }
00132     catch (SMultiPKFileRep::BadMPKFile) {
00133         // opening non-MultiPKFile indicates programming error
00134         cerr << "Fatal error: tried reading bad MultiPKFile "
00135              << "in SMultiPKFile::SeekToPKFile; exiting..." << endl;
00136         assert(false);
00137     }
00138     catch (FS::EndOfFile) {
00139         // premature end-of-file indicates programming error
00140         cerr << "Fatal error: premature EOF in "
00141              << "SMultiPKFile::SeekToPKFile; exiting..." << endl;
00142         assert(false);
00143     }
00144     return 0; // not reached
00145 }
00146 
00147 streampos SMultiPKFile::SeekInListV1(ifstream &ifs,
00148   SMultiPKFileRep::UShort numEntries, const FP::Tag &pk)
00149   throw (SMultiPKFileRep::NotFound, FS::EndOfFile, FS::Failure)
00150 {
00151     while (numEntries > 0) {
00152         SMultiPKFileRep::HeaderEntry ent(ifs);
00153         if (ent.pk == pk) return (streampos)(ent.offset);
00154         numEntries--;
00155     }
00156     throw SMultiPKFileRep::NotFound();
00157 }
00158 
00159 streampos SMultiPKFile::SeekInSortedListV1(ifstream &ifs,
00160   SMultiPKFileRep::UShort numEntries, const FP::Tag &pk)
00161   throw (SMultiPKFileRep::NotFound, FS::EndOfFile, FS::Failure)
00162 {
00163     // lookup the entry using binary search
00164     int lo = 0, hi = numEntries;
00165     streampos base = FS::Posn(ifs); // record start of entries on disk
00166     while (lo < hi) {
00167         // Loop invariant: Matching entry (if any) has index in [lo, hi).
00168         // On each iteration, the difference "hi - lo" is guaranteed to
00169         // decrease, so the loop is guaranteed to terminate.
00170 
00171         // read middle entry
00172         int mid = (lo + hi) / 2;
00173         assert(lo <= mid && mid < hi); // mid is in [lo, hi)
00174         FS::Seek(ifs, base +
00175                  (streampos) (mid * SMultiPKFileRep::HeaderEntry::Size()));
00176         SMultiPKFileRep::HeaderEntry midEntry(ifs);
00177 
00178         // compare middle CFP to "pk"
00179         int cmp = Compare(pk, midEntry.pk);
00180         if (cmp < 0) {
00181             // matching entry in [lo, mid) => make "hi" smaller
00182             hi = mid;
00183         } else if (cmp > 0) {
00184             // matching entry in [mid+1, hi) => make "lo" larger
00185             lo = mid + 1;
00186         } else {
00187             return (streampos)(midEntry.offset);
00188         }
00189     }
00190     throw SMultiPKFileRep::NotFound();
00191 }
00192 
00193 // Rewrite methods ------------------------------------------------------------
00194 
00195 bool SMultiPKFile::ChkptForRewrite(VPKFileMap& vpkFiles,
00196                                    const BitVector *toDelete,
00197                                    /*OUT*/ ChkPtTbl &vpkChkptTbl)
00198      throw()
00199 /* REQUIRES (forall vf: VPKFile :: Sup(LL) < vf.mu) */
00200 
00201 /* First, if toDelete is NULL, check each VPKFile in "vpkFiles" to see
00202    if it has any new entries.  If there are no new entries in any of
00203    these, return false to indicate that no rw-write is needed.
00204 
00205    If we have not decided that a re-write is unneccessary, Each
00206    VPKFile in "vpkFiles" is checkpointed (see the "VPKFileChkPt"
00207    interface for a description of how a checkpoint is represented)
00208    with the checkpoints placed in "vpkChkptTbl". Only the entries
00209    stored in the checkpoint are considered during the process of
00210    re-writing a MultiPKFile (with SMultiPKFile::Rewrite, below).
00211 
00212    The return value indicates whether a re-write is neccessary, and
00213    consequently whether the VPKFiles have been checkpointed into
00214    "vpkChkptTbl". */
00215 {
00216     FP::Tag pk;
00217     VPKFile *vpkfile;
00218     VPKFileIter it(&vpkFiles);
00219 
00220     /* If no deletions are pending and none of the VPKFiles has any
00221        new entries to flush, then this MultiPKFile will be unchanged,
00222        so return false to indicate that no re-wrtoe will be
00223        performed. */
00224     if ((toDelete == (BitVector *)NULL)) {
00225         bool anyNewEntries = false;
00226         while (!anyNewEntries && it.Next(/*OUT*/ pk, /*OUT*/ vpkfile)) {
00227             vpkfile->mu.lock();
00228             if (vpkfile->HasNewEntries()) { anyNewEntries = true; }
00229             vpkfile->mu.unlock();
00230         }
00231         if (!anyNewEntries) return false;
00232     }
00233 
00234     // checkpoint entries in "vpkFiles"; store them in "chkptTbl"
00235     for (it.Reset(); it.Next(/*OUT*/ pk, /*OUT*/ vpkfile); /*SKIP*/) {
00236         VPKFileChkPt *chkpt = vpkfile->CheckPoint();
00237         bool inTbl = vpkChkptTbl.Put(pk, chkpt, /*resize=*/ false);
00238         assert(!inTbl);
00239     }
00240     vpkChkptTbl.Resize();
00241 
00242     // If we made it this far, we've decided that there may be some
00243     // re-writing to do, so return true to indicate that.
00244     return true;
00245 }
00246 
00247 bool SMultiPKFile::PrepareForRewrite(const PKPrefix::T &pfx,
00248                                      ifstream &ifs,
00249                                      /*OUT*/ SMultiPKFileRep::Header *&hdr)
00250   throw (FS::Failure, FS::EndOfFile)
00251 {
00252   bool mpkFileExists; // true iff "ifs" is an open file
00253 
00254   try
00255     {
00256       OpenRead(pfx, /*OUT*/ ifs);
00257       mpkFileExists = true;
00258     }
00259   catch (SMultiPKFileRep::NotFound)
00260     {
00261       mpkFileExists = false;
00262     }
00263 
00264   // Read existing MultiPKFile header and header entries
00265   if (mpkFileExists)
00266     {
00267       try
00268         {
00269           hdr = SMultiPKFile::ReadHeader(pfx, /*readEntries=*/ true, ifs);
00270         }
00271       catch (...)
00272         {
00273           // Log something about which file we encountered a problem
00274           // with.
00275           cerr << "Exception while reeading MPKFile header "
00276                << FullName(pfx)
00277                << endl;
00278           // Make sure we close the input stream if we opened it.
00279           if (mpkFileExists) FS::Close(ifs);
00280           // Re-throw the exception.
00281           throw;
00282         }
00283     }
00284   else
00285     {
00286       // allocate an empty one
00287       hdr = NEW(SMultiPKFileRep::Header);
00288     }
00289 
00290   return mpkFileExists;
00291 }
00292 
00293 void SMultiPKFile::Rewrite(const PKPrefix::T &pfx,
00294 
00295                            // From PrepareForRewrite above
00296                            bool mpkFileExists, ifstream &ifs,
00297                            SMultiPKFileRep::Header *hdr,
00298 
00299                            VPKFileMap& vpkFiles,
00300                            ChkPtTbl &vpkChkptTbl,
00301                            const BitVector *toDelete,
00302                            EmptyPKLog *emptyPKLog,
00303                            /*INOUT*/ EntryState &state)
00304   throw (FS::Failure, FS::EndOfFile, VestaLog::Error)
00305 /* REQUIRES (forall vf: VPKFile :: Sup(LL) < vf.mu) */
00306 /* This is the routine for rewriting a MultiPKFile. It is roughly equivalent
00307    to the "VToSCache" function of the "SVCache" specification, except
00308    that it is working with a *group* of PKFiles stored in a single
00309    MultiPKFile.
00310 
00311    For each PKFile contained in the MultiPKFile with prefix "pfx" (i.e. those
00312    appearing in the range of "vpkfile"), it reads the PKFile (if any) from
00313    disk, and then writes out a new version of the PKFile that includes any new
00314    entries contained in the "VPKFile"'s stored in "vpkFiles", removing any
00315    entries named in "toDelete" (if it is non-NULL).
00316 
00317    "vpkChkptTbl" must contain checkpoints of each VPKFile in
00318    "vpkFiles". (These checkpoints are normally created by
00319    SMultiPKFile::ChkptForRewrite, above.)  Only the entries stored in
00320    these checkpoints are considered in the rest of the procedure. The
00321    checkpoints are local, so fields within them that don't point back
00322    to the original VPKFile can be accessed without a lock; those that
00323    point back to the VPKFile must be accessed with a lock held.
00324 
00325    The MultiPKFile is read from disk into "SMultPKFileRep::Header" and
00326    "SMultiPKFileRep::HeaderEntry" structures in memory. The latter in turn
00327    contain "SPKFile" structures that contain the contents of individual
00328    PKFiles within the MultiPKFile. All of the updates are performed on these
00329    in-memory structures, and then the result is written out to disk.
00330 
00331    The new MultiPKFile is written to disk using a "AtomicFile" object.
00332    This object causes a temporary file object to be written. The pointer for
00333    the actual file is swung when the "AtomicFile" object is closed.
00334    Hence, the entire file can be written without a lock; the lock is only
00335    required when the file is closed.
00336 
00337    However, before the file pointer can be swung, each of the "VPKFile"'s for
00338    "pfx" must be modified. These modifications must occur with the appropriate
00339    "VPKFile" locks held. Since it would be unsafe for a thread to have access
00340    to an updated "VPKFile" before it had access to the updated MultiPKFile on
00341    disk, all of the "VPKFile" locks for the MultiPKFile are held until after
00342    the pointer has been swung. */
00343 {
00344     FP::Tag pk;
00345     VPKFile *vpkfile;
00346     VPKFileIter it(&vpkFiles);
00347 
00348     AtomicFile ofs;
00349     try {
00350       /* Add new empty VPKFiles to "hdr" for those with pending entries to
00351          flush that are not yet in this MultiPKFile. Remove from "vpkFiles"
00352          those that have no pending entries and no PKFile on disk. */
00353       for (it.Reset(); it.Next(/*OUT*/ pk, /*OUT*/ vpkfile); /*SKIP*/) {
00354         VPKFileChkPt *chkpt;
00355         SMultiPKFileRep::HeaderEntry *dummyHE;
00356         bool inTbl = vpkChkptTbl.Get(pk, /*OUT*/ chkpt); assert(inTbl);
00357         if (chkpt->hasNewEntries) {
00358           (void)(hdr->AppendNewHeaderEntry(pk));
00359         } else if (!hdr->pkTbl.Get(pk, /*OUT*/ dummyHE)) {
00360           // if not in "hdr" already, remove "pk" from "vpkFiles"
00361           inTbl = vpkFiles.Delete(pk, /*OUT*/ vpkfile, /*resize=*/ false);
00362           assert(inTbl);
00363         }
00364       }
00365       vpkFiles.Resize();
00366 
00367       // Determine whether the MultiPKFile is written in an old format.
00368       // When this is the case, we will always re-write every PKFile in
00369       // it, even ones that have no changes (no new entries and no
00370       // deletions).  Ordinarilly, we copy such PKFiles from the old
00371       // stable MultiPKFile to the new one, but if the file format has
00372       // changed, this can create a MultiPKFile to contain PKFiles with
00373       // different formats.  Since the file version is only stored at
00374       // the MultiPKFile granularity, we can't handle PKFiles with
00375       // different formats in the same MultiPKFile.
00376       bool l_fileFormatChange = mpkFileExists &&
00377         (hdr->version < SPKFileRep::LastVersion);
00378 
00379       // update the individual PKFiles (in memory only)
00380       int i;
00381       for (i = 0; i < hdr->num; i++) {
00382         FP::Tag *pki = hdr->pkSeq[i];    // "pki" == an alias for the "i"th PK
00383         SMultiPKFileRep::HeaderEntry *he;
00384         bool inTbl = hdr->pkTbl.Get(*pki, /*OUT*/ he); assert(inTbl);
00385         VPKFileChkPt *chkpt = (VPKFileChkPt *)NULL;
00386         (void)(vpkChkptTbl.Get(he->pk, /*OUT*/ chkpt));
00387 
00388         // There must be a volatile PKFile for every stable PKFile.
00389         inTbl = vpkFiles.Get(he->pk, /*OUT*/ vpkfile); assert(inTbl);
00390 
00391         // only read and update the SPKFile if there are deletions pending
00392         // or if this PKFile has new entries pending
00393         if ((toDelete != (BitVector *)NULL) ||
00394             ((chkpt != (VPKFileChkPt *)NULL) && (chkpt->hasNewEntries))) {
00395 
00396           // read/create the SPKFile
00397           if (he->SPKFileExists()) {
00398             // read existing SPKFile from disk
00399             assert(mpkFileExists);
00400             FS::Seek(ifs, he->offset);
00401             he->pkfile = NEW_CONSTR(SPKFile,
00402                                     (he->pk, ifs, he->offset,
00403                                      hdr->version, /*OUT*/ he->pkhdr,
00404                                      /*readEntries=*/ true,
00405                                      /*readImmutable=*/ Config_ReadImmutable));
00406 
00407 #ifdef MISTRUST_PKLEN
00408             // Check for incorrect pkLen
00409             if(FS::Posn(ifs) != (streampos) (he->offset + he->pkLen))
00410               {
00411                 Debug::Lock();
00412 
00413                 cerr << Debug::Timestamp()
00414                      << " -- Detected incorrect pkLen" << endl
00415                      << "  pkfile             = " << i << endl
00416                      << "  expoected position = "
00417                      << (he->offset + he->pkLen) << endl
00418                      << "  actual position    = " << FS::Posn(ifs) << endl;
00419                 hdr->Debug(cerr, true);
00420 
00421                 Debug::Unlock();
00422               }
00423 #endif
00424           } else {
00425             // create a new, empty SPKFile
00426             he->pkfile = NEW_CONSTR(SPKFile, (he->pk));
00427           }
00428 
00429           // Before updating either, the volatile PKFile's common
00430           // names set must match that of the stable PKFile.
00431           assert(*(he->pkfile->CommonNames()) == *(vpkfile->CommonNames()));
00432 
00433           // update it (this is where all the merging work is done)
00434           he->pkfileModified = he->pkfile->Update(chkpt, toDelete,
00435                                                   /*OUT*/ he->exCommonNames,
00436                                                   /*OUT*/ he->exUncommonNames,
00437                                                   /*OUT*/ he->packMask,
00438                                                   /*OUT*/ he->reMap,
00439                                                   /*OUT*/ he->becameEmpty);
00440 
00441           // delete this PKFile from the MultiPKFile if it is now empty
00442           if (he->becameEmpty) {
00443             // remove it from "hdr"
00444             hdr->RemoveHeaderEntry(*pki);
00445             i--; // don't advance index in "pkSeq" array
00446 
00447             // add it to the "EmptyPKLog"
00448             emptyPKLog->Write(*pki, he->pkfile->PKEpoch());
00449           }
00450         } else
00451           // If we're rewriting a MultiPKFile that's written in an old
00452           // format, load even PKFiles with no changes pending.
00453           if(l_fileFormatChange
00454 #ifdef MISTRUST_PKLEN
00455              // If we suspect that pkLens may be wrong, always load
00456              // all PKFiles in a MultiPKFile being rewritten.
00457              || true
00458 #endif
00459              ) {
00460             // Note that we should never get here for a PKFile that
00461             // doesn't already exist on disk.  A loop above removes
00462             // all references to VPKFiles that are not on disk and
00463             // don't have changes pending, and the previous if clause
00464             // handles all PKFiles with changes pending.
00465             assert(he->SPKFileExists());
00466 
00467             // read existing SPKFile from disk
00468             assert(mpkFileExists);
00469             FS::Seek(ifs, he->offset);
00470             he->pkfile = NEW_CONSTR(SPKFile,
00471                                     (he->pk, ifs, he->offset,
00472                                      hdr->version, /*OUT*/ he->pkhdr,
00473                                      /*readEntries=*/ true,
00474                                      /*readImmutable=*/ Config_ReadImmutable));
00475 
00476           // Even if we're not updating, the volatile PKFile's common
00477           // names set must match that of the stable PKFile.
00478           assert(*(he->pkfile->CommonNames()) == *(vpkfile->CommonNames()));
00479 
00480 #ifdef MISTRUST_PKLEN
00481           // Check for incorrect pkLen
00482           if(FS::Posn(ifs) != (streampos) (he->offset + he->pkLen))
00483             {
00484               Debug::Lock();
00485 
00486               cerr << Debug::Timestamp()
00487                    << " -- Detected incorrect pkLen:" << endl
00488                    << "  pkfile             = " << i << endl
00489                    << "  expoected position = "
00490                    << (he->offset + he->pkLen) << endl
00491                    << "  actual position    = " << FS::Posn(ifs) << endl;
00492               hdr->Debug(cerr, true);
00493 
00494               Debug::Unlock();
00495             }
00496 #endif
00497 
00498           } else {
00499             he->pkfileModified = false;
00500           }
00501       }
00502 
00503       // write the SMultiPKFile to disk if it is non-empty
00504       if (hdr->num > 0) {
00505         // create a new file to write result to
00506         OpenAtomicWrite(pfx, /*OUT*/ ofs);
00507 
00508         // write "hdr" and its header entries to disk
00509         hdr->Write(ofs);
00510         hdr->WriteEntries(ofs);
00511 
00512         // write PKFiles to disk
00513         for (i = 0; i < hdr->num; i++) {
00514           FP::Tag *pki = hdr->pkSeq[i]; // an alias for the "i"th PK
00515           SMultiPKFileRep::HeaderEntry *he;
00516           bool inTbl = hdr->pkTbl.Get(*pki, /*OUT*/ he); assert(inTbl);
00517           if (he->pkfileModified || l_fileFormatChange
00518 #ifdef MISTRUST_PKLEN
00519               // If we suspect that pkLens may be wrong, always
00520               // rewrite all PKFiles.
00521               || true
00522 #endif
00523               ) {
00524             // write new version of PKFile to disk (either because
00525             // the contents of the PKFile have changed, or because
00526             // the file format has changed).
00527             he->offset = FS::Posn(ofs);
00528             assert(he->pkfile != NULL);
00529             he->pkfile->Write(ifs, ofs, he->offset, /*OUT*/ he->pkhdr);
00530           } else {
00531             // copy PKFile on disk from "ifs" to "ofs"
00532             FS::Seek(ifs, he->offset);
00533             he->offset = FS::Posn(ofs);
00534             FS::Copy(ifs, ofs, he->pkLen);
00535           }
00536         }
00537 
00538         // back-patch the SMultiPKFile and relevant SPKFiles
00539         hdr->BackPatch(ofs);
00540         for (i = 0; i < hdr->num; i++) {
00541           FP::Tag *pki = hdr->pkSeq[i]; // an alias for the "i"th PK
00542           SMultiPKFileRep::HeaderEntry *he;
00543           bool inTbl = hdr->pkTbl.Get(*pki, /*OUT*/ he); assert(inTbl);
00544           /* We only have to backpatch the PKFiles that were
00545              changed, since the offsets stored in a PKFile header
00546              are all relative to the start of the PKFile within the
00547              MultiPKFile.  (If we're writing out a different file
00548              format, all PKFiles have probably changed, so we should
00549              backpatch the offsets for them all.) */
00550           if (he->pkfileModified || l_fileFormatChange
00551 #ifdef MISTRUST_PKLEN
00552               // If we suspect that pkLens may be wrong, we rewrite
00553               // the PKfile, so we backpatch it.  Shouldn't be
00554               // strictly neccessary, but seems like a good idea.
00555               || true
00556 #endif
00557               ) {
00558             assert(he->pkfile != NULL && he->pkhdr != NULL);
00559             he->pkhdr->BackPatch(ofs, he->offset);
00560           }
00561         }
00562       }
00563     }
00564     catch (...)
00565       {
00566         // Log something about which file we encountered a problem
00567         // with.
00568         cerr << "Exception while re-writing MPKFile "
00569              << FullName(pfx)
00570              << endl;
00571         // Make sure we close the input stream if we opened it.
00572         if (mpkFileExists) FS::Close(ifs);
00573         // Re-throw the exception.
00574         throw;
00575       }
00576     if (mpkFileExists) FS::Close(ifs);
00577 
00578     // pause if necessary (for debugging)
00579     if (Config_WeedPauseDur > 0) PauseDeletionThread();
00580 
00581     // atomically replace VPKFiles and swing SMultiPKFile pointer.
00582     for (it.Reset(); it.Next(/*OUT*/ pk, /*OUT*/ vpkfile); /*SKIP*/) {
00583         // find HeaderEntry and corresponding checkpoint
00584         bool inTbl;
00585         SMultiPKFileRep::HeaderEntry *he;
00586         VPKFileChkPt *chkpt;
00587         inTbl = hdr->pkTbl.Get(pk, /*OUT*/ he); assert(inTbl);
00588         inTbl = vpkChkptTbl.Get(pk, /*OUT*/ chkpt); assert(inTbl);
00589 
00590         // acquire lock on "vpkfile"
00591         vpkfile->mu.lock();
00592 
00593         // VPKfiles in use by a flush to the stable cache cannot be
00594         // evicted.  This should be guaranteed, but we check it just
00595         // to be sure.
00596         if(vpkfile->Evicted())
00597           {
00598             Debug::Lock();
00599 
00600             cerr << Debug::Timestamp()
00601                  << "INTERNAL ERROR: "
00602                  << "evicted VPKFile in SMultiPKFile::Rewrite:" << endl
00603                  << "  pk           = " << pk << endl
00604                  << " (Please report this as a bug.)" << endl;
00605 
00606             Debug::Unlock();
00607 
00608             // Crash and dump core, as we can't continue correctly.
00609             abort();
00610           }
00611 
00612         /* It is safe to decrement the "pkEpoch" of the VPKFile if the
00613            SPKFile was unmodified and if no new entries have arrived
00614            since the VPKFile was checkpointed. */
00615         if (!he->pkfileModified && !vpkfile->HasNewEntries()) {
00616             vpkfile->DecrPKEpoch();
00617         }
00618 
00619         // update the VPKFile
00620         /* This is only necessary if the SPKFile was modified on disk
00621            or if the VPKFile has new entries pending. The latter implies the
00622            former, even if there were new entries in the checkpoint but they
00623            were all deleted. */
00624         assert(!chkpt->hasNewEntries || he->pkfileModified);
00625         if (he->pkfileModified) {
00626             assert(he->pkfile != (SPKFile *)NULL);
00627             // The following assertion assumes that no more than one flush
00628             // can be in progress at a time.  --mann
00629             assert((vpkfile->PKEpoch() == he->pkfile->PKEpoch()) &&
00630                    (vpkfile->PKEpoch() == (chkpt->pkEpoch + 1)));
00631             vpkfile->Update(*(he->pkfile), *chkpt,
00632               toDelete, he->exCommonNames, he->exUncommonNames,
00633               he->packMask, he->reMap, he->becameEmpty, /*INOUT*/ state);
00634         }
00635 
00636         // After updating the volatile PKFile if necessary, its set of
00637         // common names should match the new set of common names in
00638         // the stable PKFile.
00639         if(he->pkfile != NULL)
00640           {
00641             assert(*(he->pkfile->CommonNames()) == *(vpkfile->CommonNames()));
00642           }
00643     }
00644 
00645     // delete file or swing file pointer atomically
00646     if (hdr->num == 0) {
00647         // delete the MultiPKFile if it exists
00648         /* We test if the file exists by whether the "totalLen"
00649            field of the SMultiPKFileRep::Header object is non-zero. */
00650         if (hdr->totalLen > 0) DeleteFile(pfx);
00651     } else {
00652         FS::Close(ofs); // swing file pointer atomically
00653     }
00654 
00655     // unlock all the VPKFile locks
00656     for (it.Reset(); it.Next(/*OUT*/ pk, /*OUT*/ vpkfile); /*SKIP*/) {
00657         vpkfile->mu.unlock();
00658     }
00659 } // SMultiPKFile::Rewrite
00660 
00661 SMultiPKFileRep::Header* SMultiPKFile::ReadHeader(const PKPrefix::T &pfx,
00662   bool readEntries, ifstream &ifs) throw (FS::Failure, FS::EndOfFile)
00663 {
00664     SMultiPKFileRep::Header *res;
00665     try {
00666       // Read MultiPKFile <Header> and all its entries
00667       res = NEW_CONSTR(SMultiPKFileRep::Header, (ifs));
00668       if (readEntries) res->ReadEntries(ifs);
00669     }
00670     catch (SMultiPKFileRep::BadMPKFile) {
00671         cerr << "Fatal error: the MultiPKFile for prefix " << pfx
00672              << " is bad; crashing..." << endl;
00673         assert(false);
00674     }
00675     return res;
00676 }
00677 
00678 Text SMultiPKFile::Name(const PKPrefix::T &pfx, int granBits) throw ()
00679 {
00680     char buff[10];
00681     int printLen = sprintf(buff, "gran-%02d/", granBits);
00682     assert(printLen == 8);
00683     Text res(buff);
00684     res += pfx.Pathname(granBits, ArcBits);
00685     return res;
00686 }
00687 
00688 Text SMultiPKFile::FullName(const PKPrefix::T &pfx) throw ()
00689 {
00690     Text nm(Name(pfx, PKPrefix::Granularity()));
00691     Text res(Config_SCachePath + "/" + nm);
00692     return res;
00693 }
00694 
00695 void SMultiPKFile::OpenAtomicWrite(const PKPrefix::T &pfx,
00696   /*OUT*/ AtomicFile &ofs) throw (FS::Failure)
00697 {
00698     // compute filename
00699     Text fpath(FullName(pfx));
00700 
00701     // open file
00702     ofs.open(fpath.chars(), ios::out);
00703     if (ofs.fail() && errno == ENOENT) {
00704         // a directory is missing, so create any necessary ones
00705         MakeDirs(fpath);
00706 
00707         // Clear the stream state before we try again, as open may not
00708         // reset it to good for us.
00709         ofs.clear(ios::goodbit);
00710 
00711         // try opening it again
00712         ofs.open(fpath.chars(), ios::out);
00713     }
00714 
00715     // check if there is still an error
00716     if (ofs.fail()) {
00717         throw(FS::Failure(Text("OpenAtomicWrite"), fpath));
00718     }
00719 }
00720 
00721 void SMultiPKFile::MakeDirs(const Text &name) throw (FS::Failure)
00722 {
00723     // local constants
00724     const char PathnameSep = '/';
00725     const int  DirMode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
00726 
00727     // first make a copy of the string, since we will be mutating it
00728     Text nm(name.chars());
00729 
00730     // find last '/'; this separates last directory from filename
00731     const char *start = nm.cchars();
00732     char *q = ((char *)start) + nm.Length();
00733 
00734     // work backwards to find first directory that does not exist
00735     int stat_res;
00736     do {
00737         struct stat buffer;
00738         for (q--; q >= start && *q != PathnameSep; q--) /*SKIP*/;
00739         assert(q >= start);
00740         *q = '\0';
00741         stat_res = stat(start, /*OUT*/ &buffer);
00742         *q = PathnameSep;
00743     } while (stat_res != 0);
00744 
00745     // try creating each prefix of the path
00746     while (true) {
00747         q = strchr(q+1, PathnameSep);
00748         if (q == (char *)NULL) break;
00749         *q = '\0';
00750         int status = mkdir(start, DirMode);
00751         *q = PathnameSep;
00752         if (status < 0 && errno != EEXIST) {
00753             throw (FS::Failure(Text("SMultiPKFile::MakeDirs"), name));
00754         }
00755     }
00756 }
00757 
00758 void SMultiPKFile::DeleteFile(const PKPrefix::T &pfx)
00759   throw (FS::Failure)
00760 {
00761     // delete the file itself
00762     Text fpath(FullName(pfx));
00763     FS::Delete(fpath);
00764 
00765     // delete successive parent directories that are empty
00766     int i = fpath.Length(); bool deleted;
00767     do {
00768         i = fpath.FindCharR('/', i - 1); assert(i >= 0);
00769         Text dir(fpath.Sub(0, i));
00770         deleted = (dir != Config_SCachePath && FS::DeleteDir(dir));
00771     } while (deleted);
00772 }
00773 
00774 void SMultiPKFile::PauseDeletionThread() throw ()
00775 {
00776     // pre debugging
00777     Debug::Lock();
00778     cout << Debug::Timestamp() << "STARTING -- Pausing Deletion Thread" <<endl;
00779     cout << "  Duration = " << Config_WeedPauseDur << " seconds" << endl;
00780     cout << endl;
00781     Debug::Unlock();
00782 
00783     // pause
00784     Basics::thread::pause(Config_WeedPauseDur);
00785 
00786     // post debugging
00787     Debug::Lock();
00788     cout << Debug::Timestamp() << "DONE -- Pausing Deletion Thread" << endl;
00789     cout << endl;
00790     Debug::Unlock();
00791 }

Generated on Mon May 8 00:48:35 2006 for Vesta by  doxygen 1.4.2