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