aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfsd
diff options
context:
space:
mode:
authorDavid Shaw <dshaw@jabberwocky.com>2009-03-05 20:16:14 -0500
committerJ. Bruce Fields <bfields@citi.umich.edu>2009-03-18 17:38:40 -0400
commit31dec2538e45e9fff2007ea1f4c6bae9f78db724 (patch)
treec2b42679c73c1c6f31312f38a1b1d049e918d635 /fs/nfsd
parent1e685ec270cb97680be4eb8cf6b615f5f7f1403a (diff)
Short write in nfsd becomes a full write to the client
If a filesystem being written to via NFS returns a short write count (as opposed to an error) to nfsd, nfsd treats that as a success for the entire write, rather than the short count that actually succeeded. For example, given a 8192 byte write, if the underlying filesystem only writes 4096 bytes, nfsd will ack back to the nfs client that all 8192 bytes were written. The nfs client does have retry logic for short writes, but this is never called as the client is told the complete write succeeded. There are probably other ways it could happen, but in my case it happened with a fuse (filesystem in userspace) filesystem which can rather easily have a partial write. Here is a patch to properly return the short write count to the client. Signed-off-by: David Shaw <dshaw@jabberwocky.com> Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
Diffstat (limited to 'fs/nfsd')
-rw-r--r--fs/nfsd/nfs3proc.c5
-rw-r--r--fs/nfsd/nfs4proc.c7
-rw-r--r--fs/nfsd/nfsproc.c3
-rw-r--r--fs/nfsd/vfs.c13
4 files changed, 17 insertions, 11 deletions
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
index 579ce8c69daa..7c9fe838f038 100644
--- a/fs/nfsd/nfs3proc.c
+++ b/fs/nfsd/nfs3proc.c
@@ -203,6 +203,7 @@ nfsd3_proc_write(struct svc_rqst *rqstp, struct nfsd3_writeargs *argp,
203 struct nfsd3_writeres *resp) 203 struct nfsd3_writeres *resp)
204{ 204{
205 __be32 nfserr; 205 __be32 nfserr;
206 unsigned long cnt = argp->len;
206 207
207 dprintk("nfsd: WRITE(3) %s %d bytes at %ld%s\n", 208 dprintk("nfsd: WRITE(3) %s %d bytes at %ld%s\n",
208 SVCFH_fmt(&argp->fh), 209 SVCFH_fmt(&argp->fh),
@@ -215,9 +216,9 @@ nfsd3_proc_write(struct svc_rqst *rqstp, struct nfsd3_writeargs *argp,
215 nfserr = nfsd_write(rqstp, &resp->fh, NULL, 216 nfserr = nfsd_write(rqstp, &resp->fh, NULL,
216 argp->offset, 217 argp->offset,
217 rqstp->rq_vec, argp->vlen, 218 rqstp->rq_vec, argp->vlen,
218 argp->len, 219 &cnt,
219 &resp->committed); 220 &resp->committed);
220 resp->count = argp->count; 221 resp->count = cnt;
221 RETURN_STATUS(nfserr); 222 RETURN_STATUS(nfserr);
222} 223}
223 224
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 77f584f69dfe..283d77a47120 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -682,6 +682,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
682 struct file *filp = NULL; 682 struct file *filp = NULL;
683 u32 *p; 683 u32 *p;
684 __be32 status = nfs_ok; 684 __be32 status = nfs_ok;
685 unsigned long cnt;
685 686
686 /* no need to check permission - this will be done in nfsd_write() */ 687 /* no need to check permission - this will be done in nfsd_write() */
687 688
@@ -700,7 +701,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
700 return status; 701 return status;
701 } 702 }
702 703
703 write->wr_bytes_written = write->wr_buflen; 704 cnt = write->wr_buflen;
704 write->wr_how_written = write->wr_stable_how; 705 write->wr_how_written = write->wr_stable_how;
705 p = (u32 *)write->wr_verifier.data; 706 p = (u32 *)write->wr_verifier.data;
706 *p++ = nfssvc_boot.tv_sec; 707 *p++ = nfssvc_boot.tv_sec;
@@ -708,10 +709,12 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
708 709
709 status = nfsd_write(rqstp, &cstate->current_fh, filp, 710 status = nfsd_write(rqstp, &cstate->current_fh, filp,
710 write->wr_offset, rqstp->rq_vec, write->wr_vlen, 711 write->wr_offset, rqstp->rq_vec, write->wr_vlen,
711 write->wr_buflen, &write->wr_how_written); 712 &cnt, &write->wr_how_written);
712 if (filp) 713 if (filp)
713 fput(filp); 714 fput(filp);
714 715
716 write->wr_bytes_written = cnt;
717
715 if (status == nfserr_symlink) 718 if (status == nfserr_symlink)
716 status = nfserr_inval; 719 status = nfserr_inval;
717 return status; 720 return status;
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index 6f7f26351227..e298e260b5f1 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -180,6 +180,7 @@ nfsd_proc_write(struct svc_rqst *rqstp, struct nfsd_writeargs *argp,
180{ 180{
181 __be32 nfserr; 181 __be32 nfserr;
182 int stable = 1; 182 int stable = 1;
183 unsigned long cnt = argp->len;
183 184
184 dprintk("nfsd: WRITE %s %d bytes at %d\n", 185 dprintk("nfsd: WRITE %s %d bytes at %d\n",
185 SVCFH_fmt(&argp->fh), 186 SVCFH_fmt(&argp->fh),
@@ -188,7 +189,7 @@ nfsd_proc_write(struct svc_rqst *rqstp, struct nfsd_writeargs *argp,
188 nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh), NULL, 189 nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh), NULL,
189 argp->offset, 190 argp->offset,
190 rqstp->rq_vec, argp->vlen, 191 rqstp->rq_vec, argp->vlen,
191 argp->len, 192 &cnt,
192 &stable); 193 &stable);
193 return nfsd_return_attrs(nfserr, resp); 194 return nfsd_return_attrs(nfserr, resp);
194} 195}
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 0c076293155d..54404d730809 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -960,7 +960,7 @@ static void kill_suid(struct dentry *dentry)
960static __be32 960static __be32
961nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, 961nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
962 loff_t offset, struct kvec *vec, int vlen, 962 loff_t offset, struct kvec *vec, int vlen,
963 unsigned long cnt, int *stablep) 963 unsigned long *cnt, int *stablep)
964{ 964{
965 struct svc_export *exp; 965 struct svc_export *exp;
966 struct dentry *dentry; 966 struct dentry *dentry;
@@ -974,7 +974,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
974 err = nfserr_perm; 974 err = nfserr_perm;
975 975
976 if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) && 976 if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) &&
977 (!lock_may_write(file->f_path.dentry->d_inode, offset, cnt))) 977 (!lock_may_write(file->f_path.dentry->d_inode, offset, *cnt)))
978 goto out; 978 goto out;
979#endif 979#endif
980 980
@@ -1006,7 +1006,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
1006 host_err = vfs_writev(file, (struct iovec __user *)vec, vlen, &offset); 1006 host_err = vfs_writev(file, (struct iovec __user *)vec, vlen, &offset);
1007 set_fs(oldfs); 1007 set_fs(oldfs);
1008 if (host_err >= 0) { 1008 if (host_err >= 0) {
1009 nfsdstats.io_write += cnt; 1009 nfsdstats.io_write += host_err;
1010 fsnotify_modify(file->f_path.dentry); 1010 fsnotify_modify(file->f_path.dentry);
1011 } 1011 }
1012 1012
@@ -1051,9 +1051,10 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
1051 } 1051 }
1052 1052
1053 dprintk("nfsd: write complete host_err=%d\n", host_err); 1053 dprintk("nfsd: write complete host_err=%d\n", host_err);
1054 if (host_err >= 0) 1054 if (host_err >= 0) {
1055 err = 0; 1055 err = 0;
1056 else 1056 *cnt = host_err;
1057 } else
1057 err = nfserrno(host_err); 1058 err = nfserrno(host_err);
1058out: 1059out:
1059 return err; 1060 return err;
@@ -1095,7 +1096,7 @@ out:
1095 */ 1096 */
1096__be32 1097__be32
1097nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, 1098nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
1098 loff_t offset, struct kvec *vec, int vlen, unsigned long cnt, 1099 loff_t offset, struct kvec *vec, int vlen, unsigned long *cnt,
1099 int *stablep) 1100 int *stablep)
1100{ 1101{
1101 __be32 err = 0; 1102 __be32 err = 0;