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