Commit a334de28665b14f0a33df82699fa9a78cfeedf31
Committed by
Linus Torvalds
1 parent
93fbf1a5de
Exists in
master
and in
7 other branches
[PATCH] knfsd: check error status from vfs_getattr and i_op->fsync
Both vfs_getattr and i_op->fsync return error statuses which nfsd was largely ignoring. This as noticed when exporting directories using fuse. This patch cleans up most of the offences, which involves moving the call to vfs_getattr out of the xdr encoding routines (where it is too late to report an error) into the main NFS procedure handling routines. There is still a called to vfs_gettattr (related to the ACL code) where the status is ignored, and called to nfsd_sync_dir don't check return status either. Signed-off-by: Neil Brown <neilb@suse.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Showing 6 changed files with 75 additions and 55 deletions Side-by-side Diff
fs/nfsd/nfs3proc.c
... | ... | @@ -56,13 +56,20 @@ |
56 | 56 | nfsd3_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp, |
57 | 57 | struct nfsd3_attrstat *resp) |
58 | 58 | { |
59 | - int nfserr; | |
59 | + int err, nfserr; | |
60 | 60 | |
61 | 61 | dprintk("nfsd: GETATTR(3) %s\n", |
62 | - SVCFH_fmt(&argp->fh)); | |
62 | + SVCFH_fmt(&argp->fh)); | |
63 | 63 | |
64 | 64 | fh_copy(&resp->fh, &argp->fh); |
65 | 65 | nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP); |
66 | + if (nfserr) | |
67 | + RETURN_STATUS(nfserr); | |
68 | + | |
69 | + err = vfs_getattr(resp->fh.fh_export->ex_mnt, | |
70 | + resp->fh.fh_dentry, &resp->stat); | |
71 | + nfserr = nfserrno(err); | |
72 | + | |
66 | 73 | RETURN_STATUS(nfserr); |
67 | 74 | } |
68 | 75 |
fs/nfsd/nfs3xdr.c
... | ... | @@ -154,37 +154,34 @@ |
154 | 154 | } |
155 | 155 | |
156 | 156 | static inline u32 * |
157 | -encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp) | |
157 | +encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp, | |
158 | + struct kstat *stat) | |
158 | 159 | { |
159 | - struct vfsmount *mnt = fhp->fh_export->ex_mnt; | |
160 | 160 | struct dentry *dentry = fhp->fh_dentry; |
161 | - struct kstat stat; | |
162 | 161 | struct timespec time; |
163 | 162 | |
164 | - vfs_getattr(mnt, dentry, &stat); | |
165 | - | |
166 | - *p++ = htonl(nfs3_ftypes[(stat.mode & S_IFMT) >> 12]); | |
167 | - *p++ = htonl((u32) stat.mode); | |
168 | - *p++ = htonl((u32) stat.nlink); | |
169 | - *p++ = htonl((u32) nfsd_ruid(rqstp, stat.uid)); | |
170 | - *p++ = htonl((u32) nfsd_rgid(rqstp, stat.gid)); | |
171 | - if (S_ISLNK(stat.mode) && stat.size > NFS3_MAXPATHLEN) { | |
163 | + *p++ = htonl(nfs3_ftypes[(stat->mode & S_IFMT) >> 12]); | |
164 | + *p++ = htonl((u32) stat->mode); | |
165 | + *p++ = htonl((u32) stat->nlink); | |
166 | + *p++ = htonl((u32) nfsd_ruid(rqstp, stat->uid)); | |
167 | + *p++ = htonl((u32) nfsd_rgid(rqstp, stat->gid)); | |
168 | + if (S_ISLNK(stat->mode) && stat->size > NFS3_MAXPATHLEN) { | |
172 | 169 | p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN); |
173 | 170 | } else { |
174 | - p = xdr_encode_hyper(p, (u64) stat.size); | |
171 | + p = xdr_encode_hyper(p, (u64) stat->size); | |
175 | 172 | } |
176 | - p = xdr_encode_hyper(p, ((u64)stat.blocks) << 9); | |
177 | - *p++ = htonl((u32) MAJOR(stat.rdev)); | |
178 | - *p++ = htonl((u32) MINOR(stat.rdev)); | |
173 | + p = xdr_encode_hyper(p, ((u64)stat->blocks) << 9); | |
174 | + *p++ = htonl((u32) MAJOR(stat->rdev)); | |
175 | + *p++ = htonl((u32) MINOR(stat->rdev)); | |
179 | 176 | if (is_fsid(fhp, rqstp->rq_reffh)) |
180 | 177 | p = xdr_encode_hyper(p, (u64) fhp->fh_export->ex_fsid); |
181 | 178 | else |
182 | - p = xdr_encode_hyper(p, (u64) huge_encode_dev(stat.dev)); | |
183 | - p = xdr_encode_hyper(p, (u64) stat.ino); | |
184 | - p = encode_time3(p, &stat.atime); | |
179 | + p = xdr_encode_hyper(p, (u64) huge_encode_dev(stat->dev)); | |
180 | + p = xdr_encode_hyper(p, (u64) stat->ino); | |
181 | + p = encode_time3(p, &stat->atime); | |
185 | 182 | lease_get_mtime(dentry->d_inode, &time); |
186 | 183 | p = encode_time3(p, &time); |
187 | - p = encode_time3(p, &stat.ctime); | |
184 | + p = encode_time3(p, &stat->ctime); | |
188 | 185 | |
189 | 186 | return p; |
190 | 187 | } |
... | ... | @@ -232,8 +229,14 @@ |
232 | 229 | { |
233 | 230 | struct dentry *dentry = fhp->fh_dentry; |
234 | 231 | if (dentry && dentry->d_inode != NULL) { |
235 | - *p++ = xdr_one; /* attributes follow */ | |
236 | - return encode_fattr3(rqstp, p, fhp); | |
232 | + int err; | |
233 | + struct kstat stat; | |
234 | + | |
235 | + err = vfs_getattr(fhp->fh_export->ex_mnt, dentry, &stat); | |
236 | + if (!err) { | |
237 | + *p++ = xdr_one; /* attributes follow */ | |
238 | + return encode_fattr3(rqstp, p, fhp, &stat); | |
239 | + } | |
237 | 240 | } |
238 | 241 | *p++ = xdr_zero; |
239 | 242 | return p; |
... | ... | @@ -616,7 +619,7 @@ |
616 | 619 | struct nfsd3_attrstat *resp) |
617 | 620 | { |
618 | 621 | if (resp->status == 0) |
619 | - p = encode_fattr3(rqstp, p, &resp->fh); | |
622 | + p = encode_fattr3(rqstp, p, &resp->fh, &resp->stat); | |
620 | 623 | return xdr_ressize_check(rqstp, p); |
621 | 624 | } |
622 | 625 |
fs/nfsd/nfsxdr.c
... | ... | @@ -152,46 +152,44 @@ |
152 | 152 | } |
153 | 153 | |
154 | 154 | static inline u32 * |
155 | -encode_fattr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp) | |
155 | +encode_fattr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp, | |
156 | + struct kstat *stat) | |
156 | 157 | { |
157 | - struct vfsmount *mnt = fhp->fh_export->ex_mnt; | |
158 | 158 | struct dentry *dentry = fhp->fh_dentry; |
159 | - struct kstat stat; | |
160 | 159 | int type; |
161 | 160 | struct timespec time; |
162 | 161 | |
163 | - vfs_getattr(mnt, dentry, &stat); | |
164 | - type = (stat.mode & S_IFMT); | |
162 | + type = (stat->mode & S_IFMT); | |
165 | 163 | |
166 | 164 | *p++ = htonl(nfs_ftypes[type >> 12]); |
167 | - *p++ = htonl((u32) stat.mode); | |
168 | - *p++ = htonl((u32) stat.nlink); | |
169 | - *p++ = htonl((u32) nfsd_ruid(rqstp, stat.uid)); | |
170 | - *p++ = htonl((u32) nfsd_rgid(rqstp, stat.gid)); | |
165 | + *p++ = htonl((u32) stat->mode); | |
166 | + *p++ = htonl((u32) stat->nlink); | |
167 | + *p++ = htonl((u32) nfsd_ruid(rqstp, stat->uid)); | |
168 | + *p++ = htonl((u32) nfsd_rgid(rqstp, stat->gid)); | |
171 | 169 | |
172 | - if (S_ISLNK(type) && stat.size > NFS_MAXPATHLEN) { | |
170 | + if (S_ISLNK(type) && stat->size > NFS_MAXPATHLEN) { | |
173 | 171 | *p++ = htonl(NFS_MAXPATHLEN); |
174 | 172 | } else { |
175 | - *p++ = htonl((u32) stat.size); | |
173 | + *p++ = htonl((u32) stat->size); | |
176 | 174 | } |
177 | - *p++ = htonl((u32) stat.blksize); | |
175 | + *p++ = htonl((u32) stat->blksize); | |
178 | 176 | if (S_ISCHR(type) || S_ISBLK(type)) |
179 | - *p++ = htonl(new_encode_dev(stat.rdev)); | |
177 | + *p++ = htonl(new_encode_dev(stat->rdev)); | |
180 | 178 | else |
181 | 179 | *p++ = htonl(0xffffffff); |
182 | - *p++ = htonl((u32) stat.blocks); | |
180 | + *p++ = htonl((u32) stat->blocks); | |
183 | 181 | if (is_fsid(fhp, rqstp->rq_reffh)) |
184 | 182 | *p++ = htonl((u32) fhp->fh_export->ex_fsid); |
185 | 183 | else |
186 | - *p++ = htonl(new_encode_dev(stat.dev)); | |
187 | - *p++ = htonl((u32) stat.ino); | |
188 | - *p++ = htonl((u32) stat.atime.tv_sec); | |
189 | - *p++ = htonl(stat.atime.tv_nsec ? stat.atime.tv_nsec / 1000 : 0); | |
184 | + *p++ = htonl(new_encode_dev(stat->dev)); | |
185 | + *p++ = htonl((u32) stat->ino); | |
186 | + *p++ = htonl((u32) stat->atime.tv_sec); | |
187 | + *p++ = htonl(stat->atime.tv_nsec ? stat->atime.tv_nsec / 1000 : 0); | |
190 | 188 | lease_get_mtime(dentry->d_inode, &time); |
191 | 189 | *p++ = htonl((u32) time.tv_sec); |
192 | 190 | *p++ = htonl(time.tv_nsec ? time.tv_nsec / 1000 : 0); |
193 | - *p++ = htonl((u32) stat.ctime.tv_sec); | |
194 | - *p++ = htonl(stat.ctime.tv_nsec ? stat.ctime.tv_nsec / 1000 : 0); | |
191 | + *p++ = htonl((u32) stat->ctime.tv_sec); | |
192 | + *p++ = htonl(stat->ctime.tv_nsec ? stat->ctime.tv_nsec / 1000 : 0); | |
195 | 193 | |
196 | 194 | return p; |
197 | 195 | } |
... | ... | @@ -199,7 +197,9 @@ |
199 | 197 | /* Helper function for NFSv2 ACL code */ |
200 | 198 | u32 *nfs2svc_encode_fattr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp) |
201 | 199 | { |
202 | - return encode_fattr(rqstp, p, fhp); | |
200 | + struct kstat stat; | |
201 | + vfs_getattr(fhp->fh_export->ex_mnt, fhp->fh_dentry, &stat); | |
202 | + return encode_fattr(rqstp, p, fhp, &stat); | |
203 | 203 | } |
204 | 204 | |
205 | 205 | /* |
... | ... | @@ -394,7 +394,7 @@ |
394 | 394 | nfssvc_encode_attrstat(struct svc_rqst *rqstp, u32 *p, |
395 | 395 | struct nfsd_attrstat *resp) |
396 | 396 | { |
397 | - p = encode_fattr(rqstp, p, &resp->fh); | |
397 | + p = encode_fattr(rqstp, p, &resp->fh, &resp->stat); | |
398 | 398 | return xdr_ressize_check(rqstp, p); |
399 | 399 | } |
400 | 400 | |
... | ... | @@ -403,7 +403,7 @@ |
403 | 403 | struct nfsd_diropres *resp) |
404 | 404 | { |
405 | 405 | p = encode_fh(p, &resp->fh); |
406 | - p = encode_fattr(rqstp, p, &resp->fh); | |
406 | + p = encode_fattr(rqstp, p, &resp->fh, &resp->stat); | |
407 | 407 | return xdr_ressize_check(rqstp, p); |
408 | 408 | } |
409 | 409 | |
... | ... | @@ -428,7 +428,7 @@ |
428 | 428 | nfssvc_encode_readres(struct svc_rqst *rqstp, u32 *p, |
429 | 429 | struct nfsd_readres *resp) |
430 | 430 | { |
431 | - p = encode_fattr(rqstp, p, &resp->fh); | |
431 | + p = encode_fattr(rqstp, p, &resp->fh, &resp->stat); | |
432 | 432 | *p++ = htonl(resp->count); |
433 | 433 | xdr_ressize_check(rqstp, p); |
434 | 434 |
fs/nfsd/vfs.c
... | ... | @@ -717,27 +717,33 @@ |
717 | 717 | * As this calls fsync (not fdatasync) there is no need for a write_inode |
718 | 718 | * after it. |
719 | 719 | */ |
720 | -static inline void nfsd_dosync(struct file *filp, struct dentry *dp, | |
721 | - struct file_operations *fop) | |
720 | +static inline int nfsd_dosync(struct file *filp, struct dentry *dp, | |
721 | + struct file_operations *fop) | |
722 | 722 | { |
723 | 723 | struct inode *inode = dp->d_inode; |
724 | 724 | int (*fsync) (struct file *, struct dentry *, int); |
725 | + int err = nfs_ok; | |
725 | 726 | |
726 | 727 | filemap_fdatawrite(inode->i_mapping); |
727 | 728 | if (fop && (fsync = fop->fsync)) |
728 | - fsync(filp, dp, 0); | |
729 | + err=fsync(filp, dp, 0); | |
729 | 730 | filemap_fdatawait(inode->i_mapping); |
731 | + | |
732 | + return nfserrno(err); | |
730 | 733 | } |
731 | 734 | |
732 | 735 | |
733 | -static void | |
736 | +static int | |
734 | 737 | nfsd_sync(struct file *filp) |
735 | 738 | { |
739 | + int err; | |
736 | 740 | struct inode *inode = filp->f_dentry->d_inode; |
737 | 741 | dprintk("nfsd: sync file %s\n", filp->f_dentry->d_name.name); |
738 | 742 | down(&inode->i_sem); |
739 | - nfsd_dosync(filp, filp->f_dentry, filp->f_op); | |
743 | + err=nfsd_dosync(filp, filp->f_dentry, filp->f_op); | |
740 | 744 | up(&inode->i_sem); |
745 | + | |
746 | + return err; | |
741 | 747 | } |
742 | 748 | |
743 | 749 | void |
... | ... | @@ -962,7 +968,7 @@ |
962 | 968 | |
963 | 969 | if (inode->i_state & I_DIRTY) { |
964 | 970 | dprintk("nfsd: write sync %d\n", current->pid); |
965 | - nfsd_sync(file); | |
971 | + err=nfsd_sync(file); | |
966 | 972 | } |
967 | 973 | #if 0 |
968 | 974 | wake_up(&inode->i_wait); |
... | ... | @@ -1066,7 +1072,7 @@ |
1066 | 1072 | return err; |
1067 | 1073 | if (EX_ISSYNC(fhp->fh_export)) { |
1068 | 1074 | if (file->f_op && file->f_op->fsync) { |
1069 | - nfsd_sync(file); | |
1075 | + err = nfsd_sync(file); | |
1070 | 1076 | } else { |
1071 | 1077 | err = nfserr_notsupp; |
1072 | 1078 | } |
include/linux/nfsd/xdr.h
... | ... | @@ -88,10 +88,12 @@ |
88 | 88 | |
89 | 89 | struct nfsd_attrstat { |
90 | 90 | struct svc_fh fh; |
91 | + struct kstat stat; | |
91 | 92 | }; |
92 | 93 | |
93 | 94 | struct nfsd_diropres { |
94 | 95 | struct svc_fh fh; |
96 | + struct kstat stat; | |
95 | 97 | }; |
96 | 98 | |
97 | 99 | struct nfsd_readlinkres { |
... | ... | @@ -101,6 +103,7 @@ |
101 | 103 | struct nfsd_readres { |
102 | 104 | struct svc_fh fh; |
103 | 105 | unsigned long count; |
106 | + struct kstat stat; | |
104 | 107 | }; |
105 | 108 | |
106 | 109 | struct nfsd_readdirres { |