- nfsstats.rpccnt[NFSPROC_READDIR]++;
- nfsm_reqhead(nfs_procids[NFSPROC_READDIR], cred, xid);
- nfsm_fhtom(vp);
- nfsm_build(p, u_long *, 2*NFSX_UNSIGNED);
- *p++ = txdr_unsigned(uiop->uio_offset);
- *p = txdr_unsigned(uiop->uio_resid);
- nfsm_request(vp, nonidempotent[NFSPROC_READDIR]);
- siz = 0;
- nfsm_disect(p, u_long *, NFSX_UNSIGNED);
- more_dirs = fxdr_unsigned(int, *p);
-
- /* Save the position so that we can do nfsm_mtouio() later */
- dpos2 = dpos;
- md2 = md;
-
- /* loop thru the dir entries, doctoring them to 4bsd form */
- savoff = off = 0;
- savdp = dp = NULL;
- while (more_dirs && siz < uiop->uio_resid) {
- savoff = off; /* Hold onto offset and dp */
- savdp = dp;
- nfsm_disecton(p, u_long *, 2*NFSX_UNSIGNED);
- dp = (struct direct *)p;
- dp->d_ino = fxdr_unsigned(u_long, *p++);
- len = fxdr_unsigned(int, *p);
- if (len <= 0 || len > NFS_MAXNAMLEN) {
- error = EBADRPC;
- m_freem(mrep);
- goto nfsmout;
+ nmp = VFSTONFS(vp->v_mount);
+ tresid = uiop->uio_resid;
+ /*
+ * Loop around doing readdir rpc's of size uio_resid or nm_rsize,
+ * whichever is smaller, truncated to a multiple of DIRBLKSIZ.
+ * The stopping criteria is EOF or buffer full.
+ */
+ while (more_dirs && uiop->uio_resid >= DIRBLKSIZ) {
+ nfsstats.rpccnt[NFSPROC_READDIR]++;
+ nfsm_reqhead(nfs_procids[NFSPROC_READDIR], cred, xid);
+ nfsm_fhtom(vp);
+ nfsm_build(p, u_long *, 2*NFSX_UNSIGNED);
+ *p++ = txdr_unsigned(uiop->uio_offset);
+ *p = txdr_unsigned(((uiop->uio_resid > nmp->nm_rsize) ?
+ nmp->nm_rsize : uiop->uio_resid) & ~(DIRBLKSIZ-1));
+ nfsm_request(vp, NFSPROC_READDIR, procp, 0);
+ siz = 0;
+ nfsm_disect(p, u_long *, NFSX_UNSIGNED);
+ more_dirs = fxdr_unsigned(int, *p);
+
+ /* Save the position so that we can do nfsm_mtouio() later */
+ dpos2 = dpos;
+ md2 = md;
+
+ /* loop thru the dir entries, doctoring them to 4bsd form */
+ off = uiop->uio_offset;
+#ifdef lint
+ dp = (struct direct *)0;
+#endif /* lint */
+ while (more_dirs && siz < uiop->uio_resid) {
+ savoff = off; /* Hold onto offset and dp */
+ savdp = dp;
+ nfsm_disecton(p, u_long *, 2*NFSX_UNSIGNED);
+ dp = (struct direct *)p;
+ dp->d_ino = fxdr_unsigned(u_long, *p++);
+ len = fxdr_unsigned(int, *p);
+ if (len <= 0 || len > NFS_MAXNAMLEN) {
+ error = EBADRPC;
+ m_freem(mrep);
+ goto nfsmout;
+ }
+ dp->d_namlen = (u_short)len;
+ nfsm_adv(len); /* Point past name */
+ tlen = nfsm_rndup(len);
+ /*
+ * This should not be necessary, but some servers have
+ * broken XDR such that these bytes are not null filled.
+ */
+ if (tlen != len) {
+ *dpos = '\0'; /* Null-terminate */
+ nfsm_adv(tlen - len);
+ len = tlen;
+ }
+ nfsm_disecton(p, u_long *, 2*NFSX_UNSIGNED);
+ off = fxdr_unsigned(off_t, *p);
+ *p++ = 0; /* Ensures null termination of name */
+ more_dirs = fxdr_unsigned(int, *p);
+ dp->d_reclen = len+4*NFSX_UNSIGNED;
+ siz += dp->d_reclen;
+ }
+ /*
+ * If at end of rpc data, get the eof boolean
+ */
+ if (!more_dirs) {
+ nfsm_disecton(p, u_long *, NFSX_UNSIGNED);
+ more_dirs = (fxdr_unsigned(int, *p) == 0);
+
+ /*
+ * If at EOF, cache directory offset
+ */
+ if (!more_dirs)
+ np->n_direofoffset = off;