macos-rollup-20051013
[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, 0);
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, 0 /* 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                 ObtainWriteLock(&tvcp->lock, 505);
713                 ReleaseWriteLock(&afs_xvcache);
714                 afs_RemoveVCB(&tfid);
715                 ReleaseWriteLock(&tvcp->lock);
716             } else {
717                 ReleaseWriteLock(&afs_xvcache);
718             }
719             if (!tvcp)
720                 goto done;      /* can't happen at present, more's the pity */
721
722 #ifdef AFS_DARWIN80_ENV
723             if (tvcp->states & CVInit) {
724                  /* XXX don't have status yet, so creating the vnode is
725                     not yet useful. we would get CDeadVnode set, and the
726                     upcoming PutVCache will cause the vcache to be flushed &
727                     freed, which in turn means the bulkstatus results won't 
728                     be used */
729             }
730 #endif
731             /* WARNING: afs_DoBulkStat uses the Length field to store a
732              * sequence number for each bulk status request. Under no
733              * circumstances should afs_DoBulkStat store a sequence number
734              * if the new length will be ignored when afs_ProcessFS is
735              * called with new stats. */
736 #ifdef AFS_SGI_ENV
737             if (!(tvcp->states & (CStatd | CBulkFetching))
738                 && (tvcp->execsOrWriters <= 0)
739                 && !afs_DirtyPages(tvcp)
740                 && !AFS_VN_MAPPED((vnode_t *) tvcp))
741 #else
742             if (!(tvcp->states & (CStatd | CBulkFetching))
743                 && (tvcp->execsOrWriters <= 0)
744                 && !afs_DirtyPages(tvcp))
745 #endif
746
747             {
748                 /* this entry doesnt exist in the cache, and is not
749                  * already being fetched by someone else, so add it to the
750                  * list of file IDs to obtain.
751                  *
752                  * We detect a callback breaking race condition by checking the
753                  * CBulkFetching state bit and the value in the file size.
754                  * It is safe to set the status only if the CBulkFetching
755                  * flag is still set and the value in the file size does
756                  * not change.
757                  *
758                  * Don't fetch status for dirty files. We need to
759                  * preserve the value of the file size. We could
760                  * flush the pages, but it wouldn't be worthwhile.
761                  */
762                 memcpy((char *)(fidsp + fidIndex), (char *)&tfid.Fid,
763                        sizeof(*fidsp));
764                 tvcp->states |= CBulkFetching;
765                 tvcp->m.Length = statSeqNo;
766                 fidIndex++;
767             }
768             afs_PutVCache(tvcp);
769         }
770
771         /* if dir vnode has non-zero entry */
772         /* move to the next dir entry by adding in the # of entries
773          * used by this dir entry.
774          */
775         temp = afs_dir_NameBlobs(dirEntryp->name) << 5;
776         DRelease((struct buffer *)dirEntryp, 0);
777         if (temp <= 0)
778             break;
779         dirCookie += temp;
780     }                           /* while loop over all dir entries */
781
782     /* now release the dir lock and prepare to make the bulk RPC */
783     ReleaseReadLock(&dcp->lock);
784     ReleaseReadLock(&adp->lock);
785
786     /* release the chunk */
787     afs_PutDCache(dcp);
788
789     /* dont make a null call */
790     if (fidIndex == 0)
791         goto done;
792
793     do {
794         /* setup the RPC parm structures */
795         fidParm.AFSCBFids_len = fidIndex;
796         fidParm.AFSCBFids_val = fidsp;
797         statParm.AFSBulkStats_len = fidIndex;
798         statParm.AFSBulkStats_val = statsp;
799         cbParm.AFSCBs_len = fidIndex;
800         cbParm.AFSCBs_val = cbsp;
801
802         /* start the timer; callback expirations are relative to this */
803         startTime = osi_Time();
804
805         tcp = afs_Conn(&adp->fid, areqp, SHARED_LOCK);
806         if (tcp) {
807             hostp = tcp->srvr->server;
808             XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_BULKSTATUS);
809             RX_AFS_GUNLOCK();
810
811             if (!(tcp->srvr->server->flags & SNO_INLINEBULK)) {
812                 code =
813                     RXAFS_InlineBulkStatus(tcp->id, &fidParm, &statParm,
814                                            &cbParm, &volSync);
815                 if (code == RXGEN_OPCODE) {
816                     tcp->srvr->server->flags |= SNO_INLINEBULK;
817                     inlinebulk = 0;
818                     code =
819                         RXAFS_BulkStatus(tcp->id, &fidParm, &statParm,
820                                          &cbParm, &volSync);
821                 } else
822                     inlinebulk = 1;
823             } else {
824                 inlinebulk = 0;
825                 code =
826                     RXAFS_BulkStatus(tcp->id, &fidParm, &statParm, &cbParm,
827                                      &volSync);
828             }
829             RX_AFS_GLOCK();
830             XSTATS_END_TIME;
831         } else
832             code = -1;
833     } while (afs_Analyze
834              (tcp, code, &adp->fid, areqp, AFS_STATS_FS_RPCIDX_BULKSTATUS,
835               SHARED_LOCK, NULL));
836
837     /* now, if we didnt get the info, bail out. */
838     if (code)
839         goto done;
840
841     /* we need vol flags to create the entries properly */
842     dotdot.Fid.Volume = 0;
843     volp = afs_GetVolume(&adp->fid, areqp, READ_LOCK);
844     if (volp) {
845         volStates = volp->states;
846         if (volp->dotdot.Fid.Volume != 0)
847             dotdot = volp->dotdot;
848     } else
849         volStates = 0;
850
851     /* find the place to merge the info into  We do this by skipping
852      * nskip entries in the LRU queue.  The more we skip, the more
853      * we preserve, since the head of the VLRU queue is the most recently
854      * referenced file.
855      */
856   reskip:
857     nskip = afs_cacheStats / 2; /* preserved fraction of the cache */
858     ObtainReadLock(&afs_xvcache);
859     if (QEmpty(&VLRU)) {
860         /* actually a serious error, probably should panic. Probably will 
861          * panic soon, oh well. */
862         ReleaseReadLock(&afs_xvcache);
863         afs_warnuser("afs_DoBulkStat: VLRU empty!");
864         goto done;
865     }
866     if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
867         refpanic("Bulkstat VLRU inconsistent");
868     }
869     for (tq = VLRU.next; tq != &VLRU; tq = QNext(tq)) {
870         if (--nskip <= 0)
871             break;
872         else if (QNext(QPrev(tq)) != tq) {
873             BStvc = QTOV(tq);
874             refpanic("BulkStat VLRU inconsistent");
875         }
876     }
877     if (tq != &VLRU)
878         lruvcp = QTOV(tq);
879     else
880         lruvcp = QTOV(VLRU.next);
881
882     /* now we have to hold this entry, so that it does not get moved
883      * into the free list while we're running.  It could still get
884      * moved within the lru queue, but hopefully that will be rare; it
885      * doesn't hurt nearly as much.
886      */
887     retry = 0;
888     osi_vnhold(lruvcp, &retry);
889     ReleaseReadLock(&afs_xvcache);      /* could be read lock */
890     if (retry)
891         goto reskip;
892 #ifdef AFS_DARWIN80_ENV
893     vnode_get(AFSTOV(lruvcp));
894 #endif
895
896     /* otherwise, merge in the info.  We have to be quite careful here,
897      * since we need to ensure that we don't merge old info over newer
898      * stuff in a stat cache entry.  We're very conservative here: we don't
899      * do the merge at all unless we ourselves create the stat cache
900      * entry.  That's pretty safe, and should work pretty well, since we
901      * typically expect to do the stat cache creation ourselves.
902      *
903      * We also have to take into account racing token revocations.
904      */
905     for (i = 0; i < fidIndex; i++) {
906         if ((&statsp[i])->errorCode)
907             continue;
908         afid.Cell = adp->fid.Cell;
909         afid.Fid.Volume = adp->fid.Fid.Volume;
910         afid.Fid.Vnode = fidsp[i].Vnode;
911         afid.Fid.Unique = fidsp[i].Unique;
912         do {
913             retry = 0;
914             ObtainReadLock(&afs_xvcache);
915             tvcp = afs_FindVCache(&afid, &retry, 0 /* !stats&!lru */ );
916             ReleaseReadLock(&afs_xvcache);
917         } while (tvcp && retry);
918
919         /* The entry may no longer exist */
920         if (tvcp == NULL) {
921             continue;
922         }
923
924         /* now we have the entry held, but we need to fill it in */
925         ObtainWriteLock(&tvcp->lock, 131);
926
927         /* if CBulkFetching is not set, or if the file size no longer
928          * matches the value we placed there when we set the CBulkFetching
929          * flag, then someone else has done something with this node,
930          * and we may not have the latest status information for this
931          * file.  Leave the entry alone.
932          */
933         if (!(tvcp->states & CBulkFetching) || (tvcp->m.Length != statSeqNo)) {
934             flagIndex++;
935             ReleaseWriteLock(&tvcp->lock);
936             afs_PutVCache(tvcp);
937             continue;
938         }
939
940         /* now copy ".." entry back out of volume structure, if necessary */
941         if (tvcp->mvstat == 2 && (dotdot.Fid.Volume != 0)) {
942             if (!tvcp->mvid)
943                 tvcp->mvid = (struct VenusFid *)
944                     osi_AllocSmallSpace(sizeof(struct VenusFid));
945             *tvcp->mvid = dotdot;
946         }
947
948         ObtainWriteLock(&afs_xvcache, 132);
949         if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
950             refpanic("Bulkstat VLRU inconsistent2");
951         }
952         if ((QNext(QPrev(&tvcp->vlruq)) != &tvcp->vlruq)
953             || (QPrev(QNext(&tvcp->vlruq)) != &tvcp->vlruq)) {
954             refpanic("Bulkstat VLRU inconsistent4");
955         }
956         if ((QNext(QPrev(&lruvcp->vlruq)) != &lruvcp->vlruq)
957             || (QPrev(QNext(&lruvcp->vlruq)) != &lruvcp->vlruq)) {
958             refpanic("Bulkstat VLRU inconsistent5");
959         }
960
961         if (tvcp != lruvcp) {   /* if they are == don't move it, don't corrupt vlru */
962             QRemove(&tvcp->vlruq);
963             QAdd(&lruvcp->vlruq, &tvcp->vlruq);
964         }
965
966         if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
967             refpanic("Bulkstat VLRU inconsistent3");
968         }
969         if ((QNext(QPrev(&tvcp->vlruq)) != &tvcp->vlruq)
970             || (QPrev(QNext(&tvcp->vlruq)) != &tvcp->vlruq)) {
971             refpanic("Bulkstat VLRU inconsistent5");
972         }
973         if ((QNext(QPrev(&lruvcp->vlruq)) != &lruvcp->vlruq)
974             || (QPrev(QNext(&lruvcp->vlruq)) != &lruvcp->vlruq)) {
975             refpanic("Bulkstat VLRU inconsistent6");
976         }
977         ReleaseWriteLock(&afs_xvcache);
978
979         ObtainWriteLock(&afs_xcbhash, 494);
980
981         /* We need to check the flags again. We may have missed
982          * something while we were waiting for a lock.
983          */
984         if (!(tvcp->states & CBulkFetching) || (tvcp->m.Length != statSeqNo)) {
985             flagIndex++;
986             ReleaseWriteLock(&tvcp->lock);
987             ReleaseWriteLock(&afs_xcbhash);
988             afs_PutVCache(tvcp);
989             continue;
990         }
991
992         /* now merge in the resulting status back into the vnode.
993          * We only do this if the entry looks clear.
994          */
995         afs_ProcessFS(tvcp, &statsp[i], areqp);
996 #if defined(AFS_LINUX22_ENV)
997         afs_fill_inode(AFSTOV(tvcp), NULL);     /* reset inode operations */
998 #endif
999
1000         /* do some accounting for bulk stats: mark this entry as
1001          * loaded, so we can tell if we use it before it gets
1002          * recycled.
1003          */
1004         tvcp->states |= CBulkStat;
1005         tvcp->states &= ~CBulkFetching;
1006         flagIndex++;
1007         afs_bulkStatsDone++;
1008
1009         /* merge in vol info */
1010         if (volStates & VRO)
1011             tvcp->states |= CRO;
1012         if (volStates & VBackup)
1013             tvcp->states |= CBackup;
1014         if (volStates & VForeign)
1015             tvcp->states |= CForeign;
1016
1017         /* merge in the callback info */
1018         tvcp->states |= CTruth;
1019
1020         /* get ptr to the callback we are interested in */
1021         tcbp = cbsp + i;
1022
1023         if (tcbp->ExpirationTime != 0) {
1024             tvcp->cbExpires = tcbp->ExpirationTime + startTime;
1025             tvcp->callback = hostp;
1026             tvcp->states |= CStatd;
1027             afs_QueueCallback(tvcp, CBHash(tcbp->ExpirationTime), volp);
1028         } else if (tvcp->states & CRO) {
1029             /* ordinary callback on a read-only volume -- AFS 3.2 style */
1030             tvcp->cbExpires = 3600 + startTime;
1031             tvcp->callback = hostp;
1032             tvcp->states |= CStatd;
1033             afs_QueueCallback(tvcp, CBHash(3600), volp);
1034         } else {
1035             tvcp->callback = 0;
1036             tvcp->states &= ~(CStatd | CUnique);
1037             afs_DequeueCallback(tvcp);
1038             if ((tvcp->states & CForeign) || (vType(tvcp) == VDIR))
1039                 osi_dnlc_purgedp(tvcp); /* if it (could be) a directory */
1040         }
1041         ReleaseWriteLock(&afs_xcbhash);
1042
1043         ReleaseWriteLock(&tvcp->lock);
1044         /* finally, we're done with the entry */
1045         afs_PutVCache(tvcp);
1046     }                           /* for all files we got back */
1047
1048     /* finally return the pointer into the LRU queue */
1049     afs_PutVCache(lruvcp);
1050
1051   done:
1052     /* Be sure to turn off the CBulkFetching flags */
1053     for (i = flagIndex; i < fidIndex; i++) {
1054         afid.Cell = adp->fid.Cell;
1055         afid.Fid.Volume = adp->fid.Fid.Volume;
1056         afid.Fid.Vnode = fidsp[i].Vnode;
1057         afid.Fid.Unique = fidsp[i].Unique;
1058         do {
1059             retry = 0;
1060             ObtainReadLock(&afs_xvcache);
1061             tvcp = afs_FindVCache(&afid, &retry, 0 /* !stats&!lru */ );
1062             ReleaseReadLock(&afs_xvcache);
1063         } while (tvcp && retry);
1064         if (tvcp != NULL && (tvcp->states & CBulkFetching)
1065             && (tvcp->m.Length == statSeqNo)) {
1066             tvcp->states &= ~CBulkFetching;
1067         }
1068         if (tvcp != NULL) {
1069             afs_PutVCache(tvcp);
1070         }
1071     }
1072     if (volp)
1073         afs_PutVolume(volp, READ_LOCK);
1074
1075     /* If we did the InlineBulk RPC pull out the return code */
1076     if (inlinebulk) {
1077         if ((&statsp[0])->errorCode) {
1078             afs_Analyze(tcp, (&statsp[0])->errorCode, &adp->fid, areqp,
1079                         AFS_STATS_FS_RPCIDX_BULKSTATUS, SHARED_LOCK, NULL);
1080             code = (&statsp[0])->errorCode;
1081         }
1082     } else {
1083         code = 0;
1084     }
1085   done2:
1086     osi_FreeLargeSpace(statMemp);
1087     osi_FreeLargeSpace(cbfMemp);
1088     return code;
1089 }
1090
1091 /* was: (AFS_DEC_ENV) || defined(AFS_OSF30_ENV) || defined(AFS_NCR_ENV) */
1092 #ifdef AFS_DARWIN80_ENV
1093 #define AFSDOBULK 0
1094 #else
1095 static int AFSDOBULK = 1;
1096 #endif
1097
1098 int
1099 #ifdef AFS_OSF_ENV
1100 afs_lookup(OSI_VC_DECL(adp), char *aname, struct vcache **avcp, struct AFS_UCRED *acred, int opflag, int wantparent)
1101 #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
1102 afs_lookup(OSI_VC_DECL(adp), char *aname, struct vcache **avcp, struct pathname *pnp, int flags, struct vnode *rdir, struct AFS_UCRED *acred)
1103 #elif defined(UKERNEL)
1104 afs_lookup(OSI_VC_DECL(adp), char *aname, struct vcache **avcp, struct AFS_UCRED *acred, int flags)
1105 #else
1106 afs_lookup(OSI_VC_DECL(adp), char *aname, struct vcache **avcp, struct AFS_UCRED *acred)
1107 #endif
1108 {
1109     struct vrequest treq;
1110     char *tname = NULL;
1111     register struct vcache *tvc = 0;
1112     register afs_int32 code;
1113     register afs_int32 bulkcode = 0;
1114     int pass = 0, hit = 0;
1115     long dirCookie;
1116     extern afs_int32 afs_mariner;       /*Writing activity to log? */
1117     afs_hyper_t versionNo;
1118     int no_read_access = 0;
1119     struct sysname_info sysState;       /* used only for @sys checking */
1120     int dynrootRetry = 1;
1121     struct afs_fakestat_state fakestate;
1122     int tryEvalOnly = 0;
1123     OSI_VC_CONVERT(adp);
1124
1125     AFS_STATCNT(afs_lookup);
1126     afs_InitFakeStat(&fakestate);
1127
1128     if ((code = afs_InitReq(&treq, acred)))
1129         goto done;
1130
1131 #ifdef  AFS_OSF_ENV
1132     ndp->ni_dvp = AFSTOV(adp);
1133 #endif /* AFS_OSF_ENV */
1134
1135 #if defined(AFS_DARWIN_ENV)
1136     /* Workaround for MacOSX Finder, which tries to look for
1137      * .DS_Store and Contents under every directory.
1138      */
1139     if (afs_fakestat_enable && adp->mvstat == 1) {
1140         if (strcmp(aname, ".DS_Store") == 0)
1141             tryEvalOnly = 1;
1142         if (strcmp(aname, "Contents") == 0)
1143             tryEvalOnly = 1;
1144     }
1145 #endif
1146
1147     if (tryEvalOnly)
1148         code = afs_TryEvalFakeStat(&adp, &fakestate, &treq);
1149     else
1150         code = afs_EvalFakeStat(&adp, &fakestate, &treq);
1151     if (tryEvalOnly && adp->mvstat == 1)
1152         code = ENOENT;
1153     if (code)
1154         goto done;
1155
1156     *avcp = NULL;               /* Since some callers don't initialize it */
1157
1158     /* come back to here if we encounter a non-existent object in a read-only
1159      * volume's directory */
1160
1161   redo:
1162     *avcp = NULL;               /* Since some callers don't initialize it */
1163     bulkcode = 0;
1164
1165     if (!(adp->states & CStatd)) {
1166         if ((code = afs_VerifyVCache2(adp, &treq))) {
1167             goto done;
1168         }
1169     } else
1170         code = 0;
1171
1172     /* watch for ".." in a volume root */
1173     if (adp->mvstat == 2 && aname[0] == '.' && aname[1] == '.' && !aname[2]) {
1174         /* looking up ".." in root via special hacks */
1175         if (adp->mvid == (struct VenusFid *)0 || adp->mvid->Fid.Volume == 0) {
1176 #ifdef  AFS_OSF_ENV
1177             extern struct vcache *afs_globalVp;
1178             if (adp == afs_globalVp) {
1179                 struct vnode *rvp = AFSTOV(adp);
1180 /*
1181                 ndp->ni_vp = rvp->v_vfsp->vfs_vnodecovered;
1182                 ndp->ni_dvp = ndp->ni_vp;
1183                 VN_HOLD(*avcp);
1184 */
1185                 code = ENODEV;
1186                 goto done;
1187             }
1188 #endif
1189             code = ENODEV;
1190             goto done;
1191         }
1192         /* otherwise we have the fid here, so we use it */
1193         tvc = afs_GetVCache(adp->mvid, &treq, NULL, NULL);
1194         afs_Trace3(afs_iclSetp, CM_TRACE_GETVCDOTDOT, ICL_TYPE_FID, adp->mvid,
1195                    ICL_TYPE_POINTER, tvc, ICL_TYPE_INT32, code);
1196         *avcp = tvc;
1197         code = (tvc ? 0 : ENOENT);
1198         hit = 1;
1199         if (tvc && !VREFCOUNT_GT(tvc, 0)) {
1200             osi_Panic("TT1");
1201         }
1202         if (code) {
1203             /*printf("LOOKUP GETVCDOTDOT -> %d\n", code); */
1204         }
1205         goto done;
1206     }
1207
1208     /* now check the access */
1209     if (treq.uid != adp->last_looker) {
1210         if (!afs_AccessOK(adp, PRSFS_LOOKUP, &treq, CHECK_MODE_BITS)) {
1211             *avcp = NULL;
1212             code = EACCES;
1213             goto done;
1214         } else
1215             adp->last_looker = treq.uid;
1216     }
1217
1218     /* Check for read access as well.  We need read access in order to
1219      * stat files, but not to stat subdirectories. */
1220     if (!afs_AccessOK(adp, PRSFS_READ, &treq, CHECK_MODE_BITS))
1221         no_read_access = 1;
1222
1223     /* special case lookup of ".".  Can we check for it sooner in this code,
1224      * for instance, way up before "redo:" ??
1225      * I'm not fiddling with the LRUQ here, either, perhaps I should, or else 
1226      * invent a lightweight version of GetVCache.
1227      */
1228     if (aname[0] == '.' && !aname[1]) { /* special case */
1229         ObtainReadLock(&afs_xvcache);
1230         osi_vnhold(adp, 0);
1231         ReleaseReadLock(&afs_xvcache);
1232 #ifdef AFS_DARWIN80_ENV
1233         vnode_get(AFSTOV(adp));
1234 #endif
1235         code = 0;
1236         *avcp = tvc = adp;
1237         hit = 1;
1238         if (adp && !VREFCOUNT_GT(adp, 0)) {
1239             osi_Panic("TT2");
1240         }
1241         goto done;
1242     }
1243
1244     Check_AtSys(adp, aname, &sysState, &treq);
1245     tname = sysState.name;
1246
1247     /* 1st Check_AtSys and lookup by tname is required here, for now,
1248      * because the dnlc is *not* told to remove entries for the parent
1249      * dir of file/dir op that afs_LocalHero likes, but dnlc is informed
1250      * if the cached entry for the parent dir is invalidated for a
1251      * non-local change.
1252      * Otherwise, we'd be able to do a dnlc lookup on an entry ending
1253      * w/@sys and know the dnlc was consistent with reality. */
1254     tvc = osi_dnlc_lookup(adp, tname, WRITE_LOCK);
1255     *avcp = tvc;                /* maybe wasn't initialized, but it is now */
1256     if (tvc) {
1257         if (no_read_access && vType(tvc) != VDIR && vType(tvc) != VLNK) {
1258             /* need read access on dir to stat non-directory / non-link */
1259             afs_PutVCache(tvc);
1260             *avcp = NULL;
1261             code = EACCES;
1262             goto done;
1263         }
1264 #ifdef AFS_LINUX22_ENV
1265         if (tvc->mvstat == 2) { /* we don't trust the dnlc for root vcaches */
1266             AFS_RELE(AFSTOV(tvc));
1267             *avcp = 0;
1268         } else {
1269             code = 0;
1270             hit = 1;
1271             goto done;
1272         }
1273 #else /* non - LINUX */
1274         code = 0;
1275         hit = 1;
1276         goto done;
1277 #endif /* linux22 */
1278     }
1279
1280     {                           /* sub-block just to reduce stack usage */
1281         register struct dcache *tdc;
1282         afs_size_t dirOffset, dirLen;
1283         struct VenusFid tfid;
1284
1285         /* now we have to lookup the next fid */
1286         tdc =
1287             afs_GetDCache(adp, (afs_size_t) 0, &treq, &dirOffset, &dirLen, 1);
1288         if (!tdc) {
1289             *avcp = NULL;       /* redundant, but harmless */
1290             code = EIO;
1291             goto done;
1292         }
1293
1294         /* now we will just call dir package with appropriate inode.
1295          * Dirs are always fetched in their entirety for now */
1296         ObtainReadLock(&adp->lock);
1297         ObtainReadLock(&tdc->lock);
1298
1299         /*
1300          * Make sure that the data in the cache is current. There are two
1301          * cases we need to worry about:
1302          * 1. The cache data is being fetched by another process.
1303          * 2. The cache data is no longer valid
1304          */
1305         while ((adp->states & CStatd)
1306                && (tdc->dflags & DFFetching)
1307                && hsame(adp->m.DataVersion, tdc->f.versionNo)) {
1308             ReleaseReadLock(&tdc->lock);
1309             ReleaseReadLock(&adp->lock);
1310             afs_osi_Sleep(&tdc->validPos);
1311             ObtainReadLock(&adp->lock);
1312             ObtainReadLock(&tdc->lock);
1313         }
1314         if (!(adp->states & CStatd)
1315             || !hsame(adp->m.DataVersion, tdc->f.versionNo)) {
1316             ReleaseReadLock(&tdc->lock);
1317             ReleaseReadLock(&adp->lock);
1318             afs_PutDCache(tdc);
1319             if (tname && tname != aname)
1320                 osi_FreeLargeSpace(tname);
1321             goto redo;
1322         }
1323
1324         /* Save the version number for when we call osi_dnlc_enter */
1325         hset(versionNo, tdc->f.versionNo);
1326
1327         /*
1328          * check for, and handle "@sys" if it's there.  We should be able
1329          * to avoid the alloc and the strcpy with a little work, but it's
1330          * not pressing.  If there aren't any remote users (ie, via the 
1331          * NFS translator), we have a slightly easier job.
1332          * the faster way to do this is to check for *aname == '@' and if 
1333          * it's there, check for @sys, otherwise, assume there's no @sys 
1334          * then, if the lookup fails, check for .*@sys...
1335          */
1336         /* above now implemented by Check_AtSys and Next_AtSys */
1337
1338         /* lookup the name in the appropriate dir, and return a cache entry
1339          * on the resulting fid */
1340         code =
1341             afs_dir_LookupOffset(tdc, sysState.name, &tfid.Fid,
1342                                  &dirCookie);
1343
1344         /* If the first lookup doesn't succeed, maybe it's got @sys in the name */
1345         while (code == ENOENT && Next_AtSys(adp, &treq, &sysState))
1346             code =
1347                 afs_dir_LookupOffset(tdc, sysState.name, &tfid.Fid,
1348                                      &dirCookie);
1349         tname = sysState.name;
1350
1351         ReleaseReadLock(&tdc->lock);
1352         afs_PutDCache(tdc);
1353
1354         if (code == ENOENT && afs_IsDynroot(adp) && dynrootRetry) {
1355             ReleaseReadLock(&adp->lock);
1356             dynrootRetry = 0;
1357             if (tname[0] == '.')
1358                 afs_LookupAFSDB(tname + 1);
1359             else
1360                 afs_LookupAFSDB(tname);
1361             if (tname && tname != aname)
1362                 osi_FreeLargeSpace(tname);
1363             goto redo;
1364         } else {
1365             ReleaseReadLock(&adp->lock);
1366         }
1367
1368         /* new fid has same cell and volume */
1369         tfid.Cell = adp->fid.Cell;
1370         tfid.Fid.Volume = adp->fid.Fid.Volume;
1371         afs_Trace4(afs_iclSetp, CM_TRACE_LOOKUP, ICL_TYPE_POINTER, adp,
1372                    ICL_TYPE_STRING, tname, ICL_TYPE_FID, &tfid,
1373                    ICL_TYPE_INT32, code);
1374
1375         if (code) {
1376             if (code != ENOENT) {
1377                 printf("LOOKUP dirLookupOff -> %d\n", code);
1378             }
1379             goto done;
1380         }
1381
1382         /* prefetch some entries, if the dir is currently open.  The variable
1383          * dirCookie tells us where to start prefetching from.
1384          */
1385         if (AFSDOBULK && adp->opens > 0 && !(adp->states & CForeign)
1386             && !afs_IsDynroot(adp)) {
1387             afs_int32 retry;
1388             /* if the entry is not in the cache, or is in the cache,
1389              * but hasn't been statd, then do a bulk stat operation.
1390              */
1391             do {
1392                 retry = 0;
1393                 ObtainReadLock(&afs_xvcache);
1394                 tvc = afs_FindVCache(&tfid, &retry, 0 /* !stats,!lru */ );
1395                 ReleaseReadLock(&afs_xvcache);
1396             } while (tvc && retry);
1397
1398             if (!tvc || !(tvc->states & CStatd))
1399                 bulkcode = afs_DoBulkStat(adp, dirCookie, &treq);
1400             else
1401                 bulkcode = 0;
1402
1403             /* if the vcache isn't usable, release it */
1404             if (tvc && !(tvc->states & CStatd)) {
1405                 afs_PutVCache(tvc);
1406                 tvc = NULL;
1407             }
1408         } else {
1409             tvc = NULL;
1410             bulkcode = 0;
1411         }
1412
1413         /* now get the status info, if we don't already have it */
1414         /* This is kind of weird, but we might wind up accidentally calling
1415          * RXAFS_Lookup because we happened upon a file which legitimately
1416          * has a 0 uniquifier. That is the result of allowing unique to wrap
1417          * to 0. This was fixed in AFS 3.4. For CForeign, Unique == 0 means that
1418          * the file has not yet been looked up.
1419          */
1420         if (!tvc) {
1421             afs_int32 cached = 0;
1422             if (!tfid.Fid.Unique && (adp->states & CForeign)) {
1423                 tvc = afs_LookupVCache(&tfid, &treq, &cached, adp, tname);
1424             }
1425             if (!tvc && !bulkcode) {    /* lookup failed or wasn't called */
1426                 tvc = afs_GetVCache(&tfid, &treq, &cached, NULL);
1427             }
1428         }                       /* if !tvc */
1429     }                           /* sub-block just to reduce stack usage */
1430
1431     if (tvc) {
1432         int force_eval = afs_fakestat_enable ? 0 : 1;
1433
1434         if (adp->states & CForeign)
1435             tvc->states |= CForeign;
1436         tvc->parentVnode = adp->fid.Fid.Vnode;
1437         tvc->parentUnique = adp->fid.Fid.Unique;
1438         tvc->states &= ~CBulkStat;
1439
1440         if (afs_fakestat_enable == 2 && tvc->mvstat == 1) {
1441             ObtainSharedLock(&tvc->lock, 680);
1442             if (!tvc->linkData) {
1443                 UpgradeSToWLock(&tvc->lock, 681);
1444                 code = afs_HandleLink(tvc, &treq);
1445                 ConvertWToRLock(&tvc->lock);
1446             } else {
1447                 ConvertSToRLock(&tvc->lock);
1448                 code = 0;
1449             }
1450             if (!code && !afs_strchr(tvc->linkData, ':'))
1451                 force_eval = 1;
1452             ReleaseReadLock(&tvc->lock);
1453         }
1454 #if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
1455         if (!(flags & AFS_LOOKUP_NOEVAL))
1456             /* don't eval mount points */
1457 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
1458             if (tvc->mvstat == 1 && force_eval) {
1459                 /* a mt point, possibly unevaluated */
1460                 struct volume *tvolp;
1461
1462                 ObtainWriteLock(&tvc->lock, 133);
1463                 code = EvalMountPoint(tvc, adp, &tvolp, &treq);
1464                 ReleaseWriteLock(&tvc->lock);
1465
1466                 if (code) {
1467                     afs_PutVCache(tvc);
1468                     if (tvolp)
1469                         afs_PutVolume(tvolp, WRITE_LOCK);
1470                     goto done;
1471                 }
1472
1473                 /* next, we want to continue using the target of the mt point */
1474                 if (tvc->mvid && (tvc->states & CMValid)) {
1475                     struct vcache *uvc;
1476                     /* now lookup target, to set .. pointer */
1477                     afs_Trace2(afs_iclSetp, CM_TRACE_LOOKUP1,
1478                                ICL_TYPE_POINTER, tvc, ICL_TYPE_FID,
1479                                &tvc->fid);
1480                     uvc = tvc;  /* remember for later */
1481
1482                     if (tvolp && (tvolp->states & VForeign)) {
1483                         /* XXXX tvolp has ref cnt on but not locked! XXX */
1484                         tvc =
1485                             afs_GetRootVCache(tvc->mvid, &treq, NULL, tvolp);
1486                     } else {
1487                         tvc = afs_GetVCache(tvc->mvid, &treq, NULL, NULL);
1488                     }
1489                     afs_PutVCache(uvc); /* we're done with it */
1490
1491                     if (!tvc) {
1492                         code = ENOENT;
1493                         if (tvolp) {
1494                             afs_PutVolume(tvolp, WRITE_LOCK);
1495                         }
1496                         goto done;
1497                     }
1498
1499                     /* now, if we came via a new mt pt (say because of a new
1500                      * release of a R/O volume), we must reevaluate the ..
1501                      * ptr to point back to the appropriate place */
1502                     if (tvolp) {
1503                         ObtainWriteLock(&tvc->lock, 134);
1504                         if (tvc->mvid == NULL) {
1505                             tvc->mvid = (struct VenusFid *)
1506                                 osi_AllocSmallSpace(sizeof(struct VenusFid));
1507                         }
1508                         /* setup backpointer */
1509                         *tvc->mvid = tvolp->dotdot;
1510                         ReleaseWriteLock(&tvc->lock);
1511                         afs_PutVolume(tvolp, WRITE_LOCK);
1512                     }
1513                 } else {
1514                     afs_PutVCache(tvc);
1515                     code = ENOENT;
1516                     if (tvolp)
1517                         afs_PutVolume(tvolp, WRITE_LOCK);
1518                     goto done;
1519                 }
1520             }
1521         *avcp = tvc;
1522         if (tvc && !VREFCOUNT_GT(tvc, 0)) {
1523             osi_Panic("TT3");
1524         }
1525         code = 0;
1526     } else {
1527         /* if we get here, we found something in a directory that couldn't
1528          * be located (a Multics "connection failure").  If the volume is
1529          * read-only, we try flushing this entry from the cache and trying
1530          * again. */
1531         if (pass == 0) {
1532             struct volume *tv;
1533             tv = afs_GetVolume(&adp->fid, &treq, READ_LOCK);
1534             if (tv) {
1535                 if (tv->states & VRO) {
1536                     pass = 1;   /* try this *once* */
1537                     ObtainWriteLock(&afs_xcbhash, 495);
1538                     afs_DequeueCallback(adp);
1539                     /* re-stat to get later version */
1540                     adp->states &= ~CStatd;
1541                     ReleaseWriteLock(&afs_xcbhash);
1542                     osi_dnlc_purgedp(adp);
1543                     afs_PutVolume(tv, READ_LOCK);
1544                     goto redo;
1545                 }
1546                 afs_PutVolume(tv, READ_LOCK);
1547             }
1548         }
1549         code = ENOENT;
1550     }
1551
1552   done:
1553     /* put the network buffer back, if need be */
1554     if (tname != aname && tname)
1555         osi_FreeLargeSpace(tname);
1556     if (code == 0) {
1557 #ifdef  AFS_OSF_ENV
1558         /* Handle RENAME; only need to check rename "."  */
1559         if (opflag == RENAME && wantparent && *ndp->ni_next == 0) {
1560             if (!FidCmp(&(tvc->fid), &(adp->fid))) {
1561                 afs_PutVCache(*avcp);
1562                 *avcp = NULL;
1563                 afs_PutFakeStat(&fakestate);
1564                 return afs_CheckCode(EISDIR, &treq, 18);
1565             }
1566         }
1567 #endif /* AFS_OSF_ENV */
1568
1569         if (afs_mariner)
1570             afs_AddMarinerName(aname, tvc);
1571
1572 #if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
1573         if (!(flags & AFS_LOOKUP_NOEVAL))
1574             /* Here we don't enter the name into the DNLC because we want the
1575              * evaluated mount dir to be there (the vcache for the mounted volume)
1576              * rather than the vc of the mount point itself.  we can still find the
1577              * mount point's vc in the vcache by its fid. */
1578 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
1579             if (!hit) {
1580                 osi_dnlc_enter(adp, aname, tvc, &versionNo);
1581             } else {
1582 #ifdef AFS_LINUX20_ENV
1583                 /* So Linux inode cache is up to date. */
1584                 code = afs_VerifyVCache(tvc, &treq);
1585 #else
1586                 afs_PutFakeStat(&fakestate);
1587                 return 0;       /* can't have been any errors if hit and !code */
1588 #endif
1589             }
1590     }
1591     if (bulkcode)
1592         code = bulkcode;
1593     else
1594         code = afs_CheckCode(code, &treq, 19);
1595     if (code) {
1596         /* If there is an error, make sure *avcp is null.
1597          * Alphas panic otherwise - defect 10719.
1598          */
1599         *avcp = NULL;
1600     }
1601
1602     afs_PutFakeStat(&fakestate);
1603     return code;
1604 }