mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 13:23:02 -04:00
nfsd: fix partial-write detection in nfsd_direct_write
nfsd_direct_write() walks a list of write segments and, after each
vfs_iocb_iter_write(), tries to detect a short write so the loop can
stop before placing the next segment at a wrong file offset:
host_err = vfs_iocb_iter_write(file, kiocb, &segments[i].iter);
if (host_err < 0)
return host_err;
*cnt += host_err;
if (host_err < segments[i].iter.count)
break; /* partial write */
vfs_iocb_iter_write() runs the iter through ->write_iter(), which
advances the iter by the number of bytes written. By the time the
check runs, segments[i].iter.count is the residual, not the original
request length:
before write_iter: iter.count == original_len
after write_iter: iter.count == original_len - host_err
The condition then reduces to host_err < original_len - host_err, so
the break fires only when less than half of the segment was written.
Any short write completing between 50% and 99% of the segment slips
through; the loop advances to the next segment with kiocb->ki_pos
only bumped by the short amount, writing the next segment's payload
at the wrong offset and over-reporting *cnt to the NFS client.
Snapshot the segment's byte count before the write and compare
host_err against that snapshot so any short write breaks the loop.
Fixes: 06c5c97293 ("NFSD: Implement NFSD_IO_DIRECT for NFS WRITE")
Cc: stable@vger.kernel.org
Assisted-by: kres:claude-opus-4-7
Reported-by: Chris Mason <clm@meta.com>
Signed-off-by: Chris Mason <clm@meta.com>
Link: https://patch.msgid.link/20260530-nfsd-fixes-v2-7-f27e8eb4d974@kernel.org
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
This commit is contained in:
@@ -1380,6 +1380,7 @@ nfsd_direct_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
struct file *file = nf->nf_file;
|
||||
unsigned int nsegs, i;
|
||||
ssize_t host_err;
|
||||
size_t expected;
|
||||
|
||||
nsegs = nfsd_write_dio_iters_init(nf, rqstp->rq_bvec, nvecs,
|
||||
kiocb, *cnt, segments);
|
||||
@@ -1401,11 +1402,13 @@ nfsd_direct_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
kiocb->ki_flags |= IOCB_DONTCACHE;
|
||||
}
|
||||
|
||||
expected = iov_iter_count(&segments[i].iter);
|
||||
|
||||
host_err = vfs_iocb_iter_write(file, kiocb, &segments[i].iter);
|
||||
if (host_err < 0)
|
||||
return host_err;
|
||||
*cnt += host_err;
|
||||
if (host_err < segments[i].iter.count)
|
||||
if (host_err < (ssize_t)expected)
|
||||
break; /* partial write */
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user