diff options
Diffstat (limited to 'fs/nfsd/vfs.c')
-rw-r--r-- | fs/nfsd/vfs.c | 122 |
1 files changed, 69 insertions, 53 deletions
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 8604e35bd48e..ee96a897a29e 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c | |||
@@ -113,21 +113,21 @@ 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); |
120 | mntput(mnt); | 120 | mntput(mnt); |
121 | goto out; | 121 | goto out; |
122 | } | 122 | } |
123 | if (exp2 && ((exp->ex_flags & NFSEXP_CROSSMOUNT) || EX_NOHIDE(exp2))) { | 123 | if ((exp->ex_flags & NFSEXP_CROSSMOUNT) || EX_NOHIDE(exp2)) { |
124 | /* successfully crossed mount point */ | 124 | /* successfully crossed mount point */ |
125 | exp_put(exp); | 125 | exp_put(exp); |
126 | *expp = exp2; | 126 | *expp = exp2; |
127 | dput(dentry); | 127 | dput(dentry); |
128 | *dpp = mounts; | 128 | *dpp = mounts; |
129 | } else { | 129 | } else { |
130 | if (exp2) exp_put(exp2); | 130 | exp_put(exp2); |
131 | dput(mounts); | 131 | dput(mounts); |
132 | } | 132 | } |
133 | mntput(mnt); | 133 | mntput(mnt); |
@@ -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; |
@@ -879,6 +888,7 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, | |||
879 | .u.data = rqstp, | 888 | .u.data = rqstp, |
880 | }; | 889 | }; |
881 | 890 | ||
891 | rqstp->rq_resused = 1; | ||
882 | host_err = splice_direct_to_actor(file, &sd, nfsd_direct_splice_actor); | 892 | host_err = splice_direct_to_actor(file, &sd, nfsd_direct_splice_actor); |
883 | } else { | 893 | } else { |
884 | oldfs = get_fs(); | 894 | oldfs = get_fs(); |
@@ -1033,7 +1043,7 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, | |||
1033 | __be32 err; | 1043 | __be32 err; |
1034 | 1044 | ||
1035 | if (file) { | 1045 | if (file) { |
1036 | err = nfsd_permission(fhp->fh_export, fhp->fh_dentry, | 1046 | err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry, |
1037 | MAY_READ|MAY_OWNER_OVERRIDE); | 1047 | MAY_READ|MAY_OWNER_OVERRIDE); |
1038 | if (err) | 1048 | if (err) |
1039 | goto out; | 1049 | goto out; |
@@ -1062,7 +1072,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, | |||
1062 | __be32 err = 0; | 1072 | __be32 err = 0; |
1063 | 1073 | ||
1064 | if (file) { | 1074 | if (file) { |
1065 | err = nfsd_permission(fhp->fh_export, fhp->fh_dentry, | 1075 | err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry, |
1066 | MAY_WRITE|MAY_OWNER_OVERRIDE); | 1076 | MAY_WRITE|MAY_OWNER_OVERRIDE); |
1067 | if (err) | 1077 | if (err) |
1068 | goto out; | 1078 | goto out; |
@@ -1787,11 +1797,17 @@ nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat) | |||
1787 | return err; | 1797 | return err; |
1788 | } | 1798 | } |
1789 | 1799 | ||
1800 | static int exp_rdonly(struct svc_rqst *rqstp, struct svc_export *exp) | ||
1801 | { | ||
1802 | return nfsexp_flags(rqstp, exp) & NFSEXP_READONLY; | ||
1803 | } | ||
1804 | |||
1790 | /* | 1805 | /* |
1791 | * Check for a user's access permissions to this inode. | 1806 | * Check for a user's access permissions to this inode. |
1792 | */ | 1807 | */ |
1793 | __be32 | 1808 | __be32 |
1794 | nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc) | 1809 | nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, |
1810 | struct dentry *dentry, int acc) | ||
1795 | { | 1811 | { |
1796 | struct inode *inode = dentry->d_inode; | 1812 | struct inode *inode = dentry->d_inode; |
1797 | int err; | 1813 | int err; |
@@ -1822,7 +1838,7 @@ nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc) | |||
1822 | */ | 1838 | */ |
1823 | if (!(acc & MAY_LOCAL_ACCESS)) | 1839 | if (!(acc & MAY_LOCAL_ACCESS)) |
1824 | if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) { | 1840 | if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) { |
1825 | if (EX_RDONLY(exp) || IS_RDONLY(inode)) | 1841 | if (exp_rdonly(rqstp, exp) || IS_RDONLY(inode)) |
1826 | return nfserr_rofs; | 1842 | return nfserr_rofs; |
1827 | if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode)) | 1843 | if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode)) |
1828 | return nfserr_perm; | 1844 | return nfserr_perm; |
@@ -1905,7 +1921,7 @@ nfsd_racache_init(int cache_size) | |||
1905 | raparm_hash[i].pb_head = NULL; | 1921 | raparm_hash[i].pb_head = NULL; |
1906 | spin_lock_init(&raparm_hash[i].pb_lock); | 1922 | spin_lock_init(&raparm_hash[i].pb_lock); |
1907 | } | 1923 | } |
1908 | nperbucket = cache_size >> RAPARM_HASH_BITS; | 1924 | nperbucket = DIV_ROUND_UP(cache_size, RAPARM_HASH_SIZE); |
1909 | for (i = 0; i < cache_size - 1; i++) { | 1925 | for (i = 0; i < cache_size - 1; i++) { |
1910 | if (i % nperbucket == 0) | 1926 | if (i % nperbucket == 0) |
1911 | raparm_hash[j++].pb_head = raparml + i; | 1927 | raparm_hash[j++].pb_head = raparml + i; |