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