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