- nmp = vfs_to_nfs(mp);
- if (error = nfs_nget(mp, &nmp->nm_fh, &np))
- return (error);
- vp = NFSTOV(np);
- nfsstats.rpccnt[NFSPROC_STATFS]++;
- cred = crget();
- cred->cr_ngroups = 1;
- nfsm_reqhead(nfs_procids[NFSPROC_STATFS], cred, NFSX_FH);
- nfsm_fhtom(vp);
- nfsm_request(vp);
- nfsm_disect(sfp, struct nfsv2_statfs *, NFSX_STATFS);
- sbp->f_type = MOUNT_NFS;
- sbp->f_flags = nmp->nm_flag;
- sbp->f_bsize = fxdr_unsigned(long, sfp->sf_tsize);
- sbp->f_fsize = fxdr_unsigned(long, sfp->sf_bsize);
- sbp->f_blocks = fxdr_unsigned(long, sfp->sf_blocks);
- sbp->f_bfree = fxdr_unsigned(long, sfp->sf_bfree);
- sbp->f_bavail = fxdr_unsigned(long, sfp->sf_bavail);
- sbp->f_files = 0;
- sbp->f_ffree = 0;
- sbp->f_fsid.val[0] = mp->m_fsid.val[0];
- sbp->f_fsid.val[1] = mp->m_fsid.val[1];
- bcopy(nmp->nm_path, sbp->f_mntonname, MNAMELEN);
- bcopy(nmp->nm_host, sbp->f_mntfromname, MNAMELEN);
- nfsm_reqdone;
- nfs_nput(vp);
- crfree(cred);
+ 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;
+ }
+ /*
+ * If there is too much to fit in the data buffer, use savoff and
+ * savdp to trim off the last record.
+ * --> we are not at eof
+ */
+ if (siz > uiop->uio_resid) {
+ off = savoff;
+ siz -= dp->d_reclen;
+ dp = savdp;
+ more_dirs = 0; /* Paranoia */
+ }
+ if (siz > 0) {
+ lastlen = dp->d_reclen;
+ md = md2;
+ dpos = dpos2;
+ nfsm_mtouio(uiop, siz);
+ uiop->uio_offset = off;
+ } else
+ more_dirs = 0; /* Ugh, never happens, but in case.. */
+ m_freem(mrep);
+ }
+ /*
+ * Fill last record, iff any, out to a multiple of DIRBLKSIZ
+ * by increasing d_reclen for the last record.
+ */
+ if (uiop->uio_resid < tresid) {
+ len = uiop->uio_resid & (DIRBLKSIZ - 1);
+ if (len > 0) {
+ dp = (struct direct *)
+ (uiop->uio_iov->iov_base - lastlen);
+ dp->d_reclen += len;
+ uiop->uio_iov->iov_base += len;
+ uiop->uio_iov->iov_len -= len;
+ uiop->uio_resid -= len;
+ }
+ }
+nfsmout: