Complete removal of DUX client code
[openafs.git] / src / afs / VNOPS / afs_vnop_readdir.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  * afs_vnop_readdir.c - afs_readdir and bulk stat
12  *
13  * Implements:
14  * BlobScan
15  * afs_readdir_move
16  * afs_bulkstat_send
17  * afs_readdir/afs_readdir2(HP)
18  * afs_readdir1 - HP NFS version
19  * 
20  */
21
22 #include <afsconfig.h>
23 #include "afs/param.h"
24
25
26 #include "afs/sysincludes.h"    /* Standard vendor system headers */
27 #include "afsincludes.h"        /* Afs-based standard headers */
28 #include "afs/afs_stats.h"      /* statistics */
29 #include "afs/afs_cbqueue.h"
30 #include "afs/nfsclient.h"
31 #include "afs/afs_osidnlc.h"
32
33
34 #if     defined(AFS_HPUX1122_ENV)
35 #define DIRPAD 7
36 #else
37 #define DIRPAD 3
38 #endif
39
40 /*
41  * AFS readdir vnodeop and bulk stat support.
42  */
43
44 /* Saber C hates negative inode #s.  We're not going to talk about software
45  * that could fail if it sees a negative inode #.
46  */
47 #define FIXUPSTUPIDINODE(a)     ((a) &= 0x7fffffff)
48
49 /* BlobScan is supposed to ensure that the blob reference refers to a valid
50     directory entry.  It consults the allocation map in the page header
51     to determine whether a blob is actually in use or not.
52
53     More formally, BlobScan is supposed to return a new blob number which is just like
54     the input parameter, only it is advanced over header or free blobs.
55     
56     Note that BlobScan switches pages if necessary.  BlobScan may return
57     either 0 or an out-of-range blob number for end of file.
58
59     BlobScan is used by the Linux port in a separate file, so it should not
60     become static.
61 */
62 int
63 BlobScan(struct dcache * afile, afs_int32 ablob)
64 {
65     register afs_int32 relativeBlob;
66     afs_int32 pageBlob;
67     register struct PageHeader *tpe;
68     register afs_int32 i;
69
70     AFS_STATCNT(BlobScan);
71     /* advance ablob over free and header blobs */
72     while (1) {
73         pageBlob = ablob & ~(EPP - 1);  /* base blob in same page */
74         tpe = (struct PageHeader *)afs_dir_GetBlob(afile, pageBlob);
75         if (!tpe)
76             return 0;           /* we've past the end */
77         relativeBlob = ablob - pageBlob;        /* relative to page's first blob */
78         /* first watch for headers */
79         if (pageBlob == 0) {    /* first dir page has extra-big header */
80             /* first page */
81             if (relativeBlob < DHE + 1)
82                 relativeBlob = DHE + 1;
83         } else {                /* others have one header blob */
84             if (relativeBlob == 0)
85                 relativeBlob = 1;
86         }
87         /* make sure blob is allocated */
88         for (i = relativeBlob; i < EPP; i++) {
89             if (tpe->freebitmap[i >> 3] & (1 << (i & 7)))
90                 break;
91         }
92         /* now relativeBlob is the page-relative first allocated blob,
93          * or EPP (if there are none in this page). */
94         DRelease(tpe, 0);
95         if (i != EPP)
96             return i + pageBlob;
97         ablob = pageBlob + EPP; /* go around again */
98     }
99     /* never get here */
100 }
101
102 #if !defined(AFS_LINUX20_ENV)
103 /* Changes to afs_readdir which affect dcache or vcache handling or use of
104  * bulk stat data should also be reflected in the Linux specific verison of
105  * the readdir routine.
106  */
107
108 /*
109  * The kernel don't like it so much to have large stuff on the stack.
110  * Here we use a watered down version of the direct struct, since
111  * its not too bright to double copy the strings anyway.
112 */
113 #if !defined(UKERNEL)
114 #if defined(AFS_SGI_ENV)
115 /* Long form for 64 bit apps and kernel requests. */
116 struct min_dirent {             /* miniature dirent structure */
117     /* If struct dirent changes, this must too */
118     ino_t d_fileno;             /* This is 32 bits for 3.5, 64 for 6.2+ */
119     off64_t d_off;
120     u_short d_reclen;
121 };
122 /* Short form for 32 bit apps. */
123 struct irix5_min_dirent {       /* miniature dirent structure */
124     /* If struct dirent changes, this must too */
125     afs_uint32 d_fileno;
126     afs_int32 d_off;
127     u_short d_reclen;
128 };
129 #ifdef AFS_SGI62_ENV
130 #define AFS_DIRENT32BASESIZE IRIX5_DIRENTBASESIZE
131 #define AFS_DIRENT64BASESIZE DIRENT64BASESIZE
132 #else
133 #define AFS_DIRENT32BASESIZE IRIX5_DIRENTBASESIZE
134 #define AFS_DIRENT64BASESIZE DIRENTBASESIZE
135 #endif /* AFS_SGI62_ENV */
136 #else
137 struct min_direct {             /* miniature direct structure */
138     /* If struct direct changes, this must too */
139 #if defined(AFS_DARWIN80_ENV)
140     ino_t d_fileno;
141     u_short d_reclen;
142     u_char d_type;
143     u_char d_namlen;
144 #elif defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
145     afs_uint32 d_fileno;
146     u_short d_reclen;
147     u_char d_type;
148     u_char d_namlen;
149 #elif defined(AFS_SUN5_ENV)
150     afs_uint32 d_fileno;
151     afs_int32 d_off;
152     u_short d_reclen;
153 #else
154 #if defined(AFS_AIX32_ENV)
155     afs_int32 d_off;
156 #elif defined(AFS_HPUX100_ENV)
157     unsigned long long d_off;
158 #endif
159     afs_uint32 d_fileno;
160     u_short d_reclen;
161     u_short d_namlen;
162 #endif
163 };
164 #endif /* AFS_SGI_ENV */
165
166 #if     defined(AFS_HPUX_ENV)
167 struct minnfs_direct {
168     afs_int32 d_off;            /* XXX */
169     afs_uint32 d_fileno;
170     u_short d_reclen;
171     u_short d_namlen;
172 };
173 #define NDIRSIZ_LEN(len)   ((sizeof (struct dirent)+4 - (MAXNAMLEN+1)) + (((len)+1 + DIRPAD) &~ DIRPAD))
174 #endif
175 #endif /* !defined(UKERNEL) */
176
177
178 /*
179  *------------------------------------------------------------------------------
180  *
181  * Keep a stack of about 256 fids for the bulk stat call.
182  * Fill it during the readdir_move.  Later empty it...
183  */
184
185 #define READDIR_STASH   AFSCBMAX
186 struct AFSFid afs_readdir_stash[READDIR_STASH];
187 int afs_rd_stash_i = 0;
188
189 /*
190  *------------------------------------------------------------------------------
191  *
192  * afs_readdir_move.
193  *      mainly a kind of macro... makes getting the struct direct
194  *      out to the user space easy... could take more parameters,
195  *      but now just takes what it needs.
196  *
197  *
198 */
199
200 #if     defined(AFS_HPUX100_ENV)
201 #define DIRSIZ_LEN(len) \
202     ((sizeof (struct __dirent) - (_MAXNAMLEN+1)) + (((len)+1 + DIRPAD) &~ DIRPAD))
203 #else
204 #if     defined(AFS_SUN56_ENV)
205 #define DIRSIZ_LEN(len) ((18 + (len) + 1 + 7) & ~7 )
206 #else
207 #ifdef  AFS_SUN5_ENV
208 #define DIRSIZ_LEN(len) ((10 + (len) + 1 + (NBPW-1)) & ~(NBPW-1))
209 #else
210 #ifdef  AFS_DIRENT
211 #define DIRSIZ_LEN(len) \
212     ((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((len)+1 + 3) &~ 3))
213 #else
214 #if defined(AFS_SGI_ENV)
215 #ifndef AFS_SGI53_ENV
216 /* SGI 5.3 and later use 32/64 bit versions of directory size. */
217 #define DIRSIZ_LEN(len)         DIRENTSIZE(len)
218 #endif
219 #else /* AFS_SGI_ENV */
220 #define DIRSIZ_LEN(len) \
221     ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((len)+1 + 3) &~ 3))
222 #endif /* AFS_SGI_ENV */
223 #endif /* AFS_DIRENT */
224 #endif /* AFS_SUN5_ENV */
225 #endif /* AFS_SUN56_ENV */
226 #endif /* AFS_HPUX100_ENV */
227
228 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
229 int
230 afs_readdir_type(struct vcache *avc, struct DirEntry *ade)
231 {
232     struct VenusFid tfid;
233     struct vcache *tvc;
234     int vtype;
235     tfid.Cell = avc->f.fid.Cell;
236     tfid.Fid.Volume = avc->f.fid.Fid.Volume;
237     tfid.Fid.Vnode = ntohl(ade->fid.vnode);
238     tfid.Fid.Unique = ntohl(ade->fid.vunique);
239     if ((avc->f.states & CForeign) == 0 && (ntohl(ade->fid.vnode) & 1)) {
240         return DT_DIR;
241     }
242     ObtainReadLock(&afs_xvcache);
243     if ((tvc = afs_FindVCache(&tfid, 0, 0))) {
244         ReleaseReadLock(&afs_xvcache);
245         if (tvc->mvstat) {
246             afs_PutVCache(tvc);
247             return DT_DIR;
248         } else if (((tvc->f.states) & (CStatd | CTruth))) {
249             /* CTruth will be set if the object has
250              *ever* been statd */
251             vtype = vType(tvc);
252             afs_PutVCache(tvc);
253             if (vtype == VDIR)
254                 return DT_DIR;
255             else if (vtype == VREG)
256                 return DT_REG;
257             /* Don't do this until we're sure it can't be a mtpt */
258             /* else if (vtype == VLNK)
259              * type=DT_LNK; */
260             /* what other types does AFS support? */
261         } else
262             afs_PutVCache(tvc);
263     } else
264         ReleaseReadLock(&afs_xvcache);
265     return DT_UNKNOWN;
266 }
267 #endif
268
269 #ifdef AFS_AIX41_ENV
270 #define AFS_MOVE_LOCK()   AFS_GLOCK()
271 #define AFS_MOVE_UNLOCK() AFS_GUNLOCK()
272 #else
273 #define AFS_MOVE_LOCK()
274 #define AFS_MOVE_UNLOCK()
275 #endif
276 char bufofzeros[64];            /* gotta fill with something */
277
278 #ifdef AFS_SGI65_ENV
279 int
280 afs_readdir_move(struct DirEntry *de, struct vcache *vc, struct uio *auio, 
281                  int slen, ssize_t rlen, afs_size_t off)
282 #else
283 int
284 afs_readdir_move(struct DirEntry *de, struct vcache *vc, struct uio *auio, 
285                  int slen, int rlen, afs_size_t off)
286 #endif
287 {
288     int code = 0;
289     struct volume *tvp;
290     afs_uint32 Volume = vc->f.fid.Fid.Volume;
291     afs_uint32 Vnode  = de->fid.vnode;
292 #if     defined(AFS_SUN56_ENV)
293     struct dirent64 *direntp;
294 #else
295 #if  defined(AFS_SUN5_ENV) || (defined(AFS_AIX51_ENV) && defined(AFS_64BIT_KERNEL))
296     struct dirent *direntp;
297 #endif
298 #endif /* AFS_SUN56_ENV */
299 #ifndef AFS_SGI53_ENV
300     struct min_direct sdirEntry;
301 #endif /* AFS_SGI53_ENV */
302
303     AFS_STATCNT(afs_readdir_move);
304
305 #define READDIR_CORRECT_INUMS
306 #ifdef READDIR_CORRECT_INUMS
307     if (de->name[0] == '.' && !de->name[1]) {
308         /* This is the '.' entry; if we are a volume root, we need to
309          * ignore the directory and use the inum for the mount point.
310          */
311         if (!FidCmp(&afs_rootFid, &vc->f.fid)) {
312             Volume = 0;
313             Vnode  = 2;
314         } else if (vc->mvstat == 2) {
315             tvp = afs_GetVolume(&vc->f.fid, 0, READ_LOCK);
316             if (tvp) {
317                 Volume = tvp->mtpoint.Fid.Volume;
318                 Vnode  = tvp->mtpoint.Fid.Vnode;
319                 afs_PutVolume(tvp, READ_LOCK);
320             }
321         }
322     }
323     else if (de->name[0] == '.' && de->name[1] == '.' && !de->name[2]) {
324         /* This is the '..' entry.  Getting this right is very tricky,
325          * because we might be a volume root (so our parent is in a
326          * different volume), or our parent might be a volume root
327          * (so we actually want the mount point) or BOTH! */
328         if (!FidCmp(&afs_rootFid, &vc->f.fid)) {
329             /* We are the root of the AFS root, and thus our own parent */
330             Volume = 0;
331             Vnode  = 2;
332         } else if (vc->mvstat == 2) {
333             /* We are a volume root, which means our parent is in another
334              * volume.  Luckily, we should have his fid cached... */
335             if (vc->mvid) {
336                 if (!FidCmp(&afs_rootFid, vc->mvid)) {
337                     /* Parent directory is the root of the AFS root */
338                     Volume = 0;
339                     Vnode  = 2;
340                 } else if (vc->mvid->Fid.Vnode == 1
341                            && vc->mvid->Fid.Unique == 1) {
342                     /* XXX The above test is evil and probably breaks DFS */
343                     /* Parent directory is the target of a mount point */
344                     tvp = afs_GetVolume(vc->mvid, 0, READ_LOCK);
345                     if (tvp) {
346                         Volume = tvp->mtpoint.Fid.Volume;
347                         Vnode  = tvp->mtpoint.Fid.Vnode;
348                         afs_PutVolume(tvp, READ_LOCK);
349                     }
350                 } else {
351                     /* Parent directory is not a volume root */
352                     Volume = vc->mvid->Fid.Volume;
353                     Vnode  = vc->mvid->Fid.Vnode;
354                 }
355             }
356         } else if (de->fid.vnode == 1 && de->fid.vunique == 1) {
357             /* XXX The above test is evil and probably breaks DFS */
358             /* Parent directory is a volume root; use the right inum */
359             tvp = afs_GetVolume(&vc->f.fid, 0, READ_LOCK);
360             if (tvp) {
361                 if (tvp->cell == afs_rootFid.Cell
362                     && tvp->volume == afs_rootFid.Fid.Volume) {
363                     /* Parent directory is the root of the AFS root */
364                     Volume = 0;
365                     Vnode  = 2;
366                 } else {
367                     /* Parent directory is the target of a mount point */
368                     Volume = tvp->mtpoint.Fid.Volume;
369                     Vnode  = tvp->mtpoint.Fid.Vnode;
370                 }
371                 afs_PutVolume(tvp, READ_LOCK);
372             }
373         }
374     }
375 #endif
376
377 #ifdef  AFS_SGI53_ENV
378     {
379         afs_int32 use64BitDirent;
380
381 #ifdef AFS_SGI61_ENV
382 #ifdef AFS_SGI62_ENV
383         use64BitDirent =
384             ABI_IS(ABI_IRIX5_64, GETDENTS_ABI(OSI_GET_CURRENT_ABI(), auio));
385 #else
386         use64BitDirent =
387             (auio->uio_segflg !=
388              UIO_USERSPACE) ? ABI_IRIX5_64 : (ABI_IS(ABI_IRIX5_64 |
389                                                      ABI_IRIX5_N32,
390                                                      u.u_procp->p_abi));
391 #endif
392 #else /* AFS_SGI61_ENV */
393         use64BitDirent =
394             (auio->uio_segflg !=
395              UIO_USERSPACE) ? ABI_IRIX5_64 : (ABI_IS(ABI_IRIX5_64,
396                                                      u.u_procp->p_abi));
397 #endif /* AFS_SGI61_ENV */
398
399         if (use64BitDirent) {
400             struct min_dirent sdirEntry;
401             sdirEntry.d_fileno = (Volume << 16) + ntohl(Vnode);
402             FIXUPSTUPIDINODE(sdirEntry.d_fileno);
403             sdirEntry.d_reclen = rlen;
404             sdirEntry.d_off = (off_t) off;
405             AFS_UIOMOVE(&sdirEntry, AFS_DIRENT64BASESIZE, UIO_READ, auio,
406                         code);
407             if (code == 0)
408                 AFS_UIOMOVE(de->name, slen - 1, UIO_READ, auio, code);
409             if (code == 0)
410                 AFS_UIOMOVE(bufofzeros,
411                             DIRENTSIZE(slen) - (AFS_DIRENT64BASESIZE + slen -
412                                                 1), UIO_READ, auio, code);
413             if (DIRENTSIZE(slen) < rlen) {
414                 while (DIRENTSIZE(slen) < rlen) {
415                     int minLen = rlen - DIRENTSIZE(slen);
416                     if (minLen > sizeof(bufofzeros))
417                         minLen = sizeof(bufofzeros);
418                     AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code);
419                     rlen -= minLen;
420                 }
421             }
422         } else {
423             struct irix5_min_dirent sdirEntry;
424             sdirEntry.d_fileno = (Volume << 16) + ntohl(Vnode);
425             FIXUPSTUPIDINODE(sdirEntry.d_fileno);
426             sdirEntry.d_reclen = rlen;
427             sdirEntry.d_off = (afs_int32) off;
428             AFS_UIOMOVE(&sdirEntry, AFS_DIRENT32BASESIZE, UIO_READ, auio,
429                         code);
430             if (code == 0)
431                 AFS_UIOMOVE(de->name, slen - 1, UIO_READ, auio, code);
432             if (code == 0)
433                 AFS_UIOMOVE(bufofzeros,
434                             IRIX5_DIRENTSIZE(slen) - (AFS_DIRENT32BASESIZE +
435                                                       slen - 1), UIO_READ,
436                             auio, code);
437             if (IRIX5_DIRENTSIZE(slen) < rlen) {
438                 while (IRIX5_DIRENTSIZE(slen) < rlen) {
439                     int minLen = rlen - IRIX5_DIRENTSIZE(slen);
440                     if (minLen > sizeof(bufofzeros))
441                         minLen = sizeof(bufofzeros);
442                     AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code);
443                     rlen -= minLen;
444                 }
445             }
446         }
447     }
448 #else /* AFS_SGI53_ENV */
449 #if  defined(AFS_SUN5_ENV) || (defined(AFS_AIX51_ENV) && defined(AFS_64BIT_KERNEL))
450 #if     defined(AFS_SUN56_ENV)
451     direntp = (struct dirent64 *)osi_AllocLargeSpace(AFS_LRALLOCSIZ);
452 #else
453     direntp = (struct dirent *)osi_AllocLargeSpace(AFS_LRALLOCSIZ);
454 #endif
455     direntp->d_ino =  (Volume << 16) + ntohl(Vnode);
456     FIXUPSTUPIDINODE(direntp->d_ino);
457 #if defined(AFS_AIX51_ENV) && defined(AFS_64BIT_KERNEL)
458     direntp->d_offset = off;
459     direntp->d_namlen = slen;
460 #else
461     direntp->d_off = off;
462 #endif
463     direntp->d_reclen = rlen;
464     strcpy(direntp->d_name, de->name);
465     AFS_UIOMOVE((caddr_t) direntp, rlen, UIO_READ, auio, code);
466     osi_FreeLargeSpace((char *)direntp);
467 #else /* AFS_SUN5_ENV */
468     /* Note the odd mechanism for building the inode number */
469     sdirEntry.d_fileno = (Volume << 16) + ntohl(Vnode);
470     FIXUPSTUPIDINODE(sdirEntry.d_fileno);
471     sdirEntry.d_reclen = rlen;
472 #if !defined(AFS_SGI_ENV)
473     sdirEntry.d_namlen = slen;
474 #endif
475 #if defined(AFS_AIX32_ENV) || defined(AFS_SGI_ENV)
476     sdirEntry.d_off = off;
477 #endif
478 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
479     sdirEntry.d_type = afs_readdir_type(vc, de);
480 #endif
481
482 #if defined(AFS_SGI_ENV)
483     AFS_UIOMOVE(&sdirEntry, DIRENTBASESIZE, UIO_READ, auio, code);
484     if (code == 0)
485         AFS_UIOMOVE(de->name, slen - 1, UIO_READ, auio, code);
486     if (code == 0)
487         AFS_UIOMOVE(bufofzeros,
488                     DIRSIZ_LEN(slen) - (DIRENTBASESIZE + slen - 1), UIO_READ,
489                     auio, code);
490 #else /* AFS_SGI_ENV */
491     AFS_MOVE_UNLOCK();
492     AFS_UIOMOVE((char *)&sdirEntry, sizeof(sdirEntry), UIO_READ, auio, code);
493
494     if (code == 0) {
495         AFS_UIOMOVE(de->name, slen, UIO_READ, auio, code);
496     }
497
498     /* pad out the remaining characters with zeros */
499     if (code == 0) {
500         AFS_UIOMOVE(bufofzeros, ((slen + 1 + DIRPAD) & ~DIRPAD) - slen,
501                     UIO_READ, auio, code);
502     }
503     AFS_MOVE_LOCK();
504 #endif /* AFS_SGI_ENV */
505
506     /* pad out the difference between rlen and slen... */
507     if (DIRSIZ_LEN(slen) < rlen) {
508         AFS_MOVE_UNLOCK();
509         while (DIRSIZ_LEN(slen) < rlen) {
510             int minLen = rlen - DIRSIZ_LEN(slen);
511             if (minLen > sizeof(bufofzeros))
512                 minLen = sizeof(bufofzeros);
513             AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code);
514             rlen -= minLen;
515         }
516         AFS_MOVE_LOCK();
517     }
518 #endif /* AFS_SUN5_ENV */
519 #endif /* AFS_SGI53_ENV */
520     return (code);
521 }
522
523
524 /*
525  *------------------------------------------------------------------------------
526  *
527  * Read directory entries.
528  * There are some weird things to look out for here.  The uio_offset
529  * field is either 0 or it is the offset returned from a previous
530  * readdir.  It is an opaque value used by the server to find the
531  * correct directory block to read.  The byte count must be at least
532  * vtoblksz(vp) bytes.  The count field is the number of blocks to
533  * read on the server.  This is advisory only, the server may return
534  * only one block's worth of entries.  Entries may be compressed on
535  * the server.
536  *
537  * This routine encodes knowledge of Vice dirs.
538  */
539
540 void
541 afs_bulkstat_send(struct vcache *avc, struct vrequest *req)
542 {
543     afs_rd_stash_i = 0;
544 }
545
546 /*
547  * Here is the bad, bad, really bad news.
548  * It has to do with 'offset' (seek locations).
549 */
550
551 int
552 #if     defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
553 afs_readdir(OSI_VC_DECL(avc), struct uio *auio, afs_ucred_t *acred, 
554             int *eofp)
555 #else
556 #if defined(AFS_HPUX100_ENV)
557 afs_readdir2(OSI_VC_DECL(avc), struct uio *auio, afs_ucred_t *acred)
558 #else
559 afs_readdir(OSI_VC_DECL(avc), struct uio *auio, afs_ucred_t *acred)
560 #endif
561 #endif
562 {
563     struct vrequest treq;
564     register struct dcache *tdc;
565     afs_size_t origOffset, tlen;
566     afs_int32 len;
567     int code = 0;
568     struct DirEntry *ode = 0, *nde = 0;
569     int o_slen = 0, n_slen = 0;
570     afs_uint32 us;
571     struct afs_fakestat_state fakestate;
572 #if defined(AFS_SGI53_ENV)
573     afs_int32 use64BitDirent, dirsiz;
574 #endif /* defined(AFS_SGI53_ENV) */
575 #ifndef AFS_HPUX_ENV
576     OSI_VC_CONVERT(avc);
577 #else
578     /*
579      * XXX All the hacks for alloced sdirEntry and inlining of afs_readdir_move instead of calling
580      * it is necessary for hpux due to stack problems that seem to occur when coming thru the nfs
581      * translator side XXX
582      */
583     struct min_direct *sdirEntry =
584         (struct min_direct *)osi_AllocSmallSpace(sizeof(struct min_direct));
585     afs_int32 rlen;
586 #endif
587
588     /* opaque value is pointer into a vice dir; use bit map to decide
589      * if the entries are in use.  Always assumed to be valid.  0 is
590      * special, means start of a new dir.  Int32 inode, followed by
591      * short reclen and short namelen.  Namelen does not include
592      * the null byte.  Followed by null-terminated string.
593      */
594     AFS_STATCNT(afs_readdir);
595
596 #if defined(AFS_SGI53_ENV)
597 #ifdef AFS_SGI61_ENV
598 #ifdef AFS_SGI62_ENV
599     use64BitDirent =
600         ABI_IS(ABI_IRIX5_64, GETDENTS_ABI(OSI_GET_CURRENT_ABI(), auio));
601 #else
602     use64BitDirent =
603         (auio->uio_segflg !=
604          UIO_USERSPACE) ? ABI_IRIX5_64 : (ABI_IS(ABI_IRIX5_64 | ABI_IRIX5_N32,
605                                                  u.u_procp->p_abi));
606 #endif /* AFS_SGI62_ENV */
607 #else /* AFS_SGI61_ENV */
608     use64BitDirent =
609         (auio->uio_segflg !=
610          UIO_USERSPACE) ? ABI_IRIX5_64 : (ABI_IS(ABI_IRIX5_64,
611                                                  u.u_procp->p_abi));
612 #endif /* AFS_SGI61_ENV */
613 #endif /* defined(AFS_SGI53_ENV) */
614
615 #if     defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
616     /* Not really used by the callee so we ignore it for now */
617     if (eofp)
618         *eofp = 0;
619 #endif
620 #ifndef AFS_64BIT_CLIENT
621     if (AfsLargeFileUio(auio)   /* file is large than 2 GB */
622         ||AfsLargeFileSize(AFS_UIO_OFFSET(auio), AFS_UIO_RESID(auio)))
623         return EFBIG;
624 #endif
625
626     if ((code = afs_InitReq(&treq, acred))) {
627 #ifdef  AFS_HPUX_ENV
628         osi_FreeSmallSpace((char *)sdirEntry);
629 #endif
630         return code;
631     }
632     /* update the cache entry */
633     afs_InitFakeStat(&fakestate);
634
635     AFS_DISCON_LOCK();
636
637     code = afs_EvalFakeStat(&avc, &fakestate, &treq);
638     if (code)
639         goto done;
640   tagain:
641     code = afs_VerifyVCache(avc, &treq);
642     if (code)
643         goto done;
644     /* get a reference to the entire directory */
645     tdc = afs_GetDCache(avc, (afs_size_t) 0, &treq, &origOffset, &tlen, 1);
646     len = tlen;
647     if (!tdc) {
648         code = ENOENT;
649         goto done;
650     }
651     ObtainReadLock(&avc->lock);
652     ObtainReadLock(&tdc->lock);
653
654     /*
655      * Make sure that the data in the cache is current. There are two
656      * cases we need to worry about:
657      * 1. The cache data is being fetched by another process.
658      * 2. The cache data is no longer valid
659      */
660     while ((avc->f.states & CStatd)
661            && (tdc->dflags & DFFetching)
662            && hsame(avc->f.m.DataVersion, tdc->f.versionNo)) {
663         afs_Trace4(afs_iclSetp, CM_TRACE_DCACHEWAIT, ICL_TYPE_STRING,
664                    __FILE__, ICL_TYPE_INT32, __LINE__, ICL_TYPE_POINTER, tdc,
665                    ICL_TYPE_INT32, tdc->dflags);
666         ReleaseReadLock(&tdc->lock);
667         ReleaseReadLock(&avc->lock);
668         afs_osi_Sleep(&tdc->validPos);
669         ObtainReadLock(&avc->lock);
670         ObtainReadLock(&tdc->lock);
671     }
672     if (!(avc->f.states & CStatd)
673         || !hsame(avc->f.m.DataVersion, tdc->f.versionNo)) {
674         ReleaseReadLock(&tdc->lock);
675         ReleaseReadLock(&avc->lock);
676         afs_PutDCache(tdc);
677         goto tagain;
678     }
679
680     /*
681      *  iterator for the directory reads.  Takes the AFS DirEntry
682      *  structure and slams them into UFS direct structures.
683      *  uses afs_readdir_move to get the struct to the user space.
684      *
685      *  The routine works by looking ahead one AFS directory entry.
686      *  That's because the AFS entry we are currenly working with
687      *  may not fit into the buffer the user has provided.  If it
688      *  doesn't we have to change the size of the LAST AFS directory
689      *  entry, so that it will FIT perfectly into the block the
690      *  user has provided.
691      *  
692      *  The 'forward looking' of the code makes it a bit tough to read.
693      *  Remember we need to get an entry, see if it it fits, then
694      *  set it up as the LAST entry, and find the next one.
695      *
696      *  Tough to take: We give out an EINVAL if we don't have enough
697      *  space in the buffer, and at the same time, don't have an entry
698      *  to put into the buffer. This CAN happen if the first AFS entry
699      *  we get can't fit into the 512 character buffer provided.  Seems
700      *  it ought not happen... 
701      *
702      *  Assumption: don't need to use anything but one dc entry:
703      *  this means the directory ought not be greater than 64k.
704      */
705     len = 0;
706 #ifdef AFS_HPUX_ENV
707     auio->uio_fpflags = 0;
708 #endif
709     while (code == 0) {
710         origOffset = AFS_UIO_OFFSET(auio);
711         /* scan for the next interesting entry scan for in-use blob otherwise up point at
712          * this blob note that ode, if non-zero, also represents a held dir page */
713         if (!(us = BlobScan(tdc, (origOffset >> 5)))
714             || !(nde = (struct DirEntry *)afs_dir_GetBlob(tdc, us))) {
715             /* failed to setup nde, return what we've got, and release ode */
716             if (len) {
717                 /* something to hand over. */
718 #ifdef  AFS_HPUX_ENV
719                 sdirEntry->d_fileno =
720                     (avc->f.fid.Fid.Volume << 16) + ntohl(ode->fid.vnode);
721                 FIXUPSTUPIDINODE(sdirEntry->d_fileno);
722                 sdirEntry->d_reclen = rlen = AFS_UIO_RESID(auio);
723                 sdirEntry->d_namlen = o_slen;
724 #if defined(AFS_SUN5_ENV) || defined(AFS_AIX32_ENV) || defined(AFS_HPUX100_ENV)
725                 sdirEntry->d_off = origOffset;
726 #endif
727                 AFS_UIOMOVE((char *)sdirEntry, sizeof(*sdirEntry), UIO_READ,
728                             auio, code);
729                 if (code == 0)
730                     AFS_UIOMOVE(ode->name, o_slen, UIO_READ, auio, code);
731                 /* pad out the remaining characters with zeros */
732                 if (code == 0) {
733                     AFS_UIOMOVE(bufofzeros,
734                                 ((o_slen + 1 + DIRPAD) & ~DIRPAD) - o_slen,
735                                 UIO_READ, auio, code);
736                 }
737                 /* pad out the difference between rlen and slen... */
738                 if (DIRSIZ_LEN(o_slen) < rlen) {
739                     while (DIRSIZ_LEN(o_slen) < rlen) {
740                         int minLen = rlen - DIRSIZ_LEN(o_slen);
741                         if (minLen > sizeof(bufofzeros))
742                             minLen = sizeof(bufofzeros);
743                         AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code);
744                         rlen -= minLen;
745                     }
746                 }
747 #else
748                 code = afs_readdir_move(ode, avc, auio, o_slen,
749 #if defined(AFS_SUN5_ENV)
750                                         len, origOffset);
751 #else
752                                         AFS_UIO_RESID(auio), origOffset);
753 #endif
754 #endif /* AFS_HPUX_ENV */
755 #if !defined(AFS_SUN5_ENV)
756                 AFS_UIO_SETRESID(auio, 0);
757 #endif
758             } else {
759                 /* nothin to hand over */
760             }
761 #if     defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
762             if (eofp)
763                 *eofp = 1;      /* Set it properly */
764 #endif
765             if (ode)
766                 DRelease(ode, 0);
767             goto dirend;
768         }
769         /* by here nde is set */
770
771         /* Do we have enough user space to carry out our mission? */
772 #if defined(AFS_SGI_ENV)
773         n_slen = strlen(nde->name) + 1; /* NULL terminate */
774 #else
775         n_slen = strlen(nde->name);
776 #endif
777 #ifdef  AFS_SGI53_ENV
778         dirsiz =
779             use64BitDirent ? DIRENTSIZE(n_slen) : IRIX5_DIRENTSIZE(n_slen);
780         if (dirsiz >= (AFS_UIO_RESID(auio) - len)) {
781 #else
782         if (DIRSIZ_LEN(n_slen) >= (AFS_UIO_RESID(auio) - len)) {
783 #endif /* AFS_SGI53_ENV */
784             /* No can do no more now; ya know... at this time */
785             DRelease(nde, 0);   /* can't use this one. */
786             if (len) {
787 #ifdef  AFS_HPUX_ENV
788                 sdirEntry->d_fileno =
789                     (avc->f.fid.Fid.Volume << 16) + ntohl(ode->fid.vnode);
790                 FIXUPSTUPIDINODE(sdirEntry->d_fileno);
791                 sdirEntry->d_reclen = rlen = AFS_UIO_RESID(auio);
792                 sdirEntry->d_namlen = o_slen;
793 #if defined(AFS_SUN5_ENV) || defined(AFS_AIX32_ENV) || defined(AFS_HPUX100_ENV)
794                 sdirEntry->d_off = origOffset;
795 #endif
796                 AFS_UIOMOVE((char *)sdirEntry, sizeof(*sdirEntry), UIO_READ,
797                             auio, code);
798                 if (code == 0)
799                     AFS_UIOMOVE(ode->name, o_slen, UIO_READ, auio, code);
800                 /* pad out the remaining characters with zeros */
801                 if (code == 0) {
802                     AFS_UIOMOVE(bufofzeros,
803                                 ((o_slen + 1 + DIRPAD) & ~DIRPAD) - o_slen,
804                                 UIO_READ, auio, code);
805                 }
806                 /* pad out the difference between rlen and slen... */
807                 if (DIRSIZ_LEN(o_slen) < rlen) {
808                     while (DIRSIZ_LEN(o_slen) < rlen) {
809                         int minLen = rlen - DIRSIZ_LEN(o_slen);
810                         if (minLen > sizeof(bufofzeros))
811                             minLen = sizeof(bufofzeros);
812                         AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code);
813                         rlen -= minLen;
814                     }
815                 }
816 #else /* AFS_HPUX_ENV */
817                 code =
818                     afs_readdir_move(ode, avc, auio, o_slen,
819                                      AFS_UIO_RESID(auio), origOffset);
820 #endif /* AFS_HPUX_ENV */
821                 /* this next line used to be AFSVFS40 or AIX 3.1, but is
822                  * really generic */
823                 AFS_UIO_SETOFFSET(auio, origOffset);
824                 AFS_UIO_SETRESID(auio, 0);
825             } else {            /* trouble, can't give anything to the user! */
826                 /* even though he has given us a buffer, 
827                  * even though we have something to give us,
828                  * Looks like we lost something somewhere.
829                  */
830                 code = EINVAL;
831             }
832             if (ode)
833                 DRelease(ode, 0);
834             goto dirend;
835         }
836
837         /*
838          * In any event, we move out the LAST de entry, getting ready
839          * to set up for the next one.
840          */
841         if (len) {
842 #ifdef  AFS_HPUX_ENV
843             sdirEntry->d_fileno =
844                 (avc->f.fid.Fid.Volume << 16) + ntohl(ode->fid.vnode);
845             FIXUPSTUPIDINODE(sdirEntry->d_fileno);
846             sdirEntry->d_reclen = rlen = len;
847             sdirEntry->d_namlen = o_slen;
848 #if defined(AFS_SUN5_ENV) || defined(AFS_AIX32_ENV) || defined(AFS_HPUX100_ENV)
849             sdirEntry->d_off = origOffset;
850 #endif
851             AFS_UIOMOVE((char *)sdirEntry, sizeof(*sdirEntry), UIO_READ, auio,
852                         code);
853             if (code == 0)
854                 AFS_UIOMOVE(ode->name, o_slen, UIO_READ, auio, code);
855             /* pad out the remaining characters with zeros */
856             if (code == 0) {
857                 AFS_UIOMOVE(bufofzeros,
858                             ((o_slen + 1 + DIRPAD) & ~DIRPAD) - o_slen,
859                             UIO_READ, auio, code);
860             }
861             /* pad out the difference between rlen and slen... */
862             if (DIRSIZ_LEN(o_slen) < rlen) {
863                 while (DIRSIZ_LEN(o_slen) < rlen) {
864                     int minLen = rlen - DIRSIZ_LEN(o_slen);
865                     if (minLen > sizeof(bufofzeros))
866                         minLen = sizeof(bufofzeros);
867                     AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code);
868                     rlen -= minLen;
869                 }
870             }
871 #else /* AFS_HPUX_ENV */
872             code = afs_readdir_move(ode, avc, auio, o_slen, len, origOffset);
873 #endif /* AFS_HPUX_ENV */
874         }
875 #ifdef  AFS_SGI53_ENV
876         len = use64BitDirent ? DIRENTSIZE(o_slen =
877                                           n_slen) : IRIX5_DIRENTSIZE(o_slen =
878                                                                      n_slen);
879 #else
880         len = DIRSIZ_LEN(o_slen = n_slen);
881 #endif /* AFS_SGI53_ENV */
882         if (ode)
883             DRelease(ode, 0);
884         ode = nde;
885         AFS_UIO_SETOFFSET(auio, (afs_int32) ((us + afs_dir_NameBlobs(nde->name)) << 5));
886     }
887     if (ode)
888         DRelease(ode, 0);
889
890   dirend:
891     ReleaseReadLock(&tdc->lock);
892     afs_PutDCache(tdc);
893     ReleaseReadLock(&avc->lock);
894
895   done:
896 #ifdef  AFS_HPUX_ENV
897     osi_FreeSmallSpace((char *)sdirEntry);
898 #endif
899     AFS_DISCON_UNLOCK();
900     afs_PutFakeStat(&fakestate);
901     code = afs_CheckCode(code, &treq, 28);
902     return code;
903 }
904
905 #if defined(AFS_HPUX_ENV)
906 int
907 afs1_readdir(struct vcache *avc, struct uio *auio, afs_ucred_t *acred)
908 {
909     struct vrequest treq;
910     register struct dcache *tdc;
911     afs_size_t origOffset, len;
912     int code = 0;
913     struct DirEntry *ode = 0, *nde = 0;
914     int o_slen = 0, n_slen = 0;
915     afs_uint32 us;
916     /*
917      * XXX All the hacks for alloced sdirEntry and inlining of
918      * afs_readdir_move instead of calling it is necessary for hpux due to
919      * stack problems that seem to occur when coming thru the nfs
920      * translator side XXX
921      */
922     struct minnfs_direct *sdirEntry = (struct minnfs_direct *)
923         osi_AllocSmallSpace(sizeof(struct min_direct));
924     afs_int32 rlen;
925
926     struct afs_fakestat_state fakestate;
927
928     AFS_STATCNT(afs_readdir);
929     if (code = afs_InitReq(&treq, acred)) {
930         osi_FreeSmallSpace((char *)sdirEntry);
931         return code;
932     }
933     afs_InitFakeStat(&fakestate);
934     AFS_DISCON_LOCK();
935     code = afs_EvalFakeStat(&avc, &fakestate, &treq);
936     if (code) {
937         osi_FreeSmallSpace((char *)sdirEntry);
938         AFS_DISCON_UNLOCK();
939         afs_PutFakeStat(&fakestate);
940         return code;
941     }
942     /* update the cache entry */
943   tagain:
944     code = afs_VerifyVCache(avc, &treq);
945     if (code)
946         goto done;
947     /* get a reference to the entire directory */
948     tdc = afs_GetDCache(avc, (afs_size_t) 0, &treq, &origOffset, &len, 1);
949     if (!tdc) {
950         code = ENOENT;
951         goto done;
952     }
953     ObtainReadLock(&avc->lock);
954     ObtainReadLock(&tdc->lock);
955
956     /*
957      * Make sure that the data in the cache is current. There are two
958      * cases we need to worry about:
959      * 1. The cache data is being fetched by another process.
960      * 2. The cache data is no longer valid
961      */
962     while ((avc->f.states & CStatd)
963            && (tdc->dflags & DFFetching)
964            && hsame(avc->f.m.DataVersion, tdc->f.versionNo)) {
965         afs_Trace4(afs_iclSetp, CM_TRACE_DCACHEWAIT, ICL_TYPE_STRING,
966                    __FILE__, ICL_TYPE_INT32, __LINE__, ICL_TYPE_POINTER, tdc,
967                    ICL_TYPE_INT32, tdc->dflags);
968         ReleaseReadLock(&tdc->lock);
969         ReleaseReadLock(&avc->lock);
970         afs_osi_Sleep(&tdc->validPos);
971         ObtainReadLock(&avc->lock);
972         ObtainReadLock(&tdc->lock);
973     }
974     if (!(avc->f.states & CStatd)
975         || !hsame(avc->f.m.DataVersion, tdc->f.versionNo)) {
976         ReleaseReadLock(&tdc->lock);
977         ReleaseReadLock(&avc->lock);
978         afs_PutDCache(tdc);
979         goto tagain;
980     }
981
982     len = 0;
983     auio->uio_fpflags = 0;
984     while (code == 0) {
985         origOffset = AFS_UIO_OFFSET(auio);
986
987         /* scan for the next interesting entry scan for in-use blob
988          * otherwise up point at this blob note that ode, if non-zero,
989          * also represents a held dir page */
990         if (!(us = BlobScan(tdc, (origOffset >> 5)))
991             || !(nde = (struct DirEntry *)afs_dir_GetBlob(tdc, us))) {
992             /* failed to setup nde, return what we've got, and release ode */
993             if (len) {
994                 /* something to hand over. */
995                 sdirEntry->d_fileno =
996                     (avc->f.fid.Fid.Volume << 16) + ntohl(ode->fid.vnode);
997                 FIXUPSTUPIDINODE(sdirEntry->d_fileno);
998                 sdirEntry->d_reclen = rlen = AFS_UIO_RESID(auio);
999                 sdirEntry->d_namlen = o_slen;
1000                 sdirEntry->d_off = origOffset;
1001                 AFS_UIOMOVE((char *)sdirEntry, sizeof(*sdirEntry), UIO_READ,
1002                             auio, code);
1003                 if (code == 0) {
1004                     AFS_UIOMOVE(ode->name, o_slen, UIO_READ, auio, code);
1005                 }
1006                 /* pad out the remaining characters with zeros */
1007                 if (code == 0) {
1008                     AFS_UIOMOVE(bufofzeros,
1009                                 ((o_slen + 1 + DIRPAD) & ~DIRPAD) - o_slen,
1010                                 UIO_READ, auio, code);
1011                 }
1012                 /* pad out the difference between rlen and slen... */
1013                 if (NDIRSIZ_LEN(o_slen) < rlen) {
1014                     while (NDIRSIZ_LEN(o_slen) < rlen) {
1015                         int minLen = rlen - NDIRSIZ_LEN(o_slen);
1016                         if (minLen > sizeof(bufofzeros))
1017                             minLen = sizeof(bufofzeros);
1018                         AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code);
1019                         rlen -= minLen;
1020                     }
1021                 }
1022                 AFS_UIO_SETRESID(auio, 0);
1023             } else {
1024                 /* nothin to hand over */
1025             }
1026             if (ode)
1027                 DRelease(ode, 0);
1028             goto dirend;
1029         }
1030         /* by here nde is set */
1031
1032         /* Do we have enough user space to carry out our mission? */
1033         n_slen = strlen(nde->name);
1034         if (NDIRSIZ_LEN(n_slen) >= (AFS_UIO_RESID(auio) - len)) {
1035             /* No can do no more now; ya know... at this time */
1036             DRelease(nde, 0);   /* can't use this one. */
1037             if (len) {
1038                 sdirEntry->d_fileno =
1039                     (avc->f.fid.Fid.Volume << 16) + ntohl(ode->fid.vnode);
1040                 FIXUPSTUPIDINODE(sdirEntry->d_fileno);
1041                 sdirEntry->d_reclen = rlen = AFS_UIO_RESID(auio);
1042                 sdirEntry->d_namlen = o_slen;
1043                 sdirEntry->d_off = origOffset;
1044                 AFS_UIOMOVE((char *)sdirEntry, sizeof(*sdirEntry), UIO_READ,
1045                             auio, code);
1046                 if (code == 0)
1047                     AFS_UIOMOVE(ode->name, o_slen, UIO_READ, auio, code);
1048                 /* pad out the remaining characters with zeros */
1049                 if (code == 0) {
1050                     AFS_UIOMOVE(bufofzeros,
1051                                 ((o_slen + 1 + DIRPAD) & ~DIRPAD) - o_slen,
1052                                 UIO_READ, auio, code);
1053                 }
1054                 /* pad out the difference between rlen and slen... */
1055                 if (NDIRSIZ_LEN(o_slen) < rlen) {
1056                     while (NDIRSIZ_LEN(o_slen) < rlen) {
1057                         int minLen = rlen - NDIRSIZ_LEN(o_slen);
1058                         if (minLen > sizeof(bufofzeros))
1059                             minLen = sizeof(bufofzeros);
1060                         AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code);
1061                         rlen -= minLen;
1062                     }
1063                 }
1064                 /* this next line used to be AFSVFS40 or AIX 3.1, but is really generic */
1065                 AFS_UIO_SETOFFSET(auio, origOffset);
1066                 AFS_UIO_SETRESID(auio, 0);
1067             } else {            /* trouble, can't give anything to the user! */
1068                 /* even though he has given us a buffer, 
1069                  * even though we have something to give us,
1070                  * Looks like we lost something somewhere.
1071                  */
1072                 code = EINVAL;
1073             }
1074             if (ode)
1075                 DRelease(ode, 0);
1076             goto dirend;
1077         }
1078
1079         /*
1080          * In any event, we move out the LAST de entry, getting ready
1081          * to set up for the next one.
1082          */
1083         if (len) {
1084             sdirEntry->d_fileno =
1085                 (avc->f.fid.Fid.Volume << 16) + ntohl(ode->fid.vnode);
1086             FIXUPSTUPIDINODE(sdirEntry->d_fileno);
1087             sdirEntry->d_reclen = rlen = len;
1088             sdirEntry->d_namlen = o_slen;
1089             sdirEntry->d_off = origOffset;
1090             AFS_UIOMOVE((char *)sdirEntry, sizeof(*sdirEntry), UIO_READ, auio,
1091                         code);
1092             if (code == 0)
1093                 AFS_UIOMOVE(ode->name, o_slen, UIO_READ, auio, code);
1094             /* pad out the remaining characters with zeros */
1095             if (code == 0) {
1096                 AFS_UIOMOVE(bufofzeros,
1097                             ((o_slen + 1 + DIRPAD) & ~DIRPAD) - o_slen,
1098                             UIO_READ, auio, code);
1099             }
1100             /* pad out the difference between rlen and slen... */
1101             if (NDIRSIZ_LEN(o_slen) < rlen) {
1102                 while (NDIRSIZ_LEN(o_slen) < rlen) {
1103                     int minLen = rlen - NDIRSIZ_LEN(o_slen);
1104                     if (minLen > sizeof(bufofzeros))
1105                         minLen = sizeof(bufofzeros);
1106                     AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code);
1107                     rlen -= minLen;
1108                 }
1109             }
1110         }
1111         len = NDIRSIZ_LEN(o_slen = n_slen);
1112         if (ode)
1113             DRelease(ode, 0);
1114         ode = nde;
1115         AFS_UIO_OFFSET(auio) = ((us + afs_dir_NameBlobs(nde->name)) << 5);
1116     }
1117     if (ode)
1118         DRelease(ode, 0);
1119
1120   dirend:
1121     ReleaseReadLock(&tdc->lock);
1122     afs_PutDCache(tdc);
1123     ReleaseReadLock(&avc->lock);
1124
1125   done:
1126     osi_FreeSmallSpace((char *)sdirEntry);
1127     AFS_DISCON_UNLOCK();
1128     afs_PutFakeStat(&fakestate);
1129     code = afs_CheckCode(code, &treq, 29);
1130     return code;
1131 }
1132
1133 #endif
1134 #endif /* !AFS_LINUX20_ENV */