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, while (follow_down(&mnt,&mounts)&&d_mountpoint(mounts)); - exp2 = exp_get_by_name(exp->ex_client, mnt, mounts, &rqstp->rq_chandle); + exp2 = rqst_exp_get_by_name(rqstp, mnt, mounts); if (IS_ERR(exp2)) { err = PTR_ERR(exp2); dput(mounts); mntput(mnt); goto out; } - if (exp2 && ((exp->ex_flags & NFSEXP_CROSSMOUNT) || EX_NOHIDE(exp2))) { + if ((exp->ex_flags & NFSEXP_CROSSMOUNT) || EX_NOHIDE(exp2)) { /* successfully crossed mount point */ exp_put(exp); *expp = exp2; dput(dentry); *dpp = mounts; } else { - if (exp2) exp_put(exp2); + exp_put(exp2); dput(mounts); } mntput(mnt); @@ -135,21 +135,10 @@ out: return err; } -/* - * Look up one component of a pathname. - * N.B. After this call _both_ fhp and resfh need an fh_put - * - * If the lookup would cross a mountpoint, and the mounted filesystem - * is exported to the client with NFSEXP_NOHIDE, then the lookup is - * accepted as it stands and the mounted directory is - * returned. Otherwise the covered directory is returned. - * NOTE: this mountpoint crossing is not supported properly by all - * clients and is explicitly disallowed for NFSv3 - * NeilBrown <neilb@cse.unsw.edu.au> - */ __be32 -nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, - int len, struct svc_fh *resfh) +nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp, + const char *name, int len, + struct svc_export **exp_ret, struct dentry **dentry_ret) { struct svc_export *exp; struct dentry *dparent; @@ -168,8 +157,6 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, exp = fhp->fh_export; exp_get(exp); - err = nfserr_acces; - /* Lookup the name, but don't follow links */ if (isdotent(name, len)) { if (len==1) @@ -190,17 +177,15 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, dput(dentry); dentry = dp; - exp2 = exp_parent(exp->ex_client, mnt, dentry, - &rqstp->rq_chandle); - if (IS_ERR(exp2)) { + exp2 = rqst_exp_parent(rqstp, mnt, dentry); + if (PTR_ERR(exp2) == -ENOENT) { + dput(dentry); + dentry = dget(dparent); + } else if (IS_ERR(exp2)) { host_err = PTR_ERR(exp2); dput(dentry); mntput(mnt); goto out_nfserr; - } - if (!exp2) { - dput(dentry); - dentry = dget(dparent); } else { exp_put(exp); exp = exp2; @@ -223,6 +208,41 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, } } } + *dentry_ret = dentry; + *exp_ret = exp; + return 0; + +out_nfserr: + exp_put(exp); + return nfserrno(host_err); +} + +/* + * Look up one component of a pathname. + * N.B. After this call _both_ fhp and resfh need an fh_put + * + * If the lookup would cross a mountpoint, and the mounted filesystem + * is exported to the client with NFSEXP_NOHIDE, then the lookup is + * accepted as it stands and the mounted directory is + * returned. Otherwise the covered directory is returned. + * NOTE: this mountpoint crossing is not supported properly by all + * clients and is explicitly disallowed for NFSv3 + * NeilBrown <neilb@cse.unsw.edu.au> + */ +__be32 +nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, + int len, struct svc_fh *resfh) +{ + struct svc_export *exp; + struct dentry *dentry; + __be32 err; + + err = nfsd_lookup_dentry(rqstp, fhp, name, len, &exp, &dentry); + if (err) + return err; + err = check_nfsd_access(exp, rqstp); + if (err) + goto out; /* * Note: we compose the file handle now, but as the * 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, err = fh_compose(resfh, exp, dentry, fhp); if (!err && !dentry->d_inode) err = nfserr_noent; - dput(dentry); out: + dput(dentry); exp_put(exp); return err; - -out_nfserr: - err = nfserrno(host_err); - goto out; } + /* * Set various file attributes. * 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, /* The size case is special. It changes the file as well as the attributes. */ if (iap->ia_valid & ATTR_SIZE) { if (iap->ia_size < inode->i_size) { - err = nfsd_permission(fhp->fh_export, dentry, MAY_TRUNC|MAY_OWNER_OVERRIDE); + err = nfsd_permission(rqstp, fhp->fh_export, dentry, MAY_TRUNC|MAY_OWNER_OVERRIDE); if (err) goto out; } @@ -435,7 +452,7 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, /* Get inode */ error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, MAY_SATTR); if (error) - goto out; + return error; dentry = fhp->fh_dentry; inode = dentry->d_inode; @@ -444,33 +461,25 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, host_error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags); if (host_error == -EINVAL) { - error = nfserr_attrnotsupp; - goto out; + return nfserr_attrnotsupp; } else if (host_error < 0) goto out_nfserr; host_error = set_nfsv4_acl_one(dentry, pacl, POSIX_ACL_XATTR_ACCESS); if (host_error < 0) - goto out_nfserr; + goto out_release; - if (S_ISDIR(inode->i_mode)) { + if (S_ISDIR(inode->i_mode)) host_error = set_nfsv4_acl_one(dentry, dpacl, POSIX_ACL_XATTR_DEFAULT); - if (host_error < 0) - goto out_nfserr; - } - error = nfs_ok; - -out: +out_release: posix_acl_release(pacl); posix_acl_release(dpacl); - return (error); out_nfserr: if (host_error == -EOPNOTSUPP) - error = nfserr_attrnotsupp; + return nfserr_attrnotsupp; else - error = nfserrno(host_error); - goto out; + return nfserrno(host_error); } static struct posix_acl * @@ -607,7 +616,7 @@ nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access, u32 *suppor sresult |= map->access; - err2 = nfsd_permission(export, dentry, map->how); + err2 = nfsd_permission(rqstp, export, dentry, map->how); switch (err2) { case nfs_ok: result |= map->access; @@ -879,6 +888,7 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, .u.data = rqstp, }; + rqstp->rq_resused = 1; host_err = splice_direct_to_actor(file, &sd, nfsd_direct_splice_actor); } else { oldfs = get_fs(); @@ -1033,7 +1043,7 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, __be32 err; if (file) { - err = nfsd_permission(fhp->fh_export, fhp->fh_dentry, + err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry, MAY_READ|MAY_OWNER_OVERRIDE); if (err) goto out; @@ -1062,7 +1072,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, __be32 err = 0; if (file) { - err = nfsd_permission(fhp->fh_export, fhp->fh_dentry, + err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry, MAY_WRITE|MAY_OWNER_OVERRIDE); if (err) goto out; @@ -1787,11 +1797,17 @@ nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat) return err; } +static int exp_rdonly(struct svc_rqst *rqstp, struct svc_export *exp) +{ + return nfsexp_flags(rqstp, exp) & NFSEXP_READONLY; +} + /* * Check for a user's access permissions to this inode. */ __be32 -nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc) +nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, + struct dentry *dentry, int acc) { struct inode *inode = dentry->d_inode; int err; @@ -1822,7 +1838,7 @@ nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc) */ if (!(acc & MAY_LOCAL_ACCESS)) if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) { - if (EX_RDONLY(exp) || IS_RDONLY(inode)) + if (exp_rdonly(rqstp, exp) || IS_RDONLY(inode)) return nfserr_rofs; if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode)) return nfserr_perm; @@ -1905,7 +1921,7 @@ nfsd_racache_init(int cache_size) raparm_hash[i].pb_head = NULL; spin_lock_init(&raparm_hash[i].pb_lock); } - nperbucket = cache_size >> RAPARM_HASH_BITS; + nperbucket = DIV_ROUND_UP(cache_size, RAPARM_HASH_SIZE); for (i = 0; i < cache_size - 1; i++) { if (i % nperbucket == 0) raparm_hash[j++].pb_head = raparml + i; |