"Fossies" - the Fresh Open Source Software Archive

Member "coda-6.9.5/coda-src/venus/fso0.cc" (23 Mar 2010, 42405 Bytes) of package /linux/misc/old/coda-6.9.5.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "fso0.cc" see the Fossies "Dox" file reference documentation.

    1 /* BLURB gpl
    2 
    3                            Coda File System
    4                               Release 6
    5 
    6           Copyright (c) 1987-2008 Carnegie Mellon University
    7                   Additional copyrights listed below
    8 
    9 This  code  is  distributed "AS IS" without warranty of any kind under
   10 the terms of the GNU General Public Licence Version 2, as shown in the
   11 file  LICENSE.  The  technical and financial  contributors to Coda are
   12 listed in the file CREDITS.
   13 
   14                         Additional copyrights
   15                            none currently
   16 
   17 #*/
   18 
   19 /*
   20  *
   21  *    Implementation of the Venus File-System Object (fso) abstraction.
   22  *
   23  *    ToDo:
   24  *       1. Need to allocate meta-data by priority (escpecially in the case of dir pages and modlog entries)
   25  */
   26 
   27 
   28 #ifdef __cplusplus
   29 extern "C" {
   30 #endif
   31 
   32 #ifdef HAVE_CONFIG_H
   33 #include <config.h>
   34 #endif
   35 
   36 #include <stdio.h>
   37 #include "coda_string.h"
   38 #include <sys/types.h>
   39 #include <sys/stat.h>
   40 #include <struct.h>
   41 #include <sys/param.h>
   42 #include <sys/wait.h>
   43 #include <unistd.h>
   44 #include <stdlib.h>
   45 #include <dirent.h>
   46 
   47 #ifdef __cplusplus
   48 }
   49 #endif
   50 
   51 /* interfaces */
   52 #include <vcrcommon.h>
   53 
   54 /* from venus */
   55 #include "fso.h"
   56 #include "hdb.h"
   57 #include "local.h"
   58 #include "mariner.h"
   59 #include "user.h"
   60 #include "venus.private.h"
   61 #include "venusrecov.h"
   62 #include "venusvol.h"
   63 #include "vproc.h"
   64 #include "worker.h"
   65 
   66 int CacheFiles = 0;
   67 int FSO_SWT = UNSET_SWT;
   68 int FSO_MWT = UNSET_MWT;
   69 int FSO_SSF = UNSET_SSF;
   70 
   71 
   72 /* Call with CacheDir the current directory. */
   73 void FSOInit() {
   74     int i; 
   75 
   76     /* Allocate the database if requested. */
   77     if (InitMetaData) {                 /* <==> FSDB == 0 */
   78         Recov_BeginTrans();
   79         RVMLIB_REC_OBJECT(FSDB);
   80         FSDB = new fsdb;
   81         Recov_EndTrans(0);
   82     }
   83 
   84     /* Check the persistent, but separately initializable, members. */
   85     Recov_BeginTrans();
   86     RVMLIB_REC_OBJECT(*FSDB);
   87 
   88     if (InitMetaData || FSDB->MaxBlocks != CacheBlocks) {
   89         if (!InitMetaData)
   90             eprint("Warning: CacheBlocks changing from %d to %d",
   91                FSDB->MaxBlocks, CacheBlocks);
   92         
   93         FSDB->MaxBlocks = CacheBlocks;
   94     }
   95     FSDB->FreeBlockMargin = FSDB->MaxBlocks / FREE_FACTOR;
   96     
   97     if (InitMetaData || (FSO_SWT != UNSET_SWT && FSDB->swt != FSO_SWT))
   98         FSDB->swt = (FSO_SWT == UNSET_SWT ? DFLT_SWT : FSO_SWT);
   99     if (InitMetaData || (FSO_MWT != UNSET_MWT && FSDB->mwt != FSO_MWT))
  100         FSDB->mwt = (FSO_MWT == UNSET_MWT ? DFLT_MWT : FSO_MWT);
  101     if (InitMetaData || (FSO_SSF != UNSET_SSF && FSDB->ssf != FSO_SSF))
  102         FSDB->ssf = (FSO_SSF == UNSET_SSF ? DFLT_SSF : FSO_SSF);
  103     FSDB->maxpri = FSDB->MakePri(FSO_MAX_SPRI, FSO_MAX_MPRI);
  104     FSDB->stdpri = FSDB->MakePri(FSO_MAX_SPRI, FSO_MAX_MPRI / 2);
  105     FSDB->marginpri = FSDB->MakePri(FSO_MAX_SPRI, 0);
  106     Recov_EndTrans(0);
  107 
  108     /* Initialize transient members. */
  109     FSDB->ResetTransient();
  110 
  111     /* Recover and initialize the fsobj's. */
  112     /* Recovery is needed because cache files are in UFS, not RVM. */
  113     {
  114     /* Validate Meta- and Non-Meta-Data version stamps. */
  115     if (!InitMetaData) {
  116         FILE *fp = fopen("CacheInfo", "r");
  117         if (fp == NULL)
  118         eprint("Warning: no CacheInfo file");
  119         else {
  120         int DataVersion = 0;
  121         (void)fscanf(fp, "%d", &DataVersion);
  122         (void)fclose(fp);
  123 
  124         if (DataVersion != FSDB->DataVersion)
  125             eprint("Warning: data version mismatch (%d, %d)",
  126                DataVersion, FSDB->DataVersion);
  127         }
  128     }
  129 
  130     /* Allocate the fsobj's if requested. */
  131     if (InitMetaData) {
  132         /* Do this in a loop to avoid one mongo transaction! */
  133         for (i = 0; i < FSDB->MaxFiles; i++) {
  134             Recov_BeginTrans();
  135             (void)new (FROMHEAP) fsobj(i);
  136             Recov_EndTrans(MAXFP);
  137         }
  138     }
  139 
  140     /* Recover the cache files (allocates as necessary). */
  141     {
  142             /* This is done in the subsequent fsobj recovery loop because we
  143              * statically associate fsobj's and cache-file descriptors (i.e.,
  144              * we embed one of the latter in the former). If we were to make
  145              * the association dynamic instead, we would need to iterate
  146              * through the cache-file descriptor handles here, validating
  147              * and/or resetting them. */
  148     }
  149 
  150     /* Recover the fsobj's. */
  151     {
  152         eprint("starting FSDB scan (%d, %d) (%d, %d, %d)",
  153            FSDB->MaxFiles, FSDB->MaxBlocks,
  154            FSDB->swt, FSDB->mwt, FSDB->ssf);
  155 
  156         /* Check entries in the table. */
  157         {
  158         fso_iterator next(NL);
  159         fsobj *f;
  160         while ((f = next())) {
  161             /* Validate the cache-file, and record its blocks. */
  162             f->cf.Validate();
  163             FSDB->ChangeDiskUsage(NBLOCKS(f->cf.Length()));
  164 
  165             /* Initialize transient members. */
  166             f->ResetTransient();
  167 
  168             /* Recover object state. */
  169             f->Recover();
  170         }
  171 
  172         eprint("\t%d cache files in table (%d blocks)",
  173                (FSDB->htab).count(), FSDB->blocks);
  174         }
  175 
  176         /* Check entries on the freelist. */
  177         {
  178         rec_olist_iterator next(FSDB->freelist);
  179         rec_olink *o;
  180         while ((o = next())) {
  181             fsobj *f = strbase(fsobj, o, primary_handle);
  182 
  183             /* Reset the cache file. */
  184             f->cf.Reset();
  185         }
  186 
  187         eprint("\t%d cache files on free-list",
  188                (FSDB->freelist).count());
  189         }
  190 
  191         if ((FSDB->htab).count() + (FSDB->freelist).count() != FSDB->MaxFiles)
  192         CHOKE("FSOInit: missing %d cache files",
  193             FSDB->MaxFiles - ((FSDB->htab).count() + (FSDB->freelist).count()));
  194     }
  195 
  196     /* Recover parent <--> child bindings. */
  197     /* This MUST wait until all fsobj's have been recovered/reset! */
  198     /* 
  199      * Need not be in a transaction for the call to SetParent, because
  200      * the parent vnode and unique arguments are the very ones in the fsobj 
  201      * (no recoverable store gets changed).
  202      */
  203     {
  204         fso_iterator next(NL);
  205         fsobj *cf;
  206         while ((cf = next()))
  207         if (!cf->IsExpandedObj())
  208             cf->SetParent(cf->pfid.Vnode, cf->pfid.Unique);
  209         else
  210             /* expanded objects need to be pinned down */
  211             FSO_HOLD(cf);
  212     }
  213 
  214     /* Recover fsobj <--> cmlent bindings: a grid-like data structure. */
  215     /* This MUST wait until all fsobj's and cmlent's have been recovered/reset! */
  216     {
  217         Recov_BeginTrans();
  218         VDB->AttachFidBindings();
  219         Recov_EndTrans(0);
  220 
  221         /* Sanity check. */
  222         fso_iterator next(NL);
  223         fsobj *f;
  224         while ((f = next())) {
  225         FSO_ASSERT(f,
  226                (DIRTY(f) && f->mle_bindings != 0 && f->mle_bindings->count() > 0) ||
  227                (!DIRTY(f) && f->mle_bindings == 0));
  228         }
  229     }
  230     }
  231 
  232     /* Set new Data version stamps. */
  233     unsigned long DataVersion = (unsigned long) Vtime();
  234 
  235     FILE *fp = fopen("CacheInfo", "w+");
  236     if (fp == NULL)
  237     CHOKE("FSOInit: fopen(CacheInfo, WR)");
  238     fprintf(fp, "%ld", DataVersion);
  239     fclose(fp);
  240 
  241     Recov_BeginTrans();
  242     RVMLIB_REC_OBJECT(FSDB->DataVersion);
  243     FSDB->DataVersion = DataVersion;
  244     Recov_EndTrans(0);
  245 
  246     RecovFlush(1);
  247     RecovTruncate(1);
  248 
  249     /* Fire up the daemon. */
  250     FSOD_Init();
  251 }
  252 
  253 
  254 static int FSO_HashFN(const void *key)
  255 {
  256     VenusFid *fid = (VenusFid *)key;
  257     return(fid->Realm + fid->Volume + fid->Vnode);
  258 }
  259 
  260 
  261 int FSO_PriorityFN(bsnode *b1, bsnode *b2) {
  262     fsobj *f1 = strbase(fsobj, b1, prio_handle);
  263     fsobj *f2 = strbase(fsobj, b2, prio_handle);
  264     CODA_ASSERT((char *)f1 != (char *)f2);
  265 
  266     if (f1->priority > f2->priority) return(1);
  267     if (f1->priority < f2->priority) return(-1);
  268 
  269     /* Use "random" bits assigned at allocation to break (most) ties. */
  270     if (f1->flags.random > f2->flags.random) return(1);
  271     if (f1->flags.random < f2->flags.random) return(-1);
  272 
  273     /* The chance of this ever happening should be miniscule! -JJK */
  274 /*
  275     eprint("FSO_PriorityFN: priorities tied (%d, %d)!\n",
  276         f1->priority, f1->flags.random);
  277 */
  278     LOG(1, ("FSO_PriorityFN: priorities tied (%d, %d)!\n",
  279          f1->priority, f1->flags.random));
  280     return(0);
  281 }
  282 
  283 
  284 void UpdateCacheStats(CacheStats *c, enum CacheEvent event, unsigned long blocks) {
  285     if (event < HIT || event > REPLACE)
  286     CHOKE("UpdateCacheStats: bogus event (%d)", event);
  287 
  288     struct CacheEventRecord *r = &c->events[event];
  289     r->count++;
  290     r->blocks += (int) blocks;
  291 }
  292 
  293 
  294 void PrintCacheStats(const char *description, CacheStats *c, int fd)
  295 {
  296     fdprint(fd, "%s ", description);
  297     for (int i = 0/*HIT*/; i < 10/*REPLACE*/; i++)
  298     fdprint(fd, "[%4d : %6d]  ", c->events[i].count, c->events[i].blocks);
  299     fdprint(fd, "\n");
  300 }
  301 
  302 
  303 void VenusToViceStatus(VenusStat *venusstat, ViceStatus *vicestat/*, uid_t uid*/) {
  304     vicestat->InterfaceVersion = 1;
  305     vicestat->VnodeType = venusstat->VnodeType;
  306     vicestat->LinkCount = venusstat->LinkCount;
  307     vicestat->Length = venusstat->Length;
  308     vicestat->DataVersion = venusstat->DataVersion;
  309     vicestat->VV = venusstat->VV;
  310 /*    ClearLocal(vicestat->VV);*/
  311     vicestat->Date = venusstat->Date;
  312     vicestat->Author = (vuid_t)venusstat->Author;
  313     vicestat->Owner = (vuid_t)venusstat->Owner;
  314 /*    vicestat->CallBack = NoCallBack;*/
  315 /*    vicestat->MyAccess = venusstat->SpecificUser[index(uid)].rights;*/
  316 /*    vicestat->AnyAccess = venusstat->AnyUser.rights;*/
  317     vicestat->Mode = venusstat->Mode;
  318 /*    vicestat->vparent = venusstat->pfid.Vnode;*/
  319 /*    vicestat->uparent = venusstat->pfid.Unique;*/
  320 }
  321 
  322 
  323 /*  *****  FSDB members  *****  */
  324 
  325 /* Allocate database from recoverable store. */
  326 void *fsdb::operator new(size_t len){
  327     fsdb *f = 0;
  328 
  329     /* Allocate recoverable store for the object. */
  330     f = (fsdb *)rvmlib_rec_malloc((int) len);
  331     CODA_ASSERT(f);
  332     return(f);
  333 }
  334 
  335 
  336 
  337 fsdb::fsdb() : htab(FSDB_NBUCKETS, FSO_HashFN) {
  338 
  339     /* Initialize the persistent members. */
  340     RVMLIB_REC_OBJECT(*this);
  341     MagicNumber = FSDB_MagicNumber;
  342     MaxFiles = CacheFiles;
  343     FreeFileMargin = MaxFiles / FREE_FACTOR;
  344 
  345     LastRef = (long *)rvmlib_rec_malloc(MaxFiles * (int)sizeof(long));
  346     rvmlib_set_range(LastRef, MaxFiles * (int)sizeof(long));
  347     memset((void *)LastRef, 0, (int)(MaxFiles * sizeof(long)));
  348 }
  349 
  350 
  351 void fsdb::ResetTransient() {
  352     /* Sanity checks. */
  353     if (MagicNumber != FSDB_MagicNumber)
  354     CHOKE("fsdb::ResetTransient: bad magic number (%d)", MagicNumber);
  355 
  356     /* MaxBlocks, FreeBlockMargin reset in FsoInit */
  357     blocks = 0;         /* this will get updated in fsobj::Recover() */
  358 
  359     htab.SetHFn(FSO_HashFN);
  360     prioq = new bstree(FSO_PriorityFN);
  361     RefCounter = 0;
  362     for (int i = 0; i < MaxFiles; i++)
  363     if (LastRef[i] > RefCounter)
  364         RefCounter = LastRef[i];
  365     RefCounter++;
  366     delq = new dlist;
  367     owriteq = new olist;
  368 
  369     memset((void *)&DirAttrStats, 0, (int)sizeof(CacheStats));
  370     memset((void *)&DirDataStats, 0, (int)sizeof(CacheStats));
  371     memset((void *)&FileAttrStats, 0, (int)sizeof(CacheStats));
  372     memset((void *)&FileDataStats, 0, (int)sizeof(CacheStats));
  373     Recomputes = 0;
  374     Reorders = 0;
  375 
  376     /* 
  377      * matriculation_sync doesn't need to be initialized. 
  378      * It's used only for LWP_Wait and LWP_Signal. 
  379      */
  380     matriculation_count = 0;
  381 }
  382 
  383 
  384 void fsdb::operator delete(void *){
  385     abort(); /* how could we possibly get here? */
  386 }
  387 
  388 
  389 fsobj *fsdb::Find(const VenusFid *key)
  390 {
  391     VenusFid OldFid;
  392 
  393     fso_iterator next(NL, key);
  394     fsobj *f;
  395     while ((f = next()))
  396     if (FID_EQ(key, &f->fid))
  397         return f;
  398 
  399     /* If we were looking for a local fid, do a full search because we may have
  400      * translated it while the upcall was in transit. */
  401     OldFid = *key;
  402     if (FID_IsDisco(MakeViceFid(&OldFid)))
  403     {
  404     fso_iterator full_search(NL);
  405     while ((f = full_search()))
  406     {
  407         OldFid.Realm  = f->fid.Realm;
  408         OldFid.Volume = f->fid.Volume;
  409         OldFid.Vnode  = f->LocalFid_Vnode;
  410         OldFid.Unique = f->LocalFid_Unique;
  411 
  412         if (FID_EQ(key, &OldFid)) {
  413         k_Replace(&OldFid, &f->fid);
  414         return f;
  415         }
  416     }
  417     }
  418     return 0;
  419 }
  420 
  421 
  422 /* MUST NOT be called from within transaction! */
  423 /* Caller MUST guarantee that the volume is cached and stable! */
  424 /* Should priority be an implicit argument? -JJK */
  425 fsobj *fsdb::Create(VenusFid *key, int priority, const char *comp,
  426             VenusFid *parent)
  427 {
  428     fsobj *f = 0;
  429     int rc = 0;
  430 
  431     /* Check whether the key is already in the database. */
  432     if ((f = Find(key)) != NULL)
  433     { f->print(logFile); CHOKE("fsdb::Create: key found"); }
  434 
  435     /* Fashion a new object.  This could be a long-running and wide-ranging transaction! */
  436     Recov_BeginTrans();
  437     /* try to make sure we have at least one object available, so we won't
  438      * crash on the C++-initializers. */
  439     rc = FSDB->AllocFso(priority, &f);
  440     if (rc != ENOSPC)
  441     {
  442     FSDB->FreeFso(f);
  443         f = new (FROMFREELIST, priority) fsobj(key, comp);
  444     }
  445 
  446     if (f && !f->IsRoot()) {
  447     if (!parent) {
  448         /* Laboriously scan database to find our parent! */
  449         struct dllist_head *p;
  450         list_for_each(p, f->vol->fso_list) {
  451         fsobj *pf = list_entry_plusplus(p, fsobj, vol_handle);
  452 
  453         if (!pf->IsDir() || pf->IsMtPt()) continue;
  454         if (!HAVEALLDATA(pf)) continue;
  455         if (!pf->dir_IsParent(&f->fid)) continue;
  456 
  457         parent = &pf->fid;
  458         break; /* Found! */
  459         }
  460     }
  461     if (parent)
  462         f->SetParent(parent->Vnode, parent->Unique);
  463     }
  464     Recov_EndTrans(MAXFP);
  465 
  466     if (!f)
  467     LOG(0, ("fsdb::Create: (%s, %d) failed\n", FID_(key), priority));
  468     else
  469     LOG(100, ("fsdb::Create: (%s, %d) suceeeded\n", FID_(key), priority));
  470 
  471     return(f);
  472 }
  473 
  474 
  475 /* 
  476  * Problem here is that we *must* have a volent pointer.  If a miss is on the
  477  * volume itself, we don't have that pointer.  Where do we put the info?
  478  */
  479 
  480 #define Hoard 1
  481 #define NonHoard 0
  482 #define UnknownHoard -1
  483 
  484 #define FSOBJSIZE NBLOCKS(sizeof(fsobj))
  485 
  486 /* local-repair modification */
  487 /* argument "rcode" added for local-repair */
  488 /* MUST NOT be called from within transaction! */
  489 /* Returns object READ-locked on success. */
  490 /* 
  491  * Should NOT call with FSO_HOLD on object.  Venus will be unable to 
  492  * correctly handle objects which go inconsistent incorrectly forcing a 
  493  * return value of ETOOMANYREFS to the user when there is *nothing* the 
  494  * poor user can do about it.
  495  */
  496 int fsdb::Get(fsobj **f_addr, VenusFid *key, uid_t uid, int rights,
  497           const char *comp, VenusFid *parent, int *rcode,
  498           int GetInconsistent)
  499 {
  500     int getdata = (rights & RC_DATA);
  501     int code = 0;
  502     *f_addr = 0;             /* OUT parameter valid on success only. */
  503     vproc *vp = VprocSelf();
  504 
  505     LOG(100, ("fsdb::Get: key = (%s), uid = %d, rights = %d, comp = %s\n",
  506            FID_(key), uid, rights, comp));
  507 
  508     /* if (vp->type != VPT_HDBDaemon)
  509      *  NotifyUserOfProgramAccess(uid, vp->u.u_pid, vp->u.u_pgid, key); */
  510 
  511     /* Volume state synchronization. */
  512     /* If a thread is already "in" one volume, we must switch contexts before entering another. */
  513     if (vp->u.u_vol &&
  514     !(vp->u.u_vol->GetRealmId() == key->Realm &&
  515       vp->u.u_vol->GetVolumeId() == key->Volume))
  516     {
  517     /* Preserve the user context. */
  518     struct uarea saved_ctxt = vp->u;
  519     vp->u.Init();
  520     vp->u.u_uid = saved_ctxt.u_uid;
  521     vp->u.u_priority = saved_ctxt.u_priority;
  522     vp->u.u_flags = saved_ctxt.u_flags;
  523     vp->u.u_pid = saved_ctxt.u_pid;
  524     vp->u.u_pgid = saved_ctxt.u_pgid;
  525 
  526     /* Do the Get on behalf of another volume. */
  527     for (;;) {
  528         int retry_call;
  529 
  530         vp->Begin_VFS(MakeVolid(key), CODA_VGET);
  531         if (vp->u.u_error) break;
  532 
  533         vp->u.u_error = Get(f_addr, key, uid, rights, comp, parent, rcode,
  534                 GetInconsistent);
  535 
  536         if (vp->u.u_error != 0)
  537         Put(f_addr);
  538 
  539         retry_call = 0;
  540         vp->End_VFS(&retry_call);
  541         if (!retry_call) break;
  542     }
  543     code = vp->u.u_error;
  544 
  545     /* Restore the user context. */
  546     vp->u = saved_ctxt;
  547 
  548     return(code);
  549     }
  550 
  551     fsobj *f = 0;
  552     int reference = (vp->u.u_flags & REFERENCE);
  553 
  554     /* Find the fsobj, or create a fresh one. */
  555 RestartFind:
  556     f = Find(key);
  557 
  558     if (f == NULL) {
  559 
  560         /* if it's in the local realm and the repair vol, and is a fake root,
  561        it must have left a dangling reference during a collapse */
  562         if (FID_IsExpandedDir(key)) {
  563 
  564         LOG(0, ("Failed to get (%s), probably a collapsed expansion dir!\n", FID_(key)));
  565 
  566         return(EIO);
  567       }
  568 
  569     /* 
  570      * check if the key is a locally generated fid.  We should never send
  571      * these to the server.  This check is not to be confused with
  572      * the routine fsobj::IsLocalFid, which checks to see if the _volume_
  573      * the object belongs to is the local volume.  yuck.  --lily
  574      */
  575     if (FID_IsDisco(MakeViceFid(key))) {
  576       LOG(0, ("fsdb::Get: Locally created fid %s not found!\n", 
  577               FID_(key)));
  578       return ETIMEDOUT;
  579     }
  580     
  581     /* Must ensure that the volume is cached. */
  582     volent *v = 0;
  583     if (VDB->Get(&v, MakeVolid(key))) {
  584       LOG(100, ("Volume not cached and we couldn't get it...\n"));
  585       return(ETIMEDOUT);
  586     }
  587     
  588     /* Retry the find, in case some other thread created the object while we blocked in vdb::Get(). */
  589     if (Find(key)) {
  590       VDB->Put(&v);
  591       goto RestartFind;
  592     }
  593     
  594     if (v->IsResolving()) {
  595       LOG(0, ("Volume resolving and file not cached, retrying VDB->Get!\n"));
  596       VDB->Put(&v);
  597       return(ERETRY);
  598     }
  599     
  600     /* Cut-out early if volume is unreachable! */
  601     if (v->IsUnreachable()) {
  602       LOG(100, ("Volume unreachable and file not cached!\n"));
  603       VDB->Put(&v);
  604       return(ETIMEDOUT);
  605     }
  606 
  607     /* Attempt the create. */
  608     f = Create(key, vp->u.u_priority, comp, parent);
  609 
  610     /* Release the volume. */
  611     VDB->Put(&v);
  612 
  613     if (!f)
  614         return(ENOSPC);
  615 
  616     /* Transform object into fake mtpt if necessary (for /coda) */
  617     /* The first clause catches the /coda root, the second catches
  618        realms as they are demand loaded */
  619     if (FID_IsLocalFake(key) || FID_IsFakeRoot(MakeViceFid(key))) {
  620       LOG(0, ("fsdb::Get: transforming %s (%s) into fake mtpt with Fakeify()\n", f->GetComp(), FID_(&f->fid)));
  621       if (f->Fakeify()) {
  622         LOG(0, ("fsdb::Get: can't transform %s (%s) into fake mt pt\n",
  623                 f->GetComp(), FID_(&f->fid)));
  624         Recov_BeginTrans();
  625         f->Kill();
  626         Recov_EndTrans(MAXFP);
  627         Put(&f);         /* will unlock and garbage collect */
  628         return(EIO);
  629       }
  630     }
  631     f->DemoteLock();
  632   }
  633   else {
  634     /* Object without status must be matriculating now.  Wait for it to complete. */
  635     int curr_matriculation_count = matriculation_count;
  636     if (!HAVESTATUS(f) && !f->IsFake()) {
  637       while (curr_matriculation_count == matriculation_count) {
  638         LOG(0, ("WAITING(MATRICULATION): count = %d\n", matriculation_count));
  639         START_TIMING();
  640         VprocWait(&matriculation_sync);
  641         END_TIMING();
  642         LOG(0, ("WAIT OVER, elapsed = %3.1f\n", elapsed));
  643       }
  644       goto RestartFind;
  645     }
  646     
  647     /* Perform GC if necessary. */
  648     if (GCABLE(f)) {
  649       Recov_BeginTrans();
  650       f->GC();
  651       Recov_EndTrans(MAXFP);
  652       goto RestartFind;
  653     }
  654     
  655     /* Read-lock the entry. */
  656     f->Lock(RD);
  657 
  658     /* Update component. */
  659     if (comp && comp[0] != '\0' &&
  660         !STREQ(comp, ".") && !STREQ(comp, "..") && !STREQ(comp, f->comp))
  661     {
  662         Recov_BeginTrans();
  663         f->SetComp(comp);
  664         Recov_EndTrans(MAXFP);
  665     }
  666 
  667     /* Update parent linkage */
  668     if (parent && !f->pfso) {
  669         Recov_BeginTrans();
  670         f->SetParent(parent->Vnode, parent->Unique);
  671         Recov_EndTrans(MAXFP);
  672     }
  673     }
  674 
  675     /* Consider fetching status and/or data. */
  676     if ((!getdata && !STATUSVALID(f)) || (getdata && !DATAVALID(f))) {
  677     /* Note that we CANNOT fetch, and must use whatever status/data we have, if : */
  678     /*     - the file is being exec'ed (or the VM system refuses to release its pages) */
  679     /*     - the file is open for write */
  680     /*     - the object has been deleted (it must also be open for read at this point) */
  681     /*     - the object's volume is unreachable */
  682     /*     - the object's volume is reachable, but the object is dirty */
  683     if (FETCHABLE(f)) {
  684         f->PromoteLock();
  685 
  686         /* Fetch status-only if we don't have any or if it is suspect. We
  687          * do this even if we want data and we don't have any so that we
  688          * ALWAYS know how many blocks to allocate when fetching data. */
  689         if (!STATUSVALID(f)) {
  690         code = f->GetAttr(uid);
  691 
  692         if (rcode) *rcode = code;   /* added for local-repair */
  693 
  694         /* Mark fsobj in server/server conflict */
  695         if (code == EINCONS && !f->IsFake()) {
  696           char path[MAXPATHLEN];
  697           f->GetPath(path, PATH_FULL);
  698 
  699           Recov_BeginTrans();
  700           RVMLIB_REC_OBJECT(f->flags);
  701           f->flags.fake = 1;
  702           Recov_EndTrans(MAXFP);
  703 
  704           k_Purge(&f->fid, 0);
  705 
  706           LOG(0, ("fsdb::Get: %s (%s) in server/server conflict\n",
  707               path, FID_(key)));
  708           MarinerLog("fsobj::CONFLICT (server/server): %s (%s)\n",
  709                  path, FID_(key));
  710         } /* s/s conflict objs fall through if(GetInconsistent) */
  711 
  712         if (code) {
  713             /* wakeup threads that may still be stuck waiting for
  714              * matriculation of this object */
  715             matriculation_count++;
  716             VprocSignal(&matriculation_sync);
  717         }
  718 
  719         if (code && !(code == EINCONS && GetInconsistent)) {
  720             if (code == EINCONS)
  721             LOG(0, ("fsdb::Get: EINCONS after GetAttr\n"));
  722             if (code == ETIMEDOUT)
  723             LOG(100, ("fsdb::Get: TIMEDOUT after GetAttr\n"));
  724             Put(&f);
  725             return(code);
  726         }
  727        }
  728 
  729         /* If we want data and we don't have any then fetch new stuff. */
  730         /* we have to re-check FETCHABLE because it may have changed as
  731            a result of the inconsistent object manipulation above. */
  732         if (getdata && FETCHABLE(f) && !f->IsFake() && !HAVEALLDATA(f))
  733         {
  734         int nblocks = BLOCKS(f);
  735 
  736         /* If we haven't got any data yet, allocate enough for the
  737          * whole file. When we have a partial file, we should
  738          * already have reserved enough blocks. */
  739         if (f->IsFile() && !HAVEDATA(f)) {
  740           code = AllocBlocks(vp->u.u_priority, nblocks);
  741           if (code != 0) {
  742             Put(&f);
  743             return(code);
  744           }
  745         }
  746         
  747         /* compensate # blocks for the amount we already have.
  748          * (only used for vmon statistical stuff later on, but
  749          * the fetch will modify f->cf.ValidData) */
  750         nblocks -= NBLOCKS(f->cf.ValidData());
  751         
  752         code = 0;
  753         /* first try the LookAside cache */
  754         if (!f->LookAside()) {
  755           /* Let fsobj::Fetch go ahead and fetch the object */
  756           code = f->Fetch(uid);
  757         }
  758         
  759         /* Restart operation in case of inconsistency. */
  760         if (code == EINCONS)
  761           code = ERETRY;
  762 
  763         if (code != 0) {
  764           Put(&f);
  765           return(code);
  766         }
  767       }
  768 
  769       f->DemoteLock();
  770     } else {    /* !FETCHABLE(f) */
  771         if (RESOLVING(f))
  772         {
  773         LOG(100, ("(MARIA) TIMEOUT !fetchable and resolving...\n"));
  774         Put(&f);
  775         return(ETIMEDOUT);
  776         }
  777 
  778         if (!HAVESTATUS(f) && !f->IsFake()) {
  779         Put(&f);
  780         return(ETIMEDOUT);
  781         }
  782 
  783         /*
  784          * Unfortunately, trying to limit access to stale STATUS
  785          * won't work because in order to gracefully recover from
  786          * the active reference to a now inconsistent object, we
  787          * have to be able to close the object.  In order to close
  788          * the object, we have to be able to get the STATUS of the
  789          * object...  I guess we allow full access to the stale
  790          * STATUS, but log that we did so.
  791          *
  792          *   if (DYING(f)) {
  793          *     LOG(0, ("Active reference prevents refetching object!  Providing limited access to stale status!\n"));
  794          *     *f_addr = f;
  795          *     Put(&f);
  796          *     return(ETOOMANYREFS);
  797          *   }
  798          */
  799         if (DYING(f))
  800         LOG(0, ("Active reference prevents refetching object! "
  801             "Allowing access to stale status! (key = <%s>)\n",
  802             FID_(key)));
  803 
  804         else if (!STATUSVALID(f))
  805         LOG(0, ("Allowing access to stale status! (key = <%s>)\n",
  806             FID_(key)));
  807 
  808         if (getdata) {
  809         if (DYING(f)) {
  810             LOG(0, ("Active reference prevents refetching object! "
  811                 "Disallowing access to stale data! (key = <%s>)\n",
  812                 FID_(key)));
  813             Put(&f);
  814             return(ETOOMANYREFS);
  815         }
  816 
  817         if (!HAVEALLDATA(f)) {
  818             int found = 0;
  819 
  820             /* try the lookaside cache */
  821             if (!f->IsLocalObj() && !f->IsFake()) {
  822             f->PromoteLock();
  823             found = f->LookAside();
  824             f->DemoteLock();
  825             }
  826 
  827             if (!found) {
  828             Put(&f);
  829             return(ETIMEDOUT);
  830           }
  831         }
  832 
  833         if (!DATAVALID(f) && !f->IsLocalObj())
  834           LOG(0, ("Allowing access to stale data! (key = <%s>)\n",
  835                   FID_(key)));
  836       }
  837     }
  838   }
  839 
  840   /* Examine the possibility of executing an ASR. */
  841   if (!GetInconsistent && f->IsFake() && f->vol->IsReplicated() &&
  842       !f->IsExpandedObj())
  843   {
  844     int ASRInvokable;
  845     repvol *v;
  846     struct timeval tv;
  847     fsobj *realobj;
  848 
  849     LOG(0, ("fsdb::Get:Volume NOT under repair and IsFake(%s)\n",
  850         FID_(&f->fid)));
  851 
  852     /* At this point, we have Fakeify'd the real object, and f is the
  853      * fake object served in its place. Unfortunately, it is not linked
  854      * into the directory structure and therefore calls to GetPath fail.
  855      * Find the underlying object, and call LaunchASR on that to get
  856      * the required information. */
  857 
  858     realobj = Find(key);
  859     if (!realobj) {
  860       LOG(0, ("fsdb::Get:Find failed!\n"));
  861       Put(&f);
  862       return EINCONS;
  863     }
  864 
  865     v = (repvol *)realobj->vol;
  866     gettimeofday(&tv, 0);
  867 
  868     /* Check that:
  869      * 1.) An ASRLauncher path was parsed in venus.conf.
  870      * 2.) This thread is a worker.
  871      * 3.) ASR's are allowed and enabled to execute within this volume.
  872      * 4.) An ASR is not currently running within this volume.
  873      * 5.) The timeout interval for ASR launching has expired.
  874      */
  875 
  876     ASRInvokable = ((ASRLauncherFile != NULL) && (vp->type == VPT_Worker) &&
  877                     v->IsASRAllowed() && !v->asr_running() &&
  878                     ((tv.tv_sec - realobj->lastresolved) > ASR_INTERVAL) &&
  879                     v->IsASREnabled() && (ASRPolicyFile != NULL));
  880 
  881     if(ASRLauncherFile == NULL)
  882       LOG(0, ("fsdb::Get: asrlauncher_file not specified in venus.conf!\n"));
  883 
  884     if(ASRPolicyFile == NULL)
  885       LOG(0, ("fsdb::Get: asrpolicy_file not specified in venus.conf!\n"));
  886 
  887     if(vp->type != VPT_Worker)
  888       LOG(0, ("fsdb::Get: Non-worker Thread\n"));
  889 
  890     if(!v->IsASREnabled())
  891       LOG(0, ("fsdb::Get: ASRs disabled by the system at the moment \n"));
  892 
  893     if(v->asr_running())
  894       LOG(0, ("fsdb::Get: ASR already running in this volume\n"));
  895 
  896     if(((tv.tv_sec - realobj->lastresolved) <= ASR_INTERVAL))
  897       LOG(0, ("fsdb::Get: ASR executed too recently for this object\n"
  898               "fsdb::Get: New time: %d\tOld time: %d\tDiff:%d\n",
  899               tv.tv_sec, realobj->lastresolved,
  900               tv.tv_sec - realobj->lastresolved));
  901 
  902     if(!v->IsASRAllowed())
  903       LOG(0, ("fsdb::Get: ASRs disabled in this volume by some user\n"));
  904 
  905     if(v->asr_running() && vp->u.u_pgid != v->asr_pgid())
  906         code = ERETRY;     /* Bounce out anything which tries to hold
  907                 * kernel locks while repairing. */
  908 
  909     else if (ASRInvokable) { /* Execute ASR. */
  910       LOG(0, ("fsdb::Get: Launching for (%s)... \n", FID_(key)));
  911       if (realobj->LaunchASR(SERVER_SERVER, realobj->IsDir() ? DIRECTORY_CONFLICT : FILE_CONFLICT) == 0)
  912         code = ERETRY;  /* wait a short duration and retry */
  913       else
  914         code = EINCONS;
  915     }
  916     else {
  917       LOG(0, ("fsdb::Get: ASR not invokable for %s\n", FID_(key)));
  918       code = EINCONS;
  919     }
  920 
  921     Put(&f);
  922     return(code);
  923   }
  924 
  925   /* Update priority. */
  926   if (reference)
  927     f->Reference();
  928   f->ComputePriority();
  929 
  930   *f_addr = f;
  931   return(0);
  932 }
  933 
  934 /* MUST NOT be called from within transaction! */
  935 void fsdb::Put(fsobj **f_addr) {
  936     if (!(*f_addr)) { LOG(100, ("fsdb::Put: Null FSO\n")); return; }
  937 
  938     fsobj *f = *f_addr;
  939     LOG(100, ("fsdb::Put: (%s), refcnt = %d, readers = %d, writers = %d, openers = %d\n",
  940          FID_(&f->fid), f->refcnt, f->readers, f->writers, f->openers));
  941 
  942     if (f->readers == 0 && f->writers == 0)
  943     { f->print(logFile); CHOKE("fsdb::Put: no locks!"); }
  944     LockLevel level = (f->readers > 0 ? RD : WR);
  945     f->UnLock(level);
  946 
  947     /* Perform GC if necessary. */
  948     if (GCABLE(f)) {
  949     LOG(10, ("fsdb::Put: GC (%s)\n", FID_(&f->fid)));
  950 
  951     Recov_BeginTrans();
  952     f->GC();
  953     Recov_EndTrans(MAXFP);
  954     }
  955 
  956     (*f_addr) = 0;
  957 }
  958 
  959 
  960 /* MUST NOT be called from within transaction! */
  961 void fsdb::Flush()
  962 {
  963     /*
  964      * don't flush volume root only because some cached objects may
  965      * not be reachable.  If the flush actually works, the object
  966      * will disappear, and some number of descendants may 
  967      * disappear as well.  In this case, the iterator must be 
  968      * restarted. We're done when there's nothing flushable left.
  969      */
  970     int restart = 1;
  971     while (restart) {
  972     fsobj *f;
  973     fso_iterator next(NL);
  974     
  975     restart = 0;
  976     while ((f = next())) 
  977         if (f->Flush() == 0) {
  978         restart = 1;
  979         break;
  980         }
  981     }
  982 }
  983 
  984 
  985 /* MUST NOT be called from within transaction! */
  986 void fsdb::Flush(Volid *vid)
  987 {
  988     volent *v;
  989     v = VDB->Find(vid);
  990     if (!v) return;
  991     
  992     /* comment in fsdb::Flush applies here */
  993     int restart = 1;
  994     while (restart) {
  995     struct dllist_head *p, *next;
  996     restart = 0;
  997 
  998     for(p = v->fso_list.next; p != &v->fso_list; p = next) {
  999         fsobj *n = NULL, *f = list_entry_plusplus(p, fsobj, vol_handle);
 1000         next = p->next;
 1001 
 1002         if (next != &v->fso_list) {
 1003         n = list_entry_plusplus(next, fsobj, vol_handle);
 1004         FSO_HOLD(n);
 1005         }
 1006     
 1007         if (f->Flush() == 0)
 1008         restart = 1;
 1009 
 1010         if (n) FSO_RELE(n);
 1011     }
 1012     }
 1013     v->release();
 1014 }
 1015 
 1016 
 1017 /* This is supports translation of "local" to "remote" Fids during 
 1018    reintegration.  Note that given a fid it can appear in several
 1019    directories:
 1020    - in itself (if a directory fid) for the "." entries
 1021    - in its directory children as ".."
 1022    - in its parent as the named entry 
 1023    so we must do 3 replacements
 1024 
 1025    MUST be called from within transaction! 
 1026 
 1027    This routine can als replace local fids with global ones and
 1028    then, exceptionally, they appear as cross volume replacements.
 1029 
 1030    Returns: 0 on success, ENOENT if OldFid cannot be found in fsdb.
 1031 
 1032 */
 1033 
 1034 int fsdb::TranslateFid(VenusFid *OldFid, VenusFid *NewFid) 
 1035 {
 1036     fsobj *f = 0;
 1037     VenusFid pFid;
 1038 
 1039     LOG(100, ("fsdb::TranslateFid: %s --> %s\n", FID_(OldFid), 
 1040           FID_(NewFid)));
 1041 
 1042     /* cross volume replacements are for local fids */
 1043     if (!FID_VolEQ(OldFid, NewFid) && NewFid->Realm != LocalRealm->Id())
 1044         CHOKE("fsdb::TranslateFid: X-VOLUME, %s --> %s",
 1045               FID_(OldFid), FID_(NewFid));
 1046 
 1047     /* First, change the object itself. */
 1048     f = Find(OldFid);
 1049     if (f == NULL) {
 1050         LOG(0, ("fsdb::TranslateFid: %s not found\n",
 1051             FID_(OldFid)));
 1052         return(ENOENT);
 1053     }
 1054 
 1055     /* Can't handle any case but reintegration. */
 1056     /* in old versions we would choke too  if (!DIRTY(f)) */
 1057     if (!HAVESTATUS(f)) {
 1058         f->print(logFile);
 1059         CHOKE("fsdb::TranslateFid: !HAVESTATUS");
 1060     }
 1061 
 1062     /* Replace the fids in kernel name cache for this object */
 1063     k_Replace(OldFid, NewFid);
 1064 
 1065     /* Check that the NewFid is not already known! */
 1066     fsobj *Newf = Find(NewFid);
 1067     if (Newf != 0) {
 1068         f->print(logFile);
 1069         Newf->print(logFile);
 1070         CHOKE("fsdb::TranslateFid: NewFid found");
 1071     }
 1072 
 1073     /* Remove OldObject from table. */
 1074     if (htab.remove(&f->fid, &f->primary_handle) !=
 1075         &f->primary_handle) {
 1076         f->print(logFile);
 1077         CHOKE("fsdb::TranslateFid: old object remove");
 1078     }
 1079 
 1080     /* An upcall may already be queued with the old local fid. Or we may
 1081      * already be reintegrating before the local fid has been passed back
 1082      * to the kernel.
 1083      *
 1084      * To avoid this race we have to remember the value of OldFid. If it
 1085      * was a localfid (reintegration related) it will have the same volume
 1086      * id, a special file or directory vnode value, and we only need to
 1087      * remember the uniquifier value.
 1088      *
 1089      * We may need the vnode for repair related objects. */
 1090     f->LocalFid_Vnode  = OldFid->Vnode;
 1091     f->LocalFid_Unique = OldFid->Unique;
 1092 
 1093     /* Change Fid, update dir and reinsert into table. */
 1094     RVMLIB_REC_OBJECT(f->fid);
 1095     f->fid = *NewFid;
 1096 
 1097     /* replace "." and its hardlinks if f is dir */
 1098     if (f->IsDir() && HAVEALLDATA(f) && !f->IsMtPt())
 1099         f->dir_TranslateFid(OldFid, NewFid);
 1100 
 1101     /* replace f in the hash table */
 1102     htab.append(&f->fid, &f->primary_handle);
 1103 
 1104     /* Update the Parent. */
 1105     pFid = f->pfid;
 1106     fsobj *pf = Find(&pFid);
 1107     if (pf)
 1108         pf->dir_TranslateFid(OldFid, NewFid);
 1109 
 1110     /* Update the children, if we are a directory. */
 1111     if (ISDIR(*OldFid) && f->children) {
 1112         dlist_iterator next(*(f->children));
 1113         dlink *d;
 1114         while ((d = next())) {
 1115         fsobj *cf = strbase(fsobj, d, child_link);
 1116         CODA_ASSERT(FID_EQ(&cf->pfid, OldFid));
 1117         RVMLIB_REC_OBJECT(cf->pfid);
 1118         cf->pfid = *NewFid;
 1119 
 1120         if (cf->IsDir() && HAVEALLDATA(cf) && !cf->IsMtPt())
 1121             cf->dir_TranslateFid(OldFid, NewFid);
 1122         }
 1123     }
 1124     return 0;
 1125 }
 1126 
 1127 
 1128 /* Called in event of callback message from server. */
 1129 /* We assume this means that the object needs to be revalidated on the next
 1130  * access and remove the callback status flags (Demote it). -JH */
 1131 /* Perhaps there should be a "MUTATED" parameter in the RPC from the server.
 1132  * -JJK */
 1133 int fsdb::CallBackBreak(const VenusFid *fid)
 1134 {
 1135     fsobj *f = Find(fid);
 1136     if (!f || !HAVESTATUS(f)) return(0);
 1137 
 1138     f->Demote();
 1139 
 1140     return(1);
 1141 }
 1142 
 1143 void fsdb::ResetUser(uid_t uid) {
 1144     /* Clear access rights for the user. */
 1145     fso_iterator next(NL);
 1146     fsobj *f;
 1147 
 1148     Recov_BeginTrans();
 1149     while ((f = next()))
 1150     if (f->IsDir())
 1151         f->ClearAcRights(uid);
 1152     Recov_EndTrans(0);
 1153 }
 1154 
 1155 
 1156 void fsdb::ClearPriorities() {
 1157     fso_iterator next(NL);
 1158     fsobj *f;
 1159     while ((f = next())) {
 1160     LastRef[f->ix] = 0;
 1161     f->ComputePriority();
 1162     }
 1163 }
 1164 
 1165 
 1166 void fsdb::InvalidateMtPts() {
 1167     fso_iterator next(NL);
 1168     fsobj *f;
 1169     while ((f = next()))
 1170     if (f->IsMtPt()) {
 1171         f->flags.ckmtpt = 1;
 1172         k_Purge(&f->pfid, 1);   /* force kernel to reevaluate! */
 1173     }
 1174 
 1175     /* N.B.  We currently have no way of invalidating the mount point
 1176      * for the Coda root volume. That mount point is a VFS mount, which
 1177      * is known to the kernel (i.e., the MiniCache).   The proper thing
 1178      * to do is have another "back-call" into the MiniCache which
 1179      * unsaves the cnode thought by the kernel to be the current root,
 1180      * and makes a forward cfs_root call to Venus asking for the (new)
 1181      * root.  Of course, Venus would also have to make another
 1182      * ViceGetRootVolume call and update its global value (rootfid) as
 1183      * well.  Someday, someone should make the necessary changes to the
 1184      * MiniCache and Venus.  In the meantime, the only way for a client
 1185      * to reevaluate the Coda root is to shut down and restart Venus.
 1186      * -JJK */
 1187 }
 1188 
 1189 
 1190 int fsdb::FreeFsoCount() {
 1191     int count = (MaxFiles - htab.count());
 1192     if (count != freelist.count())
 1193     LOG(0, ("fsdb::FreeFsoCount: counts disagree (%d - %d != %d)\n",
 1194         MaxFiles, htab.count(), freelist.count()));
 1195 
 1196     return(count);
 1197 }
 1198 
 1199 
 1200 /* MUST be called from within transaction! */
 1201 int fsdb::AllocFso(int priority, fsobj **outf) {
 1202     /* Satisfy with free fso where possible. */
 1203     if (GrabFreeFso(priority, outf))
 1204     return(0);
 1205 
 1206     /* Maybe a garbage collection would help. */
 1207     if (delq->count() > 0) {
 1208     GarbageCollect();
 1209     if (GrabFreeFso(priority, outf))
 1210         return(0);
 1211     }
 1212 
 1213     /* Reclaim an object if possible. */
 1214     /* Try regular GetDown first, specific replacement second. */
 1215     GetDown();
 1216     if (GrabFreeFso(priority, outf))
 1217     return(0);
 1218     ReclaimFsos(priority, 1);
 1219     if (GrabFreeFso(priority, outf))
 1220     return(0);
 1221 
 1222     /* No luck. */
 1223     *outf = 0;
 1224     return(ENOSPC);
 1225 }
 1226 
 1227 
 1228 /* MUST be called from within transaction! */
 1229 int fsdb::GrabFreeFso(int priority, fsobj **f) {
 1230     int free_fsos = FreeFsoCount();
 1231     if (free_fsos > FreeFileMargin ||
 1232      (free_fsos > 0 && priority >= MarginPri())) {
 1233     *f = strbase(fsobj, freelist.get(), primary_handle);
 1234     return(1);
 1235     }
 1236 
 1237     return(0);
 1238 }
 1239 
 1240 
 1241 /* MUST be called from within transaction! */
 1242 void fsdb::ReclaimFsos(int priority, int count) {
 1243     vproc *vp = VprocSelf();
 1244     int reclaimed = 0;
 1245     bstree_iterator next(*prioq);
 1246     bsnode *b, *bnext;
 1247 
 1248     bnext = next();
 1249     while ((b = bnext) != NULL) {
 1250     bnext = next();
 1251     fsobj *f = strbase(fsobj, b, prio_handle);
 1252 
 1253     if (!REPLACEABLE(f))
 1254         { f->print(logFile); CHOKE("fsdb::ReclaimFsos: !REPLACEABLE"); }
 1255 
 1256     /* Remaining replaceable entries have equal or higher priority! */
 1257     if (vp->type == VPT_HDBDaemon) 
 1258         { if (priority <= f->priority) break; }
 1259 
 1260     /* Can't reclaim if busy. */
 1261     if (BUSY(f)) continue;
 1262 
 1263     /* Reclaim fso and data. */
 1264     MarinerLog("cache::Replace [%s] %s [%d, %d]\n",
 1265            (HAVEDATA(f) ? "status/data" : "status"),
 1266            f->GetComp(), f->priority, NBLOCKS(f->cf.Length()));
 1267     UpdateCacheStats((f->IsDir() ? &DirAttrStats : &FileAttrStats),
 1268              REPLACE, NBLOCKS(sizeof(fsobj)));
 1269     if (HAVEDATA(f))
 1270         UpdateCacheStats((f->IsDir() ? &DirDataStats : &FileDataStats),
 1271                  REPLACE, BLOCKS(f));
 1272 
 1273     f->Kill();
 1274     f->GC();
 1275 
 1276     reclaimed++;
 1277     if (reclaimed == count) break;
 1278     }
 1279 }
 1280 
 1281 
 1282 /* MUST be called from within transaction! */
 1283 void fsdb::FreeFso(fsobj *f) {
 1284     freelist.append(&f->primary_handle);
 1285 }
 1286 
 1287 
 1288 int fsdb::FreeBlockCount() {
 1289     int count = MaxBlocks - blocks;
 1290 
 1291     /* Subtract out blocks belonging to objects currently open for write. */
 1292     if (owriteq->count() > 0) {
 1293     olist_iterator onext(*owriteq);
 1294     olink *o;
 1295     while ((o = onext())) {
 1296         fsobj *f = strbase(fsobj, o, owrite_handle);
 1297 
 1298         if (f->flags.owrite == 0)
 1299         { f->print(logFile); CHOKE("fsdb::FreeBlockCount: on owriteq && !owrite"); }
 1300 
 1301         struct stat tstat;
 1302         f->cf.Stat(&tstat);
 1303         count -= (int) NBLOCKS(tstat.st_size);
 1304     }
 1305     }
 1306 
 1307     return(count);
 1308 }
 1309 
 1310 int fsdb::DirtyBlockCount() {
 1311     unsigned int count = 0;
 1312 
 1313     /* Count blocks belonging to non replaceable objects. */
 1314     fso_iterator next(NL);
 1315     fsobj *f;
 1316     while ((f = next())) {
 1317         if (!REPLACEABLE(f) && !f->IsSymLink()) {
 1318         count += NBLOCKS(f->cf.Length());
 1319     }
 1320     }
 1321 
 1322     return(count);
 1323 }
 1324 
 1325 /* MUST NOT be called from within transaction! */
 1326 int fsdb::AllocBlocks(int priority, int nblocks) {
 1327     /* Satisfy with free blocks where possible. */
 1328     if (GrabFreeBlocks(priority, nblocks))
 1329     return(0);
 1330 
 1331     /* Maybe a garbage collection would help. */
 1332     if (delq->count() > 0) {
 1333     Recov_BeginTrans();
 1334     GarbageCollect();
 1335     Recov_EndTrans(MAXFP);
 1336     if (GrabFreeBlocks(priority, nblocks))
 1337         return(0);
 1338     }
 1339 
 1340     /* Reclaim blocks if possible. */
 1341     /* Try regular GetDown first, specific replacement second. */
 1342     Recov_BeginTrans();
 1343     GetDown();
 1344     Recov_EndTrans(MAXFP);
 1345     if (GrabFreeBlocks(priority, nblocks))
 1346     return(0);
 1347     Recov_BeginTrans();
 1348     int BlocksNeeded = nblocks +
 1349         (priority >= MarginPri() ? 0 : FreeBlockMargin) - FreeBlockCount();
 1350     ReclaimBlocks(priority, BlocksNeeded);
 1351     Recov_EndTrans(MAXFP);
 1352     if (GrabFreeBlocks(priority, nblocks))
 1353     return(0);
 1354 
 1355     /* No luck. */
 1356     return(ENOSPC);
 1357 }
 1358 
 1359 
 1360 /* Needn't be called from within transaction. */
 1361 int fsdb::GrabFreeBlocks(int priority, int nblocks) {
 1362     int remaining_blocks = FreeBlockCount() - nblocks;
 1363     if (remaining_blocks >= FreeBlockMargin ||
 1364     (remaining_blocks >= 0 && priority >= MarginPri())) {
 1365     ChangeDiskUsage(nblocks);
 1366     return(1);
 1367     }
 1368 
 1369     return(0);
 1370 }
 1371 
 1372 
 1373 /* MUST be called from within transaction! */
 1374 void fsdb::ReclaimBlocks(int priority, int nblocks) {
 1375     int reclaimed = 0;
 1376     bstree_iterator next(*prioq);
 1377     bsnode *b;
 1378     while ((b = next())) {
 1379     fsobj *f = strbase(fsobj, b, prio_handle);
 1380 
 1381     if (!REPLACEABLE(f))
 1382         { f->print(logFile); CHOKE("fsdb::ReclaimBlocks: !REPLACEABLE"); }
 1383 
 1384     /* Remaining replaceable entries have higher priority! */
 1385     if (priority <= f->priority) break;
 1386 
 1387     /* No point in reclaiming entries without data! */
 1388     int ufs_blocks = NBLOCKS(f->cf.Length());
 1389     if (ufs_blocks == 0) continue;
 1390 
 1391     /* Can't reclaim if busy. */
 1392     if (BUSY(f) || f->IsLocalObj()) continue;
 1393 
 1394     /* Reclaim data.  Return if we've got enough. */
 1395     MarinerLog("cache::Replace [data] %s [%d, %d]\n",
 1396            f->GetComp(), f->priority, ufs_blocks);
 1397     UpdateCacheStats((f->IsDir() ? &DirDataStats : &FileDataStats),
 1398              REPLACE, BLOCKS(f));
 1399 
 1400     f->DiscardData();
 1401 
 1402     reclaimed += ufs_blocks;
 1403     if (reclaimed >= nblocks) break;
 1404     }
 1405 }
 1406 
 1407 
 1408 /* Needn't be called from within transaction. */
 1409 void fsdb::FreeBlocks(int nblocks) {
 1410     if (nblocks < 0)
 1411     eprint("fsdb::FreeBlocks: nblocks = %d\n", nblocks);
 1412     ChangeDiskUsage(-nblocks);
 1413 }
 1414 
 1415 
 1416 /*
 1417  *    Eventually, this should only be called by {Alloc,Free}Blocks.
 1418  *    We currently have the following exceptions:
 1419  *       1. When valid data is discovered at startup in the fsobj constructor
 1420  *       2. When (re)derivating a udir
 1421  *       3. When FetchData retrieves more data than expected (due to a
 1422  *          write-sharing race)
 1423  *       4. When a file opened for write is closed.
 1424  *
 1425  *    Items 1-2 are either inconsequential or will disappear when we switch to
 1426  *    RVM. Item 3 is a rare event (presumably), and can be fixed either by
 1427  *    implementing the ByteQuota feature of SFTP, or by making the version of
 1428  *    the data requested an IN parameter to ViceFetch.  Item 4 is the sticky
 1429  *    one.  What I have in mind is to pass back an "allowance" parameter to
 1430  *    the VFS_OPEN(WR) call.  If the MiniCache (specifically, cfs_rdwr)
 1431  *    detects that a write would cause the allowance to be exceeded, it makes
 1432  *    a new call (an ioctl?) asking for more.  On VFS_CLOSE(WR), any unused
 1433  *    allowance would be reclaimed.
 1434  */
 1435 void fsdb::ChangeDiskUsage(int delta_blocks) {
 1436     LOG(10, ("fsdb::ChangeDiskUsage: %d blocks\n", delta_blocks));
 1437 
 1438     blocks += delta_blocks;
 1439 }
 1440 
 1441 void fsdb::print(int fd, int SummaryOnly) {
 1442     if (this == 0) return;
 1443 
 1444     fdprint(fd, "FSDB: magic = %d, data version = %d\n",
 1445          MagicNumber, DataVersion);
 1446     fdprint(fd, "Files = (%d, %d, %d), Blocks = (%d, %d, %d)\n",
 1447          MaxFiles, htab.count(), FreeFileMargin, MaxBlocks, blocks, FreeBlockMargin);
 1448     fdprint(fd, "Counts: fl = %d, prioq = %d, delq = %d, owq = %d\n",
 1449          freelist.count(), prioq->count(), delq->count(), owriteq->count());
 1450 #ifdef  VENUSDEBUG
 1451     {
 1452     int normal_blocks = 0;
 1453     int got_blocks = 0;
 1454     int udir_blocks = 0;
 1455     int ow_blocks = 0;
 1456     fso_iterator next(NL);
 1457     fsobj *f;
 1458     while ((f = next()))
 1459         if (HAVEDATA(f)) {
 1460         switch(f->stat.VnodeType) {
 1461             case File:
 1462             if (f->flags.owrite) {
 1463                 struct stat tstat;
 1464                 f->cf.Stat(&tstat);
 1465                 ow_blocks += (int) NBLOCKS(tstat.st_size);
 1466             } else {
 1467                 normal_blocks += NBLOCKS(f->cf.Length());
 1468                 got_blocks += NBLOCKS(f->cf.ValidData());
 1469             }
 1470             break;
 1471 
 1472             case Directory:
 1473             udir_blocks += NBLOCKS(f->cf.Length());
 1474             break;
 1475 
 1476             case SymbolicLink:
 1477             CODA_ASSERT(NBLOCKS(f->cf.Length()) == 0);
 1478             break;
 1479 
 1480             case Invalid:
 1481             f->print(logFile);
 1482             CHOKE("fsdb::print: bogus vnode type");
 1483         }
 1484         }
 1485     fdprint(fd, "Real Blocks: validdata %d, allocated %d, directory %d, owrite %d\n", got_blocks, normal_blocks, udir_blocks, ow_blocks);
 1486     }
 1487 #endif /* VENUSDEBUG */
 1488     fdprint(fd, "Cache Statistics:  [ count : blocks ]\n");
 1489     fdprint(fd,     "                [     HIT     ]  [    MISS     ]  [    RETRY    ]  [   TIMEOUT   ]  [   NOSPACE   ]  [   FAILURE   ]  [   CREATE    ]  [    WRITE    ]  [   REMOVE    ]  [   REPLACE   ]\n");
 1490     PrintCacheStats("Directory ATTR:", &DirAttrStats, fd);
 1491     PrintCacheStats("Directory DATA:", &DirDataStats, fd);
 1492     PrintCacheStats("File ATTR:     ", &FileAttrStats, fd);
 1493     PrintCacheStats("File DATA:     ", &FileDataStats, fd);
 1494     fdprint(fd, "VolumeLevelMisses = %d\n", VolumeLevelMiss);
 1495     fdprint(fd, "recomputes = %d, reorders = %d, matr count = %d\n",
 1496          Recomputes, Reorders, matriculation_count);
 1497 
 1498     if (!SummaryOnly) {
 1499     fso_iterator next(NL);
 1500     fsobj *f;
 1501     while ((f = next()))
 1502         f->print(fd);
 1503     }
 1504 
 1505     fdprint(fd, "\n");
 1506 }