venus: Remove dedebug
[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 #if defined(AFS_HPUX1122_ENV)
34 #define DIRPAD 7
35 #elif defined(AFS_NBSD40_ENV)
36 #define DIRPAD 7
37 #else
38 #define DIRPAD 3
39 #endif
40
41 /*
42  * AFS readdir vnodeop and bulk stat support.
43  */
44
45 /**
46  * Ensure that the blob reference refers to a valid directory entry.
47  * It consults the allocation map in the page header to determine
48  * whether a blob is actually in use or not.
49  *
50  * More formally, BlobScan is supposed to return a new blob number
51  * which is just like the input parameter, only it is advanced over
52  * header or free blobs.
53  *
54  * Note that BlobScan switches pages if necessary.  BlobScan may
55  * return either 0 for success or an error code.  Upon successful
56  * return, the new blob value is assigned to *ablobOut.  The new
57  * blob value (*ablobOut) is set to 0 when the end of the file has
58  * been reached.
59  *
60  * BlobScan is used by the Linux port in a separate file, so it should not
61  * become static.
62  */
63 int
64 BlobScan(struct dcache * afile, afs_int32 ablob, int *ablobOut)
65 {
66     afs_int32 relativeBlob;
67     afs_int32 pageBlob;
68     struct PageHeader *tpe;
69     struct DirBuffer headerbuf;
70     afs_int32 i;
71     int code;
72
73     AFS_STATCNT(BlobScan);
74     /* advance ablob over free and header blobs */
75     while (1) {
76         pageBlob = ablob & ~(EPP - 1);  /* base blob in same page */
77         code = afs_dir_GetBlob(afile, pageBlob, &headerbuf);
78         if (code == ENOENT) {
79             *ablobOut = 0; /* past the end of file */
80             return 0;      /* not an error */
81         }
82         if (code)
83             return code;
84         tpe = (struct PageHeader *)headerbuf.data;
85
86         relativeBlob = ablob - pageBlob;        /* relative to page's first blob */
87         /* first watch for headers */
88         if (pageBlob == 0) {    /* first dir page has extra-big header */
89             /* first page */
90             if (relativeBlob < DHE + 1)
91                 relativeBlob = DHE + 1;
92         } else {                /* others have one header blob */
93             if (relativeBlob == 0)
94                 relativeBlob = 1;
95         }
96         /* make sure blob is allocated */
97         for (i = relativeBlob; i < EPP; i++) {
98             if (tpe->freebitmap[i >> 3] & (1 << (i & 7)))
99                 break;
100         }
101         /* now relativeBlob is the page-relative first allocated blob,
102          * or EPP (if there are none in this page). */
103         DRelease(&headerbuf, 0);
104         if (i != EPP) {
105             *ablobOut = i + pageBlob;
106             return 0;
107         }
108         ablob = pageBlob + EPP; /* go around again */
109     }
110     /* never get here */
111 }
112
113
114 #if !defined(AFS_LINUX_ENV)
115 /* Changes to afs_readdir which affect dcache or vcache handling or use of
116  * bulk stat data should also be reflected in the Linux specific verison of
117  * the readdir routine.
118  */
119
120 /*
121  * The kernel don't like it so much to have large stuff on the stack.
122  * Here we use a watered down version of the direct struct, since
123  * its not too bright to double copy the strings anyway.
124 */
125 #if !defined(UKERNEL)
126 #if defined(AFS_SGI_ENV)
127 /* Long form for 64 bit apps and kernel requests. */
128 struct min_dirent {             /* miniature dirent structure */
129     /* If struct dirent changes, this must too */
130     ino_t d_fileno;             /* This is 32 bits for 3.5, 64 for 6.2+ */
131     off64_t d_off;
132     u_short d_reclen;
133 };
134 /* Short form for 32 bit apps. */
135 struct irix5_min_dirent {       /* miniature dirent structure */
136     /* If struct dirent changes, this must too */
137     afs_uint32 d_fileno;
138     afs_int32 d_off;
139     u_short d_reclen;
140 };
141 #define AFS_DIRENT32BASESIZE IRIX5_DIRENTBASESIZE
142 #define AFS_DIRENT64BASESIZE DIRENT64BASESIZE
143 #else
144 struct min_direct {             /* miniature direct structure */
145     /* If struct direct changes, this must too */
146 #if defined(AFS_DARWIN80_ENV)
147     ino_t d_fileno;
148     u_short d_reclen;
149     u_char d_type;
150     u_char d_namlen;
151 #elif defined(AFS_NBSD40_ENV)
152     ino_t d_fileno;             /* file number of entry */
153     uint16_t d_reclen;          /* length of this record */
154     uint16_t d_namlen;          /* length of string in d_name */
155     uint8_t  d_type;            /* file type, see below */
156 #elif defined(AFS_FBSD120_ENV)
157     /* FreeBSD 12.0 moved to 64-bit inodes and bumped d_namlen to 16 bits. */
158     ino_t d_fileno;
159     off_t d_off;
160     u_short d_reclen;
161     u_char d_type;
162     u_char  d_pad0;
163     u_short d_namlen;
164     u_short d_pad1;
165 #elif defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
166     afs_uint32 d_fileno;
167     u_short d_reclen;
168     u_char d_type;
169     u_char d_namlen;
170 #elif defined(AFS_SUN5_ENV)
171     afs_uint32 d_fileno;
172     afs_int32 d_off;
173     u_short d_reclen;
174 #else
175 #if defined(AFS_AIX32_ENV)
176     afs_int32 d_off;
177 #elif defined(AFS_HPUX100_ENV)
178     unsigned long long d_off;
179 #endif
180     afs_uint32 d_fileno;
181     u_short d_reclen;
182     u_short d_namlen;
183 #endif
184 };
185 #endif /* AFS_SGI_ENV */
186
187 #if     defined(AFS_HPUX_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_SUN5_ENV)
226 #define DIRSIZ_LEN(len) ((18 + (len) + 1 + 7) & ~7 )
227 #else
228 #ifdef  AFS_NBSD40_ENV
229 #define DIRSIZ_LEN(len) \
230     ((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((len)+1 + 7) & ~7))
231 #else
232 #ifdef  AFS_DIRENT
233 #define DIRSIZ_LEN(len) \
234     ((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((len)+1 + 3) &~ 3))
235 #else
236 #ifndef AFS_SGI_ENV
237 #define DIRSIZ_LEN(len) \
238     ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((len)+1 + 3) &~ 3))
239 #endif /* AFS_SGI_ENV */
240 #endif /* AFS_DIRENT */
241 #endif /* AFS_NBSD40_ENV */
242 #endif /* AFS_SUN5_ENV */
243 #endif /* AFS_HPUX100_ENV */
244
245 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
246 int
247 afs_readdir_type(struct vcache *avc, struct DirEntry *ade)
248 {
249     struct VenusFid tfid;
250     struct vcache *tvc;
251     int vtype;
252     tfid.Cell = avc->f.fid.Cell;
253     tfid.Fid.Volume = avc->f.fid.Fid.Volume;
254     tfid.Fid.Vnode = ntohl(ade->fid.vnode);
255     tfid.Fid.Unique = ntohl(ade->fid.vunique);
256     if ((avc->f.states & CForeign) == 0 && (ntohl(ade->fid.vnode) & 1)) {
257         return DT_DIR;
258     }
259     ObtainReadLock(&afs_xvcache);
260     if ((tvc = afs_FindVCache(&tfid, 0))) {
261         ReleaseReadLock(&afs_xvcache);
262         if (tvc->mvstat != AFS_MVSTAT_FILE) {
263             afs_PutVCache(tvc);
264             return DT_DIR;
265         } else if (((tvc->f.states) & (CStatd | CTruth))) {
266             /* CTruth will be set if the object has
267              *ever* been statd */
268             vtype = vType(tvc);
269             afs_PutVCache(tvc);
270             if (vtype == VDIR)
271                 return DT_DIR;
272             else if (vtype == VREG)
273                 return DT_REG;
274             /* Don't do this until we're sure it can't be a mtpt */
275             /* if we're CStatd and CTruth and mvstat==AFS_MVSTAT_FILE, it's a link */
276             else if (vtype == VLNK)
277                 return DT_LNK;
278             /* what other types does AFS support? */
279         } else
280             afs_PutVCache(tvc);
281     } else
282         ReleaseReadLock(&afs_xvcache);
283     return DT_UNKNOWN;
284 }
285 #endif
286
287 #ifdef AFS_AIX41_ENV
288 #define AFS_MOVE_LOCK()   AFS_GLOCK()
289 #define AFS_MOVE_UNLOCK() AFS_GUNLOCK()
290 #else
291 #define AFS_MOVE_LOCK()
292 #define AFS_MOVE_UNLOCK()
293 #endif
294 char bufofzeros[64];            /* gotta fill with something */
295
296 #ifdef AFS_SGI_ENV
297 int
298 afs_readdir_move(struct DirEntry *de, struct vcache *vc, struct uio *auio, 
299                  int slen, ssize_t rlen, afs_size_t off)
300 #else
301 int
302 afs_readdir_move(struct DirEntry *de, struct vcache *vc, struct uio *auio, 
303                  int slen, int rlen, afs_size_t off)
304 #endif
305 {
306     int code = 0;
307     struct volume *tvp;
308     afs_uint32 Volume = vc->f.fid.Fid.Volume;
309     afs_uint32 Vnode  = de->fid.vnode;
310 #if     defined(AFS_SUN5_ENV)
311     struct dirent64 *direntp;
312 #else
313 #if  (defined(AFS_AIX51_ENV) && defined(AFS_64BIT_KERNEL))
314     struct dirent *direntp;
315 #endif
316 #endif /* AFS_SUN5_ENV */
317 #ifndef AFS_SGI_ENV
318     struct min_direct sdirEntry;
319     memset(&sdirEntry, 0, sizeof(sdirEntry));
320 #endif /* AFS_SGI_ENV */
321
322     AFS_STATCNT(afs_readdir_move);
323
324 #define READDIR_CORRECT_INUMS
325 #ifdef READDIR_CORRECT_INUMS
326     if (de->name[0] == '.' && !de->name[1]) {
327         /* This is the '.' entry; if we are a volume root, we need to
328          * ignore the directory and use the inum for the mount point.
329          */
330         if (!FidCmp(&afs_rootFid, &vc->f.fid)) {
331             Volume = 0;
332             Vnode  = 2;
333         } else if (vc->mvstat == AFS_MVSTAT_ROOT) {
334             tvp = afs_GetVolume(&vc->f.fid, 0, READ_LOCK);
335             if (tvp) {
336                 Volume = tvp->mtpoint.Fid.Volume;
337                 Vnode  = tvp->mtpoint.Fid.Vnode;
338                 afs_PutVolume(tvp, READ_LOCK);
339             }
340         }
341     }
342     else if (de->name[0] == '.' && de->name[1] == '.' && !de->name[2]) {
343         /* This is the '..' entry.  Getting this right is very tricky,
344          * because we might be a volume root (so our parent is in a
345          * different volume), or our parent might be a volume root
346          * (so we actually want the mount point) or BOTH! */
347         if (!FidCmp(&afs_rootFid, &vc->f.fid)) {
348             /* We are the root of the AFS root, and thus our own parent */
349             Volume = 0;
350             Vnode  = 2;
351         } else if (vc->mvstat == AFS_MVSTAT_ROOT) {
352             /* We are a volume root, which means our parent is in another
353              * volume.  Luckily, we should have his fid cached... */
354             if (vc->mvid.parent) {
355                 if (!FidCmp(&afs_rootFid, vc->mvid.parent)) {
356                     /* Parent directory is the root of the AFS root */
357                     Volume = 0;
358                     Vnode  = 2;
359                 } else if (vc->mvid.parent->Fid.Vnode == 1
360                            && vc->mvid.parent->Fid.Unique == 1) {
361                     /* XXX The above test is evil and probably breaks DFS */
362                     /* Parent directory is the target of a mount point */
363                     tvp = afs_GetVolume(vc->mvid.parent, 0, READ_LOCK);
364                     if (tvp) {
365                         Volume = tvp->mtpoint.Fid.Volume;
366                         Vnode  = tvp->mtpoint.Fid.Vnode;
367                         afs_PutVolume(tvp, READ_LOCK);
368                     }
369                 } else {
370                     /* Parent directory is not a volume root */
371                     Volume = vc->mvid.parent->Fid.Volume;
372                     Vnode  = vc->mvid.parent->Fid.Vnode;
373                 }
374             }
375         } else if (de->fid.vnode == 1 && de->fid.vunique == 1) {
376             /* XXX The above test is evil and probably breaks DFS */
377             /* Parent directory is a volume root; use the right inum */
378             tvp = afs_GetVolume(&vc->f.fid, 0, READ_LOCK);
379             if (tvp) {
380                 if (tvp->cell == afs_rootFid.Cell
381                     && tvp->volume == afs_rootFid.Fid.Volume) {
382                     /* Parent directory is the root of the AFS root */
383                     Volume = 0;
384                     Vnode  = 2;
385                 } else {
386                     /* Parent directory is the target of a mount point */
387                     Volume = tvp->mtpoint.Fid.Volume;
388                     Vnode  = tvp->mtpoint.Fid.Vnode;
389                 }
390                 afs_PutVolume(tvp, READ_LOCK);
391             }
392         }
393     }
394 #endif
395
396 #ifdef  AFS_SGI_ENV
397     {
398         afs_int32 use64BitDirent;
399
400         use64BitDirent =
401             ABI_IS(ABI_IRIX5_64, GETDENTS_ABI(OSI_GET_CURRENT_ABI(), auio));
402
403         if (use64BitDirent) {
404             struct min_dirent sdirEntry;
405             sdirEntry.d_fileno = afs_calc_inum(vc->f.fid.Cell,
406                                                Volume, ntohl(Vnode));
407             sdirEntry.d_reclen = rlen;
408             sdirEntry.d_off = (off_t) off;
409             AFS_UIOMOVE(&sdirEntry, AFS_DIRENT64BASESIZE, UIO_READ, auio,
410                         code);
411             if (code == 0)
412                 AFS_UIOMOVE(de->name, slen - 1, UIO_READ, auio, code);
413             if (code == 0)
414                 AFS_UIOMOVE(bufofzeros,
415                             DIRENTSIZE(slen) - (AFS_DIRENT64BASESIZE + slen -
416                                                 1), UIO_READ, auio, code);
417             if (DIRENTSIZE(slen) < rlen) {
418                 while (DIRENTSIZE(slen) < rlen) {
419                     int minLen = rlen - DIRENTSIZE(slen);
420                     if (minLen > sizeof(bufofzeros))
421                         minLen = sizeof(bufofzeros);
422                     AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code);
423                     rlen -= minLen;
424                 }
425             }
426         } else {
427             struct irix5_min_dirent sdirEntry;
428             sdirEntry.d_fileno = afs_calc_inum(vc->f.fid.Cell,
429                                                Volume, ntohl(Vnode));
430             sdirEntry.d_reclen = rlen;
431             sdirEntry.d_off = (afs_int32) off;
432             AFS_UIOMOVE(&sdirEntry, AFS_DIRENT32BASESIZE, UIO_READ, auio,
433                         code);
434             if (code == 0)
435                 AFS_UIOMOVE(de->name, slen - 1, UIO_READ, auio, code);
436             if (code == 0)
437                 AFS_UIOMOVE(bufofzeros,
438                             IRIX5_DIRENTSIZE(slen) - (AFS_DIRENT32BASESIZE +
439                                                       slen - 1), UIO_READ,
440                             auio, code);
441             if (IRIX5_DIRENTSIZE(slen) < rlen) {
442                 while (IRIX5_DIRENTSIZE(slen) < rlen) {
443                     int minLen = rlen - IRIX5_DIRENTSIZE(slen);
444                     if (minLen > sizeof(bufofzeros))
445                         minLen = sizeof(bufofzeros);
446                     AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code);
447                     rlen -= minLen;
448                 }
449             }
450         }
451     }
452 #else /* AFS_SGI_ENV */
453 #if  defined(AFS_SUN5_ENV) || (defined(AFS_AIX51_ENV) && defined(AFS_64BIT_KERNEL))
454     direntp = osi_AllocLargeSpace(AFS_LRALLOCSIZ);
455     direntp->d_ino = afs_calc_inum(vc->f.fid.Cell, Volume, ntohl(Vnode));
456 #if defined(AFS_AIX51_ENV) && defined(AFS_64BIT_KERNEL)
457     direntp->d_offset = off;
458     direntp->d_namlen = slen;
459 #else
460     direntp->d_off = off;
461 #endif
462     direntp->d_reclen = rlen;
463     strcpy(direntp->d_name, de->name);
464     AFS_UIOMOVE((caddr_t) direntp, rlen, UIO_READ, auio, code);
465     osi_FreeLargeSpace((char *)direntp);
466 #else /* AFS_SUN5_ENV */
467     /* Note the odd mechanism for building the inode number */
468     sdirEntry.d_fileno = afs_calc_inum(vc->f.fid.Cell, Volume, ntohl(Vnode));
469     sdirEntry.d_reclen = rlen;
470 #if !defined(AFS_SGI_ENV)
471     sdirEntry.d_namlen = slen;
472 #endif
473 #if defined(AFS_AIX32_ENV) || defined(AFS_SGI_ENV)
474     sdirEntry.d_off = off;
475 #endif
476 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
477     sdirEntry.d_type = afs_readdir_type(vc, de);
478 #endif
479
480 #if defined(AFS_SGI_ENV)
481     AFS_UIOMOVE(&sdirEntry, DIRENTBASESIZE, UIO_READ, auio, code);
482     if (code == 0)
483         AFS_UIOMOVE(de->name, slen - 1, UIO_READ, auio, code);
484     if (code == 0)
485         AFS_UIOMOVE(bufofzeros,
486                     DIRSIZ_LEN(slen) - (DIRENTBASESIZE + slen - 1), UIO_READ,
487                     auio, code);
488 #else /* AFS_SGI_ENV */
489     AFS_MOVE_UNLOCK();
490 #if defined(AFS_NBSD40_ENV)
491     {
492         struct dirent *dp;
493         dp = osi_AllocLargeSpace(sizeof(struct dirent));
494         memset(dp, 0, sizeof(struct dirent));
495         dp->d_ino = afs_calc_inum(vc->f.fid.Cell, Volume, ntohl(Vnode));
496         dp->d_namlen = slen;
497         dp->d_type = afs_readdir_type(vc, de);
498         strcpy(dp->d_name, de->name);
499         dp->d_reclen = _DIRENT_SIZE(dp) /* rlen */;
500         if ((afs_debug & AFSDEB_VNLAYER) != 0) {
501             afs_warn("%s: %s type %d slen %d rlen %d act. rlen %zu\n", __func__,
502                 dp->d_name, dp->d_type, slen, rlen, _DIRENT_SIZE(dp));
503         }
504         AFS_UIOMOVE(dp, dp->d_reclen, UIO_READ, auio, code);
505         osi_FreeLargeSpace((char *)dp);
506     }
507 #else
508     AFS_UIOMOVE((char *) &sdirEntry, sizeof(sdirEntry), UIO_READ, auio, code);
509     if (code == 0) {
510         AFS_UIOMOVE(de->name, slen, UIO_READ, auio, code);
511     }
512     /* pad out the remaining characters with zeros */
513     if (code == 0) {
514         AFS_UIOMOVE(bufofzeros, ((slen + 1 + DIRPAD) & ~DIRPAD) - slen,
515                     UIO_READ, auio, code);
516     }
517 #endif
518     AFS_MOVE_LOCK();
519 #endif /* AFS_SGI_ENV */
520 #if !defined(AFS_NBSD_ENV)
521     /* pad out the difference between rlen and slen... */
522     if (DIRSIZ_LEN(slen) < rlen) {
523         AFS_MOVE_UNLOCK();
524         while (DIRSIZ_LEN(slen) < rlen) {
525             int minLen = rlen - DIRSIZ_LEN(slen);
526             if (minLen > sizeof(bufofzeros))
527                 minLen = sizeof(bufofzeros);
528             AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code);
529             rlen -= minLen;
530         }
531         AFS_MOVE_LOCK();
532     }
533 #endif
534 #endif /* AFS_SUN5_ENV */
535 #endif /* AFS_SGI_ENV */
536     return (code);
537 }
538
539
540 /*
541  *------------------------------------------------------------------------------
542  *
543  * Read directory entries.
544  * There are some weird things to look out for here.  The uio_offset
545  * field is either 0 or it is the offset returned from a previous
546  * readdir.  It is an opaque value used by the server to find the
547  * correct directory block to read.  The byte count must be at least
548  * vtoblksz(vp) bytes.  The count field is the number of blocks to
549  * read on the server.  This is advisory only, the server may return
550  * only one block's worth of entries.  Entries may be compressed on
551  * the server.
552  *
553  * This routine encodes knowledge of Vice dirs.
554  */
555
556 void
557 afs_bulkstat_send(struct vcache *avc, struct vrequest *req)
558 {
559     afs_rd_stash_i = 0;
560 }
561
562 /*
563  * Here is the bad, bad, really bad news.
564  * It has to do with 'offset' (seek locations).
565 */
566
567 int
568 #if     defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
569 afs_readdir(OSI_VC_DECL(avc), struct uio *auio, afs_ucred_t *acred, 
570             int *eofp)
571 #else
572 #if defined(AFS_HPUX100_ENV)
573 afs_readdir2(OSI_VC_DECL(avc), struct uio *auio, afs_ucred_t *acred)
574 #else
575 afs_readdir(OSI_VC_DECL(avc), struct uio *auio, afs_ucred_t *acred)
576 #endif
577 #endif
578 {
579     struct vrequest *treq = NULL;
580     struct dcache *tdc;
581     afs_size_t origOffset, tlen;
582     afs_int32 len;
583     int code = 0;
584     struct DirBuffer oldEntry, nextEntry;
585     struct DirEntry *ode = 0, *nde = 0;
586     int o_slen = 0, n_slen = 0;
587     afs_int32 us;
588     struct afs_fakestat_state fakestate;
589 #if defined(AFS_SGI_ENV)
590     afs_int32 use64BitDirent, dirsiz;
591 #endif /* defined(AFS_SGI_ENV) */
592 #ifndef AFS_HPUX_ENV
593     OSI_VC_CONVERT(avc);
594 #else
595     /*
596      * XXX All the hacks for alloced sdirEntry and inlining of afs_readdir_move instead of calling
597      * it is necessary for hpux due to stack problems that seem to occur when coming thru the nfs
598      * translator side XXX
599      */
600     struct min_direct *sdirEntry = osi_AllocSmallSpace(sizeof(struct min_direct));
601     afs_int32 rlen;
602 #endif
603
604     /* opaque value is pointer into a vice dir; use bit map to decide
605      * if the entries are in use.  Always assumed to be valid.  0 is
606      * special, means start of a new dir.  Int32 inode, followed by
607      * short reclen and short namelen.  Namelen does not include
608      * the null byte.  Followed by null-terminated string.
609      */
610     AFS_STATCNT(afs_readdir);
611
612     memset(&oldEntry, 0, sizeof(struct DirBuffer));
613     memset(&nextEntry, 0, sizeof(struct DirBuffer));
614
615 #if defined(AFS_SGI_ENV)
616     use64BitDirent =
617         ABI_IS(ABI_IRIX5_64, GETDENTS_ABI(OSI_GET_CURRENT_ABI(), auio));
618 #endif /* defined(AFS_SGI_ENV) */
619
620 #if     defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
621     /* Not really used by the callee so we ignore it for now */
622     if (eofp)
623         *eofp = 0;
624 #endif
625 #ifndef AFS_64BIT_CLIENT
626     if (AfsLargeFileUio(auio)   /* file is large than 2 GB */
627         ||AfsLargeFileSize(AFS_UIO_OFFSET(auio), AFS_UIO_RESID(auio)))
628         return EFBIG;
629 #endif
630
631     if ((code = afs_CreateReq(&treq, acred))) {
632 #ifdef  AFS_HPUX_ENV
633         osi_FreeSmallSpace((char *)sdirEntry);
634 #endif
635         return code;
636     }
637     /* update the cache entry */
638     afs_InitFakeStat(&fakestate);
639
640     AFS_DISCON_LOCK();
641
642     code = afs_EvalFakeStat(&avc, &fakestate, treq);
643     if (code)
644         goto done;
645   tagain:
646     code = afs_VerifyVCache(avc, treq);
647     if (code)
648         goto done;
649     /* get a reference to the entire directory */
650     tdc = afs_GetDCache(avc, (afs_size_t) 0, treq, &origOffset, &tlen, 1);
651     if (!tdc) {
652         code = EIO;
653         goto done;
654     }
655     ObtainReadLock(&avc->lock);
656     ObtainReadLock(&tdc->lock);
657
658     /*
659      * Make sure that the data in the cache is current. There are two
660      * cases we need to worry about:
661      * 1. The cache data is being fetched by another process.
662      * 2. The cache data is no longer valid
663      */
664     while ((avc->f.states & CStatd)
665            && (tdc->dflags & DFFetching)
666            && afs_IsDCacheFresh(tdc, avc)) {
667         afs_Trace4(afs_iclSetp, CM_TRACE_DCACHEWAIT, ICL_TYPE_STRING,
668                    __FILE__, ICL_TYPE_INT32, __LINE__, ICL_TYPE_POINTER, tdc,
669                    ICL_TYPE_INT32, tdc->dflags);
670         ReleaseReadLock(&tdc->lock);
671         ReleaseReadLock(&avc->lock);
672         afs_osi_Sleep(&tdc->validPos);
673         ObtainReadLock(&avc->lock);
674         ObtainReadLock(&tdc->lock);
675     }
676     if (!(avc->f.states & CStatd)
677         || !afs_IsDCacheFresh(tdc, avc)) {
678         ReleaseReadLock(&tdc->lock);
679         ReleaseReadLock(&avc->lock);
680         afs_PutDCache(tdc);
681         goto tagain;
682     }
683
684     /*
685      *  iterator for the directory reads.  Takes the AFS DirEntry
686      *  structure and slams them into UFS direct structures.
687      *  uses afs_readdir_move to get the struct to the user space.
688      *
689      *  The routine works by looking ahead one AFS directory entry.
690      *  That's because the AFS entry we are currenly working with
691      *  may not fit into the buffer the user has provided.  If it
692      *  doesn't we have to change the size of the LAST AFS directory
693      *  entry, so that it will FIT perfectly into the block the
694      *  user has provided.
695      *  
696      *  The 'forward looking' of the code makes it a bit tough to read.
697      *  Remember we need to get an entry, see if it it fits, then
698      *  set it up as the LAST entry, and find the next one.
699      *
700      *  Tough to take: We give out an EINVAL if we don't have enough
701      *  space in the buffer, and at the same time, don't have an entry
702      *  to put into the buffer. This CAN happen if the first AFS entry
703      *  we get can't fit into the 512 character buffer provided.  Seems
704      *  it ought not happen... 
705      *
706      *  Assumption: don't need to use anything but one dc entry:
707      *  this means the directory ought not be greater than 64k.
708      */
709     len = 0;
710 #ifdef AFS_HPUX_ENV
711     auio->uio_fpflags = 0;
712 #endif
713     while (code == 0) {
714         origOffset = AFS_UIO_OFFSET(auio);
715         /* scan for the next interesting entry scan for in-use blob otherwise up point at
716          * this blob note that ode, if non-zero, also represents a held dir page */
717         code = BlobScan(tdc, (origOffset >> 5), &us);
718
719         if (code == 0 && us)
720            code = afs_dir_GetVerifiedBlob(tdc, us, &nextEntry);
721
722         if (us == 0 || code != 0) {
723             code = 0; /* Reset code - keep old failure behaviour */
724             /* failed to setup nde, return what we've got, and release ode */
725             if (len) {
726                 /* something to hand over. */
727 #ifdef  AFS_HPUX_ENV
728                 sdirEntry->d_fileno = afs_calc_inum(avc->f.fid.Cell,
729                                                     avc->f.fid.Fid.Volume,
730                                                     ntohl(ode->fid.vnode));
731                 sdirEntry->d_reclen = rlen = AFS_UIO_RESID(auio);
732                 sdirEntry->d_namlen = o_slen;
733 #if defined(AFS_SUN5_ENV) || defined(AFS_AIX32_ENV) || defined(AFS_HPUX100_ENV)
734                 sdirEntry->d_off = origOffset;
735 #endif
736                 AFS_UIOMOVE((char *)sdirEntry, sizeof(*sdirEntry), UIO_READ,
737                             auio, code);
738                 if (code == 0)
739                     AFS_UIOMOVE(ode->name, o_slen, UIO_READ, auio, code);
740                 /* pad out the remaining characters with zeros */
741                 if (code == 0) {
742                     AFS_UIOMOVE(bufofzeros,
743                                 ((o_slen + 1 + DIRPAD) & ~DIRPAD) - o_slen,
744                                 UIO_READ, auio, code);
745                 }
746                 /* pad out the difference between rlen and slen... */
747                 if (DIRSIZ_LEN(o_slen) < rlen) {
748                     while (DIRSIZ_LEN(o_slen) < rlen) {
749                         int minLen = rlen - DIRSIZ_LEN(o_slen);
750                         if (minLen > sizeof(bufofzeros))
751                             minLen = sizeof(bufofzeros);
752                         AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code);
753                         rlen -= minLen;
754                     }
755                 }
756 #else
757                 code = afs_readdir_move(ode, avc, auio, o_slen,
758 #if defined(AFS_SUN5_ENV) || defined(AFS_NBSD_ENV)
759                                         len, origOffset);
760 #else
761                                         AFS_UIO_RESID(auio), origOffset);
762 #endif
763 #endif /* AFS_HPUX_ENV */
764 #if !defined(AFS_SUN5_ENV) && !defined(AFS_NBSD_ENV)
765                 AFS_UIO_SETRESID(auio, 0);
766 #endif
767             } else {
768                 /* nothin to hand over */
769             }
770 #if     defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
771             if (eofp)
772                 *eofp = 1;      /* Set it properly */
773 #endif
774             DRelease(&oldEntry, 0);
775             goto dirend;
776         }
777         nde = (struct DirEntry *)nextEntry.data;
778         
779         /* Do we have enough user space to carry out our mission? */
780 #if defined(AFS_SGI_ENV)
781         n_slen = strlen(nde->name) + 1; /* NULL terminate */
782 #else
783         n_slen = strlen(nde->name);
784 #endif
785 #ifdef  AFS_SGI_ENV
786         dirsiz =
787             use64BitDirent ? DIRENTSIZE(n_slen) : IRIX5_DIRENTSIZE(n_slen);
788         if (dirsiz >= (AFS_UIO_RESID(auio) - len)) {
789 #else
790         if (DIRSIZ_LEN(n_slen) >= (AFS_UIO_RESID(auio) - len)) {
791 #endif /* AFS_SGI_ENV */
792             /* No can do no more now; ya know... at this time */
793             DRelease(&nextEntry, 0);    /* can't use this one. */
794             if (len) {
795 #ifdef  AFS_HPUX_ENV
796                 sdirEntry->d_fileno = afs_calc_inum(avc->f.fid.Cell,
797                                                     avc->f.fid.Fid.Volume,
798                                                     ntohl(ode->fid.vnode));
799                 sdirEntry->d_reclen = rlen = AFS_UIO_RESID(auio);
800                 sdirEntry->d_namlen = o_slen;
801 #if defined(AFS_SUN5_ENV) || defined(AFS_AIX32_ENV) || defined(AFS_HPUX100_ENV)
802                 sdirEntry->d_off = origOffset;
803 #endif
804                 AFS_UIOMOVE((char *)sdirEntry, sizeof(*sdirEntry), UIO_READ,
805                             auio, code);
806                 if (code == 0)
807                     AFS_UIOMOVE(ode->name, o_slen, UIO_READ, auio, code);
808                 /* pad out the remaining characters with zeros */
809                 if (code == 0) {
810                     AFS_UIOMOVE(bufofzeros,
811                                 ((o_slen + 1 + DIRPAD) & ~DIRPAD) - o_slen,
812                                 UIO_READ, auio, code);
813                 }
814                 /* pad out the difference between rlen and slen... */
815                 if (DIRSIZ_LEN(o_slen) < rlen) {
816                     while (DIRSIZ_LEN(o_slen) < rlen) {
817                         int minLen = rlen - DIRSIZ_LEN(o_slen);
818                         if (minLen > sizeof(bufofzeros))
819                             minLen = sizeof(bufofzeros);
820                         AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code);
821                         rlen -= minLen;
822                     }
823                 }
824 #else /* AFS_HPUX_ENV */
825                 code =
826                     afs_readdir_move(ode, avc, auio, o_slen,
827                                      AFS_UIO_RESID(auio), origOffset);
828 #endif /* AFS_HPUX_ENV */
829                 /* this next line used to be AFSVFS40 or AIX 3.1, but is
830                  * really generic */
831                 AFS_UIO_SETOFFSET(auio, origOffset);
832 #if !defined(AFS_NBSD_ENV)
833                 AFS_UIO_SETRESID(auio, 0);
834 #endif
835             } else {            /* trouble, can't give anything to the user! */
836                 /* even though he has given us a buffer, 
837                  * even though we have something to give us,
838                  * Looks like we lost something somewhere.
839                  */
840                 code = EINVAL;
841             }
842             DRelease(&oldEntry, 0);
843             goto dirend;
844         }
845
846         /*
847          * In any event, we move out the LAST de entry, getting ready
848          * to set up for the next one.
849          */
850         if (len) {
851 #ifdef  AFS_HPUX_ENV
852             sdirEntry->d_fileno = afs_calc_inum(avc->f.fid.Cell,
853                                                 avc->f.fid.Fid.Volume,
854                                                 ntohl(ode->fid.vnode));
855             sdirEntry->d_reclen = rlen = len;
856             sdirEntry->d_namlen = o_slen;
857 #if defined(AFS_SUN5_ENV) || defined(AFS_AIX32_ENV) || defined(AFS_HPUX100_ENV)
858             sdirEntry->d_off = origOffset;
859 #endif
860             AFS_UIOMOVE((char *)sdirEntry, sizeof(*sdirEntry), UIO_READ, auio,
861                         code);
862             if (code == 0)
863                 AFS_UIOMOVE(ode->name, o_slen, UIO_READ, auio, code);
864             /* pad out the remaining characters with zeros */
865             if (code == 0) {
866                 AFS_UIOMOVE(bufofzeros,
867                             ((o_slen + 1 + DIRPAD) & ~DIRPAD) - o_slen,
868                             UIO_READ, auio, code);
869             }
870             /* pad out the difference between rlen and slen... */
871             if (DIRSIZ_LEN(o_slen) < rlen) {
872                 while (DIRSIZ_LEN(o_slen) < rlen) {
873                     int minLen = rlen - DIRSIZ_LEN(o_slen);
874                     if (minLen > sizeof(bufofzeros))
875                         minLen = sizeof(bufofzeros);
876                     AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code);
877                     rlen -= minLen;
878                 }
879             }
880 #else /* AFS_HPUX_ENV */
881             code = afs_readdir_move(ode, avc, auio, o_slen, len, origOffset);
882 #endif /* AFS_HPUX_ENV */
883         }
884 #ifdef  AFS_SGI_ENV
885         len = use64BitDirent ? DIRENTSIZE(o_slen =
886                                           n_slen) : IRIX5_DIRENTSIZE(o_slen =
887                                                                      n_slen);
888 #else
889         len = DIRSIZ_LEN(o_slen = n_slen);
890 #endif /* AFS_SGI_ENV */
891         
892         DRelease(&oldEntry, 0);
893         oldEntry = nextEntry;
894         ode = nde;
895         AFS_UIO_SETOFFSET(auio, (us + afs_dir_NameBlobs(nde->name)) << 5);
896     }
897     
898     DRelease(&oldEntry, 0);
899
900   dirend:
901     ReleaseReadLock(&tdc->lock);
902     afs_PutDCache(tdc);
903     ReleaseReadLock(&avc->lock);
904
905   done:
906 #ifdef  AFS_HPUX_ENV
907     osi_FreeSmallSpace((char *)sdirEntry);
908 #endif
909     AFS_DISCON_UNLOCK();
910     afs_PutFakeStat(&fakestate);
911     code = afs_CheckCode(code, treq, 28);
912     afs_DestroyReq(treq);
913     return code;
914 }
915
916 #endif /* !AFS_LINUX_ENV */