diff options
-rw-r--r-- | sys/fs/nfsclient/nfs_clvnops.c | 20 |
1 files changed, 18 insertions, 2 deletions
diff --git a/sys/fs/nfsclient/nfs_clvnops.c b/sys/fs/nfsclient/nfs_clvnops.c index 85b5dd9cfbb1..0b60100d1fc9 100644 --- a/sys/fs/nfsclient/nfs_clvnops.c +++ b/sys/fs/nfsclient/nfs_clvnops.c @@ -3702,12 +3702,15 @@ nfs_deallocate(struct vop_deallocate_args *ap) struct thread *td = curthread; struct nfsvattr nfsva; struct nfsmount *nmp; - off_t tlen; + struct nfsnode *np; + off_t tlen, mlen; int attrflag, error, ret; + bool clipped; error = 0; attrflag = 0; nmp = VFSTONFS(vp->v_mount); + np = VTONFS(vp); mtx_lock(&nmp->nm_mtx); if (NFSHASNFSV4(nmp) && nmp->nm_minorvers >= NFSV42_MINORVERSION && (nmp->nm_privflag & NFSMNTP_NODEALLOCATE) == 0) { @@ -3721,8 +3724,18 @@ nfs_deallocate(struct vop_deallocate_args *ap) *ap->a_len = 0; return (0); } + clipped = false; if ((uint64_t)*ap->a_offset + tlen > nmp->nm_maxfilesize) tlen = nmp->nm_maxfilesize - *ap->a_offset; + if ((uint64_t)*ap->a_offset < np->n_size) { + /* Limit the len to nfs_maxalloclen before EOF. */ + mlen = omin((off_t)np->n_size - *ap->a_offset, tlen); + if ((uint64_t)mlen > nfs_maxalloclen) { + NFSCL_DEBUG(4, "dealloc: tlen maxalloclen\n"); + tlen = nfs_maxalloclen; + clipped = true; + } + } if (error == 0) error = ncl_vinvalbuf(vp, V_SAVE, td, 1); if (error == 0) { @@ -3741,7 +3754,10 @@ nfs_deallocate(struct vop_deallocate_args *ap) nfsva.na_size - *ap->a_offset, tlen); } - *ap->a_len = 0; + if (clipped && tlen < *ap->a_len) + *ap->a_len -= tlen; + else + *ap->a_len = 0; } else if (error == NFSERR_NOTSUPP) { mtx_lock(&nmp->nm_mtx); nmp->nm_privflag |= NFSMNTP_NODEALLOCATE; |