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