summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRick Macklem <rmacklem@FreeBSD.org>2020-07-26 02:42:09 +0000
committerRick Macklem <rmacklem@FreeBSD.org>2020-07-26 02:42:09 +0000
commit18a48314bae9d51ba2a18f20df3d8ed6cd2fd33d (patch)
tree5400a1b3a9afbb0cb1bdadd68e4cb1ae7afe8e71
parent7201590bbf94e0abe5550a203021bfc802612401 (diff)
Notes
-rw-r--r--sys/fs/nfs/nfs_var.h2
-rw-r--r--sys/fs/nfsserver/nfs_nfsdport.c17
-rw-r--r--sys/fs/nfsserver/nfs_nfsdserv.c8
-rw-r--r--sys/fs/nfsserver/nfs_nfsdsubs.c112
4 files changed, 94 insertions, 45 deletions
diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h
index 0e826a2b6524f..1cf792edeced0 100644
--- a/sys/fs/nfs/nfs_var.h
+++ b/sys/fs/nfs/nfs_var.h
@@ -391,7 +391,7 @@ int nfsv4_fillattr(struct nfsrv_descript *, struct mount *, vnode_t, NFSACL_T *,
struct vattr *, fhandle_t *, int, nfsattrbit_t *,
struct ucred *, NFSPROC_T *, int, int, int, int, uint64_t, struct statfs *);
void nfsrv_fillattr(struct nfsrv_descript *, struct nfsvattr *);
-void nfsrv_adj(struct mbuf *, int, int);
+struct mbuf *nfsrv_adj(struct mbuf *, int, int);
void nfsrv_postopattr(struct nfsrv_descript *, int, struct nfsvattr *);
int nfsd_errmap(struct nfsrv_descript *);
void nfsv4_uidtostr(uid_t, u_char **, int *);
diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c
index 050ca6df7aa81..fbe77fcaf9b66 100644
--- a/sys/fs/nfsserver/nfs_nfsdport.c
+++ b/sys/fs/nfsserver/nfs_nfsdport.c
@@ -757,7 +757,12 @@ nfsvno_readlink(struct vnode *vp, struct ucred *cred, struct thread *p,
if (uiop->uio_resid > 0) {
len -= uiop->uio_resid;
tlen = NFSM_RNDUP(len);
- nfsrv_adj(mp3, NFS_MAXPATHLEN - tlen, tlen - len);
+ if (tlen == 0) {
+ m_freem(mp3);
+ mp3 = mp = NULL;
+ } else if (tlen != NFS_MAXPATHLEN || tlen != len)
+ mp = nfsrv_adj(mp3, NFS_MAXPATHLEN - tlen,
+ tlen - len);
}
*lenp = len;
*mpp = mp3;
@@ -872,9 +877,9 @@ nfsvno_read(struct vnode *vp, off_t off, int cnt, struct ucred *cred,
tlen = NFSM_RNDUP(cnt);
if (tlen == 0) {
m_freem(m3);
- m3 = NULL;
+ m3 = m = NULL;
} else if (len != tlen || tlen != cnt)
- nfsrv_adj(m3, len - tlen, tlen - cnt);
+ m = nfsrv_adj(m3, len - tlen, tlen - cnt);
*mpp = m3;
*mpendp = m;
@@ -6247,7 +6252,11 @@ nfsvno_getxattr(struct vnode *vp, char *name, uint32_t maxresp,
tlen = NFSM_RNDUP(len);
if (alen != tlen)
printf("nfsvno_getxattr: weird size read\n");
- nfsrv_adj(m, alen - tlen, tlen - len);
+ if (tlen == 0) {
+ m_freem(m);
+ m = m2 = NULL;
+ } else if (alen != tlen || tlen != len)
+ m2 = nfsrv_adj(m, alen - tlen, tlen - len);
}
*lenp = len;
*mpp = m;
diff --git a/sys/fs/nfsserver/nfs_nfsdserv.c b/sys/fs/nfsserver/nfs_nfsdserv.c
index 09aa87ae2e2c7..1b34cb27dd015 100644
--- a/sys/fs/nfsserver/nfs_nfsdserv.c
+++ b/sys/fs/nfsserver/nfs_nfsdserv.c
@@ -690,9 +690,11 @@ nfsrvd_readlink(struct nfsrv_descript *nd, __unused int isdgram,
goto out;
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(len);
- nd->nd_mb->m_next = mp;
- nd->nd_mb = mpend;
- nd->nd_bpos = mtod(mpend, caddr_t) + mpend->m_len;
+ if (mp != NULL) {
+ nd->nd_mb->m_next = mp;
+ nd->nd_mb = mpend;
+ nd->nd_bpos = mtod(mpend, caddr_t) + mpend->m_len;
+ }
out:
NFSEXITCODE2(0, nd);
diff --git a/sys/fs/nfsserver/nfs_nfsdsubs.c b/sys/fs/nfsserver/nfs_nfsdsubs.c
index 17349343d5e02..428751372121b 100644
--- a/sys/fs/nfsserver/nfs_nfsdsubs.c
+++ b/sys/fs/nfsserver/nfs_nfsdsubs.c
@@ -1268,62 +1268,100 @@ static short *nfsrv_v4errmap[] = {
};
/*
- * A fiddled version of m_adj() that ensures null fill to a long
- * boundary and only trims off the back end
+ * Trim tlen bytes off the end of the mbuf list and then ensure
+ * the end of the last mbuf is nul filled to a long boundary,
+ * as indicated by the value of "nul".
+ * Return the last mbuf in the updated list and free and mbufs
+ * that follow it in the original list.
+ * This is somewhat different than the old nfsrv_adj() with
+ * support for ext_pgs mbufs. It frees the remaining mbufs
+ * instead of setting them 0 length, since lists of ext_pgs
+ * mbufs are all expected to be non-empty.
*/
-void
+struct mbuf *
nfsrv_adj(struct mbuf *mp, int len, int nul)
{
- struct mbuf *m;
- int count, i;
+ struct mbuf *m, *m2;
+ vm_page_t pg;
+ int i, lastlen, pgno, plen, tlen, trim;
+ uint16_t off;
char *cp;
/*
- * Trim from tail. Scan the mbuf chain,
- * calculating its length and finding the last mbuf.
- * If the adjustment only affects this mbuf, then just
- * adjust and return. Otherwise, rescan and truncate
- * after the remaining size.
+ * Find the last mbuf after adjustment and
+ * how much it needs to be adjusted by.
*/
- count = 0;
+ tlen = 0;
m = mp;
for (;;) {
- count += m->m_len;
+ tlen += m->m_len;
if (m->m_next == NULL)
break;
m = m->m_next;
}
- if (m->m_len > len) {
- m->m_len -= len;
- if (nul > 0) {
- cp = mtod(m, caddr_t) + m->m_len - nul;
- for (i = 0; i < nul; i++)
- *cp++ = '\0';
+ /* m is now the last mbuf and tlen the total length. */
+
+ if (len >= m->m_len) {
+ /* Need to trim away the last mbuf(s). */
+ i = tlen - len;
+ m = mp;
+ for (;;) {
+ if (m->m_len >= i)
+ break;
+ i -= m->m_len;
+ m = m->m_next;
}
- return;
- }
- count -= len;
- if (count < 0)
- count = 0;
+ lastlen = i;
+ } else
+ lastlen = m->m_len - len;
+
/*
- * Correct length for chain is "count".
- * Find the mbuf with last data, adjust its length,
- * and toss data from remaining mbufs on chain.
+ * m is now the last mbuf after trimming and its length needs to
+ * be lastlen.
+ * Adjust the last mbuf and set cp to point to where nuls must be
+ * written.
*/
- for (m = mp; m; m = m->m_next) {
- if (m->m_len >= count) {
- m->m_len = count;
- if (nul > 0) {
- cp = mtod(m, caddr_t) + m->m_len - nul;
- for (i = 0; i < nul; i++)
- *cp++ = '\0';
+ if ((m->m_flags & M_EXTPG) != 0) {
+ pgno = m->m_epg_npgs - 1;
+ off = (pgno == 0) ? m->m_epg_1st_off : 0;
+ plen = m_epg_pagelen(m, pgno, off);
+ if (m->m_len > lastlen) {
+ /* Trim this mbuf. */
+ trim = m->m_len - lastlen;
+ while (trim >= plen) {
+ KASSERT(pgno > 0,
+ ("nfsrv_adj: freeing page 0"));
+ /* Free page. */
+ pg = PHYS_TO_VM_PAGE(m->m_epg_pa[pgno]);
+ vm_page_unwire_noq(pg);
+ vm_page_free(pg);
+ trim -= plen;
+ m->m_epg_npgs--;
+ pgno--;
+ off = (pgno == 0) ? m->m_epg_1st_off : 0;
+ plen = m_epg_pagelen(m, pgno, off);
}
- break;
+ plen -= trim;
+ m->m_epg_last_len = plen;
+ m->m_len = lastlen;
}
- count -= m->m_len;
+ cp = (char *)(void *)PHYS_TO_DMAP(m->m_epg_pa[pgno]);
+ cp += off + plen - nul;
+ } else {
+ m->m_len = lastlen;
+ cp = mtod(m, char *) + m->m_len - nul;
}
- for (m = m->m_next; m; m = m->m_next)
- m->m_len = 0;
+
+ /* Write the nul bytes. */
+ for (i = 0; i < nul; i++)
+ *cp++ = '\0';
+
+ /* Free up any mbufs past "m". */
+ m2 = m->m_next;
+ m->m_next = NULL;
+ if (m2 != NULL)
+ m_freem(m2);
+ return (m);
}
/*