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