4ee681d61469ddddf50554f879b5358141d8efb0
[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  * Locals:
17  * afs_strcat
18  * AFS_EQ_ATSYS (macro)
19  * afs_index
20  */
21
22 #include <afsconfig.h>
23 #include "../afs/param.h"
24
25 RCSID("$Header$");
26
27 #include "../afs/sysincludes.h" /* Standard vendor system headers */
28 #include "../afs/afsincludes.h" /* Afs-based standard headers */
29 #include "../afs/afs_stats.h" /* statistics */
30 #include "../afs/afs_cbqueue.h"
31 #include "../afs/nfsclient.h"
32 #include "../afs/exporter.h"
33 #include "../afs/afs_osidnlc.h"
34
35
36 /**
37  * A few definitions. This is until we have a proper header file        
38  * which has prototypes for all functions
39  */
40
41 extern struct DirEntry * afs_dir_GetBlob();
42
43 extern afs_rwlock_t afs_xvcache;
44 extern afs_rwlock_t afs_xcbhash;
45 extern struct afs_exporter *afs_nfsexporter;
46 extern char *afs_sysname;
47 extern char *afs_sysnamelist[];
48 extern int afs_sysnamecount;
49 extern struct afs_q VLRU;                       /*vcache LRU*/
50 #ifdef AFS_LINUX22_ENV
51 extern struct inode_operations afs_symlink_iops, afs_dir_iops;
52 #endif
53
54
55 afs_int32 afs_bulkStatsDone;
56 static int bulkStatCounter = 0; /* counter for bulk stat seq. numbers */
57
58
59 /* this would be faster if it did comparison as int32word, but would be 
60  * dependant on byte-order and alignment, and I haven't figured out
61  * what "@sys" is in binary... */
62 #define AFS_EQ_ATSYS(name) (((name)[0]=='@')&&((name)[1]=='s')&&((name)[2]=='y')&&((name)[3]=='s')&&(!(name)[4]))
63
64 char *
65 afs_strcat(s1, s2)
66         register char *s1, *s2;
67 {
68         register char *os1;
69
70         AFS_STATCNT(strcat);
71         os1 = s1;
72         while (*s1++)
73                 ;
74         --s1;
75         while (*s1++ = *s2++)
76                 ;
77         return (os1);
78 }
79
80
81 char *afs_index(a, c)
82     register char *a, c; {
83     register char tc;
84     AFS_STATCNT(afs_index);
85     while (tc = *a) {
86         if (tc == c) return a;
87         else a++;
88     }
89     return (char *) 0;
90 }
91
92 /* call under write lock, evaluate mvid field from a mt pt.
93  * avc is the vnode of the mount point object.
94  * advc is the vnode of the containing directory
95  * avolpp is where we return a pointer to the volume named by the mount pt, if success
96  * areq is the identity of the caller.
97  *
98  * NOTE: this function returns a held volume structure in *volpp if it returns 0!
99  */
100 EvalMountPoint(avc, advc, avolpp, areq)
101     register struct vcache *avc;
102     struct volume **avolpp;
103     struct vcache *advc;            /* the containing dir */
104     register struct vrequest *areq;
105 {
106     afs_int32  code;
107     struct volume *tvp = 0;
108     struct VenusFid tfid;
109     struct cell *tcell;
110     char   *cpos, *volnamep;
111     char   type, buf[128];
112     afs_int32  prefetchRO;          /* 1=>No  2=>Yes */
113     afs_int32  mtptCell, assocCell, hac=0;
114     afs_int32  samecell, roname, len;
115
116     AFS_STATCNT(EvalMountPoint);
117 #ifdef notdef
118     if (avc->mvid && (avc->states & CMValid)) return 0; /* done while racing */
119 #endif
120     *avolpp = (struct volume *)0;
121     code = afs_HandleLink(avc, areq);
122     if (code) return code;
123
124     /* Determine which cell and volume the mointpoint goes to */
125     type = avc->linkData[0];                   /* '#'=>Regular '%'=>RW */
126     cpos = afs_index(&avc->linkData[1], ':');  /* if cell name present */
127     if (cpos) {
128        volnamep = cpos+1;
129        *cpos = 0;
130        tcell = afs_GetCellByName(&avc->linkData[1], READ_LOCK);
131        *cpos =  ':';
132     } else {
133        volnamep = &avc->linkData[1];
134        tcell = afs_GetCell(avc->fid.Cell, READ_LOCK);
135     }
136     if (!tcell) return ENODEV;
137
138     mtptCell = tcell->cell;               /* The cell for the mountpoint */
139     if (tcell->lcellp) {
140        hac = 1;                           /* has associated cell */
141        assocCell = tcell->lcellp->cell;   /* The associated cell */
142     }
143     afs_PutCell(tcell, READ_LOCK);          
144
145     /* Is volume name a "<n>.backup" or "<n>.readonly" name */
146     len = strlen(volnamep);
147     roname = ((len > 9) && (strcmp(&volnamep[len - 9],".readonly") == 0)) ||
148              ((len > 7) && (strcmp(&volnamep[len - 7],".backup")   == 0));
149
150     /* When we cross mountpoint, do we stay in the same cell */
151     samecell = (avc->fid.Cell == mtptCell) || (hac && (avc->fid.Cell == assocCell));
152
153     /* Decide whether to prefetch the RO. Also means we want the RO.
154      * If this is a regular mountpoint with a RW volume name and
155      * we cross a cell boundary -or- start from a RO volume, then we will
156      * want to prefetch the RO volume when we get the RW below.
157      */
158     if ( (type == '#') && !roname && (!samecell || (avc->states & CRO)) ) {
159        prefetchRO = 2; /* Yes, prefetch the RO */
160     } else {
161        prefetchRO = 1; /* No prefetch of the RO */
162     }
163
164     /* Get the volume struct. Unless this volume name has ".readonly" or
165      * ".backup" in it, this will get the volume struct for the RW volume.
166      * The RO volume will be prefetched if requested (but not returned).
167      */
168     tvp = afs_GetVolumeByName(volnamep, mtptCell, prefetchRO, areq, WRITE_LOCK);
169
170     /* If no volume was found in this cell, try the associated linked cell */
171     if (!tvp && hac && areq->volumeError) {
172        tvp = afs_GetVolumeByName(volnamep, assocCell, prefetchRO, areq, WRITE_LOCK);
173     }
174
175     /* Still not found. If we are looking for the RO, then perhaps the RW 
176      * doesn't exist? Try adding ".readonly" to volname and look for that.
177      * Don't know why we do this. Would have still found it in above call - jpm.
178      */
179     if (!tvp && (prefetchRO == 2)) {
180        strcpy(buf, volnamep);
181        afs_strcat(buf, ".readonly");
182
183        tvp = afs_GetVolumeByName(buf, mtptCell, 1, areq, WRITE_LOCK);
184
185        /* Try the associated linked cell if failed */
186        if (!tvp && hac && areq->volumeError) {
187           tvp = afs_GetVolumeByName(buf, assocCell, 1, areq, WRITE_LOCK);
188        }
189     }
190   
191     if (!tvp) return ENODEV;       /* Couldn't find the volume */
192
193     /* Don't cross mountpoint from a BK to a BK volume */
194     if ((avc->states & CBackup) && (tvp->states & VBackup)) {
195         afs_PutVolume(tvp, WRITE_LOCK);
196         return ELOOP;
197     }
198
199     /* If we want (prefetched) the RO and it exists, then drop the
200      * RW volume and get the RO. Othewise, go with the RW.
201      */
202     if ((prefetchRO == 2) && tvp->roVol) {
203        tfid.Fid.Volume = tvp->roVol;                 /* remember RO volume */
204        tfid.Cell       = tvp->cell;
205        afs_PutVolume(tvp, WRITE_LOCK);               /* release old volume */
206        tvp = afs_GetVolume(&tfid, areq, WRITE_LOCK); /* get the new one */
207        if (!tvp) return ENODEV;                      /* oops, can't do it */
208     }
209
210     if (avc->mvid == 0)
211         avc->mvid = (struct VenusFid *) osi_AllocSmallSpace(sizeof(struct VenusFid));
212     avc->mvid->Cell = tvp->cell;
213     avc->mvid->Fid.Volume = tvp->volume;
214     avc->mvid->Fid.Vnode = 1;
215     avc->mvid->Fid.Unique = 1;
216     avc->states |= CMValid;
217
218     /* Used to: if the mount point is stored within a backup volume,
219      * then we should only update the parent pointer information if
220      * there's none already set, so as to avoid updating a volume's ..
221      * info with something in an OldFiles directory.
222      *
223      * Next two lines used to be under this if:
224      *
225      * if (!(avc->states & CBackup) || tvp->dotdot.Fid.Volume == 0)
226      *
227      * Now: update mount point back pointer on every call, so that we handle
228      * multiple mount points better.  This way, when du tries to go back
229      * via chddir(".."), it will end up exactly where it started, yet
230      * cd'ing via a new path to a volume will reset the ".." pointer
231      * to the new path.
232      */
233     tvp->mtpoint = avc->fid;    /* setup back pointer to mtpoint */
234     tvp->dotdot  = advc->fid;
235
236     *avolpp = tvp;
237     return 0;
238 }
239     
240 afs_ENameOK(aname)
241     register char *aname; {
242     register char tc;
243     register int tlen;
244
245     AFS_STATCNT(ENameOK);
246     tlen = strlen(aname);
247     if (tlen >= 4 && strcmp(aname+tlen-4, "@sys") == 0) return 0;
248     return 1;
249 }
250
251 afs_getsysname(areq, adp, bufp)
252     register struct vrequest *areq;
253     register struct vcache *adp;
254     register char *bufp;
255 {
256     static char sysname[MAXSYSNAME];
257     register struct unixuser *au;
258     register afs_int32 error;
259
260     if (!afs_nfsexporter) {
261       strcpy(bufp, afs_sysname);
262       return 0;
263     }
264     AFS_STATCNT(getsysname);
265     au = afs_GetUser(areq->uid, adp->fid.Cell, 0);
266     afs_PutUser(au, 0); 
267     if (au->exporter) {
268       error = EXP_SYSNAME(au->exporter, (char *)0, bufp);
269       if (error) 
270         strcpy(bufp, "@sys");
271       return -1;
272     } else {
273       strcpy(bufp, afs_sysname);
274       return 0;
275     }
276 }
277
278 Check_AtSys(avc, aname, state, areq)
279     register struct vcache *avc;
280     char *aname;
281     struct sysname_info *state;
282     struct vrequest *areq;
283 {
284     if (AFS_EQ_ATSYS(aname)) {
285       state->offset = 0;
286       state->name = (char *) osi_AllocLargeSpace(AFS_SMALLOCSIZ);
287       state->allocked = 1;
288       state->index = afs_getsysname(areq, avc, state->name);
289     } else {
290       state->offset = -1;
291       state->allocked = 0;
292       state->index = 0;
293       state->name = aname;
294     }
295 }
296
297 Next_AtSys(avc, areq, state)
298      register struct vcache *avc;
299      struct vrequest *areq;
300      struct sysname_info *state;
301 {
302   if (state->index == -1)
303     return 0;   /* No list */
304
305   /* Check for the initial state of aname != "@sys" in Check_AtSys*/
306   if (state->offset == -1 && state->allocked == 0) {
307     register char *tname;
308     /* Check for .*@sys */
309       for (tname=state->name; *tname; tname++)
310         /*Move to the end of the string*/;
311       if ((tname > state->name + 4) && (AFS_EQ_ATSYS(tname-4))) {
312         state->offset = (tname - 4) - state->name;
313         tname = (char *) osi_AllocLargeSpace(AFS_LRALLOCSIZ);
314         strncpy(tname, state->name, state->offset);
315         state->name = tname;
316         state->allocked = 1;
317         state->index = afs_getsysname(areq, avc, state->name+state->offset);
318         return 1;
319       } else
320         return 0; /* .*@sys doesn't match either */
321   } else if (++(state->index) >= afs_sysnamecount
322              || !afs_sysnamelist[state->index])
323     return 0;   /* end of list */
324   strcpy(state->name+state->offset, afs_sysnamelist[state->index]);
325   return 1;
326 }
327
328 #if (defined(AFS_SGI62_ENV) || defined(AFS_SUN57_64BIT_ENV))
329 extern int BlobScan(ino64_t *afile, afs_int32 ablob);
330 #else
331 #if defined AFS_LINUX_64BIT_KERNEL
332 extern int BlobScan(long *afile, afs_int32 ablob);
333 #else
334 extern int BlobScan(afs_int32 *afile, afs_int32 ablob);
335 #endif
336 #endif
337
338
339 /* called with an unlocked directory and directory cookie.  Areqp
340  * describes who is making the call.
341  * Scans the next N (about 30, typically) directory entries, and does
342  * a bulk stat call to stat them all.
343  *
344  * Must be very careful when merging in RPC responses, since we dont
345  * want to overwrite newer info that was added by a file system mutating
346  * call that ran concurrently with our bulk stat call.
347  *
348  * We do that, as described below, by not merging in our info (always
349  * safe to skip the merge) if the status info is valid in the vcache entry.
350  *
351  * If adapt ever implements the bulk stat RPC, then this code will need to
352  * ensure that vcaches created for failed RPC's to older servers have the
353  * CForeign bit set.
354  */
355 struct vcache * BStvc = (struct vcache *) 0;
356 void afs_DoBulkStat(adp, dirCookie, areqp)
357   struct vcache *adp;
358   long dirCookie;
359   struct vrequest *areqp;
360 {
361     int nentries;               /* # of entries to prefetch */
362     int nskip;                  /* # of slots in the LRU queue to skip */
363     struct vcache *lruvcp;      /* vcache ptr of our goal pos in LRU queue */
364     struct dcache *dcp;         /* chunk containing the dir block */
365     char *statMemp;             /* status memory block */
366     char *cbfMemp;              /* callback and fid memory block */
367     long temp;                  /* temp for holding chunk length, &c. */
368     struct AFSFid *fidsp;       /* file IDs were collecting */
369     struct AFSCallBack *cbsp;   /* call back pointers */
370     struct AFSCallBack *tcbp;   /* temp callback ptr */
371     struct AFSFetchStatus *statsp;      /* file status info */
372     struct AFSVolSync volSync;  /* vol sync return info */
373     struct vcache *tvcp;        /* temp vcp */
374     struct afs_q *tq;           /* temp queue variable */
375     AFSCBFids fidParm;          /* file ID parm for bulk stat */
376     AFSBulkStats statParm;      /* stat info parm for bulk stat */
377     int fidIndex;               /* which file were stating */
378     struct conn *tcp;           /* conn for call */
379     AFSCBs cbParm;              /* callback parm for bulk stat */
380     struct server *hostp = 0;   /* host we got callback from */
381     long origEvenCBs;           /* original # of callbacks for even-fid files */
382     long origOddCBs;            /* original # of callbacks for odd-fid files */
383     long origEvenZaps;          /* original # of recycles for even-fid files */
384     long origOddZaps;           /* original # of recycles for odd-fid files */
385     long startTime;             /* time we started the call,
386                                  * for callback expiration base
387                                  */
388     int statSeqNo;              /* Valued of file size to detect races */
389     int code;                   /* error code */
390     long newIndex;              /* new index in the dir */
391     struct DirEntry *dirEntryp; /* dir entry we are examining */
392     int i;
393     struct VenusFid afid;       /* file ID we are using now */
394     struct VenusFid tfid;       /* another temp. file ID */
395     afs_int32 retry;                  /* handle low-level SGI MP race conditions */
396     long volStates;             /* flags from vol structure */
397     struct volume *volp=0;      /* volume ptr */
398     struct VenusFid dotdot;
399     int flagIndex;              /* First file with bulk fetch flag set */
400     XSTATS_DECLS
401
402     /* first compute some basic parameters.  We dont want to prefetch more
403      * than a fraction of the cache in any given call, and we want to preserve
404      * a portion of the LRU queue in any event, so as to avoid thrashing
405      * the entire stat cache (we will at least leave some of it alone).
406      * presently dont stat more than 1/8 the cache in any one call.      */
407     nentries = afs_cacheStats / 8;
408
409     /* dont bother prefetching more than one calls worth of info */
410     if (nentries > AFSCBMAX) nentries = AFSCBMAX;
411
412     /* heuristic to make sure that things fit in 4K.  This means that
413      * we shouldnt make it any bigger than 47 entries.  I am typically
414      * going to keep it a little lower, since we don't want to load
415      * too much of the stat cache.
416      */
417     if (nentries > 30) nentries = 30;
418
419     /* now, to reduce the stack size, well allocate two 4K blocks,
420      * one for fids and callbacks, and one for stat info.  Well set
421      * up our pointers to the memory from there, too.
422      */
423     statMemp = osi_AllocLargeSpace(nentries * sizeof(AFSFetchStatus));
424     statsp = (struct AFSFetchStatus *) statMemp;
425     cbfMemp = osi_AllocLargeSpace(nentries *
426         (sizeof(AFSCallBack) + sizeof(AFSFid)));
427     fidsp = (AFSFid *) cbfMemp;
428     cbsp = (AFSCallBack *) (cbfMemp + nentries * sizeof(AFSFid));
429
430     /* next, we must iterate over the directory, starting from the specified
431      * cookie offset (dirCookie), and counting out nentries file entries.
432      * We skip files that already have stat cache entries, since we
433      * dont want to bulk stat files that are already in the cache.
434      */
435 tagain:
436     code = afs_VerifyVCache(adp, areqp);
437     if (code) goto done;
438
439     dcp = afs_GetDCache(adp, 0, areqp, &temp, &temp, 1);
440     if (!dcp) {
441         code = ENOENT;
442         goto done;
443     }
444
445     /* lock the directory cache entry */
446     ObtainReadLock(&adp->lock);
447
448     /*
449      * Make sure that the data in the cache is current. There are two
450      * cases we need to worry about:
451      * 1. The cache data is being fetched by another process.
452      * 2. The cache data is no longer valid
453      */
454     while ((adp->states & CStatd)
455            && (dcp->flags & DFFetching)
456            && hsame(adp->m.DataVersion, dcp->f.versionNo)) {
457         dcp->flags |= DFWaiting;
458         ReleaseReadLock(&adp->lock);
459         afs_osi_Sleep(&dcp->validPos);
460         ObtainReadLock(&adp->lock);
461     }
462     if (!(adp->states & CStatd)
463         || !hsame(adp->m.DataVersion, dcp->f.versionNo)) {
464         ReleaseReadLock(&adp->lock);
465         afs_PutDCache(dcp);
466         goto tagain;
467     }
468
469     /* Generate a sequence number so we can tell whether we should
470      * store the attributes when processing the response. This number is
471      * stored in the file size when we set the CBulkFetching bit. If the
472      * CBulkFetching is still set and this value hasn't changed, then
473      * we know we were the last to set CBulkFetching bit for this file,
474      * and it is safe to set the status information for this file.
475      */
476     statSeqNo = bulkStatCounter++;
477
478     /* now we have dir data in the cache, so scan the dir page */
479     fidIndex = 0;
480     flagIndex = 0;
481     while (1) { /* Should probably have some constant bound */
482         /* look for first safe entry to examine in the directory.  BlobScan
483          * looks for a the 1st allocated dir after the dirCookie slot.
484          */
485         newIndex = BlobScan(&dcp->f.inode, (dirCookie>>5));
486         if (newIndex == 0) break;
487
488         /* remember the updated directory cookie */
489         dirCookie = newIndex << 5;
490
491         /* get a ptr to the dir entry */
492         dirEntryp =(struct DirEntry *)afs_dir_GetBlob(&dcp->f.inode, newIndex);
493         if (!dirEntryp) break;
494
495         /* dont copy more than we have room for */
496         if (fidIndex >= nentries) {
497           DRelease((char *) dirEntryp, 0);
498           break;
499         }
500
501         /* now, if the dir entry looks good, copy it out to our list.  Vnode
502          * 0 means deleted, although it should also be free were it deleted.
503          */
504         if (dirEntryp->fid.vnode != 0) {
505             /* dont copy entries we have in our cache.  This check will
506              * also make us skip "." and probably "..", unless it has
507              * disappeared from the cache since we did our namei call.
508              */
509             tfid.Cell = adp->fid.Cell;
510             tfid.Fid.Volume = adp->fid.Fid.Volume;
511             tfid.Fid.Vnode = ntohl(dirEntryp->fid.vnode);
512             tfid.Fid.Unique = ntohl(dirEntryp->fid.vunique);
513             do {
514               retry = 0;
515               ObtainWriteLock(&afs_xvcache, 130);
516               tvcp = afs_FindVCache(&tfid, 0, 0, &retry, 0 /* no stats | LRU */);
517               if (tvcp && retry) {
518                 ReleaseWriteLock(&afs_xvcache);
519                 afs_PutVCache(tvcp);
520               }
521             } while (tvcp && retry);
522             if (!tvcp) {          /* otherwise, create manually */
523               tvcp = afs_NewVCache(&tfid, hostp, 0, 0);
524               ObtainWriteLock(&tvcp->lock, 505);
525               ReleaseWriteLock(&afs_xvcache);
526               afs_RemoveVCB(&tfid);
527               ReleaseWriteLock(&tvcp->lock);
528             } else {
529               ReleaseWriteLock(&afs_xvcache);
530             }
531             if (!tvcp)
532               goto done; /* can't happen at present, more's the pity */
533
534             /* WARNING: afs_DoBulkStat uses the Length field to store a
535              * sequence number for each bulk status request. Under no
536              * circumstances should afs_DoBulkStat store a sequence number
537              * if the new length will be ignored when afs_ProcessFS is
538              * called with new stats. */
539 #ifdef AFS_SGI_ENV
540             if (!(tvcp->states & (CStatd|CBulkFetching))
541                 && (tvcp->execsOrWriters <= 0)
542                 && !afs_DirtyPages(tvcp)
543                 && !AFS_VN_MAPPED((vnode_t*)tvcp))
544 #else
545             if (!(tvcp->states & (CStatd|CBulkFetching))
546                 && (tvcp->execsOrWriters <= 0) 
547                 && !afs_DirtyPages(tvcp))
548 #endif
549
550             {
551                 /* this entry doesnt exist in the cache, and is not
552                  * already being fetched by someone else, so add it to the
553                  * list of file IDs to obtain.
554                  *
555                  * We detect a callback breaking race condition by checking the
556                  * CBulkFetching state bit and the value in the file size.
557                  * It is safe to set the status only if the CBulkFetching
558                  * flag is still set and the value in the file size does
559                  * not change.
560                  *
561                  * Don't fetch status for dirty files. We need to
562                  * preserve the value of the file size. We could
563                  * flush the pages, but it wouldn't be worthwhile.
564                  */
565                 memcpy((char *)(fidsp+fidIndex), (char *) &tfid.Fid, sizeof(*fidsp));
566                 tvcp->states |= CBulkFetching;
567                 tvcp->m.Length = statSeqNo;
568                 fidIndex++;
569             }
570             afs_PutVCache(tvcp);
571         }       /* if dir vnode has non-zero entry */
572
573         /* move to the next dir entry by adding in the # of entries
574          * used by this dir entry.
575          */
576         temp = afs_dir_NameBlobs(dirEntryp->name) << 5;
577         DRelease((char *) dirEntryp, 0);
578         if (temp <= 0) break;
579         dirCookie += temp;
580     }   /* while loop over all dir entries */
581
582     /* now release the dir lock and prepare to make the bulk RPC */
583     ReleaseReadLock(&adp->lock);
584
585     /* release the chunk */
586     afs_PutDCache(dcp);
587
588     /* dont make a null call */
589     if (fidIndex == 0) goto done;
590
591     do {
592         /* setup the RPC parm structures */
593         fidParm.AFSCBFids_len = fidIndex;
594         fidParm.AFSCBFids_val = fidsp;
595         statParm.AFSBulkStats_len = fidIndex;
596         statParm.AFSBulkStats_val = statsp;
597         cbParm.AFSCBs_len = fidIndex;
598         cbParm.AFSCBs_val = cbsp;
599
600         /* start the timer; callback expirations are relative to this */
601         startTime = osi_Time();
602
603         tcp = afs_Conn(&adp->fid, areqp, SHARED_LOCK);
604         if (tcp) {
605             hostp = tcp->srvr->server;
606             XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_BULKSTATUS);
607 #ifdef RX_ENABLE_LOCKS
608             AFS_GUNLOCK();
609 #endif /* RX_ENABLE_LOCKS */
610             code = RXAFS_BulkStatus(tcp->id, &fidParm, &statParm, &cbParm,
611                                     &volSync);
612 #ifdef RX_ENABLE_LOCKS
613             AFS_GLOCK();
614 #endif /* RX_ENABLE_LOCKS */
615             XSTATS_END_TIME;
616         }
617         else code = -1;
618     } while (afs_Analyze(tcp, code, &adp->fid, areqp, 
619                          AFS_STATS_FS_RPCIDX_BULKSTATUS, SHARED_LOCK, (struct cell *)0));
620
621     /* now, if we didnt get the info, bail out. */
622     if (code) goto done;
623
624     /* we need vol flags to create the entries properly */
625     dotdot.Fid.Volume = 0;
626     volp = afs_GetVolume(&adp->fid, areqp, READ_LOCK);
627     if (volp) {
628         volStates = volp->states;
629         if (volp->dotdot.Fid.Volume != 0)
630             dotdot = volp->dotdot;
631     }
632     else volStates = 0;
633
634     /* find the place to merge the info into  We do this by skipping
635      * nskip entries in the LRU queue.  The more we skip, the more
636      * we preserve, since the head of the VLRU queue is the most recently
637      * referenced file.
638      */
639   reskip:
640     nskip = afs_cacheStats / 2;         /* preserved fraction of the cache */
641     ObtainReadLock(&afs_xvcache);
642     if (QEmpty(&VLRU)) {
643       /* actually a serious error, probably should panic. Probably will 
644        * panic soon, oh well. */
645       ReleaseReadLock(&afs_xvcache);
646       afs_warnuser("afs_DoBulkStat: VLRU empty!");
647       goto done;
648     }
649     if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
650        refpanic ("Bulkstat VLRU inconsistent");
651     }
652     for(tq = VLRU.next; tq != &VLRU; tq = QNext(tq)) {
653         if (--nskip <= 0) break;
654         else if (QNext(QPrev(tq)) != tq) {
655            BStvc = QTOV(tq);
656            refpanic ("BulkStat VLRU inconsistent");
657         }
658     }
659     if (tq != &VLRU) lruvcp = QTOV(tq);
660     else lruvcp = QTOV(VLRU.next);
661
662     /* now we have to hold this entry, so that it does not get moved
663      * into the free list while we're running.  It could still get
664      * moved within the lru queue, but hopefully that will be rare; it
665      * doesn't hurt nearly as much.
666      */
667     retry = 0;
668     osi_vnhold(lruvcp, &retry);
669     ReleaseReadLock(&afs_xvcache);           /* could be read lock */
670     if (retry)
671       goto reskip;
672
673     /* otherwise, merge in the info.  We have to be quite careful here,
674      * since we need to ensure that we don't merge old info over newer
675      * stuff in a stat cache entry.  We're very conservative here: we don't
676      * do the merge at all unless we ourselves create the stat cache
677      * entry.  That's pretty safe, and should work pretty well, since we
678      * typically expect to do the stat cache creation ourselves.
679      *
680      * We also have to take into account racing token revocations.
681      */
682     for(i=0; i<fidIndex; i++) {
683         afid.Cell = adp->fid.Cell;
684         afid.Fid.Volume = adp->fid.Fid.Volume;
685         afid.Fid.Vnode = fidsp[i].Vnode;
686         afid.Fid.Unique = fidsp[i].Unique;
687         do {
688            retry = 0;
689            ObtainReadLock(&afs_xvcache);
690            tvcp = afs_FindVCache(&afid, 1, 0, &retry, 0/* !stats&!lru*/);
691            ReleaseReadLock(&afs_xvcache);
692         } while (tvcp && retry);
693
694         /* The entry may no longer exist */
695         if (tvcp == NULL) {
696             continue;
697         }
698
699         /* now we have the entry held, but we need to fill it in */
700         ObtainWriteLock(&tvcp->lock,131);
701
702         /* if CBulkFetching is not set, or if the file size no longer
703          * matches the value we placed there when we set the CBulkFetching
704          * flag, then someone else has done something with this node,
705          * and we may not have the latest status information for this
706          * file.  Leave the entry alone.
707          */
708         if (!(tvcp->states & CBulkFetching) || (tvcp->m.Length != statSeqNo)) {
709             flagIndex++;
710             ReleaseWriteLock(&tvcp->lock);
711             afs_PutVCache(tvcp);
712             continue;
713         }
714
715         /* now copy ".." entry back out of volume structure, if necessary */
716         if (tvcp->mvstat == 2  && (dotdot.Fid.Volume != 0)) {
717             if (!tvcp->mvid)
718                 tvcp->mvid = (struct VenusFid *) osi_AllocSmallSpace(sizeof(struct VenusFid));
719             *tvcp->mvid = dotdot;
720         }
721
722         ObtainWriteLock(&afs_xvcache,132);
723         if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
724            refpanic ("Bulkstat VLRU inconsistent2");
725         }
726         if ((QNext(QPrev(&tvcp->vlruq)) != &tvcp->vlruq) 
727             || (QPrev(QNext(&tvcp->vlruq)) != &tvcp->vlruq))
728            refpanic ("Bulkstat VLRU inconsistent4");
729         if ((QNext(QPrev(&lruvcp->vlruq)) != &lruvcp->vlruq) 
730             || (QPrev(QNext(&lruvcp->vlruq)) != &lruvcp->vlruq)) 
731            refpanic ("Bulkstat VLRU inconsistent5");
732
733         if (tvcp != lruvcp) {  /* if they are == don't move it, don't corrupt vlru */
734            QRemove(&tvcp->vlruq);
735            QAdd(&lruvcp->vlruq, &tvcp->vlruq);
736         }
737
738         if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
739            refpanic ("Bulkstat VLRU inconsistent3");
740         }
741         if ((QNext(QPrev(&tvcp->vlruq)) != &tvcp->vlruq) 
742             || (QPrev(QNext(&tvcp->vlruq)) != &tvcp->vlruq))
743            refpanic ("Bulkstat VLRU inconsistent5");
744         if ((QNext(QPrev(&lruvcp->vlruq)) != &lruvcp->vlruq) 
745             || (QPrev(QNext(&lruvcp->vlruq)) != &lruvcp->vlruq))
746            refpanic ("Bulkstat VLRU inconsistent6");
747         ReleaseWriteLock(&afs_xvcache);
748
749         ObtainWriteLock(&afs_xcbhash, 494);
750
751         /* We need to check the flags again. We may have missed
752          * something while we were waiting for a lock.
753          */
754         if (!(tvcp->states & CBulkFetching) || (tvcp->m.Length != statSeqNo)) {
755             flagIndex++;
756             ReleaseWriteLock(&tvcp->lock);
757             ReleaseWriteLock(&afs_xcbhash);
758             afs_PutVCache(tvcp);
759             continue;
760         }
761
762         /* now merge in the resulting status back into the vnode.
763          * We only do this if the entry looks clear.
764          */
765         afs_ProcessFS(tvcp, &statsp[i], areqp);
766 #ifdef AFS_LINUX22_ENV
767         /* overwrite the ops if it's a directory or symlink. */
768         if (vType(tvcp) == VDIR)
769             tvcp->v.v_op = &afs_dir_iops;
770         else if (vType(tvcp) == VLNK)
771             tvcp->v.v_op = &afs_symlink_iops;
772 #endif
773
774         /* do some accounting for bulk stats: mark this entry as
775          * loaded, so we can tell if we use it before it gets
776          * recycled.
777          */
778         tvcp->states |= CBulkStat;
779         tvcp->states &= ~CBulkFetching;
780         flagIndex++;
781         afs_bulkStatsDone++;
782
783         /* merge in vol info */
784         if (volStates & VRO) tvcp->states |= CRO;
785         if (volStates & VBackup) tvcp->states |= CBackup;
786         if (volStates & VForeign) tvcp->states |= CForeign;
787
788         /* merge in the callback info */
789         tvcp->states |= CTruth;
790
791         /* get ptr to the callback we are interested in */
792         tcbp = cbsp + i;
793
794         if (tcbp->ExpirationTime != 0) {
795             tvcp->cbExpires = tcbp->ExpirationTime+startTime;
796             tvcp->callback = hostp;
797             tvcp->states |= CStatd;
798             afs_QueueCallback(tvcp, CBHash(tcbp->ExpirationTime), volp);
799         }
800         else if (tvcp->states & CRO) {
801             /* ordinary callback on a read-only volume -- AFS 3.2 style */
802             tvcp->cbExpires = 3600+startTime;
803             tvcp->callback = hostp;
804             tvcp->states |= CStatd;
805             afs_QueueCallback(tvcp, CBHash(3600), volp);
806         }
807         else {
808             tvcp->callback = 0;
809             tvcp->states &= ~(CStatd|CUnique);  
810             afs_DequeueCallback(tvcp);
811             if ((tvcp->states & CForeign) || (vType(tvcp) == VDIR)) 
812               osi_dnlc_purgedp (tvcp);  /* if it (could be) a directory */
813         }
814         ReleaseWriteLock(&afs_xcbhash);
815
816         ReleaseWriteLock(&tvcp->lock);
817         /* finally, we're done with the entry */
818         afs_PutVCache(tvcp);
819     }   /* for all files we got back */
820
821     /* finally return the pointer into the LRU queue */
822     afs_PutVCache(lruvcp);
823
824   done:
825     /* Be sure to turn off the CBulkFetching flags */
826     for(i=flagIndex; i<fidIndex; i++) {
827         afid.Cell = adp->fid.Cell;
828         afid.Fid.Volume = adp->fid.Fid.Volume;
829         afid.Fid.Vnode = fidsp[i].Vnode;
830         afid.Fid.Unique = fidsp[i].Unique;
831         do {
832            retry = 0;
833            ObtainReadLock(&afs_xvcache);
834            tvcp = afs_FindVCache(&afid, 1, 0, &retry, 0/* !stats&!lru*/);
835            ReleaseReadLock(&afs_xvcache);
836         } while (tvcp && retry);
837         if (tvcp != NULL
838             && (tvcp->states & CBulkFetching)
839             && (tvcp->m.Length == statSeqNo)) {
840           tvcp->states &= ~CBulkFetching;
841         }
842         if (tvcp != NULL) {
843           afs_PutVCache(tvcp);
844         }
845     }
846     if ( volp )
847         afs_PutVolume(volp, READ_LOCK);
848     
849     osi_FreeLargeSpace(statMemp);
850     osi_FreeLargeSpace(cbfMemp);
851 }
852
853 /* was: (AFS_DEC_ENV) || defined(AFS_OSF30_ENV) || defined(AFS_NCR_ENV) */
854 int AFSDOBULK = 1;
855
856 #ifdef  AFS_OSF_ENV
857 afs_lookup(adp, ndp)
858     struct vcache *adp;
859     struct nameidata *ndp; {
860     char aname[MAXNAMLEN+1];    /* XXX */
861     struct vcache **avcp = (struct vcache **)&(ndp->ni_vp);
862     struct ucred *acred = ndp->ni_cred;
863     int wantparent = ndp->ni_nameiop & WANTPARENT;
864     int opflag = ndp->ni_nameiop & OPFLAG;
865 #else   /* AFS_OSF_ENV */
866 #if     defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
867 afs_lookup(OSI_VC_ARG(adp), aname, avcp, pnp, flags, rdir, acred)
868     struct pathname *pnp;
869     int flags;
870     struct vnode *rdir;
871 #else
872 #if defined(UKERNEL)
873 afs_lookup(adp, aname, avcp, acred, flags)
874     int flags;
875 #else    
876 afs_lookup(adp, aname, avcp, acred)
877 #endif /* UKERNEL */
878 #endif /* SUN5 || SGI */
879     OSI_VC_DECL(adp);
880     struct vcache **avcp;
881     char *aname;
882     struct AFS_UCRED *acred; {
883 #endif
884     struct vrequest treq;
885     char *tname = (char *)0;
886     register struct vcache *tvc=0;
887     register afs_int32 code;
888     int pass = 0, hit = 0;
889     long dirCookie;
890     extern afs_int32 afs_mariner;                       /*Writing activity to log?*/
891     OSI_VC_CONVERT(adp)
892     afs_hyper_t versionNo;
893     int no_read_access = 0;
894     struct sysname_info sysState;   /* used only for @sys checking */
895
896     AFS_STATCNT(afs_lookup);
897 #ifdef  AFS_OSF_ENV
898     ndp->ni_dvp = (struct vnode *)adp;
899     memcpy(aname, ndp->ni_ptr, ndp->ni_namelen);
900     aname[ndp->ni_namelen] = '\0';
901 #endif  /* AFS_OSF_ENV */
902
903     *avcp = (struct vcache *) 0;   /* Since some callers don't initialize it */
904
905     if (code = afs_InitReq(&treq, acred)) { 
906       goto done;
907     }
908
909     /* come back to here if we encounter a non-existent object in a read-only
910        volume's directory */
911
912   redo:
913     *avcp = (struct vcache *) 0;   /* Since some callers don't initialize it */
914
915     if (!(adp->states & CStatd)) {
916         if (code = afs_VerifyVCache2(adp, &treq))
917           goto done;
918     }
919     else code = 0;
920
921     /* watch for ".." in a volume root */
922     if (adp->mvstat == 2 && aname[0] == '.' && aname[1] == '.' && !aname[2]) {
923         /* looking up ".." in root via special hacks */
924         if (adp->mvid == (struct VenusFid *) 0 || adp->mvid->Fid.Volume == 0) {
925 #ifdef  AFS_OSF_ENV
926             extern struct vcache *afs_globalVp;
927             if (adp == afs_globalVp) {
928                 struct vnode *rvp = (struct vnode *)adp;
929 /*
930                 ndp->ni_vp = rvp->v_vfsp->vfs_vnodecovered;
931                 ndp->ni_dvp = ndp->ni_vp;
932                 VN_HOLD(*avcp);
933 */
934                 code = ENODEV;
935                 goto done;
936             }
937 #endif
938             code = ENODEV;
939             goto done;
940         }
941         /* otherwise we have the fid here, so we use it */
942         tvc = afs_GetVCache(adp->mvid, &treq, (afs_int32 *)0,
943                             (struct vcache*)0, 0);
944         afs_Trace3(afs_iclSetp, CM_TRACE_GETVCDOTDOT,
945                    ICL_TYPE_FID, adp->mvid, ICL_TYPE_POINTER, tvc, 
946                    ICL_TYPE_INT32,  code);
947         *avcp = tvc;
948         code = (tvc ? 0 : ENOENT);
949         hit = 1;
950         if (tvc && !tvc->vrefCount) {
951             osi_Panic("TT1");
952         }
953         if (code) {
954             /*printf("LOOKUP GETVCDOTDOT -> %d\n", code);*/
955         }
956         goto done;
957     }
958
959     /* now check the access */
960     if (treq.uid != adp->last_looker) {  
961        if (!afs_AccessOK(adp, PRSFS_LOOKUP, &treq, CHECK_MODE_BITS)) {
962          *avcp = (struct vcache *)0;
963          code = EACCES;
964          goto done;
965        }
966        else adp->last_looker = treq.uid;
967     } 
968
969     /* Check for read access as well.  We need read access in order to
970        stat files, but not to stat subdirectories. */
971     if (!afs_AccessOK(adp, PRSFS_READ, &treq, CHECK_MODE_BITS))
972         no_read_access = 1;
973
974     /* special case lookup of ".".  Can we check for it sooner in this code,
975      * for instance, way up before "redo:" ??
976      * I'm not fiddling with the LRUQ here, either, perhaps I should, or else 
977      * invent a lightweight version of GetVCache.
978      */
979     if (aname[0] == '.' && !aname[1]) { /* special case */
980         ObtainReadLock(&afs_xvcache);   
981         osi_vnhold(adp, 0);
982         ReleaseReadLock(&afs_xvcache);  
983       code = 0;
984       *avcp = tvc = adp;
985       hit = 1;
986         if (adp && !adp->vrefCount) {
987             osi_Panic("TT2");
988         }
989       goto done;
990     }
991
992     Check_AtSys(adp, aname, &sysState, &treq);
993     tname = sysState.name;
994
995     /* 1st Check_AtSys and lookup by tname is required here, for now,
996        because the dnlc is *not* told to remove entries for the parent
997        dir of file/dir op that afs_LocalHero likes, but dnlc is informed
998        if the cached entry for the parent dir is invalidated for a
999        non-local change.
1000        Otherwise, we'd be able to do a dnlc lookup on an entry ending
1001        w/@sys and know the dnlc was consistent with reality. */
1002     tvc = osi_dnlc_lookup (adp, tname, WRITE_LOCK);
1003     *avcp = tvc;  /* maybe wasn't initialized, but it is now */
1004     if (tvc) {
1005         if (no_read_access && vType(tvc) != VDIR && vType(tvc) != VLNK) {
1006             /* need read access on dir to stat non-directory / non-link */
1007             afs_PutVCache(tvc, WRITE_LOCK);
1008             *avcp = (struct vcache *)0;
1009             code = EACCES;
1010             goto done;
1011         }
1012 #ifdef AFS_LINUX22_ENV
1013         if (tvc->mvstat == 2) { /* we don't trust the dnlc for root vcaches */
1014             AFS_RELE(tvc);
1015             *avcp = 0;
1016         }
1017         else {  
1018             code = 0;
1019             hit = 1;
1020             goto done;
1021         }
1022 #else /* non - LINUX */
1023         code = 0;
1024         hit = 1;
1025         goto done;
1026 #endif /* linux22 */
1027     }
1028
1029     {
1030     register struct dcache *tdc;
1031     afs_int32 dirOffset, dirLen;
1032     ino_t theDir;
1033     struct VenusFid tfid;
1034
1035     /* now we have to lookup the next fid */
1036     tdc = afs_GetDCache(adp, 0, &treq, &dirOffset, &dirLen, 1);
1037     if (!tdc) {
1038       *avcp = (struct vcache *)0;  /* redundant, but harmless */
1039       code = EIO;
1040       goto done;
1041     }
1042
1043     /* now we will just call dir package with appropriate inode.
1044       Dirs are always fetched in their entirety for now */
1045     ObtainReadLock(&adp->lock);
1046
1047     /*
1048      * Make sure that the data in the cache is current. There are two
1049      * cases we need to worry about:
1050      * 1. The cache data is being fetched by another process.
1051      * 2. The cache data is no longer valid
1052      */
1053     while ((adp->states & CStatd)
1054            && (tdc->flags & DFFetching)
1055            && hsame(adp->m.DataVersion, tdc->f.versionNo)) {
1056         tdc->flags |= DFWaiting;
1057         ReleaseReadLock(&adp->lock);
1058         afs_osi_Sleep(&tdc->validPos);
1059         ObtainReadLock(&adp->lock);
1060     }
1061     if (!(adp->states & CStatd)
1062         || !hsame(adp->m.DataVersion, tdc->f.versionNo)) {
1063         ReleaseReadLock(&adp->lock);
1064         afs_PutDCache(tdc);
1065         goto redo;
1066     }
1067
1068     /* Save the version number for when we call osi_dnlc_enter */
1069     hset(versionNo, tdc->f.versionNo);
1070
1071     /*
1072      * check for, and handle "@sys" if it's there.  We should be able
1073      * to avoid the alloc and the strcpy with a little work, but it's
1074      * not pressing.  If there aren't any remote users (ie, via the 
1075      * NFS translator), we have a slightly easier job.
1076      * the faster way to do this is to check for *aname == '@' and if 
1077      * it's there, check for @sys, otherwise, assume there's no @sys 
1078      * then, if the lookup fails, check for .*@sys...
1079      */
1080     /* above now implemented by Check_AtSys and Next_AtSys */
1081
1082     /* lookup the name in the appropriate dir, and return a cache entry
1083        on the resulting fid */
1084     theDir = tdc->f.inode;
1085     code = afs_dir_LookupOffset(&theDir, sysState.name, &tfid.Fid, &dirCookie);
1086
1087     /* If the first lookup doesn't succeed, maybe it's got @sys in the name */
1088     while (code == ENOENT && Next_AtSys(adp, &treq, &sysState)) {
1089       code = afs_dir_LookupOffset(&theDir, sysState.name, &tfid.Fid, &dirCookie);
1090     }
1091     tname = sysState.name;
1092
1093     ReleaseReadLock(&adp->lock);
1094     afs_PutDCache(tdc);
1095
1096     /* new fid has same cell and volume */
1097     tfid.Cell = adp->fid.Cell;
1098     tfid.Fid.Volume = adp->fid.Fid.Volume;
1099     afs_Trace4(afs_iclSetp, CM_TRACE_LOOKUP, ICL_TYPE_POINTER, adp, 
1100                ICL_TYPE_STRING, tname,
1101                ICL_TYPE_FID, &tfid, ICL_TYPE_INT32, code);
1102
1103     if (code) {
1104         if (code != ENOENT) {
1105             printf("LOOKUP dirLookupOff -> %d\n", code);
1106         }
1107         goto done;
1108     }  
1109
1110     /* prefetch some entries, if the dir is currently open.  The variable
1111      * dirCookie tells us where to start prefetching from.
1112      */
1113     if (AFSDOBULK && adp->opens > 0 && !(adp->states & CForeign)) {
1114         afs_int32 retry;
1115         /* if the entry is not in the cache, or is in the cache,
1116          * but hasn't been statd, then do a bulk stat operation.
1117          */
1118         do {
1119            retry = 0;
1120            ObtainReadLock(&afs_xvcache);        
1121            tvc = afs_FindVCache(&tfid, 1, 0, &retry, 0/* !stats,!lru */);
1122            ReleaseReadLock(&afs_xvcache);       
1123         } while (tvc && retry);
1124
1125         if (!tvc || !(tvc->states & CStatd)) {
1126             afs_DoBulkStat(adp, dirCookie, &treq);
1127         }
1128
1129         /* if the vcache isn't usable, release it */
1130         if (tvc && !(tvc->states & CStatd)) {
1131             afs_PutVCache(tvc);
1132             tvc = (struct vcache *) 0;
1133         }
1134     }
1135     else tvc = (struct vcache *) 0;
1136     
1137     /* now get the status info, if we don't already have it */
1138     /* This is kind of weird, but we might wind up accidentally calling
1139      * RXAFS_Lookup because we happened upon a file which legitimately
1140      * has a 0 uniquifier. That is the result of allowing unique to wrap
1141      * to 0. This was fixed in AFS 3.4. For CForeigh, Unique == 0 means that
1142      * the file has not yet been looked up.
1143      */
1144     if (!tvc) {
1145        afs_int32 cached = 0;
1146        if (!tfid.Fid.Unique && (adp->states & CForeign)) {
1147             tvc = afs_LookupVCache(&tfid, &treq, &cached, WRITE_LOCK, 
1148                                    adp, tname);
1149        } 
1150        if (!tvc) {  /* lookup failed or wasn't called */
1151             tvc = afs_GetVCache(&tfid, &treq, &cached, (struct vcache*)0,
1152                                 WRITE_LOCK);
1153        }
1154     } /* if !tvc */
1155     } /* sub-block just to reduce stack usage */
1156
1157     if (tvc) {
1158        if (adp->states & CForeign)
1159            tvc->states |= CForeign;
1160         tvc->parentVnode = adp->fid.Fid.Vnode;
1161         tvc->parentUnique = adp->fid.Fid.Unique;
1162         tvc->states &= ~CBulkStat;
1163
1164 #if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
1165         if (!(flags & AFS_LOOKUP_NOEVAL))
1166           /* don't eval mount points */
1167 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
1168         if (tvc->mvstat == 1) {
1169           /* a mt point, possibly unevaluated */
1170           struct volume *tvolp;
1171
1172             ObtainWriteLock(&tvc->lock,133);
1173             code = EvalMountPoint(tvc, adp, &tvolp, &treq);
1174             ReleaseWriteLock(&tvc->lock);
1175
1176             if (code) {
1177                 if (tvolp) afs_PutVolume(tvolp, WRITE_LOCK);
1178                 goto done;
1179             }
1180
1181             /* next, we want to continue using the target of the mt point */
1182             if (tvc->mvid && (tvc->states & CMValid)) {
1183               struct vcache *uvc;
1184                 /* now lookup target, to set .. pointer */
1185                 afs_Trace2(afs_iclSetp, CM_TRACE_LOOKUP1,
1186                            ICL_TYPE_POINTER, tvc, ICL_TYPE_FID, &tvc->fid);
1187                 uvc = tvc;      /* remember for later */
1188
1189                 if (tvolp && (tvolp->states & VForeign)) {
1190                     /* XXXX tvolp has ref cnt on but not locked! XXX */
1191                     tvc = afs_GetRootVCache(tvc->mvid, &treq, (afs_int32 *)0, tvolp, WRITE_LOCK);
1192                 } else {
1193                     tvc = afs_GetVCache(tvc->mvid, &treq, (afs_int32 *)0,
1194                                         (struct vcache*)0, WRITE_LOCK);
1195                 }
1196                 afs_PutVCache(uvc, WRITE_LOCK); /* we're done with it */
1197
1198                 if (!tvc) {
1199                     code = ENOENT;
1200                     if (tvolp) {
1201                         afs_PutVolume(tvolp, WRITE_LOCK);
1202                     }
1203                     goto done;
1204                 }
1205
1206                 /* now, if we came via a new mt pt (say because of a new
1207                  * release of a R/O volume), we must reevaluate the ..
1208                  * ptr to point back to the appropriate place */
1209                 if (tvolp) {
1210                     ObtainWriteLock(&tvc->lock,134);
1211                     if (tvc->mvid == (struct VenusFid *) 0) {
1212                         tvc->mvid = (struct VenusFid *) osi_AllocSmallSpace(sizeof(struct VenusFid));
1213                     }
1214                     /* setup backpointer */
1215                     *tvc->mvid = tvolp->dotdot;
1216                     ReleaseWriteLock(&tvc->lock);
1217                     afs_PutVolume(tvolp, WRITE_LOCK);
1218                 }
1219             }
1220             else {
1221                 afs_PutVCache(tvc, WRITE_LOCK);
1222                 code = ENOENT;
1223                 if (tvolp) afs_PutVolume(tvolp, WRITE_LOCK);
1224                 goto done;
1225             }
1226         }
1227         *avcp = tvc;
1228         if (tvc && !tvc->vrefCount) {
1229             osi_Panic("TT3");
1230         }
1231         code = 0;
1232     }
1233     else {
1234         /* if we get here, we found something in a directory that couldn't
1235            be located (a Multics "connection failure").  If the volume is
1236            read-only, we try flushing this entry from the cache and trying
1237            again. */
1238         if (pass == 0) {
1239             struct volume *tv;
1240             tv = afs_GetVolume(&adp->fid, &treq, READ_LOCK);
1241             if (tv) {
1242                 if (tv->states & VRO) {
1243                     pass = 1;                   /* try this *once* */
1244                     ObtainWriteLock(&afs_xcbhash, 495);
1245                     afs_DequeueCallback(adp);
1246                     /* re-stat to get later version */
1247                     adp->states &= ~CStatd;
1248                     ReleaseWriteLock(&afs_xcbhash);
1249                     osi_dnlc_purgedp(adp);
1250                     afs_PutVolume(tv, READ_LOCK);
1251                     goto redo;
1252                 }
1253                 afs_PutVolume(tv, READ_LOCK);
1254             }
1255         }
1256         code = ENOENT;
1257     }
1258
1259 done:
1260     /* put the network buffer back, if need be */
1261     if (tname != aname && tname) osi_FreeLargeSpace(tname);
1262     if (code == 0) {
1263 #ifdef  AFS_OSF_ENV
1264         /* Handle RENAME; only need to check rename "."  */
1265         if (opflag == RENAME && wantparent && *ndp->ni_next == 0) {
1266             if (!FidCmp(&(tvc->fid), &(adp->fid))) { 
1267                 afs_PutVCache(*avcp, WRITE_LOCK);
1268                 *avcp = NULL;
1269                 return afs_CheckCode(EISDIR, &treq, 18);
1270             }
1271         }
1272 #endif  /* AFS_OSF_ENV */
1273
1274         if (afs_mariner)
1275           afs_AddMarinerName(aname, tvc); 
1276
1277 #if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
1278         if (!(flags & AFS_LOOKUP_NOEVAL))
1279         /* Here we don't enter the name into the DNLC because we want the
1280         evaluated mount dir to be there (the vcache for the mounted volume)
1281         rather than the vc of the mount point itself.  we can still find the
1282         mount point's vc in the vcache by its fid. */
1283 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
1284         if (!hit) {
1285           osi_dnlc_enter (adp, aname, tvc, &versionNo);
1286         }
1287         else {
1288 #ifdef AFS_LINUX20_ENV
1289             /* So Linux inode cache is up to date. */
1290             code = afs_VerifyVCache(tvc, &treq);
1291 #else
1292             return 0;  /* can't have been any errors if hit and !code */
1293 #endif
1294         }
1295     }
1296     code = afs_CheckCode(code, &treq, 19);
1297     if (code) {
1298        /* If there is an error, make sure *avcp is null.
1299         * Alphas panic otherwise - defect 10719.
1300         */
1301        *avcp = (struct vcache *)0;
1302     }
1303
1304     return code;
1305 }