afs-web-interface-enhancements-20010623
[openafs.git] / src / afs / VNOPS / afs_vnop_lookup.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * Implements:
12  * afs_lookup
13  * EvalMountPoint
14  * afs_DoBulkStat
15  *
16  * Locals:
17  * afs_strcat
18  * AFS_EQ_ATSYS (macro)
19  * afs_index
20  */
21
22 #include "../afs/param.h"       /* Should be always first */
23 #include "../afs/sysincludes.h" /* Standard vendor system headers */
24 #include "../afs/afsincludes.h" /* Afs-based standard headers */
25 #include "../afs/afs_stats.h" /* statistics */
26 #include "../afs/afs_cbqueue.h"
27 #include "../afs/nfsclient.h"
28 #include "../afs/exporter.h"
29 #include "../afs/afs_osidnlc.h"
30
31
32 /**
33  * A few definitions. This is until we have a proper header file        
34  * which has prototypes for all functions
35  */
36
37 extern struct DirEntry * afs_dir_GetBlob();
38
39 extern afs_rwlock_t afs_xvcache;
40 extern afs_rwlock_t afs_xcbhash;
41 extern struct afs_exporter *afs_nfsexporter;
42 extern char *afs_sysname;
43 extern char *afs_sysnamelist[];
44 extern int afs_sysnamecount;
45 extern struct afs_q VLRU;                       /*vcache LRU*/
46 #ifdef AFS_LINUX22_ENV
47 extern struct inode_operations afs_symlink_iops, afs_dir_iops;
48 #endif
49
50
51 afs_int32 afs_bulkStatsDone;
52 static int bulkStatCounter = 0; /* counter for bulk stat seq. numbers */
53
54
55 /* this would be faster if it did comparison as int32word, but would be 
56  * dependant on byte-order and alignment, and I haven't figured out
57  * what "@sys" is in binary... */
58 #define AFS_EQ_ATSYS(name) (((name)[0]=='@')&&((name)[1]=='s')&&((name)[2]=='y')&&((name)[3]=='s')&&(!(name)[4]))
59
60 char *
61 afs_strcat(s1, s2)
62         register char *s1, *s2;
63 {
64         register char *os1;
65
66         AFS_STATCNT(strcat);
67         os1 = s1;
68         while (*s1++)
69                 ;
70         --s1;
71         while (*s1++ = *s2++)
72                 ;
73         return (os1);
74 }
75
76
77 char *afs_index(a, c)
78     register char *a, c; {
79     register char tc;
80     AFS_STATCNT(afs_index);
81     while (tc = *a) {
82         if (tc == c) return a;
83         else a++;
84     }
85     return (char *) 0;
86 }
87
88 /* call under write lock, evaluate mvid field from a mt pt.
89  * avc is the vnode of the mount point object.
90  * advc is the vnode of the containing directory
91  * avolpp is where we return a pointer to the volume named by the mount pt, if success
92  * areq is the identity of the caller.
93  *
94  * NOTE: this function returns a held volume structure in *volpp if it returns 0!
95  */
96 EvalMountPoint(avc, advc, avolpp, areq)
97     register struct vcache *avc;
98     struct volume **avolpp;
99     struct vcache *advc;            /* the containing dir */
100     register struct vrequest *areq;
101 {
102     afs_int32  code;
103     struct volume *tvp = 0;
104     struct VenusFid tfid;
105     struct cell *tcell;
106     char   *cpos, *volnamep;
107     char   type, buf[128];
108     afs_int32  prefetchRO;          /* 1=>No  2=>Yes */
109     afs_int32  mtptCell, assocCell, hac=0;
110     afs_int32  samecell, roname, len;
111
112     AFS_STATCNT(EvalMountPoint);
113 #ifdef notdef
114     if (avc->mvid && (avc->states & CMValid)) return 0; /* done while racing */
115 #endif
116     *avolpp = (struct volume *)0;
117     code = afs_HandleLink(avc, areq);
118     if (code) return code;
119
120     /* Determine which cell and volume the mointpoint goes to */
121     type = avc->linkData[0];                   /* '#'=>Regular '%'=>RW */
122     cpos = afs_index(&avc->linkData[1], ':');  /* if cell name present */
123     if (cpos) {
124        volnamep = cpos+1;
125        *cpos = 0;
126        tcell = afs_GetCellByName(&avc->linkData[1], READ_LOCK);
127        *cpos =  ':';
128     } else {
129        volnamep = &avc->linkData[1];
130        tcell = afs_GetCell(avc->fid.Cell, READ_LOCK);
131     }
132     if (!tcell) return ENOENT;
133
134     mtptCell = tcell->cell;               /* The cell for the mountpoint */
135     if (tcell->lcellp) {
136        hac = 1;                           /* has associated cell */
137        assocCell = tcell->lcellp->cell;   /* The associated cell */
138     }
139     afs_PutCell(tcell, READ_LOCK);          
140
141     /* Is volume name a "<n>.backup" or "<n>.readonly" name */
142     len = strlen(volnamep);
143     roname = ((len > 9) && (strcmp(&volnamep[len - 9],".readonly") == 0)) ||
144              ((len > 7) && (strcmp(&volnamep[len - 7],".backup")   == 0));
145
146     /* When we cross mountpoint, do we stay in the same cell */
147     samecell = (avc->fid.Cell == mtptCell) || (hac && (avc->fid.Cell == assocCell));
148
149     /* Decide whether to prefetch the RO. Also means we want the RO.
150      * If this is a regular mountpoint with a RW volume name and
151      * we cross a cell boundary -or- start from a RO volume, then we will
152      * want to prefetch the RO volume when we get the RW below.
153      */
154     if ( (type == '#') && !roname && (!samecell || (avc->states & CRO)) ) {
155        prefetchRO = 2; /* Yes, prefetch the RO */
156     } else {
157        prefetchRO = 1; /* No prefetch of the RO */
158     }
159
160     /* Get the volume struct. Unless this volume name has ".readonly" or
161      * ".backup" in it, this will get the volume struct for the RW volume.
162      * The RO volume will be prefetched if requested (but not returned).
163      */
164     tvp = afs_GetVolumeByName(volnamep, mtptCell, prefetchRO, areq, WRITE_LOCK);
165
166     /* If no volume was found in this cell, try the associated linked cell */
167     if (!tvp && hac && areq->volumeError) {
168        tvp = afs_GetVolumeByName(volnamep, assocCell, prefetchRO, areq, WRITE_LOCK);
169     }
170
171     /* Still not found. If we are looking for the RO, then perhaps the RW 
172      * doesn't exist? Try adding ".readonly" to volname and look for that.
173      * Don't know why we do this. Would have still found it in above call - jpm.
174      */
175     if (!tvp && (prefetchRO == 2)) {
176        strcpy(buf, volnamep);
177        afs_strcat(buf, ".readonly");
178
179        tvp = afs_GetVolumeByName(buf, mtptCell, 1, areq, WRITE_LOCK);
180
181        /* Try the associated linked cell if failed */
182        if (!tvp && hac && areq->volumeError) {
183           tvp = afs_GetVolumeByName(buf, assocCell, 1, areq, WRITE_LOCK);
184        }
185     }
186   
187     if (!tvp) return ENODEV;       /* Couldn't find the volume */
188
189     /* Don't cross mountpoint from a BK to a BK volume */
190     if ((avc->states & CBackup) && (tvp->states & VBackup)) {
191         afs_PutVolume(tvp, WRITE_LOCK);
192         return ELOOP;
193     }
194
195     /* If we want (prefetched) the RO and it exists, then drop the
196      * RW volume and get the RO. Othewise, go with the RW.
197      */
198     if ((prefetchRO == 2) && tvp->roVol) {
199        tfid.Fid.Volume = tvp->roVol;                 /* remember RO volume */
200        tfid.Cell       = tvp->cell;
201        afs_PutVolume(tvp, WRITE_LOCK);               /* release old volume */
202        tvp = afs_GetVolume(&tfid, areq, WRITE_LOCK); /* get the new one */
203        if (!tvp) return ENODEV;                      /* oops, can't do it */
204     }
205
206     if (avc->mvid == 0)
207         avc->mvid = (struct VenusFid *) osi_AllocSmallSpace(sizeof(struct VenusFid));
208     avc->mvid->Cell = tvp->cell;
209     avc->mvid->Fid.Volume = tvp->volume;
210     avc->mvid->Fid.Vnode = 1;
211     avc->mvid->Fid.Unique = 1;
212     avc->states |= CMValid;
213
214     /* Used to: if the mount point is stored within a backup volume,
215      * then we should only update the parent pointer information if
216      * there's none already set, so as to avoid updating a volume's ..
217      * info with something in an OldFiles directory.
218      *
219      * Next two lines used to be under this if:
220      *
221      * if (!(avc->states & CBackup) || tvp->dotdot.Fid.Volume == 0)
222      *
223      * Now: update mount point back pointer on every call, so that we handle
224      * multiple mount points better.  This way, when du tries to go back
225      * via chddir(".."), it will end up exactly where it started, yet
226      * cd'ing via a new path to a volume will reset the ".." pointer
227      * to the new path.
228      */
229     tvp->mtpoint = avc->fid;    /* setup back pointer to mtpoint */
230     tvp->dotdot  = advc->fid;
231
232     *avolpp = tvp;
233     return 0;
234 }
235     
236 afs_ENameOK(aname)
237     register char *aname; {
238     register char tc;
239     register int tlen;
240
241     AFS_STATCNT(ENameOK);
242     tlen = strlen(aname);
243     if (tlen >= 4 && strcmp(aname+tlen-4, "@sys") == 0) return 0;
244     return 1;
245 }
246
247 afs_getsysname(areq, adp, bufp)
248     register struct vrequest *areq;
249     register struct vcache *adp;
250     register char *bufp;
251 {
252     static char sysname[MAXSYSNAME];
253     register struct unixuser *au;
254     register afs_int32 error;
255
256     if (!afs_nfsexporter) {
257       strcpy(bufp, afs_sysname);
258       return 0;
259     }
260     AFS_STATCNT(getsysname);
261     au = afs_GetUser(areq->uid, adp->fid.Cell, 0);
262     afs_PutUser(au, 0); 
263     if (au->exporter) {
264       error = EXP_SYSNAME(au->exporter, (char *)0, bufp);
265       if (error) 
266         strcpy(bufp, "@sys");
267       return -1;
268     } else {
269       strcpy(bufp, afs_sysname);
270       return 0;
271     }
272 }
273
274 Check_AtSys(avc, aname, state, areq)
275     register struct vcache *avc;
276     char *aname;
277     struct sysname_info *state;
278     struct vrequest *areq;
279 {
280     if (AFS_EQ_ATSYS(aname)) {
281       state->offset = 0;
282       state->name = (char *) osi_AllocLargeSpace(AFS_SMALLOCSIZ);
283       state->allocked = 1;
284       state->index = afs_getsysname(areq, avc, state->name);
285     } else {
286       state->offset = -1;
287       state->allocked = 0;
288       state->index = 0;
289       state->name = aname;
290     }
291 }
292
293 Next_AtSys(avc, areq, state)
294      register struct vcache *avc;
295      struct vrequest *areq;
296      struct sysname_info *state;
297 {
298   if (state->index == -1)
299     return 0;   /* No list */
300
301   /* Check for the initial state of aname != "@sys" in Check_AtSys*/
302   if (state->offset == -1 && state->allocked == 0) {
303     register char *tname;
304     /* Check for .*@sys */
305       for (tname=state->name; *tname; tname++)
306         /*Move to the end of the string*/;
307       if ((tname > state->name + 4) && (AFS_EQ_ATSYS(tname-4))) {
308         state->offset = (tname - 4) - state->name;
309         tname = (char *) osi_AllocLargeSpace(AFS_LRALLOCSIZ);
310         strncpy(tname, state->name, state->offset);
311         state->name = tname;
312         state->allocked = 1;
313         state->index = afs_getsysname(areq, avc, state->name+state->offset);
314         return 1;
315       } else
316         return 0; /* .*@sys doesn't match either */
317   } else if (++(state->index) >= afs_sysnamecount
318              || !afs_sysnamelist[state->index])
319     return 0;   /* end of list */
320   strcpy(state->name+state->offset, afs_sysnamelist[state->index]);
321   return 1;
322 }
323
324 #if (defined(AFS_SGI62_ENV) || defined(AFS_SUN57_64BIT_ENV))
325 extern int BlobScan(ino64_t *afile, afs_int32 ablob);
326 #else
327 #if defined AFS_LINUX_64BIT_KERNEL
328 extern int BlobScan(long *afile, afs_int32 ablob);
329 #else
330 extern int BlobScan(afs_int32 *afile, afs_int32 ablob);
331 #endif
332 #endif
333
334
335 /* called with an unlocked directory and directory cookie.  Areqp
336  * describes who is making the call.
337  * Scans the next N (about 30, typically) directory entries, and does
338  * a bulk stat call to stat them all.
339  *
340  * Must be very careful when merging in RPC responses, since we dont
341  * want to overwrite newer info that was added by a file system mutating
342  * call that ran concurrently with our bulk stat call.
343  *
344  * We do that, as described below, by not merging in our info (always
345  * safe to skip the merge) if the status info is valid in the vcache entry.
346  *
347  * If adapt ever implements the bulk stat RPC, then this code will need to
348  * ensure that vcaches created for failed RPC's to older servers have the
349  * CForeign bit set.
350  */
351 struct vcache * BStvc = (struct vcache *) 0;
352 void afs_DoBulkStat(adp, dirCookie, areqp)
353   struct vcache *adp;
354   long dirCookie;
355   struct vrequest *areqp;
356 {
357     int nentries;               /* # of entries to prefetch */
358     int nskip;                  /* # of slots in the LRU queue to skip */
359     struct vcache *lruvcp;      /* vcache ptr of our goal pos in LRU queue */
360     struct dcache *dcp;         /* chunk containing the dir block */
361     char *statMemp;             /* status memory block */
362     char *cbfMemp;              /* callback and fid memory block */
363     long temp;                  /* temp for holding chunk length, &c. */
364     struct AFSFid *fidsp;       /* file IDs were collecting */
365     struct AFSCallBack *cbsp;   /* call back pointers */
366     struct AFSCallBack *tcbp;   /* temp callback ptr */
367     struct AFSFetchStatus *statsp;      /* file status info */
368     struct AFSVolSync volSync;  /* vol sync return info */
369     struct vcache *tvcp;        /* temp vcp */
370     struct afs_q *tq;           /* temp queue variable */
371     AFSCBFids fidParm;          /* file ID parm for bulk stat */
372     AFSBulkStats statParm;      /* stat info parm for bulk stat */
373     int fidIndex;               /* which file were stating */
374     struct conn *tcp;           /* conn for call */
375     AFSCBs cbParm;              /* callback parm for bulk stat */
376     struct server *hostp = 0;   /* host we got callback from */
377     long origEvenCBs;           /* original # of callbacks for even-fid files */
378     long origOddCBs;            /* original # of callbacks for odd-fid files */
379     long origEvenZaps;          /* original # of recycles for even-fid files */
380     long origOddZaps;           /* original # of recycles for odd-fid files */
381     long startTime;             /* time we started the call,
382                                  * for callback expiration base
383                                  */
384     int statSeqNo;              /* Valued of file size to detect races */
385     int code;                   /* error code */
386     long newIndex;              /* new index in the dir */
387     struct DirEntry *dirEntryp; /* dir entry we are examining */
388     int i;
389     struct VenusFid afid;       /* file ID we are using now */
390     struct VenusFid tfid;       /* another temp. file ID */
391     afs_int32 retry;                  /* handle low-level SGI MP race conditions */
392     long volStates;             /* flags from vol structure */
393     struct volume *volp=0;      /* volume ptr */
394     struct VenusFid dotdot;
395     int flagIndex;              /* First file with bulk fetch flag set */
396     XSTATS_DECLS
397
398     /* first compute some basic parameters.  We dont want to prefetch more
399      * than a fraction of the cache in any given call, and we want to preserve
400      * a portion of the LRU queue in any event, so as to avoid thrashing
401      * the entire stat cache (we will at least leave some of it alone).
402      * presently dont stat more than 1/8 the cache in any one call.      */
403     nentries = afs_cacheStats / 8;
404
405     /* dont bother prefetching more than one calls worth of info */
406     if (nentries > AFSCBMAX) nentries = AFSCBMAX;
407
408     /* heuristic to make sure that things fit in 4K.  This means that
409      * we shouldnt make it any bigger than 47 entries.  I am typically
410      * going to keep it a little lower, since we don't want to load
411      * too much of the stat cache.
412      */
413     if (nentries > 30) nentries = 30;
414
415     /* now, to reduce the stack size, well allocate two 4K blocks,
416      * one for fids and callbacks, and one for stat info.  Well set
417      * up our pointers to the memory from there, too.
418      */
419     statMemp = osi_AllocLargeSpace(nentries * sizeof(AFSFetchStatus));
420     statsp = (struct AFSFetchStatus *) statMemp;
421     cbfMemp = osi_AllocLargeSpace(nentries *
422         (sizeof(AFSCallBack) + sizeof(AFSFid)));
423     fidsp = (AFSFid *) cbfMemp;
424     cbsp = (AFSCallBack *) (cbfMemp + nentries * sizeof(AFSFid));
425
426     /* next, we must iterate over the directory, starting from the specified
427      * cookie offset (dirCookie), and counting out nentries file entries.
428      * We skip files that already have stat cache entries, since we
429      * dont want to bulk stat files that are already in the cache.
430      */
431 tagain:
432     code = afs_VerifyVCache(adp, areqp);
433     if (code) goto done;
434
435     dcp = afs_GetDCache(adp, 0, areqp, &temp, &temp, 1);
436     if (!dcp) {
437         code = ENOENT;
438         goto done;
439     }
440
441     /* lock the directory cache entry */
442     ObtainReadLock(&adp->lock);
443
444     /*
445      * Make sure that the data in the cache is current. There are two
446      * cases we need to worry about:
447      * 1. The cache data is being fetched by another process.
448      * 2. The cache data is no longer valid
449      */
450     while ((adp->states & CStatd)
451            && (dcp->flags & DFFetching)
452            && hsame(adp->m.DataVersion, dcp->f.versionNo)) {
453         dcp->flags |= DFWaiting;
454         ReleaseReadLock(&adp->lock);
455         afs_osi_Sleep(&dcp->validPos);
456         ObtainReadLock(&adp->lock);
457     }
458     if (!(adp->states & CStatd)
459         || !hsame(adp->m.DataVersion, dcp->f.versionNo)) {
460         ReleaseReadLock(&adp->lock);
461         afs_PutDCache(dcp);
462         goto tagain;
463     }
464
465     /* Generate a sequence number so we can tell whether we should
466      * store the attributes when processing the response. This number is
467      * stored in the file size when we set the CBulkFetching bit. If the
468      * CBulkFetching is still set and this value hasn't changed, then
469      * we know we were the last to set CBulkFetching bit for this file,
470      * and it is safe to set the status information for this file.
471      */
472     statSeqNo = bulkStatCounter++;
473
474     /* now we have dir data in the cache, so scan the dir page */
475     fidIndex = 0;
476     flagIndex = 0;
477     while (1) { /* Should probably have some constant bound */
478         /* look for first safe entry to examine in the directory.  BlobScan
479          * looks for a the 1st allocated dir after the dirCookie slot.
480          */
481         newIndex = BlobScan(&dcp->f.inode, (dirCookie>>5));
482         if (newIndex == 0) break;
483
484         /* remember the updated directory cookie */
485         dirCookie = newIndex << 5;
486
487         /* get a ptr to the dir entry */
488         dirEntryp =(struct DirEntry *)afs_dir_GetBlob(&dcp->f.inode, newIndex);
489         if (!dirEntryp) break;
490
491         /* dont copy more than we have room for */
492         if (fidIndex >= nentries) {
493           DRelease((char *) dirEntryp, 0);
494           break;
495         }
496
497         /* now, if the dir entry looks good, copy it out to our list.  Vnode
498          * 0 means deleted, although it should also be free were it deleted.
499          */
500         if (dirEntryp->fid.vnode != 0) {
501             /* dont copy entries we have in our cache.  This check will
502              * also make us skip "." and probably "..", unless it has
503              * disappeared from the cache since we did our namei call.
504              */
505             tfid.Cell = adp->fid.Cell;
506             tfid.Fid.Volume = adp->fid.Fid.Volume;
507             tfid.Fid.Vnode = ntohl(dirEntryp->fid.vnode);
508             tfid.Fid.Unique = ntohl(dirEntryp->fid.vunique);
509             do {
510               retry = 0;
511               ObtainWriteLock(&afs_xvcache, 130);
512               tvcp = afs_FindVCache(&tfid, 0, 0, &retry, 0 /* no stats | LRU */);
513               if (tvcp && retry) {
514                 ReleaseWriteLock(&afs_xvcache);
515                 afs_PutVCache(tvcp);
516               }
517             } while (tvcp && retry);
518             if (!tvcp) {          /* otherwise, create manually */
519               tvcp = afs_NewVCache(&tfid, hostp, 0, 0);
520               ObtainWriteLock(&tvcp->lock, 505);
521               ReleaseWriteLock(&afs_xvcache);
522               afs_RemoveVCB(&tfid);
523               ReleaseWriteLock(&tvcp->lock);
524             } else {
525               ReleaseWriteLock(&afs_xvcache);
526             }
527             if (!tvcp)
528               goto done; /* can't happen at present, more's the pity */
529
530             /* WARNING: afs_DoBulkStat uses the Length field to store a
531              * sequence number for each bulk status request. Under no
532              * circumstances should afs_DoBulkStat store a sequence number
533              * if the new length will be ignored when afs_ProcessFS is
534              * called with new stats. */
535 #ifdef AFS_SGI_ENV
536             if (!(tvcp->states & (CStatd|CBulkFetching))
537                 && (tvcp->execsOrWriters <= 0)
538                 && !afs_DirtyPages(tvcp)
539                 && !AFS_VN_MAPPED((vnode_t*)tvcp))
540 #else
541             if (!(tvcp->states & (CStatd|CBulkFetching))
542                 && (tvcp->execsOrWriters <= 0) 
543                 && !afs_DirtyPages(tvcp))
544 #endif
545
546             {
547                 /* this entry doesnt exist in the cache, and is not
548                  * already being fetched by someone else, so add it to the
549                  * list of file IDs to obtain.
550                  *
551                  * We detect a callback breaking race condition by checking the
552                  * CBulkFetching state bit and the value in the file size.
553                  * It is safe to set the status only if the CBulkFetching
554                  * flag is still set and the value in the file size does
555                  * not change.
556                  *
557                  * Don't fetch status for dirty files. We need to
558                  * preserve the value of the file size. We could
559                  * flush the pages, but it wouldn't be worthwhile.
560                  */
561                 bcopy((char *) &tfid.Fid, (char *)(fidsp+fidIndex),
562                       sizeof(*fidsp));
563                 tvcp->states |= CBulkFetching;
564                 tvcp->m.Length = statSeqNo;
565                 fidIndex++;
566             }
567             afs_PutVCache(tvcp);
568         }       /* if dir vnode has non-zero entry */
569
570         /* move to the next dir entry by adding in the # of entries
571          * used by this dir entry.
572          */
573         temp = afs_dir_NameBlobs(dirEntryp->name) << 5;
574         DRelease((char *) dirEntryp, 0);
575         if (temp <= 0) break;
576         dirCookie += temp;
577     }   /* while loop over all dir entries */
578
579     /* now release the dir lock and prepare to make the bulk RPC */
580     ReleaseReadLock(&adp->lock);
581
582     /* release the chunk */
583     afs_PutDCache(dcp);
584
585     /* dont make a null call */
586     if (fidIndex == 0) goto done;
587
588     do {
589         /* setup the RPC parm structures */
590         fidParm.AFSCBFids_len = fidIndex;
591         fidParm.AFSCBFids_val = fidsp;
592         statParm.AFSBulkStats_len = fidIndex;
593         statParm.AFSBulkStats_val = statsp;
594         cbParm.AFSCBs_len = fidIndex;
595         cbParm.AFSCBs_val = cbsp;
596
597         /* start the timer; callback expirations are relative to this */
598         startTime = osi_Time();
599
600         tcp = afs_Conn(&adp->fid, areqp, SHARED_LOCK);
601         if (tcp) {
602             hostp = tcp->srvr->server;
603             XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_BULKSTATUS);
604 #ifdef RX_ENABLE_LOCKS
605             AFS_GUNLOCK();
606 #endif /* RX_ENABLE_LOCKS */
607             code = RXAFS_BulkStatus(tcp->id, &fidParm, &statParm, &cbParm,
608                                     &volSync);
609 #ifdef RX_ENABLE_LOCKS
610             AFS_GLOCK();
611 #endif /* RX_ENABLE_LOCKS */
612             XSTATS_END_TIME;
613         }
614         else code = -1;
615     } while (afs_Analyze(tcp, code, &adp->fid, areqp, 
616                          AFS_STATS_FS_RPCIDX_BULKSTATUS, SHARED_LOCK, (struct cell *)0));
617
618     /* now, if we didnt get the info, bail out. */
619     if (code) goto done;
620
621     /* we need vol flags to create the entries properly */
622     dotdot.Fid.Volume = 0;
623     volp = afs_GetVolume(&adp->fid, areqp, READ_LOCK);
624     if (volp) {
625         volStates = volp->states;
626         if (volp->dotdot.Fid.Volume != 0)
627             dotdot = volp->dotdot;
628     }
629     else volStates = 0;
630
631     /* find the place to merge the info into  We do this by skipping
632      * nskip entries in the LRU queue.  The more we skip, the more
633      * we preserve, since the head of the VLRU queue is the most recently
634      * referenced file.
635      */
636   reskip:
637     nskip = afs_cacheStats / 2;         /* preserved fraction of the cache */
638     ObtainReadLock(&afs_xvcache);
639     if (QEmpty(&VLRU)) {
640       /* actually a serious error, probably should panic. Probably will 
641        * panic soon, oh well. */
642       ReleaseReadLock(&afs_xvcache);
643       afs_warnuser("afs_DoBulkStat: VLRU empty!");
644       goto done;
645     }
646     if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
647        refpanic ("Bulkstat VLRU inconsistent");
648     }
649     for(tq = VLRU.next; tq != &VLRU; tq = QNext(tq)) {
650         if (--nskip <= 0) break;
651         else if (QNext(QPrev(tq)) != tq) {
652            BStvc = QTOV(tq);
653            refpanic ("BulkStat VLRU inconsistent");
654         }
655     }
656     if (tq != &VLRU) lruvcp = QTOV(tq);
657     else lruvcp = QTOV(VLRU.next);
658
659     /* now we have to hold this entry, so that it does not get moved
660      * into the free list while we're running.  It could still get
661      * moved within the lru queue, but hopefully that will be rare; it
662      * doesn't hurt nearly as much.
663      */
664     retry = 0;
665     osi_vnhold(lruvcp, &retry);
666     ReleaseReadLock(&afs_xvcache);           /* could be read lock */
667     if (retry)
668       goto reskip;
669
670     /* otherwise, merge in the info.  We have to be quite careful here,
671      * since we need to ensure that we don't merge old info over newer
672      * stuff in a stat cache entry.  We're very conservative here: we don't
673      * do the merge at all unless we ourselves create the stat cache
674      * entry.  That's pretty safe, and should work pretty well, since we
675      * typically expect to do the stat cache creation ourselves.
676      *
677      * We also have to take into account racing token revocations.
678      */
679     for(i=0; i<fidIndex; i++) {
680         afid.Cell = adp->fid.Cell;
681         afid.Fid.Volume = adp->fid.Fid.Volume;
682         afid.Fid.Vnode = fidsp[i].Vnode;
683         afid.Fid.Unique = fidsp[i].Unique;
684         do {
685            retry = 0;
686            ObtainReadLock(&afs_xvcache);
687            tvcp = afs_FindVCache(&afid, 1, 0, &retry, 0/* !stats&!lru*/);
688            ReleaseReadLock(&afs_xvcache);
689         } while (tvcp && retry);
690
691         /* The entry may no longer exist */
692         if (tvcp == NULL) {
693             continue;
694         }
695
696         /* now we have the entry held, but we need to fill it in */
697         ObtainWriteLock(&tvcp->lock,131);
698
699         /* if CBulkFetching is not set, or if the file size no longer
700          * matches the value we placed there when we set the CBulkFetching
701          * flag, then someone else has done something with this node,
702          * and we may not have the latest status information for this
703          * file.  Leave the entry alone.
704          */
705         if (!(tvcp->states & CBulkFetching) || (tvcp->m.Length != statSeqNo)) {
706             flagIndex++;
707             ReleaseWriteLock(&tvcp->lock);
708             afs_PutVCache(tvcp);
709             continue;
710         }
711
712         /* now copy ".." entry back out of volume structure, if necessary */
713         if (tvcp->mvstat == 2  && (dotdot.Fid.Volume != 0)) {
714             if (!tvcp->mvid)
715                 tvcp->mvid = (struct VenusFid *) osi_AllocSmallSpace(sizeof(struct VenusFid));
716             *tvcp->mvid = dotdot;
717         }
718
719         ObtainWriteLock(&afs_xvcache,132);
720         if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
721            refpanic ("Bulkstat VLRU inconsistent2");
722         }
723         if ((QNext(QPrev(&tvcp->vlruq)) != &tvcp->vlruq) 
724             || (QPrev(QNext(&tvcp->vlruq)) != &tvcp->vlruq))
725            refpanic ("Bulkstat VLRU inconsistent4");
726         if ((QNext(QPrev(&lruvcp->vlruq)) != &lruvcp->vlruq) 
727             || (QPrev(QNext(&lruvcp->vlruq)) != &lruvcp->vlruq)) 
728            refpanic ("Bulkstat VLRU inconsistent5");
729
730         if (tvcp != lruvcp) {  /* if they are == don't move it, don't corrupt vlru */
731            QRemove(&tvcp->vlruq);
732            QAdd(&lruvcp->vlruq, &tvcp->vlruq);
733         }
734
735         if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
736            refpanic ("Bulkstat VLRU inconsistent3");
737         }
738         if ((QNext(QPrev(&tvcp->vlruq)) != &tvcp->vlruq) 
739             || (QPrev(QNext(&tvcp->vlruq)) != &tvcp->vlruq))
740            refpanic ("Bulkstat VLRU inconsistent5");
741         if ((QNext(QPrev(&lruvcp->vlruq)) != &lruvcp->vlruq) 
742             || (QPrev(QNext(&lruvcp->vlruq)) != &lruvcp->vlruq))
743            refpanic ("Bulkstat VLRU inconsistent6");
744         ReleaseWriteLock(&afs_xvcache);
745
746         ObtainWriteLock(&afs_xcbhash, 494);
747
748         /* We need to check the flags again. We may have missed
749          * something while we were waiting for a lock.
750          */
751         if (!(tvcp->states & CBulkFetching) || (tvcp->m.Length != statSeqNo)) {
752             flagIndex++;
753             ReleaseWriteLock(&tvcp->lock);
754             ReleaseWriteLock(&afs_xcbhash);
755             afs_PutVCache(tvcp);
756             continue;
757         }
758
759         /* now merge in the resulting status back into the vnode.
760          * We only do this if the entry looks clear.
761          */
762         afs_ProcessFS(tvcp, &statsp[i], areqp);
763 #ifdef AFS_LINUX22_ENV
764         /* overwrite the ops if it's a directory or symlink. */
765         if (vType(tvcp) == VDIR)
766             tvcp->v.v_op = &afs_dir_iops;
767         else if (vType(tvcp) == VLNK)
768             tvcp->v.v_op = &afs_symlink_iops;
769 #endif
770
771         /* do some accounting for bulk stats: mark this entry as
772          * loaded, so we can tell if we use it before it gets
773          * recycled.
774          */
775         tvcp->states |= CBulkStat;
776         tvcp->states &= ~CBulkFetching;
777         flagIndex++;
778         afs_bulkStatsDone++;
779
780         /* merge in vol info */
781         if (volStates & VRO) tvcp->states |= CRO;
782         if (volStates & VBackup) tvcp->states |= CBackup;
783         if (volStates & VForeign) tvcp->states |= CForeign;
784
785         /* merge in the callback info */
786         tvcp->states |= CTruth;
787
788         /* get ptr to the callback we are interested in */
789         tcbp = cbsp + i;
790
791         if (tcbp->ExpirationTime != 0) {
792             tvcp->cbExpires = tcbp->ExpirationTime+startTime;
793             tvcp->callback = hostp;
794             tvcp->states |= CStatd;
795             afs_QueueCallback(tvcp, CBHash(tcbp->ExpirationTime), volp);
796         }
797         else if (tvcp->states & CRO) {
798             /* ordinary callback on a read-only volume -- AFS 3.2 style */
799             tvcp->cbExpires = 3600+startTime;
800             tvcp->callback = hostp;
801             tvcp->states |= CStatd;
802             afs_QueueCallback(tvcp, CBHash(3600), volp);
803         }
804         else {
805             tvcp->callback = 0;
806             tvcp->states &= ~(CStatd|CUnique);  
807             afs_DequeueCallback(tvcp);
808             if ((tvcp->states & CForeign) || (vType(tvcp) == VDIR)) 
809               osi_dnlc_purgedp (tvcp);  /* if it (could be) a directory */
810         }
811         ReleaseWriteLock(&afs_xcbhash);
812
813         ReleaseWriteLock(&tvcp->lock);
814         /* finally, we're done with the entry */
815         afs_PutVCache(tvcp);
816     }   /* for all files we got back */
817
818     /* finally return the pointer into the LRU queue */
819     afs_PutVCache(lruvcp);
820
821   done:
822     /* Be sure to turn off the CBulkFetching flags */
823     for(i=flagIndex; i<fidIndex; i++) {
824         afid.Cell = adp->fid.Cell;
825         afid.Fid.Volume = adp->fid.Fid.Volume;
826         afid.Fid.Vnode = fidsp[i].Vnode;
827         afid.Fid.Unique = fidsp[i].Unique;
828         do {
829            retry = 0;
830            ObtainReadLock(&afs_xvcache);
831            tvcp = afs_FindVCache(&afid, 1, 0, &retry, 0/* !stats&!lru*/);
832            ReleaseReadLock(&afs_xvcache);
833         } while (tvcp && retry);
834         if (tvcp != NULL
835             && (tvcp->states & CBulkFetching)
836             && (tvcp->m.Length == statSeqNo)) {
837           tvcp->states &= ~CBulkFetching;
838         }
839         if (tvcp != NULL) {
840           afs_PutVCache(tvcp);
841         }
842     }
843     if ( volp )
844         afs_PutVolume(volp, READ_LOCK);
845     
846     osi_FreeLargeSpace(statMemp);
847     osi_FreeLargeSpace(cbfMemp);
848 }
849
850 /* was: (AFS_DEC_ENV) || defined(AFS_OSF30_ENV) || defined(AFS_NCR_ENV) */
851 int AFSDOBULK = 1;
852
853 #ifdef  AFS_OSF_ENV
854 afs_lookup(adp, ndp)
855     struct vcache *adp;
856     struct nameidata *ndp; {
857     char aname[MAXNAMLEN+1];    /* XXX */
858     struct vcache **avcp = (struct vcache **)&(ndp->ni_vp);
859     struct ucred *acred = ndp->ni_cred;
860     int wantparent = ndp->ni_nameiop & WANTPARENT;
861     int opflag = ndp->ni_nameiop & OPFLAG;
862 #else   /* AFS_OSF_ENV */
863 #if     defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
864 afs_lookup(OSI_VC_ARG(adp), aname, avcp, pnp, flags, rdir, acred)
865     struct pathname *pnp;
866     int flags;
867     struct vnode *rdir;
868 #else
869 #if defined(UKERNEL)
870 afs_lookup(adp, aname, avcp, acred, flags)
871     int flags;
872 #else    
873 afs_lookup(adp, aname, avcp, acred)
874 #endif /* UKERNEL */
875 #endif /* SUN5 || SGI */
876     OSI_VC_DECL(adp);
877     struct vcache **avcp;
878     char *aname;
879     struct AFS_UCRED *acred; {
880 #endif
881     struct vrequest treq;
882     char *tname = (char *)0;
883     register struct vcache *tvc=0;
884     register afs_int32 code;
885     int pass = 0, hit = 0;
886     long dirCookie;
887     extern afs_int32 afs_mariner;                       /*Writing activity to log?*/
888     OSI_VC_CONVERT(adp)
889     afs_hyper_t versionNo;
890     int no_read_access = 0;
891     struct sysname_info sysState;   /* used only for @sys checking */
892
893     AFS_STATCNT(afs_lookup);
894 #ifdef  AFS_OSF_ENV
895     ndp->ni_dvp = (struct vnode *)adp;
896     bcopy(ndp->ni_ptr, aname, ndp->ni_namelen);
897     aname[ndp->ni_namelen] = '\0';
898 #endif  /* AFS_OSF_ENV */
899
900     *avcp = (struct vcache *) 0;   /* Since some callers don't initialize it */
901
902     if (code = afs_InitReq(&treq, acred)) { 
903       goto done;
904     }
905
906     /* come back to here if we encounter a non-existent object in a read-only
907        volume's directory */
908
909   redo:
910     *avcp = (struct vcache *) 0;   /* Since some callers don't initialize it */
911
912     if (!(adp->states & CStatd)) {
913         if (code = afs_VerifyVCache2(adp, &treq))
914           goto done;
915     }
916     else code = 0;
917
918     /* watch for ".." in a volume root */
919     if (adp->mvstat == 2 && aname[0] == '.' && aname[1] == '.' && !aname[2]) {
920         /* looking up ".." in root via special hacks */
921         if (adp->mvid == (struct VenusFid *) 0 || adp->mvid->Fid.Volume == 0) {
922 #ifdef  AFS_OSF_ENV
923             extern struct vcache *afs_globalVp;
924             if (adp == afs_globalVp) {
925                 struct vnode *rvp = (struct vnode *)adp;
926 /*
927                 ndp->ni_vp = rvp->v_vfsp->vfs_vnodecovered;
928                 ndp->ni_dvp = ndp->ni_vp;
929                 VN_HOLD(*avcp);
930 */
931                 code = ENODEV;
932                 goto done;
933             }
934 #endif
935             code = ENODEV;
936             goto done;
937         }
938         /* otherwise we have the fid here, so we use it */
939         tvc = afs_GetVCache(adp->mvid, &treq, (afs_int32 *)0,
940                             (struct vcache*)0, 0);
941         afs_Trace3(afs_iclSetp, CM_TRACE_GETVCDOTDOT,
942                    ICL_TYPE_FID, adp->mvid, ICL_TYPE_POINTER, tvc, 
943                    ICL_TYPE_INT32,  code);
944         *avcp = tvc;
945         code = (tvc ? 0 : ENOENT);
946         hit = 1;
947         if (tvc && !tvc->vrefCount) {
948             osi_Panic("TT1");
949         }
950         if (code) {
951             /*printf("LOOKUP GETVCDOTDOT -> %d\n", code);*/
952         }
953         goto done;
954     }
955
956     /* now check the access */
957     if (treq.uid != adp->last_looker) {  
958        if (!afs_AccessOK(adp, PRSFS_LOOKUP, &treq, CHECK_MODE_BITS)) {
959          *avcp = (struct vcache *)0;
960          code = EACCES;
961          goto done;
962        }
963        else adp->last_looker = treq.uid;
964     } 
965
966     /* Check for read access as well.  We need read access in order to
967        stat files, but not to stat subdirectories. */
968     if (!afs_AccessOK(adp, PRSFS_READ, &treq, CHECK_MODE_BITS))
969         no_read_access = 1;
970
971     /* special case lookup of ".".  Can we check for it sooner in this code,
972      * for instance, way up before "redo:" ??
973      * I'm not fiddling with the LRUQ here, either, perhaps I should, or else 
974      * invent a lightweight version of GetVCache.
975      */
976     if (aname[0] == '.' && !aname[1]) { /* special case */
977         ObtainReadLock(&afs_xvcache);   
978         osi_vnhold(adp, 0);
979         ReleaseReadLock(&afs_xvcache);  
980       code = 0;
981       *avcp = tvc = adp;
982       hit = 1;
983         if (adp && !adp->vrefCount) {
984             osi_Panic("TT2");
985         }
986       goto done;
987     }
988
989     Check_AtSys(adp, aname, &sysState, &treq);
990     tname = sysState.name;
991
992     /* 1st Check_AtSys and lookup by tname is required here, for now,
993        because the dnlc is *not* told to remove entries for the parent
994        dir of file/dir op that afs_LocalHero likes, but dnlc is informed
995        if the cached entry for the parent dir is invalidated for a
996        non-local change.
997        Otherwise, we'd be able to do a dnlc lookup on an entry ending
998        w/@sys and know the dnlc was consistent with reality. */
999     tvc = osi_dnlc_lookup (adp, tname, WRITE_LOCK);
1000     *avcp = tvc;  /* maybe wasn't initialized, but it is now */
1001     if (tvc) {
1002         if (no_read_access && vType(tvc) != VDIR) {
1003             /* need read access on dir to stat non-directory */
1004             afs_PutVCache(tvc, WRITE_LOCK);
1005             *avcp = (struct vcache *)0;
1006             code = EACCES;
1007             goto done;
1008         }
1009 #ifdef AFS_LINUX22_ENV
1010         if (tvc->mvstat == 2) { /* we don't trust the dnlc for root vcaches */
1011             AFS_RELE(tvc);
1012             *avcp = 0;
1013         }
1014         else {  
1015             code = 0;
1016             hit = 1;
1017             goto done;
1018         }
1019 #else /* non - LINUX */
1020         code = 0;
1021         hit = 1;
1022         goto done;
1023 #endif /* linux22 */
1024     }
1025
1026     {
1027     register struct dcache *tdc;
1028     afs_int32 dirOffset, dirLen;
1029     ino_t theDir;
1030     struct VenusFid tfid;
1031
1032     /* now we have to lookup the next fid */
1033     tdc = afs_GetDCache(adp, 0, &treq, &dirOffset, &dirLen, 1);
1034     if (!tdc) {
1035       *avcp = (struct vcache *)0;  /* redundant, but harmless */
1036       code = EIO;
1037       goto done;
1038     }
1039
1040     /* now we will just call dir package with appropriate inode.
1041       Dirs are always fetched in their entirety for now */
1042     ObtainReadLock(&adp->lock);
1043
1044     /*
1045      * Make sure that the data in the cache is current. There are two
1046      * cases we need to worry about:
1047      * 1. The cache data is being fetched by another process.
1048      * 2. The cache data is no longer valid
1049      */
1050     while ((adp->states & CStatd)
1051            && (tdc->flags & DFFetching)
1052            && hsame(adp->m.DataVersion, tdc->f.versionNo)) {
1053         tdc->flags |= DFWaiting;
1054         ReleaseReadLock(&adp->lock);
1055         afs_osi_Sleep(&tdc->validPos);
1056         ObtainReadLock(&adp->lock);
1057     }
1058     if (!(adp->states & CStatd)
1059         || !hsame(adp->m.DataVersion, tdc->f.versionNo)) {
1060         ReleaseReadLock(&adp->lock);
1061         afs_PutDCache(tdc);
1062         goto redo;
1063     }
1064
1065     /* Save the version number for when we call osi_dnlc_enter */
1066     hset(versionNo, tdc->f.versionNo);
1067
1068     /*
1069      * check for, and handle "@sys" if it's there.  We should be able
1070      * to avoid the alloc and the strcpy with a little work, but it's
1071      * not pressing.  If there aren't any remote users (ie, via the 
1072      * NFS translator), we have a slightly easier job.
1073      * the faster way to do this is to check for *aname == '@' and if 
1074      * it's there, check for @sys, otherwise, assume there's no @sys 
1075      * then, if the lookup fails, check for .*@sys...
1076      */
1077     /* above now implemented by Check_AtSys and Next_AtSys */
1078
1079     /* lookup the name in the appropriate dir, and return a cache entry
1080        on the resulting fid */
1081     theDir = tdc->f.inode;
1082     code = afs_dir_LookupOffset(&theDir, sysState.name, &tfid.Fid, &dirCookie);
1083
1084     /* If the first lookup doesn't succeed, maybe it's got @sys in the name */
1085     while (code == ENOENT && Next_AtSys(adp, &treq, &sysState)) {
1086       code = afs_dir_LookupOffset(&theDir, sysState.name, &tfid.Fid, &dirCookie);
1087     }
1088     tname = sysState.name;
1089
1090     ReleaseReadLock(&adp->lock);
1091     afs_PutDCache(tdc);
1092
1093     /* new fid has same cell and volume */
1094     tfid.Cell = adp->fid.Cell;
1095     tfid.Fid.Volume = adp->fid.Fid.Volume;
1096     afs_Trace4(afs_iclSetp, CM_TRACE_LOOKUP, ICL_TYPE_POINTER, adp, 
1097                ICL_TYPE_STRING, tname,
1098                ICL_TYPE_FID, &tfid, ICL_TYPE_INT32, code);
1099
1100     if (code) {
1101         if (code != ENOENT) {
1102             printf("LOOKUP dirLookupOff -> %d\n", code);
1103         }
1104         goto done;
1105     }  
1106
1107     /* prefetch some entries, if the dir is currently open.  The variable
1108      * dirCookie tells us where to start prefetching from.
1109      */
1110     if (AFSDOBULK && adp->opens > 0 && !(adp->states & CForeign)) {
1111         afs_int32 retry;
1112         /* if the entry is not in the cache, or is in the cache,
1113          * but hasn't been statd, then do a bulk stat operation.
1114          */
1115         do {
1116            retry = 0;
1117            ObtainReadLock(&afs_xvcache);        
1118            tvc = afs_FindVCache(&tfid, 1, 0, &retry, 0/* !stats,!lru */);
1119            ReleaseReadLock(&afs_xvcache);       
1120         } while (tvc && retry);
1121
1122         if (!tvc || !(tvc->states & CStatd)) {
1123             afs_DoBulkStat(adp, dirCookie, &treq);
1124         }
1125
1126         /* if the vcache isn't usable, release it */
1127         if (tvc && !(tvc->states & CStatd)) {
1128             afs_PutVCache(tvc);
1129             tvc = (struct vcache *) 0;
1130         }
1131     }
1132     else tvc = (struct vcache *) 0;
1133     
1134     /* now get the status info, if we don't already have it */
1135     /* This is kind of weird, but we might wind up accidentally calling
1136      * RXAFS_Lookup because we happened upon a file which legitimately
1137      * has a 0 uniquifier. That is the result of allowing unique to wrap
1138      * to 0. This was fixed in AFS 3.4. For CForeigh, Unique == 0 means that
1139      * the file has not yet been looked up.
1140      */
1141     if (!tvc) {
1142        afs_int32 cached = 0;
1143        if (!tfid.Fid.Unique && (adp->states & CForeign)) {
1144             tvc = afs_LookupVCache(&tfid, &treq, &cached, WRITE_LOCK, 
1145                                    adp, tname);
1146        } 
1147        if (!tvc) {  /* lookup failed or wasn't called */
1148             tvc = afs_GetVCache(&tfid, &treq, &cached, (struct vcache*)0,
1149                                 WRITE_LOCK);
1150        }
1151     } /* if !tvc */
1152     } /* sub-block just to reduce stack usage */
1153
1154     if (tvc) {
1155        if (adp->states & CForeign)
1156            tvc->states |= CForeign;
1157         tvc->parentVnode = adp->fid.Fid.Vnode;
1158         tvc->parentUnique = adp->fid.Fid.Unique;
1159         tvc->states &= ~CBulkStat;
1160
1161 #if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
1162         if (!(flags & AFS_LOOKUP_NOEVAL))
1163           /* don't eval mount points */
1164 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
1165         if (tvc->mvstat == 1) {
1166           /* a mt point, possibly unevaluated */
1167           struct volume *tvolp;
1168
1169             ObtainWriteLock(&tvc->lock,133);
1170             code = EvalMountPoint(tvc, adp, &tvolp, &treq);
1171             ReleaseWriteLock(&tvc->lock);
1172
1173             if (code) {
1174                 if (tvolp) afs_PutVolume(tvolp, WRITE_LOCK);
1175                 goto done;
1176             }
1177
1178             /* next, we want to continue using the target of the mt point */
1179             if (tvc->mvid && (tvc->states & CMValid)) {
1180               struct vcache *uvc;
1181                 /* now lookup target, to set .. pointer */
1182                 afs_Trace2(afs_iclSetp, CM_TRACE_LOOKUP1,
1183                            ICL_TYPE_POINTER, tvc, ICL_TYPE_FID, &tvc->fid);
1184                 uvc = tvc;      /* remember for later */
1185
1186                 if (tvolp && (tvolp->states & VForeign)) {
1187                     /* XXXX tvolp has ref cnt on but not locked! XXX */
1188                     tvc = afs_GetRootVCache(tvc->mvid, &treq, (afs_int32 *)0, tvolp, WRITE_LOCK);
1189                 } else {
1190                     tvc = afs_GetVCache(tvc->mvid, &treq, (afs_int32 *)0,
1191                                         (struct vcache*)0, WRITE_LOCK);
1192                 }
1193                 afs_PutVCache(uvc, WRITE_LOCK); /* we're done with it */
1194
1195                 if (!tvc) {
1196                     code = ENOENT;
1197                     if (tvolp) {
1198                         afs_PutVolume(tvolp, WRITE_LOCK);
1199                     }
1200                     goto done;
1201                 }
1202
1203                 /* now, if we came via a new mt pt (say because of a new
1204                  * release of a R/O volume), we must reevaluate the ..
1205                  * ptr to point back to the appropriate place */
1206                 if (tvolp) {
1207                     ObtainWriteLock(&tvc->lock,134);
1208                     if (tvc->mvid == (struct VenusFid *) 0) {
1209                         tvc->mvid = (struct VenusFid *) osi_AllocSmallSpace(sizeof(struct VenusFid));
1210                     }
1211                     /* setup backpointer */
1212                     *tvc->mvid = tvolp->dotdot;
1213                     ReleaseWriteLock(&tvc->lock);
1214                     afs_PutVolume(tvolp, WRITE_LOCK);
1215                 }
1216             }
1217             else {
1218                 afs_PutVCache(tvc, WRITE_LOCK);
1219                 code = ENOENT;
1220                 if (tvolp) afs_PutVolume(tvolp, WRITE_LOCK);
1221                 goto done;
1222             }
1223         }
1224         *avcp = tvc;
1225         if (tvc && !tvc->vrefCount) {
1226             osi_Panic("TT3");
1227         }
1228         code = 0;
1229     }
1230     else {
1231         /* if we get here, we found something in a directory that couldn't
1232            be located (a Multics "connection failure").  If the volume is
1233            read-only, we try flushing this entry from the cache and trying
1234            again. */
1235         if (pass == 0) {
1236             struct volume *tv;
1237             tv = afs_GetVolume(&adp->fid, &treq, READ_LOCK);
1238             if (tv) {
1239                 if (tv->states & VRO) {
1240                     pass = 1;                   /* try this *once* */
1241                     ObtainWriteLock(&afs_xcbhash, 495);
1242                     afs_DequeueCallback(adp);
1243                     /* re-stat to get later version */
1244                     adp->states &= ~CStatd;
1245                     ReleaseWriteLock(&afs_xcbhash);
1246                     osi_dnlc_purgedp(adp);
1247                     afs_PutVolume(tv, READ_LOCK);
1248                     goto redo;
1249                 }
1250                 afs_PutVolume(tv, READ_LOCK);
1251             }
1252         }
1253         code = ENOENT;
1254     }
1255
1256 done:
1257     /* put the network buffer back, if need be */
1258     if (tname != aname && tname) osi_FreeLargeSpace(tname);
1259     if (code == 0) {
1260 #ifdef  AFS_OSF_ENV
1261         /* Handle RENAME; only need to check rename "."  */
1262         if (opflag == RENAME && wantparent && *ndp->ni_next == 0) {
1263             if (!FidCmp(&(tvc->fid), &(adp->fid))) { 
1264                 afs_PutVCache(*avcp, WRITE_LOCK);
1265                 *avcp = NULL;
1266                 return afs_CheckCode(EISDIR, &treq, 18);
1267             }
1268         }
1269 #endif  /* AFS_OSF_ENV */
1270
1271         if (afs_mariner)
1272           afs_AddMarinerName(aname, tvc); 
1273
1274 #if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
1275         if (!(flags & AFS_LOOKUP_NOEVAL))
1276         /* Here we don't enter the name into the DNLC because we want the
1277         evaluated mount dir to be there (the vcache for the mounted volume)
1278         rather than the vc of the mount point itself.  we can still find the
1279         mount point's vc in the vcache by its fid. */
1280 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
1281         if (!hit) {
1282           osi_dnlc_enter (adp, aname, tvc, &versionNo);
1283         }
1284         else {
1285 #ifdef AFS_LINUX20_ENV
1286             /* So Linux inode cache is up to date. */
1287             code = afs_VerifyVCache(tvc, &treq);
1288 #else
1289             return 0;  /* can't have been any errors if hit and !code */
1290 #endif
1291         }
1292     }
1293     code = afs_CheckCode(code, &treq, 19);
1294     if (code) {
1295        /* If there is an error, make sure *avcp is null.
1296         * Alphas panic otherwise - defect 10719.
1297         */
1298        *avcp = (struct vcache *)0;
1299     }
1300
1301     return code;
1302 }