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