summaryrefslogtreecommitdiffstats
path: root/fs/nfsd
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2008-12-31 08:31:57 +0100
committerIngo Molnar <mingo@elte.hu>2008-12-31 08:31:57 +0100
commita9de18eb761f7c1c860964b2e5addc1a35c7e861 (patch)
tree886e75fdfd09690cd262ca69cb7f5d1d42b48602 /fs/nfsd
parentb2aaf8f74cdc84a9182f6cabf198b7763bcb9d40 (diff)
parent6a94cb73064c952255336cc57731904174b2c58f (diff)
Merge branch 'linus' into stackprotector
Conflicts: arch/x86/include/asm/pda.h kernel/fork.c
Diffstat (limited to 'fs/nfsd')
-rw-r--r--fs/nfsd/auth.c95
-rw-r--r--fs/nfsd/export.c144
-rw-r--r--fs/nfsd/nfs4callback.c9
-rw-r--r--fs/nfsd/nfs4recover.c120
-rw-r--r--fs/nfsd/nfs4state.c25
-rw-r--r--fs/nfsd/nfsctl.c10
-rw-r--r--fs/nfsd/nfsfh.c11
-rw-r--r--fs/nfsd/nfssvc.c4
-rw-r--r--fs/nfsd/vfs.c139
9 files changed, 355 insertions, 202 deletions
diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c
index 294992e9bf6..0184fe9b514 100644
--- a/fs/nfsd/auth.c
+++ b/fs/nfsd/auth.c
@@ -27,53 +27,70 @@ int nfsexp_flags(struct svc_rqst *rqstp, struct svc_export *exp)
int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
{
- struct svc_cred cred = rqstp->rq_cred;
+ struct group_info *rqgi;
+ struct group_info *gi;
+ struct cred *new;
int i;
int flags = nfsexp_flags(rqstp, exp);
int ret;
+ /* discard any old override before preparing the new set */
+ revert_creds(get_cred(current->real_cred));
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+
+ new->fsuid = rqstp->rq_cred.cr_uid;
+ new->fsgid = rqstp->rq_cred.cr_gid;
+
+ rqgi = rqstp->rq_cred.cr_group_info;
+
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);
+ new->fsuid = exp->ex_anon_uid;
+ new->fsgid = exp->ex_anon_gid;
+ gi = groups_alloc(0);
} else if (flags & NFSEXP_ROOTSQUASH) {
- struct group_info *gi;
- if (!cred.cr_uid)
- cred.cr_uid = exp->ex_anon_uid;
- if (!cred.cr_gid)
- cred.cr_gid = exp->ex_anon_gid;
- gi = groups_alloc(cred.cr_group_info->ngroups);
- if (gi)
- for (i = 0; i < cred.cr_group_info->ngroups; i++) {
- if (!GROUP_AT(cred.cr_group_info, i))
- GROUP_AT(gi, i) = exp->ex_anon_gid;
- else
- GROUP_AT(gi, i) = GROUP_AT(cred.cr_group_info, i);
- }
- cred.cr_group_info = gi;
- } else
- get_group_info(cred.cr_group_info);
-
- if (cred.cr_uid != (uid_t) -1)
- current->fsuid = cred.cr_uid;
- else
- current->fsuid = exp->ex_anon_uid;
- if (cred.cr_gid != (gid_t) -1)
- current->fsgid = cred.cr_gid;
- else
- current->fsgid = exp->ex_anon_gid;
+ if (!new->fsuid)
+ new->fsuid = exp->ex_anon_uid;
+ if (!new->fsgid)
+ new->fsgid = exp->ex_anon_gid;
- if (!cred.cr_group_info)
- return -ENOMEM;
- ret = set_current_groups(cred.cr_group_info);
- put_group_info(cred.cr_group_info);
- if ((cred.cr_uid)) {
- current->cap_effective =
- cap_drop_nfsd_set(current->cap_effective);
+ gi = groups_alloc(rqgi->ngroups);
+ if (!gi)
+ goto oom;
+
+ for (i = 0; i < rqgi->ngroups; i++) {
+ if (!GROUP_AT(rqgi, i))
+ GROUP_AT(gi, i) = exp->ex_anon_gid;
+ else
+ GROUP_AT(gi, i) = GROUP_AT(rqgi, i);
+ }
} else {
- current->cap_effective =
- cap_raise_nfsd_set(current->cap_effective,
- current->cap_permitted);
+ gi = get_group_info(rqgi);
}
+
+ if (new->fsuid == (uid_t) -1)
+ new->fsuid = exp->ex_anon_uid;
+ if (new->fsgid == (gid_t) -1)
+ new->fsgid = exp->ex_anon_gid;
+
+ ret = set_groups(new, gi);
+ put_group_info(gi);
+ if (!ret)
+ goto error;
+
+ if (new->uid)
+ new->cap_effective = cap_drop_nfsd_set(new->cap_effective);
+ else
+ new->cap_effective = cap_raise_nfsd_set(new->cap_effective,
+ new->cap_permitted);
+ put_cred(override_creds(new));
+ return 0;
+
+oom:
+ ret = -ENOMEM;
+error:
+ abort_creds(new);
return ret;
}
+
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
index 9dc036f1835..5839b229cd0 100644
--- a/fs/nfsd/export.c
+++ b/fs/nfsd/export.c
@@ -99,7 +99,7 @@ static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen)
int fsidtype;
char *ep;
struct svc_expkey key;
- struct svc_expkey *ek;
+ struct svc_expkey *ek = NULL;
if (mesg[mlen-1] != '\n')
return -EINVAL;
@@ -107,7 +107,8 @@ static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen)
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
err = -ENOMEM;
- if (!buf) goto out;
+ if (!buf)
+ goto out;
err = -EINVAL;
if ((len=qword_get(&mesg, buf, PAGE_SIZE)) <= 0)
@@ -151,34 +152,32 @@ static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen)
/* now we want a pathname, or empty meaning NEGATIVE */
err = -EINVAL;
- if ((len=qword_get(&mesg, buf, PAGE_SIZE)) < 0)
+ len = qword_get(&mesg, buf, PAGE_SIZE);
+ if (len < 0)
goto out;
dprintk("Path seems to be <%s>\n", buf);
err = 0;
if (len == 0) {
set_bit(CACHE_NEGATIVE, &key.h.flags);
ek = svc_expkey_update(&key, ek);
- if (ek)
- cache_put(&ek->h, &svc_expkey_cache);
- else err = -ENOMEM;
+ if (!ek)
+ err = -ENOMEM;
} else {
- struct nameidata nd;
- err = path_lookup(buf, 0, &nd);
+ err = kern_path(buf, 0, &key.ek_path);
if (err)
goto out;
dprintk("Found the path %s\n", buf);
- key.ek_path = nd.path;
ek = svc_expkey_update(&key, ek);
- if (ek)
- cache_put(&ek->h, &svc_expkey_cache);
- else
+ if (!ek)
err = -ENOMEM;
- path_put(&nd.path);
+ path_put(&key.ek_path);
}
cache_flush();
out:
+ if (ek)
+ cache_put(&ek->h, &svc_expkey_cache);
if (dom)
auth_domain_put(dom);
kfree(buf);
@@ -500,35 +499,22 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
int len;
int err;
struct auth_domain *dom = NULL;
- struct nameidata nd;
- struct svc_export exp, *expp;
+ struct svc_export exp = {}, *expp;
int an_int;
- nd.path.dentry = NULL;
- exp.ex_pathname = NULL;
-
- /* fs locations */
- exp.ex_fslocs.locations = NULL;
- exp.ex_fslocs.locations_count = 0;
- exp.ex_fslocs.migrated = 0;
-
- exp.ex_uuid = NULL;
-
- /* secinfo */
- exp.ex_nflavors = 0;
-
if (mesg[mlen-1] != '\n')
return -EINVAL;
mesg[mlen-1] = 0;
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
- err = -ENOMEM;
- if (!buf) goto out;
+ if (!buf)
+ return -ENOMEM;
/* client */
- len = qword_get(&mesg, buf, PAGE_SIZE);
err = -EINVAL;
- if (len <= 0) goto out;
+ len = qword_get(&mesg, buf, PAGE_SIZE);
+ if (len <= 0)
+ goto out;
err = -ENOENT;
dom = auth_domain_find(buf);
@@ -537,25 +523,25 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
/* path */
err = -EINVAL;
- if ((len=qword_get(&mesg, buf, PAGE_SIZE)) <= 0)
- goto out;
- err = path_lookup(buf, 0, &nd);
- if (err) goto out_no_path;
+ if ((len = qword_get(&mesg, buf, PAGE_SIZE)) <= 0)
+ goto out1;
+
+ err = kern_path(buf, 0, &exp.ex_path);
+ if (err)
+ goto out1;
- exp.h.flags = 0;
exp.ex_client = dom;
- exp.ex_path.mnt = nd.path.mnt;
- exp.ex_path.dentry = nd.path.dentry;
- exp.ex_pathname = kstrdup(buf, GFP_KERNEL);
+
err = -ENOMEM;
+ exp.ex_pathname = kstrdup(buf, GFP_KERNEL);
if (!exp.ex_pathname)
- goto out;
+ goto out2;
/* expiry */
err = -EINVAL;
exp.h.expiry_time = get_expiry(&mesg);
if (exp.h.expiry_time == 0)
- goto out;
+ goto out3;
/* flags */
err = get_int(&mesg, &an_int);
@@ -563,22 +549,26 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
err = 0;
set_bit(CACHE_NEGATIVE, &exp.h.flags);
} else {
- if (err || an_int < 0) goto out;
+ if (err || an_int < 0)
+ goto out3;
exp.ex_flags= an_int;
/* anon uid */
err = get_int(&mesg, &an_int);
- if (err) goto out;
+ if (err)
+ goto out3;
exp.ex_anon_uid= an_int;
/* anon gid */
err = get_int(&mesg, &an_int);
- if (err) goto out;
+ if (err)
+ goto out3;
exp.ex_anon_gid= an_int;
/* fsid */
err = get_int(&mesg, &an_int);
- if (err) goto out;
+ if (err)
+ goto out3;
exp.ex_fsid = an_int;
while ((len = qword_get(&mesg, buf, PAGE_SIZE)) > 0) {
@@ -604,12 +594,13 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
*/
break;
if (err)
- goto out;
+ goto out4;
}
- err = check_export(nd.path.dentry->d_inode, exp.ex_flags,
+ err = check_export(exp.ex_path.dentry->d_inode, exp.ex_flags,
exp.ex_uuid);
- if (err) goto out;
+ if (err)
+ goto out4;
}
expp = svc_export_lookup(&exp);
@@ -622,15 +613,16 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
err = -ENOMEM;
else
exp_put(expp);
- out:
+out4:
nfsd4_fslocs_free(&exp.ex_fslocs);
kfree(exp.ex_uuid);
+out3:
kfree(exp.ex_pathname);
- if (nd.path.dentry)
- path_put(&nd.path);
- out_no_path:
- if (dom)
- auth_domain_put(dom);
+out2:
+ path_put(&exp.ex_path);
+out1:
+ auth_domain_put(dom);
+out:
kfree(buf);
return err;
}
@@ -998,7 +990,7 @@ exp_export(struct nfsctl_export *nxp)
struct svc_export *exp = NULL;
struct svc_export new;
struct svc_expkey *fsid_key = NULL;
- struct nameidata nd;
+ struct path path;
int err;
/* Consistency check */
@@ -1021,12 +1013,12 @@ exp_export(struct nfsctl_export *nxp)
/* Look up the dentry */
- err = path_lookup(nxp->ex_path, 0, &nd);
+ err = kern_path(nxp->ex_path, 0, &path);
if (err)
goto out_put_clp;
err = -EINVAL;
- exp = exp_get_by_name(clp, nd.path.mnt, nd.path.dentry, NULL);
+ exp = exp_get_by_name(clp, path.mnt, path.dentry, NULL);
memset(&new, 0, sizeof(new));
@@ -1034,8 +1026,8 @@ exp_export(struct nfsctl_export *nxp)
if ((nxp->ex_flags & NFSEXP_FSID) &&
(!IS_ERR(fsid_key = exp_get_fsid_key(clp, nxp->ex_dev))) &&
fsid_key->ek_path.mnt &&
- (fsid_key->ek_path.mnt != nd.path.mnt ||
- fsid_key->ek_path.dentry != nd.path.dentry))
+ (fsid_key->ek_path.mnt != path.mnt ||
+ fsid_key->ek_path.dentry != path.dentry))
goto finish;
if (!IS_ERR(exp)) {
@@ -1051,7 +1043,7 @@ exp_export(struct nfsctl_export *nxp)
goto finish;
}
- err = check_export(nd.path.dentry->d_inode, nxp->ex_flags, NULL);
+ err = check_export(path.dentry->d_inode, nxp->ex_flags, NULL);
if (err) goto finish;
err = -ENOMEM;
@@ -1064,7 +1056,7 @@ exp_export(struct nfsctl_export *nxp)
if (!new.ex_pathname)
goto finish;
new.ex_client = clp;
- new.ex_path = nd.path;
+ new.ex_path = path;
new.ex_flags = nxp->ex_flags;
new.ex_anon_uid = nxp->ex_anon_uid;
new.ex_anon_gid = nxp->ex_anon_gid;
@@ -1090,7 +1082,7 @@ finish:
exp_put(exp);
if (fsid_key && !IS_ERR(fsid_key))
cache_put(&fsid_key->h, &svc_expkey_cache);
- path_put(&nd.path);
+ path_put(&path);
out_put_clp:
auth_domain_put(clp);
out_unlock:
@@ -1121,7 +1113,7 @@ exp_unexport(struct nfsctl_export *nxp)
{
struct auth_domain *dom;
svc_export *exp;
- struct nameidata nd;
+ struct path path;
int err;
/* Consistency check */
@@ -1138,13 +1130,13 @@ exp_unexport(struct nfsctl_export *nxp)
goto out_unlock;
}
- err = path_lookup(nxp->ex_path, 0, &nd);
+ err = kern_path(nxp->ex_path, 0, &path);
if (err)
goto out_domain;
err = -EINVAL;
- exp = exp_get_by_name(dom, nd.path.mnt, nd.path.dentry, NULL);
- path_put(&nd.path);
+ exp = exp_get_by_name(dom, path.mnt, path.dentry, NULL);
+ path_put(&path);
if (IS_ERR(exp))
goto out_domain;
@@ -1166,26 +1158,26 @@ out_unlock:
* since its harder to fool a kernel module than a user space program.
*/
int
-exp_rootfh(svc_client *clp, char *path, struct knfsd_fh *f, int maxsize)
+exp_rootfh(svc_client *clp, char *name, struct knfsd_fh *f, int maxsize)
{
struct svc_export *exp;
- struct nameidata nd;
+ struct path path;
struct inode *inode;
struct svc_fh fh;
int err;
err = -EPERM;
/* NB: we probably ought to check that it's NUL-terminated */
- if (path_lookup(path, 0, &nd)) {
- printk("nfsd: exp_rootfh path not found %s", path);
+ if (kern_path(name, 0, &path)) {
+ printk("nfsd: exp_rootfh path not found %s", name);
return err;
}
- inode = nd.path.dentry->d_inode;
+ inode = path.dentry->d_inode;
dprintk("nfsd: exp_rootfh(%s [%p] %s:%s/%ld)\n",
- path, nd.path.dentry, clp->name,
+ name, path.dentry, clp->name,
inode->i_sb->s_id, inode->i_ino);
- exp = exp_parent(clp, nd.path.mnt, nd.path.dentry, NULL);
+ exp = exp_parent(clp, path.mnt, path.dentry, NULL);
if (IS_ERR(exp)) {
err = PTR_ERR(exp);
goto out;
@@ -1195,7 +1187,7 @@ exp_rootfh(svc_client *clp, char *path, struct knfsd_fh *f, int maxsize)
* fh must be initialized before calling fh_compose
*/
fh_init(&fh, maxsize);
- if (fh_compose(&fh, exp, nd.path.dentry, NULL))
+ if (fh_compose(&fh, exp, path.dentry, NULL))
err = -EINVAL;
else
err = 0;
@@ -1203,7 +1195,7 @@ exp_rootfh(svc_client *clp, char *path, struct knfsd_fh *f, int maxsize)
fh_put(&fh);
exp_put(exp);
out:
- path_put(&nd.path);
+ path_put(&path);
return err;
}
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 094747a1227..6d7d8c02c19 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -358,6 +358,7 @@ static struct rpc_program cb_program = {
.nrvers = ARRAY_SIZE(nfs_cb_version),
.version = nfs_cb_version,
.stats = &cb_stats,
+ .pipe_dir_name = "/nfsd4_cb",
};
/* Reference counting, callback cleanup, etc., all look racy as heck.
@@ -382,8 +383,9 @@ static int do_probe_callback(void *data)
.program = &cb_program,
.prognumber = cb->cb_prog,
.version = nfs_cb_version[1]->number,
- .authflavor = RPC_AUTH_UNIX, /* XXX: need AUTH_GSS... */
+ .authflavor = clp->cl_flavor,
.flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET),
+ .client_name = clp->cl_principal,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
@@ -392,6 +394,11 @@ static int do_probe_callback(void *data)
struct rpc_clnt *client;
int status;
+ if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5)) {
+ status = nfserr_cb_path_down;
+ goto out_err;
+ }
+
/* Initialize address */
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
index 145b3c877a2..0f9d6efaa62 100644
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -51,23 +51,29 @@
#define NFSDDBG_FACILITY NFSDDBG_PROC
/* Globals */
-static struct nameidata rec_dir;
+static struct path rec_dir;
static int rec_dir_init = 0;
-static void
-nfs4_save_user(uid_t *saveuid, gid_t *savegid)
+static int
+nfs4_save_creds(const struct cred **original_creds)
{
- *saveuid = current->fsuid;
- *savegid = current->fsgid;
- current->fsuid = 0;
- current->fsgid = 0;
+ struct cred *new;
+
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+
+ new->fsuid = 0;
+ new->fsgid = 0;
+ *original_creds = override_creds(new);
+ put_cred(new);
+ return 0;
}
static void
-nfs4_reset_user(uid_t saveuid, gid_t savegid)
+nfs4_reset_creds(const struct cred *original)
{
- current->fsuid = saveuid;
- current->fsgid = savegid;
+ revert_creds(original);
}
static void
@@ -121,18 +127,17 @@ out_no_tfm:
static void
nfsd4_sync_rec_dir(void)
{
- mutex_lock(&rec_dir.path.dentry->d_inode->i_mutex);
- nfsd_sync_dir(rec_dir.path.dentry);
- mutex_unlock(&rec_dir.path.dentry->d_inode->i_mutex);
+ mutex_lock(&rec_dir.dentry->d_inode->i_mutex);
+ nfsd_sync_dir(rec_dir.dentry);
+ mutex_unlock(&rec_dir.dentry->d_inode->i_mutex);
}
int
nfsd4_create_clid_dir(struct nfs4_client *clp)
{
+ const struct cred *original_cred;
char *dname = clp->cl_recdir;
struct dentry *dentry;
- uid_t uid;
- gid_t gid;
int status;
dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname);
@@ -140,12 +145,14 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
if (!rec_dir_init || clp->cl_firststate)
return 0;
- nfs4_save_user(&uid, &gid);
+ status = nfs4_save_creds(&original_cred);
+ if (status < 0)
+ return status;
/* lock the parent */
- mutex_lock(&rec_dir.path.dentry->d_inode->i_mutex);
+ mutex_lock(&rec_dir.dentry->d_inode->i_mutex);
- dentry = lookup_one_len(dname, rec_dir.path.dentry, HEXDIR_LEN-1);
+ dentry = lookup_one_len(dname, rec_dir.dentry, HEXDIR_LEN-1);
if (IS_ERR(dentry)) {
status = PTR_ERR(dentry);
goto out_unlock;
@@ -155,20 +162,20 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
dprintk("NFSD: nfsd4_create_clid_dir: DIRECTORY EXISTS\n");
goto out_put;
}
- status = mnt_want_write(rec_dir.path.mnt);
+ status = mnt_want_write(rec_dir.mnt);
if (status)
goto out_put;
- status = vfs_mkdir(rec_dir.path.dentry->d_inode, dentry, S_IRWXU);
- mnt_drop_write(rec_dir.path.mnt);
+ status = vfs_mkdir(rec_dir.dentry->d_inode, dentry, S_IRWXU);
+ mnt_drop_write(rec_dir.mnt);
out_put:
dput(dentry);
out_unlock:
- mutex_unlock(&rec_dir.path.dentry->d_inode->i_mutex);
+ mutex_unlock(&rec_dir.dentry->d_inode->i_mutex);
if (status == 0) {
clp->cl_firststate = 1;
nfsd4_sync_rec_dir();
}
- nfs4_reset_user(uid, gid);
+ nfs4_reset_creds(original_cred);
dprintk("NFSD: nfsd4_create_clid_dir returns %d\n", status);
return status;
}
@@ -211,22 +218,25 @@ nfsd4_build_dentrylist(void *arg, const char *name, int namlen,
static int
nfsd4_list_rec_dir(struct dentry *dir, recdir_func *f)
{
+ const struct cred *original_cred;
struct file *filp;
struct dentry_list_arg dla = {
.parent = dir,
};
struct list_head *dentries = &dla.dentries;
struct dentry_list *child;
- uid_t uid;
- gid_t gid;
int status;
if (!rec_dir_init)
return 0;
- nfs4_save_user(&uid, &gid);
+ status = nfs4_save_creds(&original_cred);
+ if (status < 0)
+ return status;
+ INIT_LIST_HEAD(dentries);
- filp = dentry_open(dget(dir), mntget(rec_dir.path.mnt), O_RDONLY);
+ filp = dentry_open(dget(dir), mntget(rec_dir.mnt), O_RDONLY,
+ current_cred());
status = PTR_ERR(filp);
if (IS_ERR(filp))
goto out;
@@ -249,7 +259,7 @@ out:
dput(child->dentry);
kfree(child);
}
- nfs4_reset_user(uid, gid);
+ nfs4_reset_creds(original_cred);
return status;
}
@@ -291,9 +301,9 @@ nfsd4_unlink_clid_dir(char *name, int namlen)
dprintk("NFSD: nfsd4_unlink_clid_dir. name %.*s\n", namlen, name);
- mutex_lock(&rec_dir.path.dentry->d_inode->i_mutex);
- dentry = lookup_one_len(name, rec_dir.path.dentry, namlen);
- mutex_unlock(&rec_dir.path.dentry->d_inode->i_mutex);
+ mutex_lock(&rec_dir.dentry->d_inode->i_mutex);
+ dentry = lookup_one_len(name, rec_dir.dentry, namlen);
+ mutex_unlock(&rec_dir.dentry->d_inode->i_mutex);
if (IS_ERR(dentry)) {
status = PTR_ERR(dentry);
return status;
@@ -302,7 +312,7 @@ nfsd4_unlink_clid_dir(char *name, int namlen)
if (!dentry->d_inode)
goto out;
- status = nfsd4_clear_clid_dir(rec_dir.path.dentry, dentry);
+ status = nfsd4_clear_clid_dir(rec_dir.dentry, dentry);
out:
dput(dentry);
return status;
@@ -311,23 +321,26 @@ out:
void
nfsd4_remove_clid_dir(struct nfs4_client *clp)
{
- uid_t uid;
- gid_t gid;
+ const struct cred *original_cred;
int status;
if (!rec_dir_init || !clp->cl_firststate)
return;
- status = mnt_want_write(rec_dir.path.mnt);
+ status = mnt_want_write(rec_dir.mnt);
if (status)
goto out;
clp->cl_firststate = 0;
- nfs4_save_user(&uid, &gid);
+
+ status = nfs4_save_creds(&original_cred);
+ if (status < 0)
+ goto out;
+
status = nfsd4_unlink_clid_dir(clp->cl_recdir, HEXDIR_LEN-1);
- nfs4_reset_user(uid, gid);
+ nfs4_reset_creds(original_cred);
if (status == 0)
nfsd4_sync_rec_dir();
- mnt_drop_write(rec_dir.path.mnt);
+ mnt_drop_write(rec_dir.mnt);
out:
if (status)
printk("NFSD: Failed to remove expired client state directory"
@@ -357,17 +370,17 @@ nfsd4_recdir_purge_old(void) {
if (!rec_dir_init)
return;
- status = mnt_want_write(rec_dir.path.mnt);
+ status = mnt_want_write(rec_dir.mnt);
if (status)
goto out;
- status = nfsd4_list_rec_dir(rec_dir.path.dentry, purge_old);
+ status = nfsd4_list_rec_dir(rec_dir.dentry, purge_old);
if (status == 0)
nfsd4_sync_rec_dir();
- mnt_drop_write(rec_dir.path.mnt);
+ mnt_drop_write(rec_dir.mnt);
out:
if (status)
printk("nfsd4: failed to purge old clients from recovery"
- " directory %s\n", rec_dir.path.dentry->d_name.name);
+ " directory %s\n", rec_dir.dentry->d_name.name);
}
static int
@@ -387,10 +400,10 @@ int
nfsd4_recdir_load(void) {
int status;
- status = nfsd4_list_rec_dir(rec_dir.path.dentry, load_recdir);
+ status = nfsd4_list_rec_dir(rec_dir.dentry, load_recdir);
if (status)
printk("nfsd4: failed loading clients from recovery"
- " directory %s\n", rec_dir.path.dentry->d_name.name);
+ " directory %s\n", rec_dir.dentry->d_name.name);
return status;
}
@@ -401,18 +414,23 @@ nfsd4_recdir_load(void) {
void
nfsd4_init_recdir(char *rec_dirname)
{
- uid_t uid = 0;
- gid_t gid = 0;
- int status;
+ const struct cred *original_cred;
+ int status;
printk("NFSD: Using %s as the NFSv4 state recovery directory\n",
rec_dirname);
BUG_ON(rec_dir_init);
- nfs4_save_user(&uid, &gid);
+ status = nfs4_save_creds(&original_cred);
+ if (status < 0) {
+ printk("NFSD: Unable to change credentials to find recovery"
+ " directory: error %d\n",
+ status);
+ return;
+ }
- status = path_lookup(rec_dirname, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
+ status = kern_path(rec_dirname, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
&rec_dir);
if (status)
printk("NFSD: unable to find recovery directory %s\n",
@@ -420,7 +438,7 @@ nfsd4_init_recdir(char *rec_dirname)
if (!status)
rec_dir_init = 1;
- nfs4_reset_user(uid, gid);
+ nfs4_reset_creds(original_cred);
}
void
@@ -429,5 +447,5 @@ nfsd4_shutdown_recdir(void)
if (!rec_dir_init)
return;
rec_dir_init = 0;
- path_put(&rec_dir.path);
+ path_put(&rec_dir);
}
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 0cc7ff5d5ab..13e0e074dbb 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -54,6 +54,7 @@
#include <linux/mutex.h>
#include <linux/lockd/bind.h>
#include <linux/module.h>
+#include <linux/sunrpc/svcauth_gss.h>
#define NFSDDBG_FACILITY NFSDDBG_PROC
@@ -377,6 +378,7 @@ free_client(struct nfs4_client *clp)
shutdown_callback_client(clp);
if (clp->cl_cred.cr_group_info)
put_group_info(clp->cl_cred.cr_group_info);
+ kfree(clp->cl_principal);
kfree(clp->cl_name.data);
kfree(clp);
}
@@ -696,6 +698,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
unsigned int strhashval;
struct nfs4_client *conf, *unconf, *new;
__be32 status;
+ char *princ;
char dname[HEXDIR_LEN];
if (!check_name(clname))
@@ -719,8 +722,8 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfserr_clid_inuse;
if (!same_creds(&conf->cl_cred, &rqstp->rq_cred)
|| conf->cl_addr != sin->sin_addr.s_addr) {
- dprintk("NFSD: setclientid: string in use by client"
- "at %u.%u.%u.%u\n", NIPQUAD(conf->cl_addr));
+ dprintk("NFSD: setclientid: string in use by clientat %pI4\n",
+ &conf->cl_addr);
goto out;
}
}
@@ -783,6 +786,15 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
}
copy_verf(new, &clverifier);
new->cl_addr = sin->sin_addr.s_addr;
+ new->cl_flavor = rqstp->rq_flavor;
+ princ = svc_gss_principal(rqstp);
+ if (princ) {
+ new->cl_principal = kstrdup(princ, GFP_KERNEL);
+ if (new->cl_principal == NULL) {
+ free_client(new);
+ goto out;
+ }
+ }
copy_cred(&new->cl_cred, &rqstp->rq_cred);
gen_confirm(new);
gen_callback(new, setclid);
@@ -3261,6 +3273,7 @@ nfs4_state_shutdown(void)
{
cancel_rearming_delayed_workqueue(laundry_wq, &laundromat_work);
destroy_workqueue(laundry_wq);
+ locks_end_grace(&nfsd4_manager);
nfs4_lock_state();
nfs4_release_reclaim();
__nfs4_state_shutdown();
@@ -3284,17 +3297,17 @@ int
nfs4_reset_recoverydir(char *recdir)
{
int status;
- struct nameidata nd;
+ struct path path;
- status = path_lookup(recdir, LOOKUP_FOLLOW, &nd);
+ status = kern_path(recdir, LOOKUP_FOLLOW, &path);
if (status)
return status;
status = -ENOTDIR;
- if (S_ISDIR(nd.path.dentry->d_inode->i_mode)) {
+ if (S_ISDIR(path.dentry->d_inode->i_mode)) {
nfs4_set_recdir(recdir);
status = 0;
}
- path_put(&nd.path);
+ path_put(&path);
return status;
}
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 97543df5824..77d7b8c531a 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -330,7 +330,7 @@ static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size)
return -EINVAL;
/* get ipv4 address */
- if (sscanf(fo_path, NIPQUAD_FMT "%c", &b1, &b2, &b3, &b4, &c) != 4)
+ if (sscanf(fo_path, "%u.%u.%u.%u%c", &b1, &b2, &b3, &b4, &c) != 4)
return -EINVAL;
if (b1 > 255 || b2 > 255 || b3 > 255 || b4 > 255)
return -EINVAL;
@@ -341,7 +341,7 @@ static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size)
static ssize_t failover_unlock_fs(struct file *file, char *buf, size_t size)
{
- struct nameidata nd;
+ struct path path;
char *fo_path;
int error;
@@ -356,13 +356,13 @@ static ssize_t failover_unlock_fs(struct file *file, char *buf, size_t size)
if (qword_get(&buf, fo_path, size) < 0)
return -EINVAL;
- error = path_lookup(fo_path, 0, &nd);
+ error = kern_path(fo_path, 0, &path);
if (error)
return error;
- error = nlmsvc_unlock_all_by_sb(nd.path.mnt->mnt_sb);
+ error = nlmsvc_unlock_all_by_sb(path.mnt->mnt_sb);
- path_put(&nd.path);
+ path_put(&path);
return error;
}
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index cd25d91895a..f0da7d9c3a9 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -186,9 +186,14 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
* access control settings being in effect, we cannot
* fix that case easily.
*/
- current->cap_effective =
- cap_raise_nfsd_set(current->cap_effective,
- current->cap_permitted);
+ struct cred *new = prepare_creds();
+ if (!new)
+ return nfserrno(-ENOMEM);
+ new->cap_effective =
+ cap_raise_nfsd_set(new->cap_effective,
+ new->cap_permitted);
+ put_cred(override_creds(new));
+ put_cred(new);
} else {
error = nfsd_setuser_and_check_port(rqstp, exp);
if (error)
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index 59eeb46f82c..07e4f5d7baa 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -249,6 +249,10 @@ static int nfsd_init_socks(int port)
if (error < 0)
return error;
+ error = lockd_up();
+ if (error < 0)
+ return error;
+
error = svc_create_xprt(nfsd_serv, "tcp", port,
SVC_SOCK_DEFAULTS);
if (error < 0)
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index aa1d0d6489a..d1c5f787b36 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -410,6 +410,7 @@ out_nfserr:
static ssize_t nfsd_getxattr(struct dentry *dentry, char *key, void **buf)
{
ssize_t buflen;
+ ssize_t ret;
buflen = vfs_getxattr(dentry, key, NULL, 0);
if (buflen <= 0)
@@ -419,7 +420,10 @@ static ssize_t nfsd_getxattr(struct dentry *dentry, char *key, void **buf)
if (!*buf)
return -ENOMEM;
- return vfs_getxattr(dentry, key, *buf, buflen);
+ ret = vfs_getxattr(dentry, key, *buf, buflen);
+ if (ret < 0)
+ kfree(*buf);
+ return ret;
}
#endif
@@ -667,6 +671,7 @@ __be32
nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
int access, struct file **filp)
{
+ const struct cred *cred = current_cred();
struct dentry *dentry;
struct inode *inode;
int flags = O_RDONLY|O_LARGEFILE;
@@ -721,7 +726,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
DQUOT_INIT(inode);
}
*filp = dentry_open(dget(dentry), mntget(fhp->fh_export->ex_path.mnt),
- flags);
+ flags, cred);
if (IS_ERR(*filp))
host_err = PTR_ERR(*filp);
out_nfserr:
@@ -1165,7 +1170,7 @@ nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *resfhp,
* send along the gid on create when it tries to implement
* setgid directories via NFS:
*/
- if (current->fsuid != 0)
+ if (current_fsuid() != 0)
iap->ia_valid &= ~(ATTR_UID|ATTR_GID);
if (iap->ia_valid)
return nfsd_setattr(rqstp, resfhp, iap, 0, (time_t)0);
@@ -1814,6 +1819,113 @@ out:
}
/*
+ * We do this buffering because we must not call back into the file
+ * system's ->lookup() method from the filldir callback. That may well
+ * deadlock a number of file systems.
+ *
+ * This is based heavily on the implementation of same in XFS.
+ */
+struct buffered_dirent {
+ u64 ino;
+ loff_t offset;
+ int namlen;
+ unsigned int d_type;
+ char name[];
+};
+
+struct readdir_data {
+ char *dirent;
+ size_t used;
+ int full;
+};
+
+static int nfsd_buffered_filldir(void *__buf, const char *name, int namlen,
+ loff_t offset, u64 ino, unsigned int d_type)
+{
+ struct readdir_data *buf = __buf;
+ struct buffered_dirent *de = (void *)(buf->dirent + buf->used);
+ unsigned int reclen;
+
+ reclen = ALIGN(sizeof(struct buffered_dirent) + namlen, sizeof(u64));
+ if (buf->used + reclen > PAGE_SIZE) {
+ buf->full = 1;
+ return -EINVAL;
+ }
+
+ de->namlen = namlen;
+ de->offset = offset;
+ de->ino = ino;
+ de->d_type = d_type;
+ memcpy(de->name, name, namlen);
+ buf->used += reclen;
+
+ return 0;
+}
+
+static int nfsd_buffered_readdir(struct file *file, filldir_t func,
+ struct readdir_cd *cdp, loff_t *offsetp)
+{
+ struct readdir_data buf;
+ struct buffered_dirent *de;
+ int host_err;
+ int size;
+ loff_t offset;
+
+ buf.dirent = (void *)__get_free_page(GFP_KERNEL);
+ if (!buf.dirent)
+ return -ENOMEM;
+
+ offset = *offsetp;
+
+ while (1) {
+ unsigned int reclen;
+
+ cdp->err = nfserr_eof; /* will be cleared on successful read */
+ buf.used = 0;
+ buf.full = 0;
+
+ host_err = vfs_readdir(file, nfsd_buffered_filldir, &buf);
+ if (buf.full)
+ host_err = 0;
+
+ if (host_err < 0)
+ break;
+
+ size = buf.used;
+
+ if (!size)
+ break;
+
+ de = (struct buffered_dirent *)buf.dirent;
+ while (size > 0) {
+ offset = de->offset;
+
+ if (func(cdp, de->name, de->namlen, de->offset,
+ de->ino, de->d_type))
+ goto done;
+
+ if (cdp->err != nfs_ok)
+ goto done;
+
+ reclen = ALIGN(sizeof(*de) + de->namlen,
+ sizeof(u64));
+ size -= reclen;
+ de = (struct buffered_dirent *)((char *)de + reclen);
+ }
+ offset = vfs_llseek(file, 0, SEEK_CUR);
+ }
+
+ done:
+ free_page((unsigned long)(buf.dirent));
+
+ if (host_err)
+ return nfserrno(host_err);
+
+ *offsetp = offset;
+ return cdp->err;
+}
+
+/*
* Read entries from a directory.
* The NFSv3/4 verifier we ignore for now.
*/
@@ -1822,7 +1934,6 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp,
struct readdir_cd *cdp, filldir_t func)
{
__be32 err;
- int host_err;
struct file *file;
loff_t offset = *offsetp;
@@ -1836,21 +1947,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp,
goto out_close;
}
- /*
- * Read the directory entries. This silly loop is necessary because
- * readdir() is not guaranteed to fill up the entire buffer, but
- * may choose to do less.
- */
-
- do {
- cdp->err = nfserr_eof; /* will be cleared on successful read */
- host_err = vfs_readdir(file, func, cdp);
- } while (host_err >=0 && cdp->err == nfs_ok);
- if (host_err)
- err = nfserrno(host_err);
- else
- err = cdp->err;
- *offsetp = vfs_llseek(file, 0, 1);
+ err = nfsd_buffered_readdir(file, func, cdp, offsetp);
if (err == nfserr_eof || err == nfserr_toosmall)
err = nfs_ok; /* can still be found in ->err */
@@ -1905,7 +2002,7 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
IS_APPEND(inode)? " append" : "",
__mnt_is_readonly(exp->ex_path.mnt)? " ro" : "");
dprintk(" owner %d/%d user %d/%d\n",
- inode->i_uid, inode->i_gid, current->fsuid, current->fsgid);
+ inode->i_uid, inode->i_gid, current_fsuid(), current_fsgid());
#endif
/* Normally we reject any write/sattr etc access on a read-only file
@@ -1948,7 +2045,7 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
* with NFSv3.
*/
if ((acc & NFSD_MAY_OWNER_OVERRIDE) &&
- inode->i_uid == current->fsuid)
+ inode->i_uid == current_fsuid())
return 0;
/* This assumes NFSD_MAY_{READ,WRITE,EXEC} == MAY_{READ,WRITE,EXEC} */