b7b30aaae8975222dc41c5feef3c36cfbd043200
[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 afs_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 = {0, 0, 0};
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     if ((code = afs_InitReq(&treq, acred)))
1237         goto done;
1238
1239 #ifdef  AFS_OSF_ENV
1240     ndp->ni_dvp = AFSTOV(adp);
1241 #endif /* AFS_OSF_ENV */
1242
1243     if (afs_fakestat_enable && adp->mvstat == 1) {
1244        if (strcmp(aname, ".directory") == 0)
1245            tryEvalOnly = 1;
1246     }
1247
1248 #if defined(AFS_DARWIN_ENV)
1249     /* Workaround for MacOSX Finder, which tries to look for
1250      * .DS_Store and Contents under every directory.
1251      */
1252     if (afs_fakestat_enable && adp->mvstat == 1) {
1253         if (strcmp(aname, ".DS_Store") == 0)
1254             tryEvalOnly = 1;
1255         if (strcmp(aname, "Contents") == 0)
1256             tryEvalOnly = 1;
1257     }
1258 #endif
1259
1260     if (tryEvalOnly)
1261         code = afs_TryEvalFakeStat(&adp, &fakestate, &treq);
1262     else
1263         code = afs_EvalFakeStat(&adp, &fakestate, &treq);
1264
1265     /*printf("Code is %d\n", code);*/
1266     
1267     if (tryEvalOnly && adp->mvstat == 1)
1268         code = ENOENT;
1269     if (code)
1270         goto done;
1271
1272     *avcp = NULL;               /* Since some callers don't initialize it */
1273
1274     /* come back to here if we encounter a non-existent object in a read-only
1275      * volume's directory */
1276
1277   redo:
1278     *avcp = NULL;               /* Since some callers don't initialize it */
1279     bulkcode = 0;
1280
1281     if (!(adp->states & CStatd) && !afs_InReadDir(adp)) {
1282         if ((code = afs_VerifyVCache2(adp, &treq))) {
1283             goto done;
1284         }
1285     } else
1286         code = 0;
1287
1288     /* watch for ".." in a volume root */
1289     if (adp->mvstat == 2 && aname[0] == '.' && aname[1] == '.' && !aname[2]) {
1290         /* looking up ".." in root via special hacks */
1291         if (adp->mvid == (struct VenusFid *)0 || adp->mvid->Fid.Volume == 0) {
1292 #ifdef  AFS_OSF_ENV
1293             if (adp == afs_globalVp) {
1294                 struct vnode *rvp = AFSTOV(adp);
1295 /*
1296                 ndp->ni_vp = rvp->v_vfsp->vfs_vnodecovered;
1297                 ndp->ni_dvp = ndp->ni_vp;
1298                 VN_HOLD(*avcp);
1299 */
1300                 code = ENODEV;
1301                 goto done;
1302             }
1303 #endif
1304             code = ENODEV;
1305             goto done;
1306         }
1307         /* otherwise we have the fid here, so we use it */
1308         /*printf("Getting vcache\n");*/
1309         tvc = afs_GetVCache(adp->mvid, &treq, NULL, NULL);
1310         afs_Trace3(afs_iclSetp, CM_TRACE_GETVCDOTDOT, ICL_TYPE_FID, adp->mvid,
1311                    ICL_TYPE_POINTER, tvc, ICL_TYPE_INT32, code);
1312         *avcp = tvc;
1313         code = (tvc ? 0 : ENOENT);
1314         hit = 1;
1315         if (tvc && !VREFCOUNT_GT(tvc, 0)) {
1316             osi_Panic("TT1");
1317         }
1318         if (code) {
1319             /*printf("LOOKUP GETVCDOTDOT -> %d\n", code); */
1320         }
1321         goto done;
1322     }
1323
1324     /* now check the access */
1325     if (treq.uid != adp->last_looker) {
1326         if (!afs_AccessOK(adp, PRSFS_LOOKUP, &treq, CHECK_MODE_BITS)) {
1327             *avcp = NULL;
1328             code = EACCES;
1329             goto done;
1330         } else
1331             adp->last_looker = treq.uid;
1332     }
1333
1334     /* Check for read access as well.  We need read access in order to
1335      * stat files, but not to stat subdirectories. */
1336     if (!afs_AccessOK(adp, PRSFS_READ, &treq, CHECK_MODE_BITS))
1337         no_read_access = 1;
1338
1339     /* special case lookup of ".".  Can we check for it sooner in this code,
1340      * for instance, way up before "redo:" ??
1341      * I'm not fiddling with the LRUQ here, either, perhaps I should, or else 
1342      * invent a lightweight version of GetVCache.
1343      */
1344     if (aname[0] == '.' && !aname[1]) { /* special case */
1345         ObtainReadLock(&afs_xvcache);
1346         osi_vnhold(adp, 0);
1347         ReleaseReadLock(&afs_xvcache);
1348 #ifdef AFS_DARWIN80_ENV
1349         vnode_get(AFSTOV(adp));
1350 #endif
1351         code = 0;
1352         *avcp = tvc = adp;
1353         hit = 1;
1354         if (adp && !VREFCOUNT_GT(adp, 0)) {
1355             osi_Panic("TT2");
1356         }
1357         goto done;
1358     }
1359
1360     /*
1361      * Special case lookup of ".." in the dynamic mount directory.
1362      * The parent of this directory is _always_ the AFS root volume.
1363      */
1364     if (afs_IsDynrootMount(adp) &&
1365         aname[0] == '.' && aname[1] == '.' && !aname[2]) {
1366
1367         ObtainReadLock(&afs_xvcache);
1368         osi_vnhold(afs_globalVp, 0);
1369         ReleaseReadLock(&afs_xvcache);
1370 #ifdef AFS_DARWIN80_ENV
1371         vnode_get(AFSTOV(afs_globalVp));
1372 #endif
1373         code = 0;
1374         *avcp = tvc = afs_globalVp;
1375         hit = 1;
1376         goto done;
1377     }
1378
1379     /*
1380      * Special case lookups in the dynamic mount directory.
1381      * The names here take the form cell:volume, similar to a mount point.
1382      * EvalMountData parses that and returns a cell and volume ID, which
1383      * we use to construct the appropriate dynroot Fid.
1384      */
1385     if (afs_IsDynrootMount(adp)) {
1386         struct VenusFid tfid;
1387         afs_uint32 cellidx, volid, vnoid;
1388
1389         code = EvalMountData('%', aname, 0, 0, NULL, &treq, &cellidx, &volid, &vnoid);
1390         if (code)
1391             goto done;
1392         afs_GetDynrootMountFid(&tfid);
1393         tfid.Fid.Vnode = VNUM_FROM_TYPEID(VN_TYPE_MOUNT, cellidx << 2);
1394         tfid.Fid.Unique = volid;
1395         *avcp = tvc = afs_GetVCache(&tfid, &treq, NULL, NULL);
1396         hit = 1;
1397         goto done;
1398     }
1399
1400 #ifdef AFS_LINUX26_ENV
1401     /*
1402      * Special case of the dynamic mount volume in a static root.
1403      * This is really unfortunate, but we need this for the translator.
1404      */
1405     if (adp == afs_globalVp && !afs_GetDynrootEnable() &&
1406         !strcmp(aname, AFS_DYNROOT_MOUNTNAME)) {
1407         struct VenusFid tfid;
1408
1409         afs_GetDynrootMountFid(&tfid);
1410         *avcp = tvc = afs_GetVCache(&tfid, &treq, NULL, NULL);
1411         code = 0;
1412         hit = 1;
1413         goto done;
1414     }
1415 #endif
1416
1417     Check_AtSys(adp, aname, &sysState, &treq);
1418     tname = sysState.name;
1419
1420     /* 1st Check_AtSys and lookup by tname is required here, for now,
1421      * because the dnlc is *not* told to remove entries for the parent
1422      * dir of file/dir op that afs_LocalHero likes, but dnlc is informed
1423      * if the cached entry for the parent dir is invalidated for a
1424      * non-local change.
1425      * Otherwise, we'd be able to do a dnlc lookup on an entry ending
1426      * w/@sys and know the dnlc was consistent with reality. */
1427     tvc = osi_dnlc_lookup(adp, tname, WRITE_LOCK);
1428     *avcp = tvc;                /* maybe wasn't initialized, but it is now */
1429     if (tvc) {
1430         if (no_read_access && vType(tvc) != VDIR && vType(tvc) != VLNK) {
1431             /* need read access on dir to stat non-directory / non-link */
1432 #ifndef AFS_FBSD80_ENV
1433             afs_PutVCache(tvc);
1434 #endif
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 #ifndef  AFS_FBSD80_ENV
1592               afs_PutVCache(tvc);
1593 #endif
1594                 tvc = NULL;
1595             }
1596         } else {
1597             tvc = NULL;
1598             bulkcode = 0;
1599         }
1600
1601         /* now get the status info, if we don't already have it */
1602         /* This is kind of weird, but we might wind up accidentally calling
1603          * RXAFS_Lookup because we happened upon a file which legitimately
1604          * has a 0 uniquifier. That is the result of allowing unique to wrap
1605          * to 0. This was fixed in AFS 3.4. For CForeign, Unique == 0 means that
1606          * the file has not yet been looked up.
1607          */
1608         if (!tvc) {
1609             afs_int32 cached = 0;
1610             if (!tfid.Fid.Unique && (adp->states & CForeign)) {
1611                 tvc = afs_LookupVCache(&tfid, &treq, &cached, adp, tname);
1612             }
1613             if (!tvc && !bulkcode) {    /* lookup failed or wasn't called */
1614                 tvc = afs_GetVCache(&tfid, &treq, &cached, NULL);
1615             }
1616         }                       /* if !tvc */
1617     }                           /* sub-block just to reduce stack usage */
1618
1619     if (tvc) {
1620         if (adp->states & CForeign)
1621             tvc->states |= CForeign;
1622         tvc->parentVnode = adp->fid.Fid.Vnode;
1623         tvc->parentUnique = adp->fid.Fid.Unique;
1624         tvc->states &= ~CBulkStat;
1625
1626         if (afs_fakestat_enable == 2 && tvc->mvstat == 1) {
1627             ObtainSharedLock(&tvc->lock, 680);
1628             if (!tvc->linkData) {
1629                 UpgradeSToWLock(&tvc->lock, 681);
1630                 code = afs_HandleLink(tvc, &treq);
1631                 ConvertWToRLock(&tvc->lock);
1632             } else {
1633                 ConvertSToRLock(&tvc->lock);
1634                 code = 0;
1635             }
1636             if (!code && !afs_strchr(tvc->linkData, ':'))
1637                 force_eval = 1;
1638             ReleaseReadLock(&tvc->lock);
1639         }
1640         if (tvc->mvstat == 1 && (tvc->states & CMValid) && tvc->mvid != NULL)
1641           force_eval = 1; /* This is now almost for free, get it correct */
1642
1643 #if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
1644         if (!(flags & AFS_LOOKUP_NOEVAL))
1645             /* don't eval mount points */
1646 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
1647             if (tvc->mvstat == 1 && force_eval) {
1648                 /* a mt point, possibly unevaluated */
1649                 struct volume *tvolp;
1650
1651                 ObtainWriteLock(&tvc->lock, 133);
1652                 code = EvalMountPoint(tvc, adp, &tvolp, &treq);
1653                 ReleaseWriteLock(&tvc->lock);
1654
1655                 if (code) {
1656 #ifndef AFS_FBSD80_ENV
1657                     afs_PutVCache(tvc);
1658 #endif
1659                     if (tvolp)
1660                         afs_PutVolume(tvolp, WRITE_LOCK);
1661                     goto done;
1662                 }
1663
1664                 /* next, we want to continue using the target of the mt point */
1665                 if (tvc->mvid && (tvc->states & CMValid)) {
1666                     struct vcache *uvc;
1667                     /* now lookup target, to set .. pointer */
1668                     afs_Trace2(afs_iclSetp, CM_TRACE_LOOKUP1,
1669                                ICL_TYPE_POINTER, tvc, ICL_TYPE_FID,
1670                                &tvc->fid);
1671                     uvc = tvc;  /* remember for later */
1672
1673                     if (tvolp && (tvolp->states & VForeign)) {
1674                         /* XXXX tvolp has ref cnt on but not locked! XXX */
1675                         tvc =
1676                             afs_GetRootVCache(tvc->mvid, &treq, NULL, tvolp);
1677                     } else {
1678                         tvc = afs_GetVCache(tvc->mvid, &treq, NULL, NULL);
1679                     }
1680 #ifndef AFS_FBSD80_ENV
1681                     afs_PutVCache(uvc); /* we're done with it */
1682 #endif
1683
1684                     if (!tvc) {
1685                         code = ENOENT;
1686                         if (tvolp) {
1687                             afs_PutVolume(tvolp, WRITE_LOCK);
1688                         }
1689                         goto done;
1690                     }
1691
1692                     /* now, if we came via a new mt pt (say because of a new
1693                      * release of a R/O volume), we must reevaluate the ..
1694                      * ptr to point back to the appropriate place */
1695                     if (tvolp) {
1696                         ObtainWriteLock(&tvc->lock, 134);
1697                         if (tvc->mvid == NULL) {
1698                             tvc->mvid = (struct VenusFid *)
1699                                 osi_AllocSmallSpace(sizeof(struct VenusFid));
1700                         }
1701                         /* setup backpointer */
1702                         *tvc->mvid = tvolp->dotdot;
1703                         ReleaseWriteLock(&tvc->lock);
1704                         afs_PutVolume(tvolp, WRITE_LOCK);
1705                     }
1706                 } else {
1707 #ifndef AFS_FBSD80_ENV
1708                     afs_PutVCache(tvc);
1709 #endif
1710                     code = ENOENT;
1711                     if (tvolp)
1712                         afs_PutVolume(tvolp, WRITE_LOCK);
1713                     goto done;
1714                 }
1715             }
1716         *avcp = tvc;
1717         if (tvc && !VREFCOUNT_GT(tvc, 0)) {
1718             osi_Panic("TT3");
1719         }
1720         code = 0;
1721     } else {
1722         /* if we get here, we found something in a directory that couldn't
1723          * be located (a Multics "connection failure").  If the volume is
1724          * read-only, we try flushing this entry from the cache and trying
1725          * again. */
1726         if (!AFS_IS_DISCONNECTED) {
1727             if (pass == 0) {
1728                 struct volume *tv;
1729                 tv = afs_GetVolume(&adp->fid, &treq, READ_LOCK);
1730                 if (tv) {
1731                     if (tv->states & VRO) {
1732                         pass = 1;       /* try this *once* */
1733                         ObtainWriteLock(&afs_xcbhash, 495);
1734                         afs_DequeueCallback(adp);
1735                         /* re-stat to get later version */
1736                         adp->states &= ~CStatd;
1737                         ReleaseWriteLock(&afs_xcbhash);
1738                         osi_dnlc_purgedp(adp);
1739                         afs_PutVolume(tv, READ_LOCK);
1740                         goto redo;
1741                     }
1742                     afs_PutVolume(tv, READ_LOCK);
1743                 }
1744             }
1745             code = ENOENT;
1746         } else {
1747             printf("Network down in afs_lookup\n");
1748             code = ENETDOWN;
1749         }
1750     }
1751
1752   done:
1753     /* put the network buffer back, if need be */
1754     if (tname != aname && tname)
1755         osi_FreeLargeSpace(tname);
1756     if (code == 0) {
1757 #ifdef  AFS_OSF_ENV
1758         /* Handle RENAME; only need to check rename "."  */
1759         if (opflag == RENAME && wantparent && *ndp->ni_next == 0) {
1760             if (!FidCmp(&(tvc->fid), &(adp->fid))) {
1761                 afs_PutVCache(*avcp);
1762                 *avcp = NULL;
1763                 afs_PutFakeStat(&fakestate);
1764                 return afs_CheckCode(EISDIR, &treq, 18);
1765             }
1766         }
1767 #endif /* AFS_OSF_ENV */
1768
1769         if (afs_mariner)
1770             afs_AddMarinerName(aname, tvc);
1771
1772 #if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
1773         if (!(flags & AFS_LOOKUP_NOEVAL))
1774             /* Here we don't enter the name into the DNLC because we want the
1775              * evaluated mount dir to be there (the vcache for the mounted volume)
1776              * rather than the vc of the mount point itself.  we can still find the
1777              * mount point's vc in the vcache by its fid. */
1778 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
1779             if (!hit && force_eval) {
1780                 osi_dnlc_enter(adp, aname, tvc, &versionNo);
1781             } else {
1782 #ifdef AFS_LINUX20_ENV
1783                 /* So Linux inode cache is up to date. */
1784                 code = afs_VerifyVCache(tvc, &treq);
1785 #else
1786                 afs_PutFakeStat(&fakestate);
1787                 AFS_DISCON_UNLOCK();
1788                 return 0;       /* can't have been any errors if hit and !code */
1789 #endif
1790             }
1791     }
1792     if (bulkcode)
1793         code = bulkcode;
1794
1795     code = afs_CheckCode(code, &treq, 19);
1796     if (code) {
1797         /* If there is an error, make sure *avcp is null.
1798          * Alphas panic otherwise - defect 10719.
1799          */
1800         *avcp = NULL;
1801     }
1802
1803     afs_PutFakeStat(&fakestate);
1804     AFS_DISCON_UNLOCK();
1805     return code;
1806 }