Work around MacOSX Finder's behavior by returning ENOENT when the user
[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 *
66 afs_strcat(s1, s2)
67         register char *s1, *s2;
68 {
69         register char *os1;
70
71         AFS_STATCNT(strcat);
72         os1 = s1;
73         while (*s1++)
74                 ;
75         --s1;
76         while (*s1++ = *s2++)
77                 ;
78         return (os1);
79 }
80
81
82 char *afs_index(a, c)
83     register char *a, c; {
84     register char tc;
85     AFS_STATCNT(afs_index);
86     while (tc = *a) {
87         if (tc == c) return a;
88         else a++;
89     }
90     return (char *) 0;
91 }
92
93 /* call under write lock, evaluate mvid field from a mt pt.
94  * avc is the vnode of the mount point object; must be write-locked.
95  * advc is the vnode of the containing directory (optional; if NULL and
96  *   EvalMountPoint succeeds, caller must initialize *avolpp->dotdot)
97  * avolpp is where we return a pointer to the volume named by the mount pt, if success
98  * areq is the identity of the caller.
99  *
100  * NOTE: this function returns a held volume structure in *volpp if it returns 0!
101  */
102 EvalMountPoint(avc, advc, avolpp, areq)
103     register struct vcache *avc;
104     struct volume **avolpp;
105     struct vcache *advc;            /* the containing dir */
106     register struct vrequest *areq;
107 {
108     afs_int32  code;
109     struct volume *tvp = 0;
110     struct VenusFid tfid;
111     struct cell *tcell;
112     char   *cpos, *volnamep;
113     char   type, buf[128];
114     afs_int32  prefetchRO;          /* 1=>No  2=>Yes */
115     afs_int32  mtptCell, assocCell, hac=0;
116     afs_int32  samecell, roname, len;
117
118     AFS_STATCNT(EvalMountPoint);
119 #ifdef notdef
120     if (avc->mvid && (avc->states & CMValid)) return 0; /* done while racing */
121 #endif
122     *avolpp = (struct volume *)0;
123     code = afs_HandleLink(avc, areq);
124     if (code) return code;
125
126     /* Determine which cell and volume the mointpoint goes to */
127     type = avc->linkData[0];                   /* '#'=>Regular '%'=>RW */
128     cpos = afs_index(&avc->linkData[1], ':');  /* if cell name present */
129     if (cpos) {
130        volnamep = cpos+1;
131        *cpos = 0;
132        tcell = afs_GetCellByName(&avc->linkData[1], READ_LOCK);
133        *cpos =  ':';
134     } else {
135        volnamep = &avc->linkData[1];
136        tcell = afs_GetCell(avc->fid.Cell, READ_LOCK);
137     }
138     if (!tcell) return ENODEV;
139
140     mtptCell = tcell->cell;               /* The cell for the mountpoint */
141     if (tcell->lcellp) {
142        hac = 1;                           /* has associated cell */
143        assocCell = tcell->lcellp->cell;   /* The associated cell */
144     }
145     afs_PutCell(tcell, READ_LOCK);          
146
147     /* Is volume name a "<n>.backup" or "<n>.readonly" name */
148     len = strlen(volnamep);
149     roname = ((len > 9) && (strcmp(&volnamep[len - 9],".readonly") == 0)) ||
150              ((len > 7) && (strcmp(&volnamep[len - 7],".backup")   == 0));
151
152     /* When we cross mountpoint, do we stay in the same cell */
153     samecell = (avc->fid.Cell == mtptCell) || (hac && (avc->fid.Cell == assocCell));
154
155     /* Decide whether to prefetch the RO. Also means we want the RO.
156      * If this is a regular mountpoint with a RW volume name and
157      * we cross a cell boundary -or- start from a RO volume, then we will
158      * want to prefetch the RO volume when we get the RW below.
159      */
160     if ( (type == '#') && !roname && (!samecell || (avc->states & CRO)) ) {
161        prefetchRO = 2; /* Yes, prefetch the RO */
162     } else {
163        prefetchRO = 1; /* No prefetch of the RO */
164     }
165
166     /* Get the volume struct. Unless this volume name has ".readonly" or
167      * ".backup" in it, this will get the volume struct for the RW volume.
168      * The RO volume will be prefetched if requested (but not returned).
169      */
170     tvp = afs_GetVolumeByName(volnamep, mtptCell, prefetchRO, areq, WRITE_LOCK);
171
172     /* If no volume was found in this cell, try the associated linked cell */
173     if (!tvp && hac && areq->volumeError) {
174        tvp = afs_GetVolumeByName(volnamep, assocCell, prefetchRO, areq, WRITE_LOCK);
175     }
176
177     /* Still not found. If we are looking for the RO, then perhaps the RW 
178      * doesn't exist? Try adding ".readonly" to volname and look for that.
179      * Don't know why we do this. Would have still found it in above call - jpm.
180      */
181     if (!tvp && (prefetchRO == 2)) {
182        strcpy(buf, volnamep);
183        afs_strcat(buf, ".readonly");
184
185        tvp = afs_GetVolumeByName(buf, mtptCell, 1, areq, WRITE_LOCK);
186
187        /* Try the associated linked cell if failed */
188        if (!tvp && hac && areq->volumeError) {
189           tvp = afs_GetVolumeByName(buf, assocCell, 1, areq, WRITE_LOCK);
190        }
191     }
192   
193     if (!tvp) return ENODEV;       /* Couldn't find the volume */
194
195     /* Don't cross mountpoint from a BK to a BK volume */
196     if ((avc->states & CBackup) && (tvp->states & VBackup)) {
197         afs_PutVolume(tvp, WRITE_LOCK);
198         return ELOOP;
199     }
200
201     /* If we want (prefetched) the RO and it exists, then drop the
202      * RW volume and get the RO. Othewise, go with the RW.
203      */
204     if ((prefetchRO == 2) && tvp->roVol) {
205        tfid.Fid.Volume = tvp->roVol;                 /* remember RO volume */
206        tfid.Cell       = tvp->cell;
207        afs_PutVolume(tvp, WRITE_LOCK);               /* release old volume */
208        tvp = afs_GetVolume(&tfid, areq, WRITE_LOCK); /* get the new one */
209        if (!tvp) return ENODEV;                      /* oops, can't do it */
210     }
211
212     if (avc->mvid == 0)
213         avc->mvid = (struct VenusFid *) osi_AllocSmallSpace(sizeof(struct VenusFid));
214     avc->mvid->Cell = tvp->cell;
215     avc->mvid->Fid.Volume = tvp->volume;
216     avc->mvid->Fid.Vnode = 1;
217     avc->mvid->Fid.Unique = 1;
218     avc->states |= CMValid;
219
220     /* Used to: if the mount point is stored within a backup volume,
221      * then we should only update the parent pointer information if
222      * there's none already set, so as to avoid updating a volume's ..
223      * info with something in an OldFiles directory.
224      *
225      * Next two lines used to be under this if:
226      *
227      * if (!(avc->states & CBackup) || tvp->dotdot.Fid.Volume == 0)
228      *
229      * Now: update mount point back pointer on every call, so that we handle
230      * multiple mount points better.  This way, when du tries to go back
231      * via chddir(".."), it will end up exactly where it started, yet
232      * cd'ing via a new path to a volume will reset the ".." pointer
233      * to the new path.
234      */
235     tvp->mtpoint = avc->fid;    /* setup back pointer to mtpoint */
236     if (advc) tvp->dotdot  = advc->fid;
237
238     *avolpp = tvp;
239     return 0;
240 }
241
242 /*
243  * afs_InitFakeStat
244  *
245  * Must be called on an afs_fakestat_state object before calling
246  * afs_EvalFakeStat or afs_PutFakeStat.  Calling afs_PutFakeStat
247  * without calling afs_EvalFakeStat is legal, as long as this
248  * function is called.
249  */
250
251 void
252 afs_InitFakeStat(state)
253     struct afs_fakestat_state *state;
254 {
255     state->valid = 1;
256     state->did_eval = 0;
257     state->need_release = 0;
258 }
259
260 /*
261  * afs_EvalFakeStat_int
262  *
263  * The actual implementation of afs_EvalFakeStat and afs_TryEvalFakeStat,
264  * which is called by those wrapper functions.
265  *
266  * Only issues RPCs if canblock is non-zero.
267  */
268 static int
269 afs_EvalFakeStat_int(avcp, state, areq, canblock)
270     struct vcache **avcp;
271     struct afs_fakestat_state *state;
272     struct vrequest *areq;
273     int canblock;
274 {
275     struct vcache *tvc, *root_vp;
276     struct volume *tvolp = NULL;
277     int code = 0;
278
279     osi_Assert(state->valid == 1);
280     osi_Assert(state->did_eval == 0);
281     state->did_eval = 1;
282     if (!afs_fakestat_enable)
283         return 0;
284     tvc = *avcp;
285     if (tvc->mvstat != 1)
286         return 0;
287
288     /* Is the call to VerifyVCache really necessary? */
289     code = afs_VerifyVCache(tvc, areq);
290     if (code)
291         goto done;
292     if (canblock) {
293         ObtainWriteLock(&tvc->lock, 599);
294         code = EvalMountPoint(tvc, NULL, &tvolp, areq);
295         ReleaseWriteLock(&tvc->lock);
296         if (code)
297             goto done;
298         if (tvolp) {
299             tvolp->dotdot = tvc->fid;
300             tvolp->dotdot.Fid.Vnode = tvc->parentVnode;
301             tvolp->dotdot.Fid.Unique = tvc->parentUnique;
302         }
303     }
304     if (tvc->mvid && (tvc->states & CMValid)) {
305         if (!canblock) {
306             afs_int32 retry;
307
308             do {
309                 retry = 0;
310                 ObtainWriteLock(&afs_xvcache, 597);
311                 root_vp = afs_FindVCache(tvc->mvid, 0, 0, &retry, 0);
312                 if (root_vp && retry) {
313                     ReleaseWriteLock(&afs_xvcache);
314                     afs_PutVCache(root_vp, 0);
315                 }
316             } while (root_vp && retry);
317             ReleaseWriteLock(&afs_xvcache);
318         } else {
319             root_vp = afs_GetVCache(tvc->mvid, areq, NULL, NULL, WRITE_LOCK);
320         }
321         if (!root_vp) {
322             code = canblock ? ENOENT : 0;
323             goto done;
324         }
325         if (tvolp) {
326             /* Is this always kosher?  Perhaps we should instead use
327              * NBObtainWriteLock to avoid potential deadlock.
328              */
329             ObtainWriteLock(&root_vp->lock, 598);
330             if (!root_vp->mvid)
331                 root_vp->mvid = osi_AllocSmallSpace(sizeof(struct VenusFid));
332             *root_vp->mvid = tvolp->dotdot;
333             ReleaseWriteLock(&root_vp->lock);
334         }
335         state->need_release = 1;
336         state->root_vp = root_vp;
337         *avcp = root_vp;
338         code = 0;
339     } else {
340         code = canblock ? ENOENT : 0;
341     }
342
343 done:
344     if (tvolp)
345         afs_PutVolume(tvolp, WRITE_LOCK);
346     return code;
347 }
348
349 /*
350  * afs_EvalFakeStat
351  *
352  * Automatically does the equivalent of EvalMountPoint for vcache entries
353  * which are mount points.  Remembers enough state to properly release
354  * the volume root vcache when afs_PutFakeStat() is called.
355  *
356  * State variable must be initialized by afs_InitFakeState() beforehand.
357  *
358  * Returns 0 when everything succeeds and *avcp points to the vcache entry
359  * that should be used for the real vnode operation.  Returns non-zero if
360  * something goes wrong and the error code should be returned to the user.
361  */
362 int
363 afs_EvalFakeStat(avcp, state, areq)
364     struct vcache **avcp;
365     struct afs_fakestat_state *state;
366     struct vrequest *areq;
367 {
368     return afs_EvalFakeStat_int(avcp, state, areq, 1);
369 }
370
371 /*
372  * afs_TryEvalFakeStat
373  *
374  * Same as afs_EvalFakeStat, but tries not to talk to remote servers
375  * and only evaluate the mount point if all the data is already in
376  * local caches.
377  *
378  * Returns 0 if everything succeeds and *avcp points to a valid
379  * vcache entry (possibly evaluated).
380  */
381 int
382 afs_TryEvalFakeStat(avcp, state, areq)
383     struct vcache **avcp;
384     struct afs_fakestat_state *state;
385     struct vrequest *areq;
386 {
387     return afs_EvalFakeStat_int(avcp, state, areq, 0);
388 }
389
390 /*
391  * afs_PutFakeStat
392  *
393  * Perform any necessary cleanup at the end of a vnode op, given that
394  * afs_InitFakeStat was previously called with this state.
395  */
396 void
397 afs_PutFakeStat(state)
398     struct afs_fakestat_state *state;
399 {
400     osi_Assert(state->valid == 1);
401     if (state->need_release)
402         afs_PutVCache(state->root_vp, 0);
403     state->valid = 0;
404 }
405     
406 afs_ENameOK(aname)
407     register char *aname; {
408     register char tc;
409     register int tlen;
410
411     AFS_STATCNT(ENameOK);
412     tlen = strlen(aname);
413     if (tlen >= 4 && strcmp(aname+tlen-4, "@sys") == 0) return 0;
414     return 1;
415 }
416
417 afs_getsysname(areq, adp, bufp)
418     register struct vrequest *areq;
419     register struct vcache *adp;
420     register char *bufp;
421 {
422     static char sysname[MAXSYSNAME];
423     register struct unixuser *au;
424     register afs_int32 error;
425
426     if (!afs_nfsexporter) {
427       strcpy(bufp, afs_sysname);
428       return 0;
429     }
430     AFS_STATCNT(getsysname);
431     au = afs_GetUser(areq->uid, adp->fid.Cell, 0);
432     afs_PutUser(au, 0); 
433     if (au->exporter) {
434       error = EXP_SYSNAME(au->exporter, (char *)0, bufp);
435       if (error) 
436         strcpy(bufp, "@sys");
437       return -1;
438     } else {
439       strcpy(bufp, afs_sysname);
440       return 0;
441     }
442 }
443
444 Check_AtSys(avc, aname, state, areq)
445     register struct vcache *avc;
446     char *aname;
447     struct sysname_info *state;
448     struct vrequest *areq;
449 {
450     if (AFS_EQ_ATSYS(aname)) {
451       state->offset = 0;
452       state->name = (char *) osi_AllocLargeSpace(AFS_SMALLOCSIZ);
453       state->allocked = 1;
454       state->index = afs_getsysname(areq, avc, state->name);
455     } else {
456       state->offset = -1;
457       state->allocked = 0;
458       state->index = 0;
459       state->name = aname;
460     }
461 }
462
463 Next_AtSys(avc, areq, state)
464      register struct vcache *avc;
465      struct vrequest *areq;
466      struct sysname_info *state;
467 {
468   if (state->index == -1)
469     return 0;   /* No list */
470
471   /* Check for the initial state of aname != "@sys" in Check_AtSys*/
472   if (state->offset == -1 && state->allocked == 0) {
473     register char *tname;
474     /* Check for .*@sys */
475       for (tname=state->name; *tname; tname++)
476         /*Move to the end of the string*/;
477       if ((tname > state->name + 4) && (AFS_EQ_ATSYS(tname-4))) {
478         state->offset = (tname - 4) - state->name;
479         tname = (char *) osi_AllocLargeSpace(AFS_LRALLOCSIZ);
480         strncpy(tname, state->name, state->offset);
481         state->name = tname;
482         state->allocked = 1;
483         state->index = afs_getsysname(areq, avc, state->name+state->offset);
484         return 1;
485       } else
486         return 0; /* .*@sys doesn't match either */
487   } else if (++(state->index) >= afs_sysnamecount
488              || !afs_sysnamelist[state->index])
489     return 0;   /* end of list */
490   strcpy(state->name+state->offset, afs_sysnamelist[state->index]);
491   return 1;
492 }
493
494 #if (defined(AFS_SGI62_ENV) || defined(AFS_SUN57_64BIT_ENV))
495 extern int BlobScan(ino64_t *afile, afs_int32 ablob);
496 #else
497 #if defined AFS_LINUX_64BIT_KERNEL
498 extern int BlobScan(long *afile, afs_int32 ablob);
499 #else
500 extern int BlobScan(afs_int32 *afile, afs_int32 ablob);
501 #endif
502 #endif
503
504
505 /* called with an unlocked directory and directory cookie.  Areqp
506  * describes who is making the call.
507  * Scans the next N (about 30, typically) directory entries, and does
508  * a bulk stat call to stat them all.
509  *
510  * Must be very careful when merging in RPC responses, since we dont
511  * want to overwrite newer info that was added by a file system mutating
512  * call that ran concurrently with our bulk stat call.
513  *
514  * We do that, as described below, by not merging in our info (always
515  * safe to skip the merge) if the status info is valid in the vcache entry.
516  *
517  * If adapt ever implements the bulk stat RPC, then this code will need to
518  * ensure that vcaches created for failed RPC's to older servers have the
519  * CForeign bit set.
520  */
521 struct vcache * BStvc = (struct vcache *) 0;
522 int afs_DoBulkStat(adp, dirCookie, areqp)
523   struct vcache *adp;
524   long dirCookie;
525   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, 0, 0, &retry, 0 /* no stats | LRU */);
692               if (tvcp && retry) {
693                 ReleaseWriteLock(&afs_xvcache);
694                 afs_PutVCache(tvcp, 0);
695               }
696             } while (tvcp && retry);
697             if (!tvcp) {          /* otherwise, create manually */
698               tvcp = afs_NewVCache(&tfid, hostp, 0, 0);
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, 0);
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 #ifdef RX_ENABLE_LOCKS
784             AFS_GUNLOCK();
785 #endif /* RX_ENABLE_LOCKS */
786
787             if (!(tcp->srvr->server->flags & SNO_INLINEBULK)) {
788                 code = RXAFS_InlineBulkStatus(tcp->id, &fidParm, &statParm,
789                                               &cbParm, &volSync);
790                 if (code == RXGEN_OPCODE) {
791                     tcp->srvr->server->flags |= SNO_INLINEBULK;
792                     inlinebulk = 0;
793                     code = RXAFS_BulkStatus(tcp->id, &fidParm, &statParm, 
794                                             &cbParm, &volSync);
795                 } else
796                     inlinebulk=1;
797             } else {
798                 inlinebulk=0;
799                 code = RXAFS_BulkStatus(tcp->id, &fidParm, &statParm, &cbParm,
800                                         &volSync);
801             }
802 #ifdef RX_ENABLE_LOCKS
803             AFS_GLOCK();
804 #endif /* RX_ENABLE_LOCKS */
805             XSTATS_END_TIME;
806         }
807         else code = -1;
808     } while (afs_Analyze(tcp, code, &adp->fid, areqp, 
809                          AFS_STATS_FS_RPCIDX_BULKSTATUS, SHARED_LOCK, (struct cell *)0));
810
811     /* now, if we didnt get the info, bail out. */
812     if (code) goto done;
813
814     /* we need vol flags to create the entries properly */
815     dotdot.Fid.Volume = 0;
816     volp = afs_GetVolume(&adp->fid, areqp, READ_LOCK);
817     if (volp) {
818         volStates = volp->states;
819         if (volp->dotdot.Fid.Volume != 0)
820             dotdot = volp->dotdot;
821     }
822     else volStates = 0;
823
824     /* find the place to merge the info into  We do this by skipping
825      * nskip entries in the LRU queue.  The more we skip, the more
826      * we preserve, since the head of the VLRU queue is the most recently
827      * referenced file.
828      */
829   reskip:
830     nskip = afs_cacheStats / 2;         /* preserved fraction of the cache */
831     ObtainReadLock(&afs_xvcache);
832     if (QEmpty(&VLRU)) {
833       /* actually a serious error, probably should panic. Probably will 
834        * panic soon, oh well. */
835       ReleaseReadLock(&afs_xvcache);
836       afs_warnuser("afs_DoBulkStat: VLRU empty!");
837       goto done;
838     }
839     if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
840        refpanic ("Bulkstat VLRU inconsistent");
841     }
842     for(tq = VLRU.next; tq != &VLRU; tq = QNext(tq)) {
843         if (--nskip <= 0) break;
844         else if (QNext(QPrev(tq)) != tq) {
845            BStvc = QTOV(tq);
846            refpanic ("BulkStat VLRU inconsistent");
847         }
848     }
849     if (tq != &VLRU) lruvcp = QTOV(tq);
850     else lruvcp = QTOV(VLRU.next);
851
852     /* now we have to hold this entry, so that it does not get moved
853      * into the free list while we're running.  It could still get
854      * moved within the lru queue, but hopefully that will be rare; it
855      * doesn't hurt nearly as much.
856      */
857     retry = 0;
858     osi_vnhold(lruvcp, &retry);
859     ReleaseReadLock(&afs_xvcache);           /* could be read lock */
860     if (retry)
861       goto reskip;
862
863     /* otherwise, merge in the info.  We have to be quite careful here,
864      * since we need to ensure that we don't merge old info over newer
865      * stuff in a stat cache entry.  We're very conservative here: we don't
866      * do the merge at all unless we ourselves create the stat cache
867      * entry.  That's pretty safe, and should work pretty well, since we
868      * typically expect to do the stat cache creation ourselves.
869      *
870      * We also have to take into account racing token revocations.
871      */
872     for(i=0; i<fidIndex; i++) {
873         if ((&statsp[i])->errorCode) 
874             continue;
875         afid.Cell = adp->fid.Cell;
876         afid.Fid.Volume = adp->fid.Fid.Volume;
877         afid.Fid.Vnode = fidsp[i].Vnode;
878         afid.Fid.Unique = fidsp[i].Unique;
879         do {
880            retry = 0;
881            ObtainReadLock(&afs_xvcache);
882            tvcp = afs_FindVCache(&afid, 1, 0, &retry, 0/* !stats&!lru*/);
883            ReleaseReadLock(&afs_xvcache);
884         } while (tvcp && retry);
885
886         /* The entry may no longer exist */
887         if (tvcp == NULL) {
888             continue;
889         }
890
891         /* now we have the entry held, but we need to fill it in */
892         ObtainWriteLock(&tvcp->lock,131);
893
894         /* if CBulkFetching is not set, or if the file size no longer
895          * matches the value we placed there when we set the CBulkFetching
896          * flag, then someone else has done something with this node,
897          * and we may not have the latest status information for this
898          * file.  Leave the entry alone.
899          */
900         if (!(tvcp->states & CBulkFetching) || (tvcp->m.Length != statSeqNo)) {
901             flagIndex++;
902             ReleaseWriteLock(&tvcp->lock);
903             afs_PutVCache(tvcp, 0);
904             continue;
905         }
906
907         /* now copy ".." entry back out of volume structure, if necessary */
908         if (tvcp->mvstat == 2  && (dotdot.Fid.Volume != 0)) {
909             if (!tvcp->mvid)
910                 tvcp->mvid = (struct VenusFid *) osi_AllocSmallSpace(sizeof(struct VenusFid));
911             *tvcp->mvid = dotdot;
912         }
913
914         ObtainWriteLock(&afs_xvcache,132);
915         if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
916            refpanic ("Bulkstat VLRU inconsistent2");
917         }
918         if ((QNext(QPrev(&tvcp->vlruq)) != &tvcp->vlruq) 
919             || (QPrev(QNext(&tvcp->vlruq)) != &tvcp->vlruq))
920            refpanic ("Bulkstat VLRU inconsistent4");
921         if ((QNext(QPrev(&lruvcp->vlruq)) != &lruvcp->vlruq) 
922             || (QPrev(QNext(&lruvcp->vlruq)) != &lruvcp->vlruq)) 
923            refpanic ("Bulkstat VLRU inconsistent5");
924
925         if (tvcp != lruvcp) {  /* if they are == don't move it, don't corrupt vlru */
926            QRemove(&tvcp->vlruq);
927            QAdd(&lruvcp->vlruq, &tvcp->vlruq);
928         }
929
930         if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
931            refpanic ("Bulkstat VLRU inconsistent3");
932         }
933         if ((QNext(QPrev(&tvcp->vlruq)) != &tvcp->vlruq) 
934             || (QPrev(QNext(&tvcp->vlruq)) != &tvcp->vlruq))
935            refpanic ("Bulkstat VLRU inconsistent5");
936         if ((QNext(QPrev(&lruvcp->vlruq)) != &lruvcp->vlruq) 
937             || (QPrev(QNext(&lruvcp->vlruq)) != &lruvcp->vlruq))
938            refpanic ("Bulkstat VLRU inconsistent6");
939         ReleaseWriteLock(&afs_xvcache);
940
941         ObtainWriteLock(&afs_xcbhash, 494);
942
943         /* We need to check the flags again. We may have missed
944          * something while we were waiting for a lock.
945          */
946         if (!(tvcp->states & CBulkFetching) || (tvcp->m.Length != statSeqNo)) {
947             flagIndex++;
948             ReleaseWriteLock(&tvcp->lock);
949             ReleaseWriteLock(&afs_xcbhash);
950             afs_PutVCache(tvcp, 0);
951             continue;
952         }
953
954         /* now merge in the resulting status back into the vnode.
955          * We only do this if the entry looks clear.
956          */
957         afs_ProcessFS(tvcp, &statsp[i], areqp);
958 #ifdef AFS_LINUX22_ENV
959         /* overwrite the ops if it's a directory or symlink. */
960         if (vType(tvcp) == VDIR)
961             tvcp->v.v_op = &afs_dir_iops;
962         else if (vType(tvcp) == VLNK)
963             tvcp->v.v_op = &afs_symlink_iops;
964 #endif
965
966         /* do some accounting for bulk stats: mark this entry as
967          * loaded, so we can tell if we use it before it gets
968          * recycled.
969          */
970         tvcp->states |= CBulkStat;
971         tvcp->states &= ~CBulkFetching;
972         flagIndex++;
973         afs_bulkStatsDone++;
974
975         /* merge in vol info */
976         if (volStates & VRO) tvcp->states |= CRO;
977         if (volStates & VBackup) tvcp->states |= CBackup;
978         if (volStates & VForeign) tvcp->states |= CForeign;
979
980         /* merge in the callback info */
981         tvcp->states |= CTruth;
982
983         /* get ptr to the callback we are interested in */
984         tcbp = cbsp + i;
985
986         if (tcbp->ExpirationTime != 0) {
987             tvcp->cbExpires = tcbp->ExpirationTime+startTime;
988             tvcp->callback = hostp;
989             tvcp->states |= CStatd;
990             afs_QueueCallback(tvcp, CBHash(tcbp->ExpirationTime), volp);
991         }
992         else if (tvcp->states & CRO) {
993             /* ordinary callback on a read-only volume -- AFS 3.2 style */
994             tvcp->cbExpires = 3600+startTime;
995             tvcp->callback = hostp;
996             tvcp->states |= CStatd;
997             afs_QueueCallback(tvcp, CBHash(3600), volp);
998         }
999         else {
1000             tvcp->callback = 0;
1001             tvcp->states &= ~(CStatd|CUnique);  
1002             afs_DequeueCallback(tvcp);
1003             if ((tvcp->states & CForeign) || (vType(tvcp) == VDIR)) 
1004               osi_dnlc_purgedp (tvcp);  /* if it (could be) a directory */
1005         }
1006         ReleaseWriteLock(&afs_xcbhash);
1007
1008         ReleaseWriteLock(&tvcp->lock);
1009         /* finally, we're done with the entry */
1010         afs_PutVCache(tvcp, 0);
1011     }   /* for all files we got back */
1012
1013     /* finally return the pointer into the LRU queue */
1014     afs_PutVCache(lruvcp, 0);
1015
1016   done:
1017     /* Be sure to turn off the CBulkFetching flags */
1018     for(i=flagIndex; i<fidIndex; i++) {
1019         afid.Cell = adp->fid.Cell;
1020         afid.Fid.Volume = adp->fid.Fid.Volume;
1021         afid.Fid.Vnode = fidsp[i].Vnode;
1022         afid.Fid.Unique = fidsp[i].Unique;
1023         do {
1024            retry = 0;
1025            ObtainReadLock(&afs_xvcache);
1026            tvcp = afs_FindVCache(&afid, 1, 0, &retry, 0/* !stats&!lru*/);
1027            ReleaseReadLock(&afs_xvcache);
1028         } while (tvcp && retry);
1029         if (tvcp != NULL
1030             && (tvcp->states & CBulkFetching)
1031             && (tvcp->m.Length == statSeqNo)) {
1032           tvcp->states &= ~CBulkFetching;
1033         }
1034         if (tvcp != NULL) {
1035           afs_PutVCache(tvcp, 0);
1036         }
1037     }
1038     if ( volp )
1039         afs_PutVolume(volp, READ_LOCK);
1040     
1041     /* If we did the InlineBulk RPC pull out the return code */
1042     if (inlinebulk) {
1043         if ((&statsp[0])->errorCode) {
1044             afs_Analyze(tcp, (&statsp[0])->errorCode, &adp->fid, areqp, 
1045                         AFS_STATS_FS_RPCIDX_BULKSTATUS, SHARED_LOCK, 
1046                         (struct cell *)0);
1047             code = (&statsp[0])->errorCode;
1048         }
1049     } else {
1050         code = 0;
1051     }
1052     osi_FreeLargeSpace(statMemp);
1053     osi_FreeLargeSpace(cbfMemp);
1054     return code;
1055 }
1056
1057 /* was: (AFS_DEC_ENV) || defined(AFS_OSF30_ENV) || defined(AFS_NCR_ENV) */
1058 int AFSDOBULK = 1;
1059
1060 #ifdef  AFS_OSF_ENV
1061 afs_lookup(adp, ndp)
1062     struct vcache *adp;
1063     struct nameidata *ndp; {
1064     char aname[MAXNAMLEN+1];    /* XXX */
1065     struct vcache **avcp = (struct vcache **)&(ndp->ni_vp);
1066     struct ucred *acred = ndp->ni_cred;
1067     int wantparent = ndp->ni_nameiop & WANTPARENT;
1068     int opflag = ndp->ni_nameiop & OPFLAG;
1069 #else   /* AFS_OSF_ENV */
1070 #if     defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
1071 afs_lookup(OSI_VC_ARG(adp), aname, avcp, pnp, flags, rdir, acred)
1072     struct pathname *pnp;
1073     int flags;
1074     struct vnode *rdir;
1075 #else
1076 #if defined(UKERNEL)
1077 afs_lookup(adp, aname, avcp, acred, flags)
1078     int flags;
1079 #else    
1080 afs_lookup(adp, aname, avcp, acred)
1081 #endif /* UKERNEL */
1082 #endif /* SUN5 || SGI */
1083     OSI_VC_DECL(adp);
1084     struct vcache **avcp;
1085     char *aname;
1086     struct AFS_UCRED *acred; {
1087 #endif
1088     struct vrequest treq;
1089     char *tname = (char *)0;
1090     register struct vcache *tvc=0;
1091     register afs_int32 code;
1092     register afs_int32 bulkcode = 0;
1093     int pass = 0, hit = 0;
1094     long dirCookie;
1095     extern afs_int32 afs_mariner;                       /*Writing activity to log?*/
1096     OSI_VC_CONVERT(adp)
1097     afs_hyper_t versionNo;
1098     int no_read_access = 0;
1099     struct sysname_info sysState;   /* used only for @sys checking */
1100     int dynrootRetry = 1;
1101     struct afs_fakestat_state fakestate;
1102     int tryEvalOnly = 0;
1103
1104     AFS_STATCNT(afs_lookup);
1105     afs_InitFakeStat(&fakestate);
1106
1107     if (code = afs_InitReq(&treq, acred))
1108         goto done;
1109
1110 #ifdef  AFS_OSF_ENV
1111     ndp->ni_dvp = AFSTOV(adp);
1112     memcpy(aname, ndp->ni_ptr, ndp->ni_namelen);
1113     aname[ndp->ni_namelen] = '\0';
1114 #endif  /* AFS_OSF_ENV */
1115
1116 #if defined(AFS_DARWIN_ENV)
1117     /* Workaround for MacOSX Finder, which tries to look for
1118      * .DS_Store and Contents under every directory.
1119      */
1120     if (afs_fakestat_enable && adp->mvstat == 1) {
1121         if (strcmp(aname, ".DS_Store") == 0)
1122             tryEvalOnly = 1;
1123         if (strcmp(aname, "Contents") == 0)
1124             tryEvalOnly = 1;
1125     }
1126 #endif
1127
1128     if (tryEvalOnly)
1129         code = afs_TryEvalFakeStat(&adp, &fakestate, &treq);
1130     else
1131         code = afs_EvalFakeStat(&adp, &fakestate, &treq);
1132     if (tryEvalOnly && adp->mvstat == 1)
1133         code = ENOENT;
1134     if (code)
1135         goto done;
1136
1137     *avcp = (struct vcache *) 0;   /* Since some callers don't initialize it */
1138
1139     /* come back to here if we encounter a non-existent object in a read-only
1140        volume's directory */
1141
1142   redo:
1143     *avcp = (struct vcache *) 0;   /* Since some callers don't initialize it */
1144     bulkcode = 0;
1145
1146     if (!(adp->states & CStatd)) {
1147         if (code = afs_VerifyVCache2(adp, &treq)) {
1148             goto done;
1149         }
1150     }
1151     else code = 0;
1152
1153     /* watch for ".." in a volume root */
1154     if (adp->mvstat == 2 && aname[0] == '.' && aname[1] == '.' && !aname[2]) {
1155         /* looking up ".." in root via special hacks */
1156         if (adp->mvid == (struct VenusFid *) 0 || adp->mvid->Fid.Volume == 0) {
1157 #ifdef  AFS_OSF_ENV
1158             extern struct vcache *afs_globalVp;
1159             if (adp == afs_globalVp) {
1160                 struct vnode *rvp = AFSTOV(adp);
1161 /*
1162                 ndp->ni_vp = rvp->v_vfsp->vfs_vnodecovered;
1163                 ndp->ni_dvp = ndp->ni_vp;
1164                 VN_HOLD(*avcp);
1165 */
1166                 code = ENODEV;
1167                 goto done;
1168             }
1169 #endif
1170             code = ENODEV;
1171             goto done;
1172         }
1173         /* otherwise we have the fid here, so we use it */
1174         tvc = afs_GetVCache(adp->mvid, &treq, (afs_int32 *)0,
1175                             (struct vcache*)0, 0);
1176         afs_Trace3(afs_iclSetp, CM_TRACE_GETVCDOTDOT,
1177                    ICL_TYPE_FID, adp->mvid, ICL_TYPE_POINTER, tvc, 
1178                    ICL_TYPE_INT32,  code);
1179         *avcp = tvc;
1180         code = (tvc ? 0 : ENOENT);
1181         hit = 1;
1182         if (tvc && !VREFCOUNT(tvc)) {
1183             osi_Panic("TT1");
1184         }
1185         if (code) {
1186             /*printf("LOOKUP GETVCDOTDOT -> %d\n", code);*/
1187         }
1188         goto done;
1189     }
1190
1191     /* now check the access */
1192     if (treq.uid != adp->last_looker) {  
1193        if (!afs_AccessOK(adp, PRSFS_LOOKUP, &treq, CHECK_MODE_BITS)) {
1194          *avcp = (struct vcache *)0;
1195          code = EACCES;
1196          goto done;
1197        }
1198        else adp->last_looker = treq.uid;
1199     } 
1200
1201     /* Check for read access as well.  We need read access in order to
1202        stat files, but not to stat subdirectories. */
1203     if (!afs_AccessOK(adp, PRSFS_LOOKUP, &treq, CHECK_MODE_BITS))
1204         no_read_access = 1;
1205
1206     /* special case lookup of ".".  Can we check for it sooner in this code,
1207      * for instance, way up before "redo:" ??
1208      * I'm not fiddling with the LRUQ here, either, perhaps I should, or else 
1209      * invent a lightweight version of GetVCache.
1210      */
1211     if (aname[0] == '.' && !aname[1]) { /* special case */
1212         ObtainReadLock(&afs_xvcache);   
1213         osi_vnhold(adp, 0);
1214         ReleaseReadLock(&afs_xvcache);  
1215         code = 0;
1216         *avcp = tvc = adp;
1217         hit = 1;
1218         if (adp && !VREFCOUNT(adp)) {
1219             osi_Panic("TT2");
1220         }
1221         goto done;
1222     }
1223
1224     Check_AtSys(adp, aname, &sysState, &treq);
1225     tname = sysState.name;
1226
1227     /* 1st Check_AtSys and lookup by tname is required here, for now,
1228        because the dnlc is *not* told to remove entries for the parent
1229        dir of file/dir op that afs_LocalHero likes, but dnlc is informed
1230        if the cached entry for the parent dir is invalidated for a
1231        non-local change.
1232        Otherwise, we'd be able to do a dnlc lookup on an entry ending
1233        w/@sys and know the dnlc was consistent with reality. */
1234     tvc = osi_dnlc_lookup (adp, tname, WRITE_LOCK);
1235     *avcp = tvc;  /* maybe wasn't initialized, but it is now */
1236     if (tvc) {
1237         if (no_read_access && vType(tvc) != VDIR && vType(tvc) != VLNK) {
1238             /* need read access on dir to stat non-directory / non-link */
1239             afs_PutVCache(tvc, WRITE_LOCK);
1240             *avcp = (struct vcache *)0;
1241             code = EACCES;
1242             goto done;
1243         }
1244 #ifdef AFS_LINUX22_ENV
1245         if (tvc->mvstat == 2) { /* we don't trust the dnlc for root vcaches */
1246             AFS_RELE(tvc);
1247             *avcp = 0;
1248         }
1249         else {  
1250             code = 0;
1251             hit = 1;
1252             goto done;
1253         }
1254 #else /* non - LINUX */
1255         code = 0;
1256         hit = 1;
1257         goto done;
1258 #endif /* linux22 */
1259     }
1260
1261     {
1262     register struct dcache *tdc;
1263     afs_size_t dirOffset, dirLen;
1264     ino_t theDir;
1265     struct VenusFid tfid;
1266
1267     /* now we have to lookup the next fid */
1268     tdc = afs_GetDCache(adp, (afs_size_t) 0, &treq, &dirOffset, &dirLen, 1);
1269     if (!tdc) {
1270       *avcp = (struct vcache *)0;  /* redundant, but harmless */
1271       code = EIO;
1272       goto done;
1273     }
1274
1275     /* now we will just call dir package with appropriate inode.
1276       Dirs are always fetched in their entirety for now */
1277     ObtainReadLock(&adp->lock);
1278     ObtainReadLock(&tdc->lock);
1279
1280     /*
1281      * Make sure that the data in the cache is current. There are two
1282      * cases we need to worry about:
1283      * 1. The cache data is being fetched by another process.
1284      * 2. The cache data is no longer valid
1285      */
1286     while ((adp->states & CStatd)
1287            && (tdc->dflags & DFFetching)
1288            && hsame(adp->m.DataVersion, tdc->f.versionNo)) {
1289         ReleaseReadLock(&tdc->lock);
1290         ReleaseReadLock(&adp->lock);
1291         afs_osi_Sleep(&tdc->validPos);
1292         ObtainReadLock(&adp->lock);
1293         ObtainReadLock(&tdc->lock);
1294     }
1295     if (!(adp->states & CStatd)
1296         || !hsame(adp->m.DataVersion, tdc->f.versionNo)) {
1297         ReleaseReadLock(&tdc->lock);
1298         ReleaseReadLock(&adp->lock);
1299         afs_PutDCache(tdc);
1300         goto redo;
1301     }
1302
1303     /* Save the version number for when we call osi_dnlc_enter */
1304     hset(versionNo, tdc->f.versionNo);
1305
1306     /*
1307      * check for, and handle "@sys" if it's there.  We should be able
1308      * to avoid the alloc and the strcpy with a little work, but it's
1309      * not pressing.  If there aren't any remote users (ie, via the 
1310      * NFS translator), we have a slightly easier job.
1311      * the faster way to do this is to check for *aname == '@' and if 
1312      * it's there, check for @sys, otherwise, assume there's no @sys 
1313      * then, if the lookup fails, check for .*@sys...
1314      */
1315     /* above now implemented by Check_AtSys and Next_AtSys */
1316
1317     /* lookup the name in the appropriate dir, and return a cache entry
1318        on the resulting fid */
1319     theDir = tdc->f.inode;
1320     code = afs_dir_LookupOffset(&theDir, sysState.name, &tfid.Fid, &dirCookie);
1321
1322     /* If the first lookup doesn't succeed, maybe it's got @sys in the name */
1323     while (code == ENOENT && Next_AtSys(adp, &treq, &sysState)) {
1324       code = afs_dir_LookupOffset(&theDir, sysState.name, &tfid.Fid, &dirCookie);
1325     }
1326     tname = sysState.name;
1327
1328     ReleaseReadLock(&tdc->lock);
1329     afs_PutDCache(tdc);
1330
1331     if (code == ENOENT && afs_IsDynroot(adp) && dynrootRetry) {
1332         struct cell *tcell;
1333
1334         ReleaseReadLock(&adp->lock);
1335         dynrootRetry = 0;
1336         if (*tname == '.')
1337             tcell = afs_GetCellByName(tname + 1, READ_LOCK);
1338         else
1339             tcell = afs_GetCellByName(tname, READ_LOCK);
1340         if (tcell) {
1341             afs_PutCell(tcell, READ_LOCK);
1342             afs_RefreshDynroot();
1343             if (tname != aname && tname) osi_FreeLargeSpace(tname);
1344             goto redo;
1345         }
1346     } else {
1347         ReleaseReadLock(&adp->lock);
1348     }
1349
1350     /* new fid has same cell and volume */
1351     tfid.Cell = adp->fid.Cell;
1352     tfid.Fid.Volume = adp->fid.Fid.Volume;
1353     afs_Trace4(afs_iclSetp, CM_TRACE_LOOKUP, ICL_TYPE_POINTER, adp, 
1354                ICL_TYPE_STRING, tname,
1355                ICL_TYPE_FID, &tfid, ICL_TYPE_INT32, code);
1356
1357     if (code) {
1358         if (code != ENOENT) {
1359             printf("LOOKUP dirLookupOff -> %d\n", code);
1360         }
1361         goto done;
1362     }  
1363
1364     /* prefetch some entries, if the dir is currently open.  The variable
1365      * dirCookie tells us where to start prefetching from.
1366      */
1367     if (AFSDOBULK && adp->opens > 0 && !(adp->states & CForeign) && !afs_IsDynroot(adp)) {
1368         afs_int32 retry;
1369         /* if the entry is not in the cache, or is in the cache,
1370          * but hasn't been statd, then do a bulk stat operation.
1371          */
1372         do {
1373            retry = 0;
1374            ObtainReadLock(&afs_xvcache);        
1375            tvc = afs_FindVCache(&tfid, 1, 0, &retry, 0/* !stats,!lru */);
1376            ReleaseReadLock(&afs_xvcache);       
1377         } while (tvc && retry);
1378
1379         if (!tvc || !(tvc->states & CStatd)) 
1380             bulkcode = afs_DoBulkStat(adp, dirCookie, &treq);
1381         else 
1382             bulkcode = 0;
1383
1384         /* if the vcache isn't usable, release it */
1385         if (tvc && !(tvc->states & CStatd)) {
1386             afs_PutVCache(tvc, 0);
1387             tvc = (struct vcache *) 0;
1388         }
1389     } else {
1390         tvc = (struct vcache *) 0;
1391         bulkcode = 0;
1392     }
1393
1394     /* now get the status info, if we don't already have it */
1395     /* This is kind of weird, but we might wind up accidentally calling
1396      * RXAFS_Lookup because we happened upon a file which legitimately
1397      * has a 0 uniquifier. That is the result of allowing unique to wrap
1398      * to 0. This was fixed in AFS 3.4. For CForeign, Unique == 0 means that
1399      * the file has not yet been looked up.
1400      */
1401     if (!tvc) {
1402        afs_int32 cached = 0;
1403        if (!tfid.Fid.Unique && (adp->states & CForeign)) {
1404             tvc = afs_LookupVCache(&tfid, &treq, &cached, WRITE_LOCK, 
1405                                    adp, tname);
1406        } 
1407        if (!tvc && !bulkcode) {  /* lookup failed or wasn't called */
1408            tvc = afs_GetVCache(&tfid, &treq, &cached, (struct vcache*)0,
1409                                WRITE_LOCK);
1410        } 
1411     } /* if !tvc */
1412     } /* sub-block just to reduce stack usage */
1413
1414     if (tvc) {
1415        if (adp->states & CForeign)
1416            tvc->states |= CForeign;
1417         tvc->parentVnode = adp->fid.Fid.Vnode;
1418         tvc->parentUnique = adp->fid.Fid.Unique;
1419         tvc->states &= ~CBulkStat;
1420
1421 #if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
1422         if (!(flags & AFS_LOOKUP_NOEVAL))
1423           /* don't eval mount points */
1424 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
1425         if (!afs_fakestat_enable && tvc->mvstat == 1) {
1426             /* a mt point, possibly unevaluated */
1427             struct volume *tvolp;
1428
1429             ObtainWriteLock(&tvc->lock,133);
1430             code = EvalMountPoint(tvc, adp, &tvolp, &treq);
1431             ReleaseWriteLock(&tvc->lock);
1432
1433             if (code) {
1434                 afs_PutVCache(tvc, WRITE_LOCK);
1435                 if (tvolp) afs_PutVolume(tvolp, WRITE_LOCK);
1436                 goto done;
1437             }
1438
1439             /* next, we want to continue using the target of the mt point */
1440             if (tvc->mvid && (tvc->states & CMValid)) {
1441               struct vcache *uvc;
1442                 /* now lookup target, to set .. pointer */
1443                 afs_Trace2(afs_iclSetp, CM_TRACE_LOOKUP1,
1444                            ICL_TYPE_POINTER, tvc, ICL_TYPE_FID, &tvc->fid);
1445                 uvc = tvc;      /* remember for later */
1446
1447                 if (tvolp && (tvolp->states & VForeign)) {
1448                     /* XXXX tvolp has ref cnt on but not locked! XXX */
1449                     tvc = afs_GetRootVCache(tvc->mvid, &treq, (afs_int32 *)0, tvolp, WRITE_LOCK);
1450                 } else {
1451                     tvc = afs_GetVCache(tvc->mvid, &treq, (afs_int32 *)0,
1452                                         (struct vcache*)0, WRITE_LOCK);
1453                 }
1454                 afs_PutVCache(uvc, WRITE_LOCK); /* we're done with it */
1455
1456                 if (!tvc) {
1457                     code = ENOENT;
1458                     if (tvolp) {
1459                         afs_PutVolume(tvolp, WRITE_LOCK);
1460                     }
1461                     goto done;
1462                 }
1463
1464                 /* now, if we came via a new mt pt (say because of a new
1465                  * release of a R/O volume), we must reevaluate the ..
1466                  * ptr to point back to the appropriate place */
1467                 if (tvolp) {
1468                     ObtainWriteLock(&tvc->lock,134);
1469                     if (tvc->mvid == (struct VenusFid *) 0) {
1470                         tvc->mvid = (struct VenusFid *) osi_AllocSmallSpace(sizeof(struct VenusFid));
1471                     }
1472                     /* setup backpointer */
1473                     *tvc->mvid = tvolp->dotdot;
1474                     ReleaseWriteLock(&tvc->lock);
1475                     afs_PutVolume(tvolp, WRITE_LOCK);
1476                 }
1477             }
1478             else {
1479                 afs_PutVCache(tvc, WRITE_LOCK);
1480                 code = ENOENT;
1481                 if (tvolp) afs_PutVolume(tvolp, WRITE_LOCK);
1482                 goto done;
1483             }
1484         }
1485         *avcp = tvc;
1486         if (tvc && !VREFCOUNT(tvc)) {
1487             osi_Panic("TT3");
1488         }
1489         code = 0;
1490     }
1491     else {
1492         /* if we get here, we found something in a directory that couldn't
1493            be located (a Multics "connection failure").  If the volume is
1494            read-only, we try flushing this entry from the cache and trying
1495            again. */
1496         if (pass == 0) {
1497             struct volume *tv;
1498             tv = afs_GetVolume(&adp->fid, &treq, READ_LOCK);
1499             if (tv) {
1500                 if (tv->states & VRO) {
1501                     pass = 1;                   /* try this *once* */
1502                     ObtainWriteLock(&afs_xcbhash, 495);
1503                     afs_DequeueCallback(adp);
1504                     /* re-stat to get later version */
1505                     adp->states &= ~CStatd;
1506                     ReleaseWriteLock(&afs_xcbhash);
1507                     osi_dnlc_purgedp(adp);
1508                     afs_PutVolume(tv, READ_LOCK);
1509                     goto redo;
1510                 }
1511                 afs_PutVolume(tv, READ_LOCK);
1512             }
1513         }
1514         code = ENOENT;
1515     }
1516
1517 done:
1518     /* put the network buffer back, if need be */
1519     if (tname != aname && tname) osi_FreeLargeSpace(tname);
1520     if (code == 0) {
1521 #ifdef  AFS_OSF_ENV
1522         /* Handle RENAME; only need to check rename "."  */
1523         if (opflag == RENAME && wantparent && *ndp->ni_next == 0) {
1524             if (!FidCmp(&(tvc->fid), &(adp->fid))) { 
1525                 afs_PutVCache(*avcp, WRITE_LOCK);
1526                 *avcp = NULL;
1527                 afs_PutFakeStat(&fakestate);
1528                 return afs_CheckCode(EISDIR, &treq, 18);
1529             }
1530         }
1531 #endif  /* AFS_OSF_ENV */
1532
1533         if (afs_mariner)
1534           afs_AddMarinerName(aname, tvc); 
1535
1536 #if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
1537         if (!(flags & AFS_LOOKUP_NOEVAL))
1538         /* Here we don't enter the name into the DNLC because we want the
1539         evaluated mount dir to be there (the vcache for the mounted volume)
1540         rather than the vc of the mount point itself.  we can still find the
1541         mount point's vc in the vcache by its fid. */
1542 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
1543         if (!hit) {
1544           osi_dnlc_enter (adp, aname, tvc, &versionNo);
1545         }
1546         else {
1547 #ifdef AFS_LINUX20_ENV
1548             /* So Linux inode cache is up to date. */
1549             code = afs_VerifyVCache(tvc, &treq);
1550 #else
1551             afs_PutFakeStat(&fakestate);
1552             return 0;  /* can't have been any errors if hit and !code */
1553 #endif
1554         }
1555     }
1556     if (bulkcode) code = bulkcode; else 
1557     code = afs_CheckCode(code, &treq, 19);
1558     if (code) {
1559        /* If there is an error, make sure *avcp is null.
1560         * Alphas panic otherwise - defect 10719.
1561         */
1562        *avcp = (struct vcache *)0;
1563     }
1564
1565     afs_PutFakeStat(&fakestate);
1566     return code;
1567 }