afs: Use named constants for mvstat
[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 != AFS_MVSTAT_FILE) {
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==AFS_MVSTAT_FILE, 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 == AFS_MVSTAT_ROOT) {
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 == AFS_MVSTAT_ROOT) {
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     direntp = osi_AllocLargeSpace(AFS_LRALLOCSIZ);
459     direntp->d_ino = afs_calc_inum(vc->f.fid.Cell, Volume, ntohl(Vnode));
460 #if defined(AFS_AIX51_ENV) && defined(AFS_64BIT_KERNEL)
461     direntp->d_offset = off;
462     direntp->d_namlen = slen;
463 #else
464     direntp->d_off = off;
465 #endif
466     direntp->d_reclen = rlen;
467     strcpy(direntp->d_name, de->name);
468     AFS_UIOMOVE((caddr_t) direntp, rlen, UIO_READ, auio, code);
469     osi_FreeLargeSpace((char *)direntp);
470 #else /* AFS_SUN5_ENV */
471     /* Note the odd mechanism for building the inode number */
472     sdirEntry.d_fileno = afs_calc_inum(vc->f.fid.Cell, Volume, ntohl(Vnode));
473     sdirEntry.d_reclen = rlen;
474 #if !defined(AFS_SGI_ENV)
475     sdirEntry.d_namlen = slen;
476 #endif
477 #if defined(AFS_AIX32_ENV) || defined(AFS_SGI_ENV)
478     sdirEntry.d_off = off;
479 #endif
480 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
481     sdirEntry.d_type = afs_readdir_type(vc, de);
482 #endif
483
484 #if defined(AFS_SGI_ENV)
485     AFS_UIOMOVE(&sdirEntry, DIRENTBASESIZE, UIO_READ, auio, code);
486     if (code == 0)
487         AFS_UIOMOVE(de->name, slen - 1, UIO_READ, auio, code);
488     if (code == 0)
489         AFS_UIOMOVE(bufofzeros,
490                     DIRSIZ_LEN(slen) - (DIRENTBASESIZE + slen - 1), UIO_READ,
491                     auio, code);
492 #else /* AFS_SGI_ENV */
493     AFS_MOVE_UNLOCK();
494 #if defined(AFS_NBSD40_ENV)
495     {
496         struct dirent *dp;
497         dp = osi_AllocLargeSpace(sizeof(struct dirent));
498         memset(dp, 0, sizeof(struct dirent));
499         dp->d_ino = afs_calc_inum(vc->f.fid.Cell, Volume, ntohl(Vnode));
500         dp->d_namlen = slen;
501         dp->d_type = afs_readdir_type(vc, de);
502         strcpy(dp->d_name, de->name);
503         dp->d_reclen = _DIRENT_SIZE(dp) /* rlen */;
504         if ((afs_debug & AFSDEB_VNLAYER) != 0) {
505             afs_warn("%s: %s type %d slen %d rlen %d act. rlen %zu\n", __func__,
506                 dp->d_name, dp->d_type, slen, rlen, _DIRENT_SIZE(dp));
507         }
508         AFS_UIOMOVE(dp, dp->d_reclen, UIO_READ, auio, code);
509         osi_FreeLargeSpace((char *)dp);
510     }
511 #else
512     AFS_UIOMOVE((char *) &sdirEntry, sizeof(sdirEntry), UIO_READ, auio, code);
513     if (code == 0) {
514         AFS_UIOMOVE(de->name, slen, UIO_READ, auio, code);
515     }
516     /* pad out the remaining characters with zeros */
517     if (code == 0) {
518         AFS_UIOMOVE(bufofzeros, ((slen + 1 + DIRPAD) & ~DIRPAD) - slen,
519                     UIO_READ, auio, code);
520     }
521 #endif
522     AFS_MOVE_LOCK();
523 #endif /* AFS_SGI_ENV */
524 #if !defined(AFS_NBSD_ENV)
525     /* pad out the difference between rlen and slen... */
526     if (DIRSIZ_LEN(slen) < rlen) {
527         AFS_MOVE_UNLOCK();
528         while (DIRSIZ_LEN(slen) < rlen) {
529             int minLen = rlen - DIRSIZ_LEN(slen);
530             if (minLen > sizeof(bufofzeros))
531                 minLen = sizeof(bufofzeros);
532             AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code);
533             rlen -= minLen;
534         }
535         AFS_MOVE_LOCK();
536     }
537 #endif
538 #endif /* AFS_SUN5_ENV */
539 #endif /* AFS_SGI53_ENV */
540     return (code);
541 }
542
543
544 /*
545  *------------------------------------------------------------------------------
546  *
547  * Read directory entries.
548  * There are some weird things to look out for here.  The uio_offset
549  * field is either 0 or it is the offset returned from a previous
550  * readdir.  It is an opaque value used by the server to find the
551  * correct directory block to read.  The byte count must be at least
552  * vtoblksz(vp) bytes.  The count field is the number of blocks to
553  * read on the server.  This is advisory only, the server may return
554  * only one block's worth of entries.  Entries may be compressed on
555  * the server.
556  *
557  * This routine encodes knowledge of Vice dirs.
558  */
559
560 void
561 afs_bulkstat_send(struct vcache *avc, struct vrequest *req)
562 {
563     afs_rd_stash_i = 0;
564 }
565
566 /*
567  * Here is the bad, bad, really bad news.
568  * It has to do with 'offset' (seek locations).
569 */
570
571 int
572 #if     defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
573 afs_readdir(OSI_VC_DECL(avc), struct uio *auio, afs_ucred_t *acred, 
574             int *eofp)
575 #else
576 #if defined(AFS_HPUX100_ENV)
577 afs_readdir2(OSI_VC_DECL(avc), struct uio *auio, afs_ucred_t *acred)
578 #else
579 afs_readdir(OSI_VC_DECL(avc), struct uio *auio, afs_ucred_t *acred)
580 #endif
581 #endif
582 {
583     struct vrequest *treq = NULL;
584     struct dcache *tdc;
585     afs_size_t origOffset, tlen;
586     afs_int32 len;
587     int code = 0;
588     struct DirBuffer oldEntry, nextEntry;
589     struct DirEntry *ode = 0, *nde = 0;
590     int o_slen = 0, n_slen = 0;
591     afs_uint32 us;
592     struct afs_fakestat_state fakestate;
593 #if defined(AFS_SGI53_ENV)
594     afs_int32 use64BitDirent, dirsiz;
595 #endif /* defined(AFS_SGI53_ENV) */
596 #ifndef AFS_HPUX_ENV
597     OSI_VC_CONVERT(avc);
598 #else
599     /*
600      * XXX All the hacks for alloced sdirEntry and inlining of afs_readdir_move instead of calling
601      * it is necessary for hpux due to stack problems that seem to occur when coming thru the nfs
602      * translator side XXX
603      */
604     struct min_direct *sdirEntry = osi_AllocSmallSpace(sizeof(struct min_direct));
605     afs_int32 rlen;
606 #endif
607
608     /* opaque value is pointer into a vice dir; use bit map to decide
609      * if the entries are in use.  Always assumed to be valid.  0 is
610      * special, means start of a new dir.  Int32 inode, followed by
611      * short reclen and short namelen.  Namelen does not include
612      * the null byte.  Followed by null-terminated string.
613      */
614     AFS_STATCNT(afs_readdir);
615
616     memset(&oldEntry, 0, sizeof(struct DirBuffer));
617     memset(&nextEntry, 0, sizeof(struct DirBuffer));
618
619 #if defined(AFS_SGI53_ENV)
620 #ifdef AFS_SGI61_ENV
621 #ifdef AFS_SGI62_ENV
622     use64BitDirent =
623         ABI_IS(ABI_IRIX5_64, GETDENTS_ABI(OSI_GET_CURRENT_ABI(), auio));
624 #else
625     use64BitDirent =
626         (auio->uio_segflg !=
627          UIO_USERSPACE) ? ABI_IRIX5_64 : (ABI_IS(ABI_IRIX5_64 | ABI_IRIX5_N32,
628                                                  u.u_procp->p_abi));
629 #endif /* AFS_SGI62_ENV */
630 #else /* AFS_SGI61_ENV */
631     use64BitDirent =
632         (auio->uio_segflg !=
633          UIO_USERSPACE) ? ABI_IRIX5_64 : (ABI_IS(ABI_IRIX5_64,
634                                                  u.u_procp->p_abi));
635 #endif /* AFS_SGI61_ENV */
636 #endif /* defined(AFS_SGI53_ENV) */
637
638 #if     defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
639     /* Not really used by the callee so we ignore it for now */
640     if (eofp)
641         *eofp = 0;
642 #endif
643 #ifndef AFS_64BIT_CLIENT
644     if (AfsLargeFileUio(auio)   /* file is large than 2 GB */
645         ||AfsLargeFileSize(AFS_UIO_OFFSET(auio), AFS_UIO_RESID(auio)))
646         return EFBIG;
647 #endif
648
649     if ((code = afs_CreateReq(&treq, acred))) {
650 #ifdef  AFS_HPUX_ENV
651         osi_FreeSmallSpace((char *)sdirEntry);
652 #endif
653         return code;
654     }
655     /* update the cache entry */
656     afs_InitFakeStat(&fakestate);
657
658     AFS_DISCON_LOCK();
659
660     code = afs_EvalFakeStat(&avc, &fakestate, treq);
661     if (code)
662         goto done;
663   tagain:
664     code = afs_VerifyVCache(avc, treq);
665     if (code)
666         goto done;
667     /* get a reference to the entire directory */
668     tdc = afs_GetDCache(avc, (afs_size_t) 0, treq, &origOffset, &tlen, 1);
669     if (!tdc) {
670         code = ENOENT;
671         goto done;
672     }
673     ObtainReadLock(&avc->lock);
674     ObtainReadLock(&tdc->lock);
675
676     /*
677      * Make sure that the data in the cache is current. There are two
678      * cases we need to worry about:
679      * 1. The cache data is being fetched by another process.
680      * 2. The cache data is no longer valid
681      */
682     while ((avc->f.states & CStatd)
683            && (tdc->dflags & DFFetching)
684            && hsame(avc->f.m.DataVersion, tdc->f.versionNo)) {
685         afs_Trace4(afs_iclSetp, CM_TRACE_DCACHEWAIT, ICL_TYPE_STRING,
686                    __FILE__, ICL_TYPE_INT32, __LINE__, ICL_TYPE_POINTER, tdc,
687                    ICL_TYPE_INT32, tdc->dflags);
688         ReleaseReadLock(&tdc->lock);
689         ReleaseReadLock(&avc->lock);
690         afs_osi_Sleep(&tdc->validPos);
691         ObtainReadLock(&avc->lock);
692         ObtainReadLock(&tdc->lock);
693     }
694     if (!(avc->f.states & CStatd)
695         || !hsame(avc->f.m.DataVersion, tdc->f.versionNo)) {
696         ReleaseReadLock(&tdc->lock);
697         ReleaseReadLock(&avc->lock);
698         afs_PutDCache(tdc);
699         goto tagain;
700     }
701
702     /*
703      *  iterator for the directory reads.  Takes the AFS DirEntry
704      *  structure and slams them into UFS direct structures.
705      *  uses afs_readdir_move to get the struct to the user space.
706      *
707      *  The routine works by looking ahead one AFS directory entry.
708      *  That's because the AFS entry we are currenly working with
709      *  may not fit into the buffer the user has provided.  If it
710      *  doesn't we have to change the size of the LAST AFS directory
711      *  entry, so that it will FIT perfectly into the block the
712      *  user has provided.
713      *  
714      *  The 'forward looking' of the code makes it a bit tough to read.
715      *  Remember we need to get an entry, see if it it fits, then
716      *  set it up as the LAST entry, and find the next one.
717      *
718      *  Tough to take: We give out an EINVAL if we don't have enough
719      *  space in the buffer, and at the same time, don't have an entry
720      *  to put into the buffer. This CAN happen if the first AFS entry
721      *  we get can't fit into the 512 character buffer provided.  Seems
722      *  it ought not happen... 
723      *
724      *  Assumption: don't need to use anything but one dc entry:
725      *  this means the directory ought not be greater than 64k.
726      */
727     len = 0;
728 #ifdef AFS_HPUX_ENV
729     auio->uio_fpflags = 0;
730 #endif
731     while (code == 0) {
732         origOffset = AFS_UIO_OFFSET(auio);
733         /* scan for the next interesting entry scan for in-use blob otherwise up point at
734          * this blob note that ode, if non-zero, also represents a held dir page */
735         us = BlobScan(tdc, (origOffset >> 5));
736
737         if (us)
738            code = afs_dir_GetVerifiedBlob(tdc, us, &nextEntry);
739
740         if (us == 0 || code != 0) {
741             code = 0; /* Reset code - keep old failure behaviour */
742             /* failed to setup nde, return what we've got, and release ode */
743             if (len) {
744                 /* something to hand over. */
745 #ifdef  AFS_HPUX_ENV
746                 sdirEntry->d_fileno = afs_calc_inum(avc->f.fid.Cell,
747                                                     avc->f.fid.Fid.Volume,
748                                                     ntohl(ode->fid.vnode));
749                 sdirEntry->d_reclen = rlen = AFS_UIO_RESID(auio);
750                 sdirEntry->d_namlen = o_slen;
751 #if defined(AFS_SUN5_ENV) || defined(AFS_AIX32_ENV) || defined(AFS_HPUX100_ENV)
752                 sdirEntry->d_off = origOffset;
753 #endif
754                 AFS_UIOMOVE((char *)sdirEntry, sizeof(*sdirEntry), UIO_READ,
755                             auio, code);
756                 if (code == 0)
757                     AFS_UIOMOVE(ode->name, o_slen, UIO_READ, auio, code);
758                 /* pad out the remaining characters with zeros */
759                 if (code == 0) {
760                     AFS_UIOMOVE(bufofzeros,
761                                 ((o_slen + 1 + DIRPAD) & ~DIRPAD) - o_slen,
762                                 UIO_READ, auio, code);
763                 }
764                 /* pad out the difference between rlen and slen... */
765                 if (DIRSIZ_LEN(o_slen) < rlen) {
766                     while (DIRSIZ_LEN(o_slen) < rlen) {
767                         int minLen = rlen - DIRSIZ_LEN(o_slen);
768                         if (minLen > sizeof(bufofzeros))
769                             minLen = sizeof(bufofzeros);
770                         AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code);
771                         rlen -= minLen;
772                     }
773                 }
774 #else
775                 code = afs_readdir_move(ode, avc, auio, o_slen,
776 #if defined(AFS_SUN5_ENV) || defined(AFS_NBSD_ENV)
777                                         len, origOffset);
778 #else
779                                         AFS_UIO_RESID(auio), origOffset);
780 #endif
781 #endif /* AFS_HPUX_ENV */
782 #if !defined(AFS_SUN5_ENV) && !defined(AFS_NBSD_ENV)
783                 AFS_UIO_SETRESID(auio, 0);
784 #endif
785             } else {
786                 /* nothin to hand over */
787             }
788 #if     defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
789             if (eofp)
790                 *eofp = 1;      /* Set it properly */
791 #endif
792             DRelease(&oldEntry, 0);
793             goto dirend;
794         }
795         nde = (struct DirEntry *)nextEntry.data;
796         
797         /* Do we have enough user space to carry out our mission? */
798 #if defined(AFS_SGI_ENV)
799         n_slen = strlen(nde->name) + 1; /* NULL terminate */
800 #else
801         n_slen = strlen(nde->name);
802 #endif
803 #ifdef  AFS_SGI53_ENV
804         dirsiz =
805             use64BitDirent ? DIRENTSIZE(n_slen) : IRIX5_DIRENTSIZE(n_slen);
806         if (dirsiz >= (AFS_UIO_RESID(auio) - len)) {
807 #else
808         if (DIRSIZ_LEN(n_slen) >= (AFS_UIO_RESID(auio) - len)) {
809 #endif /* AFS_SGI53_ENV */
810             /* No can do no more now; ya know... at this time */
811             DRelease(&nextEntry, 0);    /* can't use this one. */
812             if (len) {
813 #ifdef  AFS_HPUX_ENV
814                 sdirEntry->d_fileno = afs_calc_inum(avc->f.fid.Cell,
815                                                     avc->f.fid.Fid.Volume,
816                                                     ntohl(ode->fid.vnode));
817                 sdirEntry->d_reclen = rlen = AFS_UIO_RESID(auio);
818                 sdirEntry->d_namlen = o_slen;
819 #if defined(AFS_SUN5_ENV) || defined(AFS_AIX32_ENV) || defined(AFS_HPUX100_ENV)
820                 sdirEntry->d_off = origOffset;
821 #endif
822                 AFS_UIOMOVE((char *)sdirEntry, sizeof(*sdirEntry), UIO_READ,
823                             auio, code);
824                 if (code == 0)
825                     AFS_UIOMOVE(ode->name, o_slen, UIO_READ, auio, code);
826                 /* pad out the remaining characters with zeros */
827                 if (code == 0) {
828                     AFS_UIOMOVE(bufofzeros,
829                                 ((o_slen + 1 + DIRPAD) & ~DIRPAD) - o_slen,
830                                 UIO_READ, auio, code);
831                 }
832                 /* pad out the difference between rlen and slen... */
833                 if (DIRSIZ_LEN(o_slen) < rlen) {
834                     while (DIRSIZ_LEN(o_slen) < rlen) {
835                         int minLen = rlen - DIRSIZ_LEN(o_slen);
836                         if (minLen > sizeof(bufofzeros))
837                             minLen = sizeof(bufofzeros);
838                         AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code);
839                         rlen -= minLen;
840                     }
841                 }
842 #else /* AFS_HPUX_ENV */
843                 code =
844                     afs_readdir_move(ode, avc, auio, o_slen,
845                                      AFS_UIO_RESID(auio), origOffset);
846 #endif /* AFS_HPUX_ENV */
847                 /* this next line used to be AFSVFS40 or AIX 3.1, but is
848                  * really generic */
849                 AFS_UIO_SETOFFSET(auio, origOffset);
850 #if !defined(AFS_NBSD_ENV)
851                 AFS_UIO_SETRESID(auio, 0);
852 #endif
853             } else {            /* trouble, can't give anything to the user! */
854                 /* even though he has given us a buffer, 
855                  * even though we have something to give us,
856                  * Looks like we lost something somewhere.
857                  */
858                 code = EINVAL;
859             }
860             DRelease(&oldEntry, 0);
861             goto dirend;
862         }
863
864         /*
865          * In any event, we move out the LAST de entry, getting ready
866          * to set up for the next one.
867          */
868         if (len) {
869 #ifdef  AFS_HPUX_ENV
870             sdirEntry->d_fileno = afs_calc_inum(avc->f.fid.Cell,
871                                                 avc->f.fid.Fid.Volume,
872                                                 ntohl(ode->fid.vnode));
873             sdirEntry->d_reclen = rlen = len;
874             sdirEntry->d_namlen = o_slen;
875 #if defined(AFS_SUN5_ENV) || defined(AFS_AIX32_ENV) || defined(AFS_HPUX100_ENV)
876             sdirEntry->d_off = origOffset;
877 #endif
878             AFS_UIOMOVE((char *)sdirEntry, sizeof(*sdirEntry), UIO_READ, auio,
879                         code);
880             if (code == 0)
881                 AFS_UIOMOVE(ode->name, o_slen, UIO_READ, auio, code);
882             /* pad out the remaining characters with zeros */
883             if (code == 0) {
884                 AFS_UIOMOVE(bufofzeros,
885                             ((o_slen + 1 + DIRPAD) & ~DIRPAD) - o_slen,
886                             UIO_READ, auio, code);
887             }
888             /* pad out the difference between rlen and slen... */
889             if (DIRSIZ_LEN(o_slen) < rlen) {
890                 while (DIRSIZ_LEN(o_slen) < rlen) {
891                     int minLen = rlen - DIRSIZ_LEN(o_slen);
892                     if (minLen > sizeof(bufofzeros))
893                         minLen = sizeof(bufofzeros);
894                     AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code);
895                     rlen -= minLen;
896                 }
897             }
898 #else /* AFS_HPUX_ENV */
899             code = afs_readdir_move(ode, avc, auio, o_slen, len, origOffset);
900 #endif /* AFS_HPUX_ENV */
901         }
902 #ifdef  AFS_SGI53_ENV
903         len = use64BitDirent ? DIRENTSIZE(o_slen =
904                                           n_slen) : IRIX5_DIRENTSIZE(o_slen =
905                                                                      n_slen);
906 #else
907         len = DIRSIZ_LEN(o_slen = n_slen);
908 #endif /* AFS_SGI53_ENV */
909         
910         DRelease(&oldEntry, 0);
911         oldEntry = nextEntry;
912         ode = nde;
913         AFS_UIO_SETOFFSET(auio, (afs_int32) ((us + afs_dir_NameBlobs(nde->name)) << 5));
914     }
915     
916     DRelease(&oldEntry, 0);
917
918   dirend:
919     ReleaseReadLock(&tdc->lock);
920     afs_PutDCache(tdc);
921     ReleaseReadLock(&avc->lock);
922
923   done:
924 #ifdef  AFS_HPUX_ENV
925     osi_FreeSmallSpace((char *)sdirEntry);
926 #endif
927     AFS_DISCON_UNLOCK();
928     afs_PutFakeStat(&fakestate);
929     code = afs_CheckCode(code, treq, 28);
930     afs_DestroyReq(treq);
931     return code;
932 }
933
934 #endif /* !AFS_LINUX20_ENV */