summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <trondmy@gmail.com>2019-08-26 13:03:11 -0400
committerJ. Bruce Fields <bfields@redhat.com>2019-09-23 16:24:08 -0400
commit83a63072c815e8a042c60fa964dcbde2a6df0e87 (patch)
tree2c768fecd75e144e5faf38951c96dd73d91e181e
parent65643f4c8217edb1f40f7fb05f996c3a4798442a (diff)
nfsd: fix nfs read eof detection
Currently, the knfsd server assumes that a short read indicates an end of file. That assumption is incorrect. The short read means that either we've hit the end of file, or we've hit a read error. In the case of a read error, the client may want to retry (as per the implementation recommendations in RFC1813 and RFC7530), but currently it is being told that it hit an eof. Move the code to detect eof from version specific code into the generic nfsd read. Report eof only in the two following cases: 1) read() returns a zero length short read with no error. 2) the offset+length of the read is >= the file size. Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r--fs/nfsd/nfs3proc.c9
-rw-r--r--fs/nfsd/nfs4xdr.c11
-rw-r--r--fs/nfsd/nfsproc.c4
-rw-r--r--fs/nfsd/vfs.c37
-rw-r--r--fs/nfsd/vfs.h28
-rw-r--r--fs/nfsd/xdr3.h2
6 files changed, 41 insertions, 50 deletions
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
index 9bc32af4e2da..cea68d8411ac 100644
--- a/fs/nfsd/nfs3proc.c
+++ b/fs/nfsd/nfs3proc.c
@@ -172,13 +172,8 @@ nfsd3_proc_read(struct svc_rqst *rqstp)
172 nfserr = nfsd_read(rqstp, &resp->fh, 172 nfserr = nfsd_read(rqstp, &resp->fh,
173 argp->offset, 173 argp->offset,
174 rqstp->rq_vec, argp->vlen, 174 rqstp->rq_vec, argp->vlen,
175 &resp->count); 175 &resp->count,
176 if (nfserr == 0) { 176 &resp->eof);
177 struct inode *inode = d_inode(resp->fh.fh_dentry);
178 resp->eof = nfsd_eof_on_read(cnt, resp->count, argp->offset,
179 inode->i_size);
180 }
181
182 RETURN_STATUS(nfserr); 177 RETURN_STATUS(nfserr);
183} 178}
184 179
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index c1fc2641e3e7..533d0fc3c96b 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3472,7 +3472,7 @@ static __be32 nfsd4_encode_splice_read(
3472 3472
3473 len = maxcount; 3473 len = maxcount;
3474 nfserr = nfsd_splice_read(read->rd_rqstp, read->rd_fhp, 3474 nfserr = nfsd_splice_read(read->rd_rqstp, read->rd_fhp,
3475 file, read->rd_offset, &maxcount); 3475 file, read->rd_offset, &maxcount, &eof);
3476 read->rd_length = maxcount; 3476 read->rd_length = maxcount;
3477 if (nfserr) { 3477 if (nfserr) {
3478 /* 3478 /*
@@ -3484,9 +3484,6 @@ static __be32 nfsd4_encode_splice_read(
3484 return nfserr; 3484 return nfserr;
3485 } 3485 }
3486 3486
3487 eof = nfsd_eof_on_read(len, maxcount, read->rd_offset,
3488 d_inode(read->rd_fhp->fh_dentry)->i_size);
3489
3490 *(p++) = htonl(eof); 3487 *(p++) = htonl(eof);
3491 *(p++) = htonl(maxcount); 3488 *(p++) = htonl(maxcount);
3492 3489
@@ -3557,15 +3554,13 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
3557 3554
3558 len = maxcount; 3555 len = maxcount;
3559 nfserr = nfsd_readv(resp->rqstp, read->rd_fhp, file, read->rd_offset, 3556 nfserr = nfsd_readv(resp->rqstp, read->rd_fhp, file, read->rd_offset,
3560 resp->rqstp->rq_vec, read->rd_vlen, &maxcount); 3557 resp->rqstp->rq_vec, read->rd_vlen, &maxcount,
3558 &eof);
3561 read->rd_length = maxcount; 3559 read->rd_length = maxcount;
3562 if (nfserr) 3560 if (nfserr)
3563 return nfserr; 3561 return nfserr;
3564 xdr_truncate_encode(xdr, starting_len + 8 + ((maxcount+3)&~3)); 3562 xdr_truncate_encode(xdr, starting_len + 8 + ((maxcount+3)&~3));
3565 3563
3566 eof = nfsd_eof_on_read(len, maxcount, read->rd_offset,
3567 d_inode(read->rd_fhp->fh_dentry)->i_size);
3568
3569 tmp = htonl(eof); 3564 tmp = htonl(eof);
3570 write_bytes_to_xdr_buf(xdr->buf, starting_len , &tmp, 4); 3565 write_bytes_to_xdr_buf(xdr->buf, starting_len , &tmp, 4);
3571 tmp = htonl(maxcount); 3566 tmp = htonl(maxcount);
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index 0d20fd161225..c83ddac22f38 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -172,6 +172,7 @@ nfsd_proc_read(struct svc_rqst *rqstp)
172 struct nfsd_readargs *argp = rqstp->rq_argp; 172 struct nfsd_readargs *argp = rqstp->rq_argp;
173 struct nfsd_readres *resp = rqstp->rq_resp; 173 struct nfsd_readres *resp = rqstp->rq_resp;
174 __be32 nfserr; 174 __be32 nfserr;
175 u32 eof;
175 176
176 dprintk("nfsd: READ %s %d bytes at %d\n", 177 dprintk("nfsd: READ %s %d bytes at %d\n",
177 SVCFH_fmt(&argp->fh), 178 SVCFH_fmt(&argp->fh),
@@ -195,7 +196,8 @@ nfsd_proc_read(struct svc_rqst *rqstp)
195 nfserr = nfsd_read(rqstp, fh_copy(&resp->fh, &argp->fh), 196 nfserr = nfsd_read(rqstp, fh_copy(&resp->fh, &argp->fh),
196 argp->offset, 197 argp->offset,
197 rqstp->rq_vec, argp->vlen, 198 rqstp->rq_vec, argp->vlen,
198 &resp->count); 199 &resp->count,
200 &eof);
199 201
200 if (nfserr) return nfserr; 202 if (nfserr) return nfserr;
201 return fh_getattr(&resp->fh, &resp->stat); 203 return fh_getattr(&resp->fh, &resp->stat);
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 0867d5319fdb..bd0a385df3fc 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -834,12 +834,23 @@ static int nfsd_direct_splice_actor(struct pipe_inode_info *pipe,
834 return __splice_from_pipe(pipe, sd, nfsd_splice_actor); 834 return __splice_from_pipe(pipe, sd, nfsd_splice_actor);
835} 835}
836 836
837static u32 nfsd_eof_on_read(struct file *file, loff_t offset, ssize_t len,
838 size_t expected)
839{
840 if (expected != 0 && len == 0)
841 return 1;
842 if (offset+len >= i_size_read(file_inode(file)))
843 return 1;
844 return 0;
845}
846
837static __be32 nfsd_finish_read(struct svc_rqst *rqstp, struct svc_fh *fhp, 847static __be32 nfsd_finish_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
838 struct file *file, loff_t offset, 848 struct file *file, loff_t offset,
839 unsigned long *count, int host_err) 849 unsigned long *count, u32 *eof, ssize_t host_err)
840{ 850{
841 if (host_err >= 0) { 851 if (host_err >= 0) {
842 nfsdstats.io_read += host_err; 852 nfsdstats.io_read += host_err;
853 *eof = nfsd_eof_on_read(file, offset, host_err, *count);
843 *count = host_err; 854 *count = host_err;
844 fsnotify_access(file); 855 fsnotify_access(file);
845 trace_nfsd_read_io_done(rqstp, fhp, offset, *count); 856 trace_nfsd_read_io_done(rqstp, fhp, offset, *count);
@@ -851,7 +862,8 @@ static __be32 nfsd_finish_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
851} 862}
852 863
853__be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp, 864__be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
854 struct file *file, loff_t offset, unsigned long *count) 865 struct file *file, loff_t offset, unsigned long *count,
866 u32 *eof)
855{ 867{
856 struct splice_desc sd = { 868 struct splice_desc sd = {
857 .len = 0, 869 .len = 0,
@@ -859,25 +871,27 @@ __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
859 .pos = offset, 871 .pos = offset,
860 .u.data = rqstp, 872 .u.data = rqstp,
861 }; 873 };
862 int host_err; 874 ssize_t host_err;
863 875
864 trace_nfsd_read_splice(rqstp, fhp, offset, *count); 876 trace_nfsd_read_splice(rqstp, fhp, offset, *count);
865 rqstp->rq_next_page = rqstp->rq_respages + 1; 877 rqstp->rq_next_page = rqstp->rq_respages + 1;
866 host_err = splice_direct_to_actor(file, &sd, nfsd_direct_splice_actor); 878 host_err = splice_direct_to_actor(file, &sd, nfsd_direct_splice_actor);
867 return nfsd_finish_read(rqstp, fhp, file, offset, count, host_err); 879 return nfsd_finish_read(rqstp, fhp, file, offset, count, eof, host_err);
868} 880}
869 881
870__be32 nfsd_readv(struct svc_rqst *rqstp, struct svc_fh *fhp, 882__be32 nfsd_readv(struct svc_rqst *rqstp, struct svc_fh *fhp,
871 struct file *file, loff_t offset, 883 struct file *file, loff_t offset,
872 struct kvec *vec, int vlen, unsigned long *count) 884 struct kvec *vec, int vlen, unsigned long *count,
885 u32 *eof)
873{ 886{
874 struct iov_iter iter; 887 struct iov_iter iter;
875 int host_err; 888 loff_t ppos = offset;
889 ssize_t host_err;
876 890
877 trace_nfsd_read_vector(rqstp, fhp, offset, *count); 891 trace_nfsd_read_vector(rqstp, fhp, offset, *count);
878 iov_iter_kvec(&iter, READ, vec, vlen, *count); 892 iov_iter_kvec(&iter, READ, vec, vlen, *count);
879 host_err = vfs_iter_read(file, &iter, &offset, 0); 893 host_err = vfs_iter_read(file, &iter, &ppos, 0);
880 return nfsd_finish_read(rqstp, fhp, file, offset, count, host_err); 894 return nfsd_finish_read(rqstp, fhp, file, offset, count, eof, host_err);
881} 895}
882 896
883/* 897/*
@@ -984,7 +998,8 @@ out_nfserr:
984 * N.B. After this call fhp needs an fh_put 998 * N.B. After this call fhp needs an fh_put
985 */ 999 */
986__be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, 1000__be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
987 loff_t offset, struct kvec *vec, int vlen, unsigned long *count) 1001 loff_t offset, struct kvec *vec, int vlen, unsigned long *count,
1002 u32 *eof)
988{ 1003{
989 struct nfsd_file *nf; 1004 struct nfsd_file *nf;
990 struct file *file; 1005 struct file *file;
@@ -997,9 +1012,9 @@ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
997 1012
998 file = nf->nf_file; 1013 file = nf->nf_file;
999 if (file->f_op->splice_read && test_bit(RQ_SPLICE_OK, &rqstp->rq_flags)) 1014 if (file->f_op->splice_read && test_bit(RQ_SPLICE_OK, &rqstp->rq_flags))
1000 err = nfsd_splice_read(rqstp, fhp, file, offset, count); 1015 err = nfsd_splice_read(rqstp, fhp, file, offset, count, eof);
1001 else 1016 else
1002 err = nfsd_readv(rqstp, fhp, file, offset, vec, vlen, count); 1017 err = nfsd_readv(rqstp, fhp, file, offset, vec, vlen, count, eof);
1003 1018
1004 nfsd_file_put(nf); 1019 nfsd_file_put(nf);
1005 1020
diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h
index e0f7792165a6..a13fd9d7e1f5 100644
--- a/fs/nfsd/vfs.h
+++ b/fs/nfsd/vfs.h
@@ -80,13 +80,16 @@ __be32 nfsd_open_verified(struct svc_rqst *, struct svc_fh *, umode_t,
80 int, struct file **); 80 int, struct file **);
81__be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp, 81__be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
82 struct file *file, loff_t offset, 82 struct file *file, loff_t offset,
83 unsigned long *count); 83 unsigned long *count,
84 u32 *eof);
84__be32 nfsd_readv(struct svc_rqst *rqstp, struct svc_fh *fhp, 85__be32 nfsd_readv(struct svc_rqst *rqstp, struct svc_fh *fhp,
85 struct file *file, loff_t offset, 86 struct file *file, loff_t offset,
86 struct kvec *vec, int vlen, 87 struct kvec *vec, int vlen,
87 unsigned long *count); 88 unsigned long *count,
89 u32 *eof);
88__be32 nfsd_read(struct svc_rqst *, struct svc_fh *, 90__be32 nfsd_read(struct svc_rqst *, struct svc_fh *,
89 loff_t, struct kvec *, int, unsigned long *); 91 loff_t, struct kvec *, int, unsigned long *,
92 u32 *eof);
90__be32 nfsd_write(struct svc_rqst *, struct svc_fh *, loff_t, 93__be32 nfsd_write(struct svc_rqst *, struct svc_fh *, loff_t,
91 struct kvec *, int, unsigned long *, int); 94 struct kvec *, int, unsigned long *, int);
92__be32 nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, 95__be32 nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
@@ -149,23 +152,4 @@ static inline int nfsd_create_is_exclusive(int createmode)
149 || createmode == NFS4_CREATE_EXCLUSIVE4_1; 152 || createmode == NFS4_CREATE_EXCLUSIVE4_1;
150} 153}
151 154
152static inline bool nfsd_eof_on_read(long requested, long read,
153 loff_t offset, loff_t size)
154{
155 /* We assume a short read means eof: */
156 if (requested > read)
157 return true;
158 /*
159 * A non-short read might also reach end of file. The spec
160 * still requires us to set eof in that case.
161 *
162 * Further operations may have modified the file size since
163 * the read, so the following check is not atomic with the read.
164 * We've only seen that cause a problem for a client in the case
165 * where the read returned a count of 0 without setting eof.
166 * That case was fixed by the addition of the above check.
167 */
168 return (offset + read >= size);
169}
170
171#endif /* LINUX_NFSD_VFS_H */ 155#endif /* LINUX_NFSD_VFS_H */
diff --git a/fs/nfsd/xdr3.h b/fs/nfsd/xdr3.h
index 2cb29e961a76..99ff9f403ff1 100644
--- a/fs/nfsd/xdr3.h
+++ b/fs/nfsd/xdr3.h
@@ -151,7 +151,7 @@ struct nfsd3_readres {
151 __be32 status; 151 __be32 status;
152 struct svc_fh fh; 152 struct svc_fh fh;
153 unsigned long count; 153 unsigned long count;
154 int eof; 154 __u32 eof;
155}; 155};
156 156
157struct nfsd3_writeres { 157struct nfsd3_writeres {