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