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