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