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