64bit-client-mods-build-cleanly-20011113
[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
449     /*
450      * Make sure that the data in the cache is current. There are two
451      * cases we need to worry about:
452      * 1. The cache data is being fetched by another process.
453      * 2. The cache data is no longer valid
454      */
455     while ((adp->states & CStatd)
456            && (dcp->flags & DFFetching)
457            && hsame(adp->m.DataVersion, dcp->f.versionNo)) {
458         afs_Trace4(afs_iclSetp, CM_TRACE_DCACHEWAIT,
459                         ICL_TYPE_STRING, __FILE__,
460                         ICL_TYPE_INT32, __LINE__,
461                         ICL_TYPE_POINTER, dcp,
462                         ICL_TYPE_INT32, dcp->flags);
463         dcp->flags |= DFWaiting;
464         ReleaseReadLock(&adp->lock);
465         afs_osi_Sleep(&dcp->validPos);
466         ObtainReadLock(&adp->lock);
467     }
468     if (!(adp->states & CStatd)
469         || !hsame(adp->m.DataVersion, dcp->f.versionNo)) {
470         ReleaseReadLock(&adp->lock);
471         afs_PutDCache(dcp);
472         goto tagain;
473     }
474
475     /* Generate a sequence number so we can tell whether we should
476      * store the attributes when processing the response. This number is
477      * stored in the file size when we set the CBulkFetching bit. If the
478      * CBulkFetching is still set and this value hasn't changed, then
479      * we know we were the last to set CBulkFetching bit for this file,
480      * and it is safe to set the status information for this file.
481      */
482     statSeqNo = bulkStatCounter++;
483
484     /* now we have dir data in the cache, so scan the dir page */
485     fidIndex = 0;
486     flagIndex = 0;
487     while (1) { /* Should probably have some constant bound */
488         /* look for first safe entry to examine in the directory.  BlobScan
489          * looks for a the 1st allocated dir after the dirCookie slot.
490          */
491         newIndex = BlobScan(&dcp->f.inode, (dirCookie>>5));
492         if (newIndex == 0) break;
493
494         /* remember the updated directory cookie */
495         dirCookie = newIndex << 5;
496
497         /* get a ptr to the dir entry */
498         dirEntryp =(struct DirEntry *)afs_dir_GetBlob(&dcp->f.inode, newIndex);
499         if (!dirEntryp) break;
500
501         /* dont copy more than we have room for */
502         if (fidIndex >= nentries) {
503           DRelease((char *) dirEntryp, 0);
504           break;
505         }
506
507         /* now, if the dir entry looks good, copy it out to our list.  Vnode
508          * 0 means deleted, although it should also be free were it deleted.
509          */
510         if (dirEntryp->fid.vnode != 0) {
511             /* dont copy entries we have in our cache.  This check will
512              * also make us skip "." and probably "..", unless it has
513              * disappeared from the cache since we did our namei call.
514              */
515             tfid.Cell = adp->fid.Cell;
516             tfid.Fid.Volume = adp->fid.Fid.Volume;
517             tfid.Fid.Vnode = ntohl(dirEntryp->fid.vnode);
518             tfid.Fid.Unique = ntohl(dirEntryp->fid.vunique);
519             do {
520               retry = 0;
521               ObtainWriteLock(&afs_xvcache, 130);
522               tvcp = afs_FindVCache(&tfid, 0, 0, &retry, 0 /* no stats | LRU */);
523               if (tvcp && retry) {
524                 ReleaseWriteLock(&afs_xvcache);
525                 afs_PutVCache(tvcp);
526               }
527             } while (tvcp && retry);
528             if (!tvcp) {          /* otherwise, create manually */
529               tvcp = afs_NewVCache(&tfid, hostp, 0, 0);
530               ObtainWriteLock(&tvcp->lock, 505);
531               ReleaseWriteLock(&afs_xvcache);
532               afs_RemoveVCB(&tfid);
533               ReleaseWriteLock(&tvcp->lock);
534             } else {
535               ReleaseWriteLock(&afs_xvcache);
536             }
537             if (!tvcp)
538               goto done; /* can't happen at present, more's the pity */
539
540             /* WARNING: afs_DoBulkStat uses the Length field to store a
541              * sequence number for each bulk status request. Under no
542              * circumstances should afs_DoBulkStat store a sequence number
543              * if the new length will be ignored when afs_ProcessFS is
544              * called with new stats. */
545 #ifdef AFS_SGI_ENV
546             if (!(tvcp->states & (CStatd|CBulkFetching))
547                 && (tvcp->execsOrWriters <= 0)
548                 && !afs_DirtyPages(tvcp)
549                 && !AFS_VN_MAPPED((vnode_t*)tvcp))
550 #else
551             if (!(tvcp->states & (CStatd|CBulkFetching))
552                 && (tvcp->execsOrWriters <= 0) 
553                 && !afs_DirtyPages(tvcp))
554 #endif
555
556             {
557                 /* this entry doesnt exist in the cache, and is not
558                  * already being fetched by someone else, so add it to the
559                  * list of file IDs to obtain.
560                  *
561                  * We detect a callback breaking race condition by checking the
562                  * CBulkFetching state bit and the value in the file size.
563                  * It is safe to set the status only if the CBulkFetching
564                  * flag is still set and the value in the file size does
565                  * not change.
566                  *
567                  * Don't fetch status for dirty files. We need to
568                  * preserve the value of the file size. We could
569                  * flush the pages, but it wouldn't be worthwhile.
570                  */
571                 memcpy((char *)(fidsp+fidIndex), (char *) &tfid.Fid, sizeof(*fidsp));
572                 tvcp->states |= CBulkFetching;
573                 tvcp->m.Length = statSeqNo;
574                 fidIndex++;
575             }
576             afs_PutVCache(tvcp);
577         }       /* if dir vnode has non-zero entry */
578
579         /* move to the next dir entry by adding in the # of entries
580          * used by this dir entry.
581          */
582         temp = afs_dir_NameBlobs(dirEntryp->name) << 5;
583         DRelease((char *) dirEntryp, 0);
584         if (temp <= 0) break;
585         dirCookie += temp;
586     }   /* while loop over all dir entries */
587
588     /* now release the dir lock and prepare to make the bulk RPC */
589     ReleaseReadLock(&adp->lock);
590
591     /* release the chunk */
592     afs_PutDCache(dcp);
593
594     /* dont make a null call */
595     if (fidIndex == 0) goto done;
596
597     do {
598         /* setup the RPC parm structures */
599         fidParm.AFSCBFids_len = fidIndex;
600         fidParm.AFSCBFids_val = fidsp;
601         statParm.AFSBulkStats_len = fidIndex;
602         statParm.AFSBulkStats_val = statsp;
603         cbParm.AFSCBs_len = fidIndex;
604         cbParm.AFSCBs_val = cbsp;
605
606         /* start the timer; callback expirations are relative to this */
607         startTime = osi_Time();
608
609         tcp = afs_Conn(&adp->fid, areqp, SHARED_LOCK);
610         if (tcp) {
611             hostp = tcp->srvr->server;
612             XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_BULKSTATUS);
613 #ifdef RX_ENABLE_LOCKS
614             AFS_GUNLOCK();
615 #endif /* RX_ENABLE_LOCKS */
616
617             if (!(tcp->srvr->server->flags & SNO_INLINEBULK)) {
618                 code = RXAFS_InlineBulkStatus(tcp->id, &fidParm, &statParm,
619                                               &cbParm, &volSync);
620                 if (code == RXGEN_OPCODE) {
621                     tcp->srvr->server->flags |= SNO_INLINEBULK;
622                     inlinebulk = 0;
623                     code = RXAFS_BulkStatus(tcp->id, &fidParm, &statParm, 
624                                             &cbParm, &volSync);
625                 } else
626                     inlinebulk=1;
627             } else {
628                 inlinebulk=0;
629                 code = RXAFS_BulkStatus(tcp->id, &fidParm, &statParm, &cbParm,
630                                         &volSync);
631             }
632 #ifdef RX_ENABLE_LOCKS
633             AFS_GLOCK();
634 #endif /* RX_ENABLE_LOCKS */
635             XSTATS_END_TIME;
636         }
637         else code = -1;
638     } while (afs_Analyze(tcp, code, &adp->fid, areqp, 
639                          AFS_STATS_FS_RPCIDX_BULKSTATUS, SHARED_LOCK, (struct cell *)0));
640
641     /* now, if we didnt get the info, bail out. */
642     if (code) goto done;
643
644     /* we need vol flags to create the entries properly */
645     dotdot.Fid.Volume = 0;
646     volp = afs_GetVolume(&adp->fid, areqp, READ_LOCK);
647     if (volp) {
648         volStates = volp->states;
649         if (volp->dotdot.Fid.Volume != 0)
650             dotdot = volp->dotdot;
651     }
652     else volStates = 0;
653
654     /* find the place to merge the info into  We do this by skipping
655      * nskip entries in the LRU queue.  The more we skip, the more
656      * we preserve, since the head of the VLRU queue is the most recently
657      * referenced file.
658      */
659   reskip:
660     nskip = afs_cacheStats / 2;         /* preserved fraction of the cache */
661     ObtainReadLock(&afs_xvcache);
662     if (QEmpty(&VLRU)) {
663       /* actually a serious error, probably should panic. Probably will 
664        * panic soon, oh well. */
665       ReleaseReadLock(&afs_xvcache);
666       afs_warnuser("afs_DoBulkStat: VLRU empty!");
667       goto done;
668     }
669     if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
670        refpanic ("Bulkstat VLRU inconsistent");
671     }
672     for(tq = VLRU.next; tq != &VLRU; tq = QNext(tq)) {
673         if (--nskip <= 0) break;
674         else if (QNext(QPrev(tq)) != tq) {
675            BStvc = QTOV(tq);
676            refpanic ("BulkStat VLRU inconsistent");
677         }
678     }
679     if (tq != &VLRU) lruvcp = QTOV(tq);
680     else lruvcp = QTOV(VLRU.next);
681
682     /* now we have to hold this entry, so that it does not get moved
683      * into the free list while we're running.  It could still get
684      * moved within the lru queue, but hopefully that will be rare; it
685      * doesn't hurt nearly as much.
686      */
687     retry = 0;
688     osi_vnhold(lruvcp, &retry);
689     ReleaseReadLock(&afs_xvcache);           /* could be read lock */
690     if (retry)
691       goto reskip;
692
693     /* otherwise, merge in the info.  We have to be quite careful here,
694      * since we need to ensure that we don't merge old info over newer
695      * stuff in a stat cache entry.  We're very conservative here: we don't
696      * do the merge at all unless we ourselves create the stat cache
697      * entry.  That's pretty safe, and should work pretty well, since we
698      * typically expect to do the stat cache creation ourselves.
699      *
700      * We also have to take into account racing token revocations.
701      */
702     for(i=0; i<fidIndex; i++) {
703         if ((&statsp[i])->errorCode) 
704             continue;
705         afid.Cell = adp->fid.Cell;
706         afid.Fid.Volume = adp->fid.Fid.Volume;
707         afid.Fid.Vnode = fidsp[i].Vnode;
708         afid.Fid.Unique = fidsp[i].Unique;
709         do {
710            retry = 0;
711            ObtainReadLock(&afs_xvcache);
712            tvcp = afs_FindVCache(&afid, 1, 0, &retry, 0/* !stats&!lru*/);
713            ReleaseReadLock(&afs_xvcache);
714         } while (tvcp && retry);
715
716         /* The entry may no longer exist */
717         if (tvcp == NULL) {
718             continue;
719         }
720
721         /* now we have the entry held, but we need to fill it in */
722         ObtainWriteLock(&tvcp->lock,131);
723
724         /* if CBulkFetching is not set, or if the file size no longer
725          * matches the value we placed there when we set the CBulkFetching
726          * flag, then someone else has done something with this node,
727          * and we may not have the latest status information for this
728          * file.  Leave the entry alone.
729          */
730         if (!(tvcp->states & CBulkFetching) || (tvcp->m.Length != statSeqNo)) {
731             flagIndex++;
732             ReleaseWriteLock(&tvcp->lock);
733             afs_PutVCache(tvcp);
734             continue;
735         }
736
737         /* now copy ".." entry back out of volume structure, if necessary */
738         if (tvcp->mvstat == 2  && (dotdot.Fid.Volume != 0)) {
739             if (!tvcp->mvid)
740                 tvcp->mvid = (struct VenusFid *) osi_AllocSmallSpace(sizeof(struct VenusFid));
741             *tvcp->mvid = dotdot;
742         }
743
744         ObtainWriteLock(&afs_xvcache,132);
745         if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
746            refpanic ("Bulkstat VLRU inconsistent2");
747         }
748         if ((QNext(QPrev(&tvcp->vlruq)) != &tvcp->vlruq) 
749             || (QPrev(QNext(&tvcp->vlruq)) != &tvcp->vlruq))
750            refpanic ("Bulkstat VLRU inconsistent4");
751         if ((QNext(QPrev(&lruvcp->vlruq)) != &lruvcp->vlruq) 
752             || (QPrev(QNext(&lruvcp->vlruq)) != &lruvcp->vlruq)) 
753            refpanic ("Bulkstat VLRU inconsistent5");
754
755         if (tvcp != lruvcp) {  /* if they are == don't move it, don't corrupt vlru */
756            QRemove(&tvcp->vlruq);
757            QAdd(&lruvcp->vlruq, &tvcp->vlruq);
758         }
759
760         if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
761            refpanic ("Bulkstat VLRU inconsistent3");
762         }
763         if ((QNext(QPrev(&tvcp->vlruq)) != &tvcp->vlruq) 
764             || (QPrev(QNext(&tvcp->vlruq)) != &tvcp->vlruq))
765            refpanic ("Bulkstat VLRU inconsistent5");
766         if ((QNext(QPrev(&lruvcp->vlruq)) != &lruvcp->vlruq) 
767             || (QPrev(QNext(&lruvcp->vlruq)) != &lruvcp->vlruq))
768            refpanic ("Bulkstat VLRU inconsistent6");
769         ReleaseWriteLock(&afs_xvcache);
770
771         ObtainWriteLock(&afs_xcbhash, 494);
772
773         /* We need to check the flags again. We may have missed
774          * something while we were waiting for a lock.
775          */
776         if (!(tvcp->states & CBulkFetching) || (tvcp->m.Length != statSeqNo)) {
777             flagIndex++;
778             ReleaseWriteLock(&tvcp->lock);
779             ReleaseWriteLock(&afs_xcbhash);
780             afs_PutVCache(tvcp);
781             continue;
782         }
783
784         /* now merge in the resulting status back into the vnode.
785          * We only do this if the entry looks clear.
786          */
787         afs_ProcessFS(tvcp, &statsp[i], areqp);
788 #ifdef AFS_LINUX22_ENV
789         /* overwrite the ops if it's a directory or symlink. */
790         if (vType(tvcp) == VDIR)
791             tvcp->v.v_op = &afs_dir_iops;
792         else if (vType(tvcp) == VLNK)
793             tvcp->v.v_op = &afs_symlink_iops;
794 #endif
795
796         /* do some accounting for bulk stats: mark this entry as
797          * loaded, so we can tell if we use it before it gets
798          * recycled.
799          */
800         tvcp->states |= CBulkStat;
801         tvcp->states &= ~CBulkFetching;
802         flagIndex++;
803         afs_bulkStatsDone++;
804
805         /* merge in vol info */
806         if (volStates & VRO) tvcp->states |= CRO;
807         if (volStates & VBackup) tvcp->states |= CBackup;
808         if (volStates & VForeign) tvcp->states |= CForeign;
809
810         /* merge in the callback info */
811         tvcp->states |= CTruth;
812
813         /* get ptr to the callback we are interested in */
814         tcbp = cbsp + i;
815
816         if (tcbp->ExpirationTime != 0) {
817             tvcp->cbExpires = tcbp->ExpirationTime+startTime;
818             tvcp->callback = hostp;
819             tvcp->states |= CStatd;
820             afs_QueueCallback(tvcp, CBHash(tcbp->ExpirationTime), volp);
821         }
822         else if (tvcp->states & CRO) {
823             /* ordinary callback on a read-only volume -- AFS 3.2 style */
824             tvcp->cbExpires = 3600+startTime;
825             tvcp->callback = hostp;
826             tvcp->states |= CStatd;
827             afs_QueueCallback(tvcp, CBHash(3600), volp);
828         }
829         else {
830             tvcp->callback = 0;
831             tvcp->states &= ~(CStatd|CUnique);  
832             afs_DequeueCallback(tvcp);
833             if ((tvcp->states & CForeign) || (vType(tvcp) == VDIR)) 
834               osi_dnlc_purgedp (tvcp);  /* if it (could be) a directory */
835         }
836         ReleaseWriteLock(&afs_xcbhash);
837
838         ReleaseWriteLock(&tvcp->lock);
839         /* finally, we're done with the entry */
840         afs_PutVCache(tvcp);
841     }   /* for all files we got back */
842
843     /* finally return the pointer into the LRU queue */
844     afs_PutVCache(lruvcp);
845
846   done:
847     /* Be sure to turn off the CBulkFetching flags */
848     for(i=flagIndex; i<fidIndex; i++) {
849         afid.Cell = adp->fid.Cell;
850         afid.Fid.Volume = adp->fid.Fid.Volume;
851         afid.Fid.Vnode = fidsp[i].Vnode;
852         afid.Fid.Unique = fidsp[i].Unique;
853         do {
854            retry = 0;
855            ObtainReadLock(&afs_xvcache);
856            tvcp = afs_FindVCache(&afid, 1, 0, &retry, 0/* !stats&!lru*/);
857            ReleaseReadLock(&afs_xvcache);
858         } while (tvcp && retry);
859         if (tvcp != NULL
860             && (tvcp->states & CBulkFetching)
861             && (tvcp->m.Length == statSeqNo)) {
862           tvcp->states &= ~CBulkFetching;
863         }
864         if (tvcp != NULL) {
865           afs_PutVCache(tvcp);
866         }
867     }
868     if ( volp )
869         afs_PutVolume(volp, READ_LOCK);
870     
871     /* If we did the InlineBulk RPC pull out the return code */
872     if (inlinebulk) {
873         if ((&statsp[0])->errorCode) {
874             afs_Analyze(tcp, (&statsp[0])->errorCode, &adp->fid, areqp, 
875                         AFS_STATS_FS_RPCIDX_BULKSTATUS, SHARED_LOCK, 
876                         (struct cell *)0);
877             code = (&statsp[0])->errorCode;
878         }
879     } else {
880         code = 0;
881     }
882     osi_FreeLargeSpace(statMemp);
883     osi_FreeLargeSpace(cbfMemp);
884     return code;
885 }
886
887 /* was: (AFS_DEC_ENV) || defined(AFS_OSF30_ENV) || defined(AFS_NCR_ENV) */
888 int AFSDOBULK = 1;
889
890 #ifdef  AFS_OSF_ENV
891 afs_lookup(adp, ndp)
892     struct vcache *adp;
893     struct nameidata *ndp; {
894     char aname[MAXNAMLEN+1];    /* XXX */
895     struct vcache **avcp = (struct vcache **)&(ndp->ni_vp);
896     struct ucred *acred = ndp->ni_cred;
897     int wantparent = ndp->ni_nameiop & WANTPARENT;
898     int opflag = ndp->ni_nameiop & OPFLAG;
899 #else   /* AFS_OSF_ENV */
900 #if     defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
901 afs_lookup(OSI_VC_ARG(adp), aname, avcp, pnp, flags, rdir, acred)
902     struct pathname *pnp;
903     int flags;
904     struct vnode *rdir;
905 #else
906 #if defined(UKERNEL)
907 afs_lookup(adp, aname, avcp, acred, flags)
908     int flags;
909 #else    
910 afs_lookup(adp, aname, avcp, acred)
911 #endif /* UKERNEL */
912 #endif /* SUN5 || SGI */
913     OSI_VC_DECL(adp);
914     struct vcache **avcp;
915     char *aname;
916     struct AFS_UCRED *acred; {
917 #endif
918     struct vrequest treq;
919     char *tname = (char *)0;
920     register struct vcache *tvc=0;
921     register afs_int32 code;
922     register afs_int32 bulkcode = 0;
923     int pass = 0, hit = 0;
924     long dirCookie;
925     extern afs_int32 afs_mariner;                       /*Writing activity to log?*/
926     OSI_VC_CONVERT(adp)
927     afs_hyper_t versionNo;
928     int no_read_access = 0;
929     struct sysname_info sysState;   /* used only for @sys checking */
930     int dynrootRetry = 1;
931
932     AFS_STATCNT(afs_lookup);
933 #ifdef  AFS_OSF_ENV
934     ndp->ni_dvp = (struct vnode *)adp;
935     memcpy(aname, ndp->ni_ptr, ndp->ni_namelen);
936     aname[ndp->ni_namelen] = '\0';
937 #endif  /* AFS_OSF_ENV */
938
939     *avcp = (struct vcache *) 0;   /* Since some callers don't initialize it */
940
941     if (code = afs_InitReq(&treq, acred)) { 
942         goto done;
943     }
944
945     /* come back to here if we encounter a non-existent object in a read-only
946        volume's directory */
947
948   redo:
949     *avcp = (struct vcache *) 0;   /* Since some callers don't initialize it */
950     bulkcode = 0;
951
952     if (!(adp->states & CStatd)) {
953         if (code = afs_VerifyVCache2(adp, &treq)) {
954             goto done;
955         }
956     }
957     else code = 0;
958
959     /* watch for ".." in a volume root */
960     if (adp->mvstat == 2 && aname[0] == '.' && aname[1] == '.' && !aname[2]) {
961         /* looking up ".." in root via special hacks */
962         if (adp->mvid == (struct VenusFid *) 0 || adp->mvid->Fid.Volume == 0) {
963 #ifdef  AFS_OSF_ENV
964             extern struct vcache *afs_globalVp;
965             if (adp == afs_globalVp) {
966                 struct vnode *rvp = (struct vnode *)adp;
967 /*
968                 ndp->ni_vp = rvp->v_vfsp->vfs_vnodecovered;
969                 ndp->ni_dvp = ndp->ni_vp;
970                 VN_HOLD(*avcp);
971 */
972                 code = ENODEV;
973                 goto done;
974             }
975 #endif
976             code = ENODEV;
977             goto done;
978         }
979         /* otherwise we have the fid here, so we use it */
980         tvc = afs_GetVCache(adp->mvid, &treq, (afs_int32 *)0,
981                             (struct vcache*)0, 0);
982         afs_Trace3(afs_iclSetp, CM_TRACE_GETVCDOTDOT,
983                    ICL_TYPE_FID, adp->mvid, ICL_TYPE_POINTER, tvc, 
984                    ICL_TYPE_INT32,  code);
985         *avcp = tvc;
986         code = (tvc ? 0 : ENOENT);
987         hit = 1;
988         if (tvc && !VREFCOUNT(tvc)) {
989             osi_Panic("TT1");
990         }
991         if (code) {
992             /*printf("LOOKUP GETVCDOTDOT -> %d\n", code);*/
993         }
994         goto done;
995     }
996
997     /* now check the access */
998     if (treq.uid != adp->last_looker) {  
999        if (!afs_AccessOK(adp, PRSFS_LOOKUP, &treq, CHECK_MODE_BITS)) {
1000          *avcp = (struct vcache *)0;
1001          code = EACCES;
1002          goto done;
1003        }
1004        else adp->last_looker = treq.uid;
1005     } 
1006
1007     /* Check for read access as well.  We need read access in order to
1008        stat files, but not to stat subdirectories. */
1009     if (!afs_AccessOK(adp, PRSFS_READ, &treq, CHECK_MODE_BITS))
1010         no_read_access = 1;
1011
1012     /* special case lookup of ".".  Can we check for it sooner in this code,
1013      * for instance, way up before "redo:" ??
1014      * I'm not fiddling with the LRUQ here, either, perhaps I should, or else 
1015      * invent a lightweight version of GetVCache.
1016      */
1017     if (aname[0] == '.' && !aname[1]) { /* special case */
1018         ObtainReadLock(&afs_xvcache);   
1019         osi_vnhold(adp, 0);
1020         ReleaseReadLock(&afs_xvcache);  
1021         code = 0;
1022         *avcp = tvc = adp;
1023         hit = 1;
1024         if (adp && !VREFCOUNT(adp)) {
1025             osi_Panic("TT2");
1026         }
1027         goto done;
1028     }
1029
1030     Check_AtSys(adp, aname, &sysState, &treq);
1031     tname = sysState.name;
1032
1033     /* 1st Check_AtSys and lookup by tname is required here, for now,
1034        because the dnlc is *not* told to remove entries for the parent
1035        dir of file/dir op that afs_LocalHero likes, but dnlc is informed
1036        if the cached entry for the parent dir is invalidated for a
1037        non-local change.
1038        Otherwise, we'd be able to do a dnlc lookup on an entry ending
1039        w/@sys and know the dnlc was consistent with reality. */
1040     tvc = osi_dnlc_lookup (adp, tname, WRITE_LOCK);
1041     *avcp = tvc;  /* maybe wasn't initialized, but it is now */
1042     if (tvc) {
1043         if (no_read_access && vType(tvc) != VDIR && vType(tvc) != VLNK) {
1044             /* need read access on dir to stat non-directory / non-link */
1045             afs_PutVCache(tvc, WRITE_LOCK);
1046             *avcp = (struct vcache *)0;
1047             code = EACCES;
1048             goto done;
1049         }
1050 #ifdef AFS_LINUX22_ENV
1051         if (tvc->mvstat == 2) { /* we don't trust the dnlc for root vcaches */
1052             AFS_RELE(tvc);
1053             *avcp = 0;
1054         }
1055         else {  
1056             code = 0;
1057             hit = 1;
1058             goto done;
1059         }
1060 #else /* non - LINUX */
1061         code = 0;
1062         hit = 1;
1063         goto done;
1064 #endif /* linux22 */
1065     }
1066
1067     {
1068     register struct dcache *tdc;
1069     afs_size_t dirOffset, dirLen;
1070     ino_t theDir;
1071     struct VenusFid tfid;
1072
1073     /* now we have to lookup the next fid */
1074     tdc = afs_GetDCache(adp, (afs_size_t) 0, &treq, &dirOffset, &dirLen, 1);
1075     if (!tdc) {
1076       *avcp = (struct vcache *)0;  /* redundant, but harmless */
1077       code = EIO;
1078       goto done;
1079     }
1080
1081     /* now we will just call dir package with appropriate inode.
1082       Dirs are always fetched in their entirety for now */
1083     ObtainReadLock(&adp->lock);
1084
1085     /*
1086      * Make sure that the data in the cache is current. There are two
1087      * cases we need to worry about:
1088      * 1. The cache data is being fetched by another process.
1089      * 2. The cache data is no longer valid
1090      */
1091     while ((adp->states & CStatd)
1092            && (tdc->flags & DFFetching)
1093            && hsame(adp->m.DataVersion, tdc->f.versionNo)) {
1094         tdc->flags |= DFWaiting;
1095         ReleaseReadLock(&adp->lock);
1096         afs_osi_Sleep(&tdc->validPos);
1097         ObtainReadLock(&adp->lock);
1098     }
1099     if (!(adp->states & CStatd)
1100         || !hsame(adp->m.DataVersion, tdc->f.versionNo)) {
1101         ReleaseReadLock(&adp->lock);
1102         afs_PutDCache(tdc);
1103         goto redo;
1104     }
1105
1106     /* Save the version number for when we call osi_dnlc_enter */
1107     hset(versionNo, tdc->f.versionNo);
1108
1109     /*
1110      * check for, and handle "@sys" if it's there.  We should be able
1111      * to avoid the alloc and the strcpy with a little work, but it's
1112      * not pressing.  If there aren't any remote users (ie, via the 
1113      * NFS translator), we have a slightly easier job.
1114      * the faster way to do this is to check for *aname == '@' and if 
1115      * it's there, check for @sys, otherwise, assume there's no @sys 
1116      * then, if the lookup fails, check for .*@sys...
1117      */
1118     /* above now implemented by Check_AtSys and Next_AtSys */
1119
1120     /* lookup the name in the appropriate dir, and return a cache entry
1121        on the resulting fid */
1122     theDir = tdc->f.inode;
1123     code = afs_dir_LookupOffset(&theDir, sysState.name, &tfid.Fid, &dirCookie);
1124
1125     /* If the first lookup doesn't succeed, maybe it's got @sys in the name */
1126     while (code == ENOENT && Next_AtSys(adp, &treq, &sysState)) {
1127       code = afs_dir_LookupOffset(&theDir, sysState.name, &tfid.Fid, &dirCookie);
1128     }
1129     tname = sysState.name;
1130
1131     afs_PutDCache(tdc);
1132
1133     if (code == ENOENT && afs_IsDynroot(adp) && dynrootRetry) {
1134         struct cell *tcell;
1135
1136         ReleaseReadLock(&adp->lock);
1137         dynrootRetry = 0;
1138         if (*tname == '.')
1139             tcell = afs_GetCellByName(tname + 1, READ_LOCK);
1140         else
1141             tcell = afs_GetCellByName(tname, READ_LOCK);
1142         if (tcell) {
1143             afs_PutCell(tcell, READ_LOCK);
1144             afs_RefreshDynroot();
1145             if (tname != aname && tname) osi_FreeLargeSpace(tname);
1146             goto redo;
1147         }
1148     } else {
1149         ReleaseReadLock(&adp->lock);
1150     }
1151
1152     /* new fid has same cell and volume */
1153     tfid.Cell = adp->fid.Cell;
1154     tfid.Fid.Volume = adp->fid.Fid.Volume;
1155     afs_Trace4(afs_iclSetp, CM_TRACE_LOOKUP, ICL_TYPE_POINTER, adp, 
1156                ICL_TYPE_STRING, tname,
1157                ICL_TYPE_FID, &tfid, ICL_TYPE_INT32, code);
1158
1159     if (code) {
1160         if (code != ENOENT) {
1161             printf("LOOKUP dirLookupOff -> %d\n", code);
1162         }
1163         goto done;
1164     }  
1165
1166     /* prefetch some entries, if the dir is currently open.  The variable
1167      * dirCookie tells us where to start prefetching from.
1168      */
1169     if (AFSDOBULK && adp->opens > 0 && !(adp->states & CForeign) && !afs_IsDynroot(adp)) {
1170         afs_int32 retry;
1171         /* if the entry is not in the cache, or is in the cache,
1172          * but hasn't been statd, then do a bulk stat operation.
1173          */
1174         do {
1175            retry = 0;
1176            ObtainReadLock(&afs_xvcache);        
1177            tvc = afs_FindVCache(&tfid, 1, 0, &retry, 0/* !stats,!lru */);
1178            ReleaseReadLock(&afs_xvcache);       
1179         } while (tvc && retry);
1180
1181         if (!tvc || !(tvc->states & CStatd)) 
1182             bulkcode = afs_DoBulkStat(adp, dirCookie, &treq);
1183         else 
1184             bulkcode = 0;
1185
1186         /* if the vcache isn't usable, release it */
1187         if (tvc && !(tvc->states & CStatd)) {
1188             afs_PutVCache(tvc);
1189             tvc = (struct vcache *) 0;
1190         }
1191     } else {
1192         tvc = (struct vcache *) 0;
1193         bulkcode = 0;
1194     }
1195
1196     /* now get the status info, if we don't already have it */
1197     /* This is kind of weird, but we might wind up accidentally calling
1198      * RXAFS_Lookup because we happened upon a file which legitimately
1199      * has a 0 uniquifier. That is the result of allowing unique to wrap
1200      * to 0. This was fixed in AFS 3.4. For CForeign, Unique == 0 means that
1201      * the file has not yet been looked up.
1202      */
1203     if (!tvc) {
1204        afs_int32 cached = 0;
1205        if (!tfid.Fid.Unique && (adp->states & CForeign)) {
1206             tvc = afs_LookupVCache(&tfid, &treq, &cached, WRITE_LOCK, 
1207                                    adp, tname);
1208        } 
1209        if (!tvc && !bulkcode) {  /* lookup failed or wasn't called */
1210            tvc = afs_GetVCache(&tfid, &treq, &cached, (struct vcache*)0,
1211                                WRITE_LOCK);
1212        } 
1213     } /* if !tvc */
1214     } /* sub-block just to reduce stack usage */
1215
1216     if (tvc) {
1217        if (adp->states & CForeign)
1218            tvc->states |= CForeign;
1219         tvc->parentVnode = adp->fid.Fid.Vnode;
1220         tvc->parentUnique = adp->fid.Fid.Unique;
1221         tvc->states &= ~CBulkStat;
1222
1223 #if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
1224         if (!(flags & AFS_LOOKUP_NOEVAL))
1225           /* don't eval mount points */
1226 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
1227         if (tvc->mvstat == 1) {
1228           /* a mt point, possibly unevaluated */
1229           struct volume *tvolp;
1230
1231             ObtainWriteLock(&tvc->lock,133);
1232             code = EvalMountPoint(tvc, adp, &tvolp, &treq);
1233             ReleaseWriteLock(&tvc->lock);
1234
1235             if (code) {
1236                 afs_PutVCache(tvc, WRITE_LOCK);
1237                 if (tvolp) afs_PutVolume(tvolp, WRITE_LOCK);
1238                 goto done;
1239             }
1240
1241             /* next, we want to continue using the target of the mt point */
1242             if (tvc->mvid && (tvc->states & CMValid)) {
1243               struct vcache *uvc;
1244                 /* now lookup target, to set .. pointer */
1245                 afs_Trace2(afs_iclSetp, CM_TRACE_LOOKUP1,
1246                            ICL_TYPE_POINTER, tvc, ICL_TYPE_FID, &tvc->fid);
1247                 uvc = tvc;      /* remember for later */
1248
1249                 if (tvolp && (tvolp->states & VForeign)) {
1250                     /* XXXX tvolp has ref cnt on but not locked! XXX */
1251                     tvc = afs_GetRootVCache(tvc->mvid, &treq, (afs_int32 *)0, tvolp, WRITE_LOCK);
1252                 } else {
1253                     tvc = afs_GetVCache(tvc->mvid, &treq, (afs_int32 *)0,
1254                                         (struct vcache*)0, WRITE_LOCK);
1255                 }
1256                 afs_PutVCache(uvc, WRITE_LOCK); /* we're done with it */
1257
1258                 if (!tvc) {
1259                     code = ENOENT;
1260                     if (tvolp) {
1261                         afs_PutVolume(tvolp, WRITE_LOCK);
1262                     }
1263                     goto done;
1264                 }
1265
1266                 /* now, if we came via a new mt pt (say because of a new
1267                  * release of a R/O volume), we must reevaluate the ..
1268                  * ptr to point back to the appropriate place */
1269                 if (tvolp) {
1270                     ObtainWriteLock(&tvc->lock,134);
1271                     if (tvc->mvid == (struct VenusFid *) 0) {
1272                         tvc->mvid = (struct VenusFid *) osi_AllocSmallSpace(sizeof(struct VenusFid));
1273                     }
1274                     /* setup backpointer */
1275                     *tvc->mvid = tvolp->dotdot;
1276                     ReleaseWriteLock(&tvc->lock);
1277                     afs_PutVolume(tvolp, WRITE_LOCK);
1278                 }
1279             }
1280             else {
1281                 afs_PutVCache(tvc, WRITE_LOCK);
1282                 code = ENOENT;
1283                 if (tvolp) afs_PutVolume(tvolp, WRITE_LOCK);
1284                 goto done;
1285             }
1286         }
1287         *avcp = tvc;
1288         if (tvc && !VREFCOUNT(tvc)) {
1289             osi_Panic("TT3");
1290         }
1291         code = 0;
1292     }
1293     else {
1294         /* if we get here, we found something in a directory that couldn't
1295            be located (a Multics "connection failure").  If the volume is
1296            read-only, we try flushing this entry from the cache and trying
1297            again. */
1298         if (pass == 0) {
1299             struct volume *tv;
1300             tv = afs_GetVolume(&adp->fid, &treq, READ_LOCK);
1301             if (tv) {
1302                 if (tv->states & VRO) {
1303                     pass = 1;                   /* try this *once* */
1304                     ObtainWriteLock(&afs_xcbhash, 495);
1305                     afs_DequeueCallback(adp);
1306                     /* re-stat to get later version */
1307                     adp->states &= ~CStatd;
1308                     ReleaseWriteLock(&afs_xcbhash);
1309                     osi_dnlc_purgedp(adp);
1310                     afs_PutVolume(tv, READ_LOCK);
1311                     goto redo;
1312                 }
1313                 afs_PutVolume(tv, READ_LOCK);
1314             }
1315         }
1316         code = ENOENT;
1317     }
1318
1319 done:
1320     /* put the network buffer back, if need be */
1321     if (tname != aname && tname) osi_FreeLargeSpace(tname);
1322     if (code == 0) {
1323 #ifdef  AFS_OSF_ENV
1324         /* Handle RENAME; only need to check rename "."  */
1325         if (opflag == RENAME && wantparent && *ndp->ni_next == 0) {
1326             if (!FidCmp(&(tvc->fid), &(adp->fid))) { 
1327                 afs_PutVCache(*avcp, WRITE_LOCK);
1328                 *avcp = NULL;
1329                 return afs_CheckCode(EISDIR, &treq, 18);
1330             }
1331         }
1332 #endif  /* AFS_OSF_ENV */
1333
1334         if (afs_mariner)
1335           afs_AddMarinerName(aname, tvc); 
1336
1337 #if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
1338         if (!(flags & AFS_LOOKUP_NOEVAL))
1339         /* Here we don't enter the name into the DNLC because we want the
1340         evaluated mount dir to be there (the vcache for the mounted volume)
1341         rather than the vc of the mount point itself.  we can still find the
1342         mount point's vc in the vcache by its fid. */
1343 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
1344         if (!hit) {
1345           osi_dnlc_enter (adp, aname, tvc, &versionNo);
1346         }
1347         else {
1348 #ifdef AFS_LINUX20_ENV
1349             /* So Linux inode cache is up to date. */
1350             code = afs_VerifyVCache(tvc, &treq);
1351 #else
1352             return 0;  /* can't have been any errors if hit and !code */
1353 #endif
1354         }
1355     }
1356     if (bulkcode) code = bulkcode; else 
1357     code = afs_CheckCode(code, &treq, 19);
1358     if (code) {
1359        /* If there is an error, make sure *avcp is null.
1360         * Alphas panic otherwise - defect 10719.
1361         */
1362        *avcp = (struct vcache *)0;
1363     }
1364
1365     return code;
1366 }