diff options
Diffstat (limited to 'fs/nfsd/vfs.c')
-rw-r--r-- | fs/nfsd/vfs.c | 156 |
1 files changed, 91 insertions, 65 deletions
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 7e6aa245b5d5..e90f4a8a1d01 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c | |||
@@ -23,7 +23,7 @@ | |||
23 | #include <linux/file.h> | 23 | #include <linux/file.h> |
24 | #include <linux/mount.h> | 24 | #include <linux/mount.h> |
25 | #include <linux/major.h> | 25 | #include <linux/major.h> |
26 | #include <linux/ext2_fs.h> | 26 | #include <linux/splice.h> |
27 | #include <linux/proc_fs.h> | 27 | #include <linux/proc_fs.h> |
28 | #include <linux/stat.h> | 28 | #include <linux/stat.h> |
29 | #include <linux/fcntl.h> | 29 | #include <linux/fcntl.h> |
@@ -113,7 +113,7 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp, | |||
113 | 113 | ||
114 | while (follow_down(&mnt,&mounts)&&d_mountpoint(mounts)); | 114 | while (follow_down(&mnt,&mounts)&&d_mountpoint(mounts)); |
115 | 115 | ||
116 | exp2 = exp_get_by_name(exp->ex_client, mnt, mounts, &rqstp->rq_chandle); | 116 | exp2 = rqst_exp_get_by_name(rqstp, mnt, mounts); |
117 | if (IS_ERR(exp2)) { | 117 | if (IS_ERR(exp2)) { |
118 | err = PTR_ERR(exp2); | 118 | err = PTR_ERR(exp2); |
119 | dput(mounts); | 119 | dput(mounts); |
@@ -135,21 +135,10 @@ out: | |||
135 | return err; | 135 | return err; |
136 | } | 136 | } |
137 | 137 | ||
138 | /* | ||
139 | * Look up one component of a pathname. | ||
140 | * N.B. After this call _both_ fhp and resfh need an fh_put | ||
141 | * | ||
142 | * If the lookup would cross a mountpoint, and the mounted filesystem | ||
143 | * is exported to the client with NFSEXP_NOHIDE, then the lookup is | ||
144 | * accepted as it stands and the mounted directory is | ||
145 | * returned. Otherwise the covered directory is returned. | ||
146 | * NOTE: this mountpoint crossing is not supported properly by all | ||
147 | * clients and is explicitly disallowed for NFSv3 | ||
148 | * NeilBrown <neilb@cse.unsw.edu.au> | ||
149 | */ | ||
150 | __be32 | 138 | __be32 |
151 | nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, | 139 | nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp, |
152 | int len, struct svc_fh *resfh) | 140 | const char *name, int len, |
141 | struct svc_export **exp_ret, struct dentry **dentry_ret) | ||
153 | { | 142 | { |
154 | struct svc_export *exp; | 143 | struct svc_export *exp; |
155 | struct dentry *dparent; | 144 | struct dentry *dparent; |
@@ -168,8 +157,6 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, | |||
168 | exp = fhp->fh_export; | 157 | exp = fhp->fh_export; |
169 | exp_get(exp); | 158 | exp_get(exp); |
170 | 159 | ||
171 | err = nfserr_acces; | ||
172 | |||
173 | /* Lookup the name, but don't follow links */ | 160 | /* Lookup the name, but don't follow links */ |
174 | if (isdotent(name, len)) { | 161 | if (isdotent(name, len)) { |
175 | if (len==1) | 162 | if (len==1) |
@@ -190,17 +177,15 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, | |||
190 | dput(dentry); | 177 | dput(dentry); |
191 | dentry = dp; | 178 | dentry = dp; |
192 | 179 | ||
193 | exp2 = exp_parent(exp->ex_client, mnt, dentry, | 180 | exp2 = rqst_exp_parent(rqstp, mnt, dentry); |
194 | &rqstp->rq_chandle); | 181 | if (PTR_ERR(exp2) == -ENOENT) { |
195 | if (IS_ERR(exp2)) { | 182 | dput(dentry); |
183 | dentry = dget(dparent); | ||
184 | } else if (IS_ERR(exp2)) { | ||
196 | host_err = PTR_ERR(exp2); | 185 | host_err = PTR_ERR(exp2); |
197 | dput(dentry); | 186 | dput(dentry); |
198 | mntput(mnt); | 187 | mntput(mnt); |
199 | goto out_nfserr; | 188 | goto out_nfserr; |
200 | } | ||
201 | if (!exp2) { | ||
202 | dput(dentry); | ||
203 | dentry = dget(dparent); | ||
204 | } else { | 189 | } else { |
205 | exp_put(exp); | 190 | exp_put(exp); |
206 | exp = exp2; | 191 | exp = exp2; |
@@ -223,6 +208,41 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, | |||
223 | } | 208 | } |
224 | } | 209 | } |
225 | } | 210 | } |
211 | *dentry_ret = dentry; | ||
212 | *exp_ret = exp; | ||
213 | return 0; | ||
214 | |||
215 | out_nfserr: | ||
216 | exp_put(exp); | ||
217 | return nfserrno(host_err); | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * Look up one component of a pathname. | ||
222 | * N.B. After this call _both_ fhp and resfh need an fh_put | ||
223 | * | ||
224 | * If the lookup would cross a mountpoint, and the mounted filesystem | ||
225 | * is exported to the client with NFSEXP_NOHIDE, then the lookup is | ||
226 | * accepted as it stands and the mounted directory is | ||
227 | * returned. Otherwise the covered directory is returned. | ||
228 | * NOTE: this mountpoint crossing is not supported properly by all | ||
229 | * clients and is explicitly disallowed for NFSv3 | ||
230 | * NeilBrown <neilb@cse.unsw.edu.au> | ||
231 | */ | ||
232 | __be32 | ||
233 | nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, | ||
234 | int len, struct svc_fh *resfh) | ||
235 | { | ||
236 | struct svc_export *exp; | ||
237 | struct dentry *dentry; | ||
238 | __be32 err; | ||
239 | |||
240 | err = nfsd_lookup_dentry(rqstp, fhp, name, len, &exp, &dentry); | ||
241 | if (err) | ||
242 | return err; | ||
243 | err = check_nfsd_access(exp, rqstp); | ||
244 | if (err) | ||
245 | goto out; | ||
226 | /* | 246 | /* |
227 | * Note: we compose the file handle now, but as the | 247 | * Note: we compose the file handle now, but as the |
228 | * dentry may be negative, it may need to be updated. | 248 | * dentry may be negative, it may need to be updated. |
@@ -230,16 +250,13 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, | |||
230 | err = fh_compose(resfh, exp, dentry, fhp); | 250 | err = fh_compose(resfh, exp, dentry, fhp); |
231 | if (!err && !dentry->d_inode) | 251 | if (!err && !dentry->d_inode) |
232 | err = nfserr_noent; | 252 | err = nfserr_noent; |
233 | dput(dentry); | ||
234 | out: | 253 | out: |
254 | dput(dentry); | ||
235 | exp_put(exp); | 255 | exp_put(exp); |
236 | return err; | 256 | return err; |
237 | |||
238 | out_nfserr: | ||
239 | err = nfserrno(host_err); | ||
240 | goto out; | ||
241 | } | 257 | } |
242 | 258 | ||
259 | |||
243 | /* | 260 | /* |
244 | * Set various file attributes. | 261 | * Set various file attributes. |
245 | * N.B. After this call fhp needs an fh_put | 262 | * N.B. After this call fhp needs an fh_put |
@@ -311,7 +328,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, | |||
311 | /* The size case is special. It changes the file as well as the attributes. */ | 328 | /* The size case is special. It changes the file as well as the attributes. */ |
312 | if (iap->ia_valid & ATTR_SIZE) { | 329 | if (iap->ia_valid & ATTR_SIZE) { |
313 | if (iap->ia_size < inode->i_size) { | 330 | if (iap->ia_size < inode->i_size) { |
314 | err = nfsd_permission(fhp->fh_export, dentry, MAY_TRUNC|MAY_OWNER_OVERRIDE); | 331 | err = nfsd_permission(rqstp, fhp->fh_export, dentry, MAY_TRUNC|MAY_OWNER_OVERRIDE); |
315 | if (err) | 332 | if (err) |
316 | goto out; | 333 | goto out; |
317 | } | 334 | } |
@@ -435,7 +452,7 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, | |||
435 | /* Get inode */ | 452 | /* Get inode */ |
436 | error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, MAY_SATTR); | 453 | error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, MAY_SATTR); |
437 | if (error) | 454 | if (error) |
438 | goto out; | 455 | return error; |
439 | 456 | ||
440 | dentry = fhp->fh_dentry; | 457 | dentry = fhp->fh_dentry; |
441 | inode = dentry->d_inode; | 458 | inode = dentry->d_inode; |
@@ -444,33 +461,25 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, | |||
444 | 461 | ||
445 | host_error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags); | 462 | host_error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags); |
446 | if (host_error == -EINVAL) { | 463 | if (host_error == -EINVAL) { |
447 | error = nfserr_attrnotsupp; | 464 | return nfserr_attrnotsupp; |
448 | goto out; | ||
449 | } else if (host_error < 0) | 465 | } else if (host_error < 0) |
450 | goto out_nfserr; | 466 | goto out_nfserr; |
451 | 467 | ||
452 | host_error = set_nfsv4_acl_one(dentry, pacl, POSIX_ACL_XATTR_ACCESS); | 468 | host_error = set_nfsv4_acl_one(dentry, pacl, POSIX_ACL_XATTR_ACCESS); |
453 | if (host_error < 0) | 469 | if (host_error < 0) |
454 | goto out_nfserr; | 470 | goto out_release; |
455 | 471 | ||
456 | if (S_ISDIR(inode->i_mode)) { | 472 | if (S_ISDIR(inode->i_mode)) |
457 | host_error = set_nfsv4_acl_one(dentry, dpacl, POSIX_ACL_XATTR_DEFAULT); | 473 | host_error = set_nfsv4_acl_one(dentry, dpacl, POSIX_ACL_XATTR_DEFAULT); |
458 | if (host_error < 0) | ||
459 | goto out_nfserr; | ||
460 | } | ||
461 | 474 | ||
462 | error = nfs_ok; | 475 | out_release: |
463 | |||
464 | out: | ||
465 | posix_acl_release(pacl); | 476 | posix_acl_release(pacl); |
466 | posix_acl_release(dpacl); | 477 | posix_acl_release(dpacl); |
467 | return (error); | ||
468 | out_nfserr: | 478 | out_nfserr: |
469 | if (host_error == -EOPNOTSUPP) | 479 | if (host_error == -EOPNOTSUPP) |
470 | error = nfserr_attrnotsupp; | 480 | return nfserr_attrnotsupp; |
471 | else | 481 | else |
472 | error = nfserrno(host_error); | 482 | return nfserrno(host_error); |
473 | goto out; | ||
474 | } | 483 | } |
475 | 484 | ||
476 | static struct posix_acl * | 485 | static struct posix_acl * |
@@ -607,7 +616,7 @@ nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access, u32 *suppor | |||
607 | 616 | ||
608 | sresult |= map->access; | 617 | sresult |= map->access; |
609 | 618 | ||
610 | err2 = nfsd_permission(export, dentry, map->how); | 619 | err2 = nfsd_permission(rqstp, export, dentry, map->how); |
611 | switch (err2) { | 620 | switch (err2) { |
612 | case nfs_ok: | 621 | case nfs_ok: |
613 | result |= map->access; | 622 | result |= map->access; |
@@ -801,26 +810,32 @@ found: | |||
801 | } | 810 | } |
802 | 811 | ||
803 | /* | 812 | /* |
804 | * Grab and keep cached pages assosiated with a file in the svc_rqst | 813 | * Grab and keep cached pages associated with a file in the svc_rqst |
805 | * so that they can be passed to the netowork sendmsg/sendpage routines | 814 | * so that they can be passed to the network sendmsg/sendpage routines |
806 | * directrly. They will be released after the sending has completed. | 815 | * directly. They will be released after the sending has completed. |
807 | */ | 816 | */ |
808 | static int | 817 | static int |
809 | nfsd_read_actor(read_descriptor_t *desc, struct page *page, unsigned long offset , unsigned long size) | 818 | nfsd_splice_actor(struct pipe_inode_info *pipe, struct pipe_buffer *buf, |
819 | struct splice_desc *sd) | ||
810 | { | 820 | { |
811 | unsigned long count = desc->count; | 821 | struct svc_rqst *rqstp = sd->u.data; |
812 | struct svc_rqst *rqstp = desc->arg.data; | ||
813 | struct page **pp = rqstp->rq_respages + rqstp->rq_resused; | 822 | struct page **pp = rqstp->rq_respages + rqstp->rq_resused; |
823 | struct page *page = buf->page; | ||
824 | size_t size; | ||
825 | int ret; | ||
826 | |||
827 | ret = buf->ops->confirm(pipe, buf); | ||
828 | if (unlikely(ret)) | ||
829 | return ret; | ||
814 | 830 | ||
815 | if (size > count) | 831 | size = sd->len; |
816 | size = count; | ||
817 | 832 | ||
818 | if (rqstp->rq_res.page_len == 0) { | 833 | if (rqstp->rq_res.page_len == 0) { |
819 | get_page(page); | 834 | get_page(page); |
820 | put_page(*pp); | 835 | put_page(*pp); |
821 | *pp = page; | 836 | *pp = page; |
822 | rqstp->rq_resused++; | 837 | rqstp->rq_resused++; |
823 | rqstp->rq_res.page_base = offset; | 838 | rqstp->rq_res.page_base = buf->offset; |
824 | rqstp->rq_res.page_len = size; | 839 | rqstp->rq_res.page_len = size; |
825 | } else if (page != pp[-1]) { | 840 | } else if (page != pp[-1]) { |
826 | get_page(page); | 841 | get_page(page); |
@@ -832,11 +847,15 @@ nfsd_read_actor(read_descriptor_t *desc, struct page *page, unsigned long offset | |||
832 | } else | 847 | } else |
833 | rqstp->rq_res.page_len += size; | 848 | rqstp->rq_res.page_len += size; |
834 | 849 | ||
835 | desc->count = count - size; | ||
836 | desc->written += size; | ||
837 | return size; | 850 | return size; |
838 | } | 851 | } |
839 | 852 | ||
853 | static int nfsd_direct_splice_actor(struct pipe_inode_info *pipe, | ||
854 | struct splice_desc *sd) | ||
855 | { | ||
856 | return __splice_from_pipe(pipe, sd, nfsd_splice_actor); | ||
857 | } | ||
858 | |||
840 | static __be32 | 859 | static __be32 |
841 | nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, | 860 | nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, |
842 | loff_t offset, struct kvec *vec, int vlen, unsigned long *count) | 861 | loff_t offset, struct kvec *vec, int vlen, unsigned long *count) |
@@ -861,10 +880,16 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, | |||
861 | if (ra && ra->p_set) | 880 | if (ra && ra->p_set) |
862 | file->f_ra = ra->p_ra; | 881 | file->f_ra = ra->p_ra; |
863 | 882 | ||
864 | if (file->f_op->sendfile && rqstp->rq_sendfile_ok) { | 883 | if (file->f_op->splice_read && rqstp->rq_splice_ok) { |
884 | struct splice_desc sd = { | ||
885 | .len = 0, | ||
886 | .total_len = *count, | ||
887 | .pos = offset, | ||
888 | .u.data = rqstp, | ||
889 | }; | ||
890 | |||
865 | rqstp->rq_resused = 1; | 891 | rqstp->rq_resused = 1; |
866 | host_err = file->f_op->sendfile(file, &offset, *count, | 892 | host_err = splice_direct_to_actor(file, &sd, nfsd_direct_splice_actor); |
867 | nfsd_read_actor, rqstp); | ||
868 | } else { | 893 | } else { |
869 | oldfs = get_fs(); | 894 | oldfs = get_fs(); |
870 | set_fs(KERNEL_DS); | 895 | set_fs(KERNEL_DS); |
@@ -1018,7 +1043,7 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, | |||
1018 | __be32 err; | 1043 | __be32 err; |
1019 | 1044 | ||
1020 | if (file) { | 1045 | if (file) { |
1021 | err = nfsd_permission(fhp->fh_export, fhp->fh_dentry, | 1046 | err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry, |
1022 | MAY_READ|MAY_OWNER_OVERRIDE); | 1047 | MAY_READ|MAY_OWNER_OVERRIDE); |
1023 | if (err) | 1048 | if (err) |
1024 | goto out; | 1049 | goto out; |
@@ -1047,7 +1072,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, | |||
1047 | __be32 err = 0; | 1072 | __be32 err = 0; |
1048 | 1073 | ||
1049 | if (file) { | 1074 | if (file) { |
1050 | err = nfsd_permission(fhp->fh_export, fhp->fh_dentry, | 1075 | err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry, |
1051 | MAY_WRITE|MAY_OWNER_OVERRIDE); | 1076 | MAY_WRITE|MAY_OWNER_OVERRIDE); |
1052 | if (err) | 1077 | if (err) |
1053 | goto out; | 1078 | goto out; |
@@ -1776,7 +1801,8 @@ nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat) | |||
1776 | * Check for a user's access permissions to this inode. | 1801 | * Check for a user's access permissions to this inode. |
1777 | */ | 1802 | */ |
1778 | __be32 | 1803 | __be32 |
1779 | nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc) | 1804 | nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, |
1805 | struct dentry *dentry, int acc) | ||
1780 | { | 1806 | { |
1781 | struct inode *inode = dentry->d_inode; | 1807 | struct inode *inode = dentry->d_inode; |
1782 | int err; | 1808 | int err; |
@@ -1807,7 +1833,7 @@ nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc) | |||
1807 | */ | 1833 | */ |
1808 | if (!(acc & MAY_LOCAL_ACCESS)) | 1834 | if (!(acc & MAY_LOCAL_ACCESS)) |
1809 | if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) { | 1835 | if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) { |
1810 | if (EX_RDONLY(exp) || IS_RDONLY(inode)) | 1836 | if (EX_RDONLY(exp, rqstp) || IS_RDONLY(inode)) |
1811 | return nfserr_rofs; | 1837 | return nfserr_rofs; |
1812 | if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode)) | 1838 | if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode)) |
1813 | return nfserr_perm; | 1839 | return nfserr_perm; |