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