diff options
Diffstat (limited to 'fs/nfsd')
-rw-r--r-- | fs/nfsd/auth.c | 18 | ||||
-rw-r--r-- | fs/nfsd/export.c | 289 | ||||
-rw-r--r-- | fs/nfsd/lockd.c | 1 | ||||
-rw-r--r-- | fs/nfsd/nfs4acl.c | 12 | ||||
-rw-r--r-- | fs/nfsd/nfs4callback.c | 2 | ||||
-rw-r--r-- | fs/nfsd/nfs4idmap.c | 13 | ||||
-rw-r--r-- | fs/nfsd/nfs4proc.c | 35 | ||||
-rw-r--r-- | fs/nfsd/nfs4state.c | 46 | ||||
-rw-r--r-- | fs/nfsd/nfs4xdr.c | 101 | ||||
-rw-r--r-- | fs/nfsd/nfsctl.c | 3 | ||||
-rw-r--r-- | fs/nfsd/nfsfh.c | 51 | ||||
-rw-r--r-- | fs/nfsd/nfsproc.c | 3 | ||||
-rw-r--r-- | fs/nfsd/nfssvc.c | 12 | ||||
-rw-r--r-- | fs/nfsd/vfs.c | 110 |
14 files changed, 553 insertions, 143 deletions
diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c index 6e92b0fe5323..cf61dc8ae942 100644 --- a/fs/nfsd/auth.c +++ b/fs/nfsd/auth.c @@ -12,17 +12,31 @@ #define CAP_NFSD_MASK (CAP_FS_MASK|CAP_TO_MASK(CAP_SYS_RESOURCE)) +static int nfsexp_flags(struct svc_rqst *rqstp, struct svc_export *exp) +{ + struct exp_flavor_info *f; + struct exp_flavor_info *end = exp->ex_flavors + exp->ex_nflavors; + + for (f = exp->ex_flavors; f < end; f++) { + if (f->pseudoflavor == rqstp->rq_flavor) + return f->flags; + } + return exp->ex_flags; + +} + int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) { struct svc_cred cred = rqstp->rq_cred; int i; + int flags = nfsexp_flags(rqstp, exp); int ret; - if (exp->ex_flags & NFSEXP_ALLSQUASH) { + if (flags & NFSEXP_ALLSQUASH) { cred.cr_uid = exp->ex_anon_uid; cred.cr_gid = exp->ex_anon_gid; cred.cr_group_info = groups_alloc(0); - } else if (exp->ex_flags & NFSEXP_ROOTSQUASH) { + } else if (flags & NFSEXP_ROOTSQUASH) { struct group_info *gi; if (!cred.cr_uid) cred.cr_uid = exp->ex_anon_uid; diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 79bd03b8bbf8..c7bbf460b009 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -26,12 +26,15 @@ #include <linux/mount.h> #include <linux/hash.h> #include <linux/module.h> +#include <linux/exportfs.h> #include <linux/sunrpc/svc.h> #include <linux/nfsd/nfsd.h> #include <linux/nfsd/nfsfh.h> #include <linux/nfsd/syscall.h> #include <linux/lockd/bind.h> +#include <linux/sunrpc/msg_prot.h> +#include <linux/sunrpc/gss_api.h> #define NFSDDBG_FACILITY NFSDDBG_EXPORT @@ -451,8 +454,48 @@ out_free_all: return err; } +static int secinfo_parse(char **mesg, char *buf, struct svc_export *exp) +{ + int listsize, err; + struct exp_flavor_info *f; + + err = get_int(mesg, &listsize); + if (err) + return err; + if (listsize < 0 || listsize > MAX_SECINFO_LIST) + return -EINVAL; + + for (f = exp->ex_flavors; f < exp->ex_flavors + listsize; f++) { + err = get_int(mesg, &f->pseudoflavor); + if (err) + return err; + /* + * Just a quick sanity check; we could also try to check + * whether this pseudoflavor is supported, but at worst + * an unsupported pseudoflavor on the export would just + * be a pseudoflavor that won't match the flavor of any + * authenticated request. The administrator will + * probably discover the problem when someone fails to + * authenticate. + */ + if (f->pseudoflavor < 0) + return -EINVAL; + err = get_int(mesg, &f->flags); + if (err) + return err; + /* Only some flags are allowed to differ between flavors: */ + if (~NFSEXP_SECINFO_FLAGS & (f->flags ^ exp->ex_flags)) + return -EINVAL; + } + exp->ex_nflavors = listsize; + return 0; +} + #else /* CONFIG_NFSD_V4 */ -static inline int fsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc) { return 0; } +static inline int +fsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc){return 0;} +static inline int +secinfo_parse(char **mesg, char *buf, struct svc_export *exp) { return 0; } #endif static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) @@ -476,6 +519,9 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) exp.ex_uuid = NULL; + /* secinfo */ + exp.ex_nflavors = 0; + if (mesg[mlen-1] != '\n') return -EINVAL; mesg[mlen-1] = 0; @@ -553,7 +599,9 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) if (exp.ex_uuid == NULL) err = -ENOMEM; } - } else + } else if (strcmp(buf, "secinfo") == 0) + err = secinfo_parse(&mesg, buf, &exp); + else /* quietly ignore unknown words and anything * following. Newer user-space can try to set * new values, then see what the result was. @@ -593,6 +641,7 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) static void exp_flags(struct seq_file *m, int flag, int fsid, uid_t anonu, uid_t anong, struct nfsd4_fs_locations *fslocs); +static void show_secinfo(struct seq_file *m, struct svc_export *exp); static int svc_export_show(struct seq_file *m, struct cache_detail *cd, @@ -622,6 +671,7 @@ static int svc_export_show(struct seq_file *m, seq_printf(m, "%02x", exp->ex_uuid[i]); } } + show_secinfo(m, exp); } seq_puts(m, ")\n"); return 0; @@ -654,6 +704,7 @@ static void export_update(struct cache_head *cnew, struct cache_head *citem) { struct svc_export *new = container_of(cnew, struct svc_export, h); struct svc_export *item = container_of(citem, struct svc_export, h); + int i; new->ex_flags = item->ex_flags; new->ex_anon_uid = item->ex_anon_uid; @@ -669,6 +720,10 @@ static void export_update(struct cache_head *cnew, struct cache_head *citem) item->ex_fslocs.locations_count = 0; new->ex_fslocs.migrated = item->ex_fslocs.migrated; item->ex_fslocs.migrated = 0; + new->ex_nflavors = item->ex_nflavors; + for (i = 0; i < MAX_SECINFO_LIST; i++) { + new->ex_flavors[i] = item->ex_flavors[i]; + } } static struct cache_head *svc_export_alloc(void) @@ -738,16 +793,18 @@ exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp) int err; if (!clp) - return NULL; + return ERR_PTR(-ENOENT); key.ek_client = clp; key.ek_fsidtype = fsid_type; memcpy(key.ek_fsid, fsidv, key_len(fsid_type)); ek = svc_expkey_lookup(&key); - if (ek != NULL) - if ((err = cache_check(&svc_expkey_cache, &ek->h, reqp))) - ek = ERR_PTR(err); + if (ek == NULL) + return ERR_PTR(-ENOMEM); + err = cache_check(&svc_expkey_cache, &ek->h, reqp); + if (err) + return ERR_PTR(err); return ek; } @@ -808,30 +865,21 @@ exp_get_by_name(svc_client *clp, struct vfsmount *mnt, struct dentry *dentry, struct cache_req *reqp) { struct svc_export *exp, key; + int err; if (!clp) - return NULL; + return ERR_PTR(-ENOENT); key.ex_client = clp; key.ex_mnt = mnt; key.ex_dentry = dentry; exp = svc_export_lookup(&key); - if (exp != NULL) { - int err; - - err = cache_check(&svc_export_cache, &exp->h, reqp); - switch (err) { - case 0: break; - case -EAGAIN: - case -ETIMEDOUT: - exp = ERR_PTR(err); - break; - default: - exp = NULL; - } - } - + if (exp == NULL) + return ERR_PTR(-ENOMEM); + err = cache_check(&svc_export_cache, &exp->h, reqp); + if (err) + return ERR_PTR(err); return exp; } @@ -847,7 +895,7 @@ exp_parent(svc_client *clp, struct vfsmount *mnt, struct dentry *dentry, dget(dentry); exp = exp_get_by_name(clp, mnt, dentry, reqp); - while (exp == NULL && !IS_ROOT(dentry)) { + while (PTR_ERR(exp) == -ENOENT && !IS_ROOT(dentry)) { struct dentry *parent; parent = dget_parent(dentry); @@ -900,7 +948,7 @@ static void exp_fsid_unhash(struct svc_export *exp) return; ek = exp_get_fsid_key(exp->ex_client, exp->ex_fsid); - if (ek && !IS_ERR(ek)) { + if (!IS_ERR(ek)) { ek->h.expiry_time = get_seconds()-1; cache_put(&ek->h, &svc_expkey_cache); } @@ -938,7 +986,7 @@ static void exp_unhash(struct svc_export *exp) struct inode *inode = exp->ex_dentry->d_inode; ek = exp_get_key(exp->ex_client, inode->i_sb->s_dev, inode->i_ino); - if (ek && !IS_ERR(ek)) { + if (!IS_ERR(ek)) { ek->h.expiry_time = get_seconds()-1; cache_put(&ek->h, &svc_expkey_cache); } @@ -989,13 +1037,12 @@ exp_export(struct nfsctl_export *nxp) /* must make sure there won't be an ex_fsid clash */ if ((nxp->ex_flags & NFSEXP_FSID) && - (fsid_key = exp_get_fsid_key(clp, nxp->ex_dev)) && - !IS_ERR(fsid_key) && + (!IS_ERR(fsid_key = exp_get_fsid_key(clp, nxp->ex_dev))) && fsid_key->ek_mnt && (fsid_key->ek_mnt != nd.mnt || fsid_key->ek_dentry != nd.dentry) ) goto finish; - if (exp) { + if (!IS_ERR(exp)) { /* just a flags/id/fsid update */ exp_fsid_unhash(exp); @@ -1104,7 +1151,7 @@ exp_unexport(struct nfsctl_export *nxp) err = -EINVAL; exp = exp_get_by_name(dom, nd.mnt, nd.dentry, NULL); path_release(&nd); - if (!exp) + if (IS_ERR(exp)) goto out_domain; exp_do_unexport(exp); @@ -1149,10 +1196,6 @@ exp_rootfh(svc_client *clp, char *path, struct knfsd_fh *f, int maxsize) err = PTR_ERR(exp); goto out; } - if (!exp) { - dprintk("nfsd: exp_rootfh export not found.\n"); - goto out; - } /* * fh must be initialized before calling fh_compose @@ -1176,17 +1219,130 @@ exp_find(struct auth_domain *clp, int fsid_type, u32 *fsidv, { struct svc_export *exp; struct svc_expkey *ek = exp_find_key(clp, fsid_type, fsidv, reqp); - if (!ek || IS_ERR(ek)) + if (IS_ERR(ek)) return ERR_PTR(PTR_ERR(ek)); exp = exp_get_by_name(clp, ek->ek_mnt, ek->ek_dentry, reqp); cache_put(&ek->h, &svc_expkey_cache); - if (!exp || IS_ERR(exp)) + if (IS_ERR(exp)) return ERR_PTR(PTR_ERR(exp)); return exp; } +__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp) +{ + struct exp_flavor_info *f; + struct exp_flavor_info *end = exp->ex_flavors + exp->ex_nflavors; + + /* legacy gss-only clients are always OK: */ + if (exp->ex_client == rqstp->rq_gssclient) + return 0; + /* ip-address based client; check sec= export option: */ + for (f = exp->ex_flavors; f < end; f++) { + if (f->pseudoflavor == rqstp->rq_flavor) + return 0; + } + /* defaults in absence of sec= options: */ + if (exp->ex_nflavors == 0) { + if (rqstp->rq_flavor == RPC_AUTH_NULL || + rqstp->rq_flavor == RPC_AUTH_UNIX) + return 0; + } + return nfserr_wrongsec; +} + +/* + * Uses rq_client and rq_gssclient to find an export; uses rq_client (an + * auth_unix client) if it's available and has secinfo information; + * otherwise, will try to use rq_gssclient. + * + * Called from functions that handle requests; functions that do work on + * behalf of mountd are passed a single client name to use, and should + * use exp_get_by_name() or exp_find(). + */ +struct svc_export * +rqst_exp_get_by_name(struct svc_rqst *rqstp, struct vfsmount *mnt, + struct dentry *dentry) +{ + struct svc_export *gssexp, *exp = NULL; + + if (rqstp->rq_client == NULL) + goto gss; + + /* First try the auth_unix client: */ + exp = exp_get_by_name(rqstp->rq_client, mnt, dentry, + &rqstp->rq_chandle); + if (PTR_ERR(exp) == -ENOENT) + goto gss; + if (IS_ERR(exp)) + return exp; + /* If it has secinfo, assume there are no gss/... clients */ + if (exp->ex_nflavors > 0) + return exp; +gss: + /* Otherwise, try falling back on gss client */ + if (rqstp->rq_gssclient == NULL) + return exp; + gssexp = exp_get_by_name(rqstp->rq_gssclient, mnt, dentry, + &rqstp->rq_chandle); + if (PTR_ERR(gssexp) == -ENOENT) + return exp; + if (exp && !IS_ERR(exp)) + exp_put(exp); + return gssexp; +} + +struct svc_export * +rqst_exp_find(struct svc_rqst *rqstp, int fsid_type, u32 *fsidv) +{ + struct svc_export *gssexp, *exp = NULL; + + if (rqstp->rq_client == NULL) + goto gss; + + /* First try the auth_unix client: */ + exp = exp_find(rqstp->rq_client, fsid_type, fsidv, &rqstp->rq_chandle); + if (PTR_ERR(exp) == -ENOENT) + goto gss; + if (IS_ERR(exp)) + return exp; + /* If it has secinfo, assume there are no gss/... clients */ + if (exp->ex_nflavors > 0) + return exp; +gss: + /* Otherwise, try falling back on gss client */ + if (rqstp->rq_gssclient == NULL) + return exp; + gssexp = exp_find(rqstp->rq_gssclient, fsid_type, fsidv, + &rqstp->rq_chandle); + if (PTR_ERR(gssexp) == -ENOENT) + return exp; + if (exp && !IS_ERR(exp)) + exp_put(exp); + return gssexp; +} + +struct svc_export * +rqst_exp_parent(struct svc_rqst *rqstp, struct vfsmount *mnt, + struct dentry *dentry) +{ + struct svc_export *exp; + + dget(dentry); + exp = rqst_exp_get_by_name(rqstp, mnt, dentry); + + while (PTR_ERR(exp) == -ENOENT && !IS_ROOT(dentry)) { + struct dentry *parent; + + parent = dget_parent(dentry); + dput(dentry); + dentry = parent; + exp = rqst_exp_get_by_name(rqstp, mnt, dentry); + } + dput(dentry); + return exp; +} /* * Called when we need the filehandle for the root of the pseudofs, @@ -1194,8 +1350,7 @@ exp_find(struct auth_domain *clp, int fsid_type, u32 *fsidv, * export point with fsid==0 */ __be32 -exp_pseudoroot(struct auth_domain *clp, struct svc_fh *fhp, - struct cache_req *creq) +exp_pseudoroot(struct svc_rqst *rqstp, struct svc_fh *fhp) { struct svc_export *exp; __be32 rv; @@ -1203,12 +1358,16 @@ exp_pseudoroot(struct auth_domain *clp, struct svc_fh *fhp, mk_fsid(FSID_NUM, fsidv, 0, 0, 0, NULL); - exp = exp_find(clp, FSID_NUM, fsidv, creq); + exp = rqst_exp_find(rqstp, FSID_NUM, fsidv); + if (PTR_ERR(exp) == -ENOENT) + return nfserr_perm; if (IS_ERR(exp)) return nfserrno(PTR_ERR(exp)); - if (exp == NULL) - return nfserr_perm; rv = fh_compose(fhp, exp, exp->ex_dentry, NULL); + if (rv) + goto out; + rv = check_nfsd_access(exp, rqstp); +out: exp_put(exp); return rv; } @@ -1296,28 +1455,62 @@ static struct flags { { 0, {"", ""}} }; -static void exp_flags(struct seq_file *m, int flag, int fsid, - uid_t anonu, uid_t anong, struct nfsd4_fs_locations *fsloc) +static void show_expflags(struct seq_file *m, int flags, int mask) { - int first = 0; struct flags *flg; + int state, first = 0; for (flg = expflags; flg->flag; flg++) { - int state = (flg->flag & flag)?0:1; + if (flg->flag & ~mask) + continue; + state = (flg->flag & flags) ? 0 : 1; if (*flg->name[state]) seq_printf(m, "%s%s", first++?",":"", flg->name[state]); } +} + +static void show_secinfo_flags(struct seq_file *m, int flags) +{ + seq_printf(m, ","); + show_expflags(m, flags, NFSEXP_SECINFO_FLAGS); +} + +static void show_secinfo(struct seq_file *m, struct svc_export *exp) +{ + struct exp_flavor_info *f; + struct exp_flavor_info *end = exp->ex_flavors + exp->ex_nflavors; + int lastflags = 0, first = 0; + + if (exp->ex_nflavors == 0) + return; + for (f = exp->ex_flavors; f < end; f++) { + if (first || f->flags != lastflags) { + if (!first) + show_secinfo_flags(m, lastflags); + seq_printf(m, ",sec=%d", f->pseudoflavor); + lastflags = f->flags; + } else { + seq_printf(m, ":%d", f->pseudoflavor); + } + } + show_secinfo_flags(m, lastflags); +} + +static void exp_flags(struct seq_file *m, int flag, int fsid, + uid_t anonu, uid_t anong, struct nfsd4_fs_locations *fsloc) +{ + show_expflags(m, flag, NFSEXP_ALLFLAGS); if (flag & NFSEXP_FSID) - seq_printf(m, "%sfsid=%d", first++?",":"", fsid); + seq_printf(m, ",fsid=%d", fsid); if (anonu != (uid_t)-2 && anonu != (0x10000-2)) - seq_printf(m, "%sanonuid=%d", first++?",":"", anonu); + seq_printf(m, ",sanonuid=%d", anonu); if (anong != (gid_t)-2 && anong != (0x10000-2)) - seq_printf(m, "%sanongid=%d", first++?",":"", anong); + seq_printf(m, ",sanongid=%d", anong); if (fsloc && fsloc->locations_count > 0) { char *loctype = (fsloc->migrated) ? "refer" : "replicas"; int i; - seq_printf(m, "%s%s=", first++?",":"", loctype); + seq_printf(m, ",%s=", loctype); seq_escape(m, fsloc->locations[0].path, ",;@ \t\n\\"); seq_putc(m, '@'); seq_escape(m, fsloc->locations[0].hosts, ",;@ \t\n\\"); diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c index 221acd1f11f6..9e4a568a5013 100644 --- a/fs/nfsd/lockd.c +++ b/fs/nfsd/lockd.c @@ -65,6 +65,7 @@ nlm_fclose(struct file *filp) static struct nlmsvc_binding nfsd_nlm_ops = { .fopen = nlm_fopen, /* open file for locking */ .fclose = nlm_fclose, /* close file */ + .get_grace_period = get_nfs4_grace_period, }; void diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c index cc3b7badd486..b6ed38380ab8 100644 --- a/fs/nfsd/nfs4acl.c +++ b/fs/nfsd/nfs4acl.c @@ -183,8 +183,13 @@ static void summarize_posix_acl(struct posix_acl *acl, struct posix_acl_summary *pas) { struct posix_acl_entry *pa, *pe; - pas->users = 0; - pas->groups = 0; + + /* + * Only pas.users and pas.groups need initialization; previous + * posix_acl_valid() calls ensure that the other fields will be + * initialized in the following loop. But, just to placate gcc: + */ + memset(pas, 0, sizeof(*pas)); pas->mask = 07; pe = acl->a_entries + acl->a_count; @@ -732,13 +737,16 @@ int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl, struct posix_acl **pacl, *pacl = posix_state_to_acl(&effective_acl_state, flags); if (IS_ERR(*pacl)) { ret = PTR_ERR(*pacl); + *pacl = NULL; goto out_dstate; } *dpacl = posix_state_to_acl(&default_acl_state, flags | NFS4_ACL_TYPE_DEFAULT); if (IS_ERR(*dpacl)) { ret = PTR_ERR(*dpacl); + *dpacl = NULL; posix_acl_release(*pacl); + *pacl = NULL; goto out_dstate; } sort_pacl(*pacl); diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 5443c52b57aa..31d6633c7fe4 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -75,7 +75,7 @@ enum nfs_cb_opnum4 { #define op_enc_sz 1 #define op_dec_sz 2 #define enc_nfs4_fh_sz (1 + (NFS4_FHSIZE >> 2)) -#define enc_stateid_sz 16 +#define enc_stateid_sz (NFS4_STATEID_SIZE >> 2) #define NFS4_enc_cb_recall_sz (cb_compound_enc_hdr_sz + \ 1 + enc_stateid_sz + \ enc_nfs4_fh_sz) diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c index 45aa21ce6784..2cf9a9a2d89c 100644 --- a/fs/nfsd/nfs4idmap.c +++ b/fs/nfsd/nfs4idmap.c @@ -587,6 +587,15 @@ idmap_lookup(struct svc_rqst *rqstp, return ret; } +static char * +rqst_authname(struct svc_rqst *rqstp) +{ + struct auth_domain *clp; + + clp = rqstp->rq_gssclient ? rqstp->rq_gssclient : rqstp->rq_client; + return clp->name; +} + static int idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, uid_t *id) @@ -600,7 +609,7 @@ idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen return -EINVAL; memcpy(key.name, name, namelen); key.name[namelen] = '\0'; - strlcpy(key.authname, rqstp->rq_client->name, sizeof(key.authname)); + strlcpy(key.authname, rqst_authname(rqstp), sizeof(key.authname)); ret = idmap_lookup(rqstp, nametoid_lookup, &key, &nametoid_cache, &item); if (ret == -ENOENT) ret = -ESRCH; /* nfserr_badname */ @@ -620,7 +629,7 @@ idmap_id_to_name(struct svc_rqst *rqstp, int type, uid_t id, char *name) }; int ret; - strlcpy(key.authname, rqstp->rq_client->name, sizeof(key.authname)); + strlcpy(key.authname, rqst_authname(rqstp), sizeof(key.authname)); ret = idmap_lookup(rqstp, idtoname_lookup, &key, &idtoname_cache, &item); if (ret == -ENOENT) return sprintf(name, "%u", id); diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 8522729830db..3c627128e205 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -47,6 +47,7 @@ #include <linux/nfsd/state.h> #include <linux/nfsd/xdr4.h> #include <linux/nfs4_acl.h> +#include <linux/sunrpc/gss_api.h> #define NFSDDBG_FACILITY NFSDDBG_PROC @@ -286,8 +287,7 @@ nfsd4_putrootfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, __be32 status; fh_put(&cstate->current_fh); - status = exp_pseudoroot(rqstp->rq_client, &cstate->current_fh, - &rqstp->rq_chandle); + status = exp_pseudoroot(rqstp, &cstate->current_fh); return status; } @@ -474,8 +474,8 @@ nfsd4_lookupp(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, __be32 ret; fh_init(&tmp_fh, NFS4_FHSIZE); - if((ret = exp_pseudoroot(rqstp->rq_client, &tmp_fh, - &rqstp->rq_chandle)) != 0) + ret = exp_pseudoroot(rqstp, &tmp_fh); + if (ret) return ret; if (tmp_fh.fh_dentry == cstate->current_fh.fh_dentry) { fh_put(&tmp_fh); @@ -611,6 +611,30 @@ nfsd4_rename(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, } static __be32 +nfsd4_secinfo(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, + struct nfsd4_secinfo *secinfo) +{ + struct svc_fh resfh; + struct svc_export *exp; + struct dentry *dentry; + __be32 err; + + fh_init(&resfh, NFS4_FHSIZE); + err = nfsd_lookup_dentry(rqstp, &cstate->current_fh, + secinfo->si_name, secinfo->si_namelen, + &exp, &dentry); + if (err) + return err; + if (dentry->d_inode == NULL) { + exp_put(exp); + err = nfserr_noent; + } else + secinfo->si_exp = exp; + dput(dentry); + return err; +} + +static __be32 nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_setattr *setattr) { @@ -1009,6 +1033,9 @@ static struct nfsd4_operation nfsd4_ops[OP_RELEASE_LOCKOWNER+1] = { [OP_SAVEFH] = { .op_func = (nfsd4op_func)nfsd4_savefh, }, + [OP_SECINFO] = { + .op_func = (nfsd4op_func)nfsd4_secinfo, + }, [OP_SETATTR] = { .op_func = (nfsd4op_func)nfsd4_setattr, }, diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 8c52913d7cb6..e4a4c87ec8c6 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -49,8 +49,10 @@ #include <linux/nfsd/state.h> #include <linux/nfsd/xdr4.h> #include <linux/namei.h> +#include <linux/swap.h> #include <linux/mutex.h> #include <linux/lockd/bind.h> +#include <linux/module.h> #define NFSDDBG_FACILITY NFSDDBG_PROC @@ -149,6 +151,7 @@ get_nfs4_file(struct nfs4_file *fi) } static int num_delegations; +unsigned int max_delegations; /* * Open owner state (share locks) @@ -192,7 +195,9 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f struct nfs4_callback *cb = &stp->st_stateowner->so_client->cl_callback; dprintk("NFSD alloc_init_deleg\n"); - if (num_delegations > STATEID_HASH_SIZE * 4) + if (fp->fi_had_conflict) + return NULL; + if (num_delegations > max_delegations) return NULL; dp = kmem_cache_alloc(deleg_slab, GFP_KERNEL); if (dp == NULL) @@ -999,6 +1004,7 @@ alloc_init_file(struct inode *ino) list_add(&fp->fi_hash, &file_hashtbl[hashval]); fp->fi_inode = igrab(ino); fp->fi_id = current_fileid++; + fp->fi_had_conflict = false; return fp; } return NULL; @@ -1325,6 +1331,7 @@ do_recall(void *__dp) { struct nfs4_delegation *dp = __dp; + dp->dl_file->fi_had_conflict = true; nfsd4_cb_recall(dp); return 0; } @@ -3190,20 +3197,49 @@ nfsd4_load_reboot_recovery_data(void) printk("NFSD: Failure reading reboot recovery data\n"); } +unsigned long +get_nfs4_grace_period(void) +{ + return max(user_lease_time, lease_time) * HZ; +} + +/* + * Since the lifetime of a delegation isn't limited to that of an open, a + * client may quite reasonably hang on to a delegation as long as it has + * the inode cached. This becomes an obvious problem the first time a + * client's inode cache approaches the size of the server's total memory. + * + * For now we avoid this problem by imposing a hard limit on the number + * of delegations, which varies according to the server's memory size. + */ +static void +set_max_delegations(void) +{ + /* + * Allow at most 4 delegations per megabyte of RAM. Quick + * estimates suggest that in the worst case (where every delegation + * is for a different inode), a delegation could take about 1.5K, + * giving a worst case usage of about 6% of memory. + */ + max_delegations = nr_free_buffer_pages() >> (20 - 2 - PAGE_SHIFT); +} + /* initialization to perform when the nfsd service is started: */ static void __nfs4_state_start(void) { - time_t grace_time; + unsigned long grace_time; boot_time = get_seconds(); - grace_time = max(user_lease_time, lease_time); + grace_time = get_nfs_grace_period(); lease_time = user_lease_time; in_grace = 1; - printk("NFSD: starting %ld-second grace period\n", grace_time); + printk(KERN_INFO "NFSD: starting %ld-second grace period\n", + grace_time/HZ); laundry_wq = create_singlethread_workqueue("nfsd4"); - queue_delayed_work(laundry_wq, &laundromat_work, grace_time*HZ); + queue_delayed_work(laundry_wq, &laundromat_work, grace_time); + set_max_delegations(); } int diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 15809dfd88a5..b3d55c6747fd 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -56,6 +56,8 @@ #include <linux/nfsd_idmap.h> #include <linux/nfs4.h> #include <linux/nfs4_acl.h> +#include <linux/sunrpc/gss_api.h> +#include <linux/sunrpc/svcauth_gss.h> #define NFSDDBG_FACILITY NFSDDBG_XDR @@ -819,6 +821,23 @@ nfsd4_decode_renew(struct nfsd4_compoundargs *argp, clientid_t *clientid) } static __be32 +nfsd4_decode_secinfo(struct nfsd4_compoundargs *argp, + struct nfsd4_secinfo *secinfo) +{ + DECODE_HEAD; + + READ_BUF(4); + READ32(secinfo->si_namelen); + READ_BUF(secinfo->si_namelen); + SAVEMEM(secinfo->si_name, secinfo->si_namelen); + status = check_filename(secinfo->si_name, secinfo->si_namelen, + nfserr_noent); + if (status) + return status; + DECODE_TAIL; +} + +static __be32 nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *setattr) { DECODE_HEAD; @@ -1131,6 +1150,9 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp) case OP_SAVEFH: op->status = nfs_ok; break; + case OP_SECINFO: + op->status = nfsd4_decode_secinfo(argp, &op->u.secinfo); + break; case OP_SETATTR: op->status = nfsd4_decode_setattr(argp, &op->u.setattr); break; @@ -1296,7 +1318,7 @@ static char *nfsd4_path(struct svc_rqst *rqstp, struct svc_export *exp, __be32 * char *path, *rootpath; fh_init(&tmp_fh, NFS4_FHSIZE); - *stat = exp_pseudoroot(rqstp->rq_client, &tmp_fh, &rqstp->rq_chandle); + *stat = exp_pseudoroot(rqstp, &tmp_fh); if (*stat) return NULL; rootpath = tmp_fh.fh_export->ex_path; @@ -1847,11 +1869,19 @@ nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd, if (d_mountpoint(dentry)) { int err; + /* + * Why the heck aren't we just using nfsd_lookup?? + * Different "."/".." handling? Something else? + * At least, add a comment here to explain.... + */ err = nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp); if (err) { nfserr = nfserrno(err); goto out_put; } + nfserr = check_nfsd_access(exp, cd->rd_rqstp); + if (nfserr) + goto out_put; } nfserr = nfsd4_encode_fattr(NULL, exp, dentry, p, buflen, cd->rd_bmval, @@ -2419,6 +2449,72 @@ nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_ } } +static void +nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, int nfserr, + struct nfsd4_secinfo *secinfo) +{ + int i = 0; + struct svc_export *exp = secinfo->si_exp; + u32 nflavs; + struct exp_flavor_info *flavs; + struct exp_flavor_info def_flavs[2]; + ENCODE_HEAD; + + if (nfserr) + goto out; + if (exp->ex_nflavors) { + flavs = exp->ex_flavors; + nflavs = exp->ex_nflavors; + } else { /* Handling of some defaults in absence of real secinfo: */ + flavs = def_flavs; + if (exp->ex_client->flavour->flavour == RPC_AUTH_UNIX) { + nflavs = 2; + flavs[0].pseudoflavor = RPC_AUTH_UNIX; + flavs[1].pseudoflavor = RPC_AUTH_NULL; + } else if (exp->ex_client->flavour->flavour == RPC_AUTH_GSS) { + nflavs = 1; + flavs[0].pseudoflavor + = svcauth_gss_flavor(exp->ex_client); + } else { + nflavs = 1; + flavs[0].pseudoflavor + = exp->ex_client->flavour->flavour; + } + } + + RESERVE_SPACE(4); + WRITE32(nflavs); + ADJUST_ARGS(); + for (i = 0; i < nflavs; i++) { + u32 flav = flavs[i].pseudoflavor; + struct gss_api_mech *gm = gss_mech_get_by_pseudoflavor(flav); + + if (gm) { + RESERVE_SPACE(4); + WRITE32(RPC_AUTH_GSS); + ADJUST_ARGS(); + RESERVE_SPACE(4 + gm->gm_oid.len); + WRITE32(gm->gm_oid.len); + WRITEMEM(gm->gm_oid.data, gm->gm_oid.len); + ADJUST_ARGS(); + RESERVE_SPACE(4); + WRITE32(0); /* qop */ + ADJUST_ARGS(); + RESERVE_SPACE(4); + WRITE32(gss_pseudoflavor_to_service(gm, flav)); + ADJUST_ARGS(); + gss_mech_put(gm); + } else { + RESERVE_SPACE(4); + WRITE32(flav); + ADJUST_ARGS(); + } + } +out: + if (exp) + exp_put(exp); +} + /* * The SETATTR encode routine is special -- it always encodes a bitmap, * regardless of the error status. @@ -2559,6 +2655,9 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op) break; case OP_SAVEFH: break; + case OP_SECINFO: + nfsd4_encode_secinfo(resp, op->status, &op->u.secinfo); + break; case OP_SETATTR: nfsd4_encode_setattr(resp, op->status, &op->u.setattr); break; diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 71c686dc7257..baac89d917ca 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -35,7 +35,6 @@ #include <linux/nfsd/cache.h> #include <linux/nfsd/xdr.h> #include <linux/nfsd/syscall.h> -#include <linux/nfsd/interface.h> #include <asm/uaccess.h> @@ -245,7 +244,7 @@ static ssize_t write_getfs(struct file *file, char *buf, size_t size) } exp_readunlock(); if (err == 0) - err = res->fh_size + (int)&((struct knfsd_fh*)0)->fh_base; + err = res->fh_size + offsetof(struct knfsd_fh, fh_base); out: return err; } diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 6ca2d24fc216..0eb464a39aae 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -15,10 +15,12 @@ #include <linux/string.h> #include <linux/stat.h> #include <linux/dcache.h> +#include <linux/exportfs.h> #include <linux/mount.h> #include <linux/sunrpc/clnt.h> #include <linux/sunrpc/svc.h> +#include <linux/sunrpc/svcauth_gss.h> #include <linux/nfsd/nfsd.h> #define NFSDDBG_FACILITY NFSDDBG_FH @@ -27,10 +29,6 @@ static int nfsd_nr_verified; static int nfsd_nr_put; -extern struct export_operations export_op_default; - -#define CALL(ops,fun) ((ops->fun)?(ops->fun):export_op_default.fun) - /* * our acceptability function. * if NOSUBTREECHECK, accept anything @@ -123,8 +121,6 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) int data_left = fh->fh_size/4; error = nfserr_stale; - if (rqstp->rq_client == NULL) - goto out; if (rqstp->rq_vers > 2) error = nfserr_badhandle; if (rqstp->rq_vers == 4 && fh->fh_size == 0) @@ -148,7 +144,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) fh->fh_fsid[1] = fh->fh_fsid[2]; } if ((data_left -= len)<0) goto out; - exp = exp_find(rqstp->rq_client, fh->fh_fsid_type, datap, &rqstp->rq_chandle); + exp = rqst_exp_find(rqstp, fh->fh_fsid_type, datap); datap += len; } else { dev_t xdev; @@ -159,19 +155,17 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) xdev = old_decode_dev(fh->ofh_xdev); xino = u32_to_ino_t(fh->ofh_xino); mk_fsid(FSID_DEV, tfh, xdev, xino, 0, NULL); - exp = exp_find(rqstp->rq_client, FSID_DEV, tfh, - &rqstp->rq_chandle); + exp = rqst_exp_find(rqstp, FSID_DEV, tfh); } - if (IS_ERR(exp) && (PTR_ERR(exp) == -EAGAIN - || PTR_ERR(exp) == -ETIMEDOUT)) { - error = nfserrno(PTR_ERR(exp)); + error = nfserr_stale; + if (PTR_ERR(exp) == -ENOENT) goto out; - } - error = nfserr_stale; - if (!exp || IS_ERR(exp)) + if (IS_ERR(exp)) { + error = nfserrno(PTR_ERR(exp)); goto out; + } /* Check if the request originated from a secure port. */ error = nfserr_perm; @@ -211,11 +205,9 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) if (fileid_type == 0) dentry = dget(exp->ex_dentry); else { - struct export_operations *nop = exp->ex_mnt->mnt_sb->s_export_op; - dentry = CALL(nop,decode_fh)(exp->ex_mnt->mnt_sb, - datap, data_left, - fileid_type, - nfsd_acceptable, exp); + dentry = exportfs_decode_fh(exp->ex_mnt, datap, + data_left, fileid_type, + nfsd_acceptable, exp); } if (dentry == NULL) goto out; @@ -257,8 +249,19 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) if (error) goto out; + if (!(access & MAY_LOCK)) { + /* + * pseudoflavor restrictions are not enforced on NLM, + * which clients virtually always use auth_sys for, + * even while using RPCSEC_GSS for NFS. + */ + error = check_nfsd_access(exp, rqstp); + if (error) + goto out; + } + /* Finally, check access permissions. */ - error = nfsd_permission(exp, dentry, access); + error = nfsd_permission(rqstp, exp, dentry, access); if (error) { dprintk("fh_verify: %s/%s permission failure, " @@ -286,15 +289,13 @@ out: static inline int _fh_update(struct dentry *dentry, struct svc_export *exp, __u32 *datap, int *maxsize) { - struct export_operations *nop = exp->ex_mnt->mnt_sb->s_export_op; - if (dentry == exp->ex_dentry) { *maxsize = 0; return 0; } - return CALL(nop,encode_fh)(dentry, datap, maxsize, - !(exp->ex_flags&NFSEXP_NOSUBTREECHECK)); + return exportfs_encode_fh(dentry, datap, maxsize, + !(exp->ex_flags & NFSEXP_NOSUBTREECHECK)); } /* diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index b2c7147aa921..977a71f64e19 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -278,7 +278,8 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp, * echo thing > device-special-file-or-pipe * by doing a CREATE with type==0 */ - nfserr = nfsd_permission(newfhp->fh_export, + nfserr = nfsd_permission(rqstp, + newfhp->fh_export, newfhp->fh_dentry, MAY_WRITE|MAY_LOCAL_ACCESS); if (nfserr && nfserr != nfserr_rofs) diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index ff55950efb43..a8c89ae4c743 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -19,6 +19,7 @@ #include <linux/slab.h> #include <linux/smp.h> #include <linux/smp_lock.h> +#include <linux/freezer.h> #include <linux/fs_struct.h> #include <linux/sunrpc/types.h> @@ -432,6 +433,7 @@ nfsd(struct svc_rqst *rqstp) * dirty pages. */ current->flags |= PF_LESS_THROTTLE; + set_freezable(); /* * The main request loop @@ -492,6 +494,15 @@ out: module_put_and_exit(0); } +static __be32 map_new_errors(u32 vers, __be32 nfserr) +{ + if (nfserr == nfserr_jukebox && vers == 2) + return nfserr_dropit; + if (nfserr == nfserr_wrongsec && vers < 4) + return nfserr_acces; + return nfserr; +} + int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp) { @@ -534,6 +545,7 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp) /* Now call the procedure handler, and encode NFS status. */ nfserr = proc->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp); + nfserr = map_new_errors(rqstp->rq_vers, nfserr); if (nfserr == nfserr_jukebox && rqstp->rq_vers == 2) nfserr = nfserr_dropit; if (nfserr == nfserr_dropit) { diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 945b1cedde2b..e90f4a8a1d01 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -113,7 +113,7 @@ 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); @@ -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; @@ -1034,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; @@ -1063,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; @@ -1792,7 +1801,8 @@ nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat) * 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; @@ -1823,7 +1833,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 (EX_RDONLY(exp, rqstp) || IS_RDONLY(inode)) return nfserr_rofs; if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode)) return nfserr_perm; |