diff options
Diffstat (limited to 'fs/nfs/idmap.c')
| -rw-r--r-- | fs/nfs/idmap.c | 734 | 
1 files changed, 345 insertions, 389 deletions
| diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 2c05f1991e1e..b7f348bb618b 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -34,11 +34,29 @@   *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.   */  #include <linux/types.h> -#include <linux/string.h> -#include <linux/kernel.h> -#include <linux/slab.h> +#include <linux/parser.h> +#include <linux/fs.h>  #include <linux/nfs_idmap.h> +#include <net/net_namespace.h> +#include <linux/sunrpc/rpc_pipe_fs.h>  #include <linux/nfs_fs.h> +#include <linux/nfs_fs_sb.h> +#include <linux/key.h> +#include <linux/keyctl.h> +#include <linux/key-type.h> +#include <keys/user-type.h> +#include <linux/module.h> + +#include "internal.h" +#include "netns.h" + +#define NFS_UINT_MAXLEN 11 + +/* Default cache timeout is 10 minutes */ +unsigned int nfs_idmap_cache_timeout = 600; +static const struct cred *id_resolver_cache; +static struct key_type key_type_id_resolver_legacy; +  /**   * nfs_fattr_init_names - initialise the nfs_fattr owner_name/group_name fields @@ -142,24 +160,7 @@ static int nfs_map_numeric_to_string(__u32 id, char *buf, size_t buflen)  	return snprintf(buf, buflen, "%u", id);  } -#ifdef CONFIG_NFS_USE_NEW_IDMAPPER - -#include <linux/cred.h> -#include <linux/sunrpc/sched.h> -#include <linux/nfs4.h> -#include <linux/nfs_fs_sb.h> -#include <linux/keyctl.h> -#include <linux/key-type.h> -#include <linux/rcupdate.h> -#include <linux/err.h> - -#include <keys/user-type.h> - -#define NFS_UINT_MAXLEN 11 - -const struct cred *id_resolver_cache; - -struct key_type key_type_id_resolver = { +static struct key_type key_type_id_resolver = {  	.name		= "id_resolver",  	.instantiate	= user_instantiate,  	.match		= user_match, @@ -169,13 +170,14 @@ struct key_type key_type_id_resolver = {  	.read		= user_read,  }; -int nfs_idmap_init(void) +static int nfs_idmap_init_keyring(void)  {  	struct cred *cred;  	struct key *keyring;  	int ret = 0; -	printk(KERN_NOTICE "Registering the %s key type\n", key_type_id_resolver.name); +	printk(KERN_NOTICE "NFS: Registering the %s key type\n", +		key_type_id_resolver.name);  	cred = prepare_kernel_cred(NULL);  	if (!cred) @@ -198,6 +200,7 @@ int nfs_idmap_init(void)  	if (ret < 0)  		goto failed_put_key; +	set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags);  	cred->thread_keyring = keyring;  	cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;  	id_resolver_cache = cred; @@ -210,7 +213,7 @@ failed_put_cred:  	return ret;  } -void nfs_idmap_quit(void) +static void nfs_idmap_quit_keyring(void)  {  	key_revoke(id_resolver_cache->thread_keyring);  	unregister_key_type(&key_type_id_resolver); @@ -245,8 +248,10 @@ static ssize_t nfs_idmap_get_desc(const char *name, size_t namelen,  	return desclen;  } -static ssize_t nfs_idmap_request_key(const char *name, size_t namelen, -		const char *type, void *data, size_t data_size) +static ssize_t nfs_idmap_request_key(struct key_type *key_type, +				     const char *name, size_t namelen, +				     const char *type, void *data, +				     size_t data_size, struct idmap *idmap)  {  	const struct cred *saved_cred;  	struct key *rkey; @@ -259,8 +264,12 @@ static ssize_t nfs_idmap_request_key(const char *name, size_t namelen,  		goto out;  	saved_cred = override_creds(id_resolver_cache); -	rkey = request_key(&key_type_id_resolver, desc, ""); +	if (idmap) +		rkey = request_key_with_auxdata(key_type, desc, "", 0, idmap); +	else +		rkey = request_key(&key_type_id_resolver, desc, "");  	revert_creds(saved_cred); +  	kfree(desc);  	if (IS_ERR(rkey)) {  		ret = PTR_ERR(rkey); @@ -293,31 +302,46 @@ out:  	return ret;  } +static ssize_t nfs_idmap_get_key(const char *name, size_t namelen, +				 const char *type, void *data, +				 size_t data_size, struct idmap *idmap) +{ +	ssize_t ret = nfs_idmap_request_key(&key_type_id_resolver, +					    name, namelen, type, data, +					    data_size, NULL); +	if (ret < 0) { +		ret = nfs_idmap_request_key(&key_type_id_resolver_legacy, +					    name, namelen, type, data, +					    data_size, idmap); +	} +	return ret; +}  /* ID -> Name */ -static ssize_t nfs_idmap_lookup_name(__u32 id, const char *type, char *buf, size_t buflen) +static ssize_t nfs_idmap_lookup_name(__u32 id, const char *type, char *buf, +				     size_t buflen, struct idmap *idmap)  {  	char id_str[NFS_UINT_MAXLEN];  	int id_len;  	ssize_t ret;  	id_len = snprintf(id_str, sizeof(id_str), "%u", id); -	ret = nfs_idmap_request_key(id_str, id_len, type, buf, buflen); +	ret = nfs_idmap_get_key(id_str, id_len, type, buf, buflen, idmap);  	if (ret < 0)  		return -EINVAL;  	return ret;  }  /* Name -> ID */ -static int nfs_idmap_lookup_id(const char *name, size_t namelen, -				const char *type, __u32 *id) +static int nfs_idmap_lookup_id(const char *name, size_t namelen, const char *type, +			       __u32 *id, struct idmap *idmap)  {  	char id_str[NFS_UINT_MAXLEN];  	long id_long;  	ssize_t data_size;  	int ret = 0; -	data_size = nfs_idmap_request_key(name, namelen, type, id_str, NFS_UINT_MAXLEN); +	data_size = nfs_idmap_get_key(name, namelen, type, id_str, NFS_UINT_MAXLEN, idmap);  	if (data_size <= 0) {  		ret = -EINVAL;  	} else { @@ -327,114 +351,103 @@ static int nfs_idmap_lookup_id(const char *name, size_t namelen,  	return ret;  } -int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *uid) -{ -	if (nfs_map_string_to_numeric(name, namelen, uid)) -		return 0; -	return nfs_idmap_lookup_id(name, namelen, "uid", uid); -} - -int nfs_map_group_to_gid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *gid) -{ -	if (nfs_map_string_to_numeric(name, namelen, gid)) -		return 0; -	return nfs_idmap_lookup_id(name, namelen, "gid", gid); -} - -int nfs_map_uid_to_name(const struct nfs_server *server, __u32 uid, char *buf, size_t buflen) -{ -	int ret = -EINVAL; - -	if (!(server->caps & NFS_CAP_UIDGID_NOMAP)) -		ret = nfs_idmap_lookup_name(uid, "user", buf, buflen); -	if (ret < 0) -		ret = nfs_map_numeric_to_string(uid, buf, buflen); -	return ret; -} -int nfs_map_gid_to_group(const struct nfs_server *server, __u32 gid, char *buf, size_t buflen) -{ -	int ret = -EINVAL; +/* idmap classic begins here */ +module_param(nfs_idmap_cache_timeout, int, 0644); -	if (!(server->caps & NFS_CAP_UIDGID_NOMAP)) -		ret = nfs_idmap_lookup_name(gid, "group", buf, buflen); -	if (ret < 0) -		ret = nfs_map_numeric_to_string(gid, buf, buflen); -	return ret; -} - -#else  /* CONFIG_NFS_USE_NEW_IDMAPPER not defined */ - -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/init.h> -#include <linux/socket.h> -#include <linux/in.h> -#include <linux/sched.h> -#include <linux/sunrpc/clnt.h> -#include <linux/workqueue.h> -#include <linux/sunrpc/rpc_pipe_fs.h> - -#include <linux/nfs_fs.h> - -#include "nfs4_fs.h" - -#define IDMAP_HASH_SZ          128 - -/* Default cache timeout is 10 minutes */ -unsigned int nfs_idmap_cache_timeout = 600 * HZ; - -static int param_set_idmap_timeout(const char *val, struct kernel_param *kp) -{ -	char *endp; -	int num = simple_strtol(val, &endp, 0); -	int jif = num * HZ; -	if (endp == val || *endp || num < 0 || jif < num) -		return -EINVAL; -	*((int *)kp->arg) = jif; -	return 0; -} - -module_param_call(idmap_cache_timeout, param_set_idmap_timeout, param_get_int, -		 &nfs_idmap_cache_timeout, 0644); - -struct idmap_hashent { -	unsigned long		ih_expires; -	__u32			ih_id; -	size_t			ih_namelen; -	char			ih_name[IDMAP_NAMESZ]; +struct idmap { +	struct rpc_pipe		*idmap_pipe; +	struct key_construction	*idmap_key_cons;  }; -struct idmap_hashtable { -	__u8			h_type; -	struct idmap_hashent	h_entries[IDMAP_HASH_SZ]; +enum { +	Opt_find_uid, Opt_find_gid, Opt_find_user, Opt_find_group, Opt_find_err  }; -struct idmap { -	struct dentry		*idmap_dentry; -	wait_queue_head_t	idmap_wq; -	struct idmap_msg	idmap_im; -	struct mutex		idmap_lock;	/* Serializes upcalls */ -	struct mutex		idmap_im_lock;	/* Protects the hashtable */ -	struct idmap_hashtable	idmap_user_hash; -	struct idmap_hashtable	idmap_group_hash; +static const match_table_t nfs_idmap_tokens = { +	{ Opt_find_uid, "uid:%s" }, +	{ Opt_find_gid, "gid:%s" }, +	{ Opt_find_user, "user:%s" }, +	{ Opt_find_group, "group:%s" }, +	{ Opt_find_err, NULL }  }; +static int nfs_idmap_legacy_upcall(struct key_construction *, const char *, void *);  static ssize_t idmap_pipe_downcall(struct file *, const char __user *,  				   size_t);  static void idmap_pipe_destroy_msg(struct rpc_pipe_msg *); -static unsigned int fnvhash32(const void *, size_t); -  static const struct rpc_pipe_ops idmap_upcall_ops = {  	.upcall		= rpc_pipe_generic_upcall,  	.downcall	= idmap_pipe_downcall,  	.destroy_msg	= idmap_pipe_destroy_msg,  }; +static struct key_type key_type_id_resolver_legacy = { +	.name		= "id_resolver", +	.instantiate	= user_instantiate, +	.match		= user_match, +	.revoke		= user_revoke, +	.destroy	= user_destroy, +	.describe	= user_describe, +	.read		= user_read, +	.request_key	= nfs_idmap_legacy_upcall, +}; + +static void __nfs_idmap_unregister(struct rpc_pipe *pipe) +{ +	if (pipe->dentry) +		rpc_unlink(pipe->dentry); +} + +static int __nfs_idmap_register(struct dentry *dir, +				     struct idmap *idmap, +				     struct rpc_pipe *pipe) +{ +	struct dentry *dentry; + +	dentry = rpc_mkpipe_dentry(dir, "idmap", idmap, pipe); +	if (IS_ERR(dentry)) +		return PTR_ERR(dentry); +	pipe->dentry = dentry; +	return 0; +} + +static void nfs_idmap_unregister(struct nfs_client *clp, +				      struct rpc_pipe *pipe) +{ +	struct net *net = clp->net; +	struct super_block *pipefs_sb; + +	pipefs_sb = rpc_get_sb_net(net); +	if (pipefs_sb) { +		__nfs_idmap_unregister(pipe); +		rpc_put_sb_net(net); +	} +} + +static int nfs_idmap_register(struct nfs_client *clp, +				   struct idmap *idmap, +				   struct rpc_pipe *pipe) +{ +	struct net *net = clp->net; +	struct super_block *pipefs_sb; +	int err = 0; + +	pipefs_sb = rpc_get_sb_net(net); +	if (pipefs_sb) { +		if (clp->cl_rpcclient->cl_dentry) +			err = __nfs_idmap_register(clp->cl_rpcclient->cl_dentry, +						   idmap, pipe); +		rpc_put_sb_net(net); +	} +	return err; +} +  int  nfs_idmap_new(struct nfs_client *clp)  {  	struct idmap *idmap; +	struct rpc_pipe *pipe;  	int error;  	BUG_ON(clp->cl_idmap != NULL); @@ -443,19 +456,19 @@ nfs_idmap_new(struct nfs_client *clp)  	if (idmap == NULL)  		return -ENOMEM; -	idmap->idmap_dentry = rpc_mkpipe(clp->cl_rpcclient->cl_path.dentry, -			"idmap", idmap, &idmap_upcall_ops, 0); -	if (IS_ERR(idmap->idmap_dentry)) { -		error = PTR_ERR(idmap->idmap_dentry); +	pipe = rpc_mkpipe_data(&idmap_upcall_ops, 0); +	if (IS_ERR(pipe)) { +		error = PTR_ERR(pipe);  		kfree(idmap);  		return error;  	} - -	mutex_init(&idmap->idmap_lock); -	mutex_init(&idmap->idmap_im_lock); -	init_waitqueue_head(&idmap->idmap_wq); -	idmap->idmap_user_hash.h_type = IDMAP_TYPE_USER; -	idmap->idmap_group_hash.h_type = IDMAP_TYPE_GROUP; +	error = nfs_idmap_register(clp, idmap, pipe); +	if (error) { +		rpc_destroy_pipe_data(pipe); +		kfree(idmap); +		return error; +	} +	idmap->idmap_pipe = pipe;  	clp->cl_idmap = idmap;  	return 0; @@ -468,211 +481,220 @@ nfs_idmap_delete(struct nfs_client *clp)  	if (!idmap)  		return; -	rpc_unlink(idmap->idmap_dentry); +	nfs_idmap_unregister(clp, idmap->idmap_pipe); +	rpc_destroy_pipe_data(idmap->idmap_pipe);  	clp->cl_idmap = NULL;  	kfree(idmap);  } -/* - * Helper routines for manipulating the hashtable - */ -static inline struct idmap_hashent * -idmap_name_hash(struct idmap_hashtable* h, const char *name, size_t len) -{ -	return &h->h_entries[fnvhash32(name, len) % IDMAP_HASH_SZ]; -} - -static struct idmap_hashent * -idmap_lookup_name(struct idmap_hashtable *h, const char *name, size_t len) +static int __rpc_pipefs_event(struct nfs_client *clp, unsigned long event, +			      struct super_block *sb)  { -	struct idmap_hashent *he = idmap_name_hash(h, name, len); +	int err = 0; -	if (he->ih_namelen != len || memcmp(he->ih_name, name, len) != 0) -		return NULL; -	if (time_after(jiffies, he->ih_expires)) -		return NULL; -	return he; +	switch (event) { +	case RPC_PIPEFS_MOUNT: +		BUG_ON(clp->cl_rpcclient->cl_dentry == NULL); +		err = __nfs_idmap_register(clp->cl_rpcclient->cl_dentry, +						clp->cl_idmap, +						clp->cl_idmap->idmap_pipe); +		break; +	case RPC_PIPEFS_UMOUNT: +		if (clp->cl_idmap->idmap_pipe) { +			struct dentry *parent; + +			parent = clp->cl_idmap->idmap_pipe->dentry->d_parent; +			__nfs_idmap_unregister(clp->cl_idmap->idmap_pipe); +			/* +			 * Note: This is a dirty hack. SUNRPC hook has been +			 * called already but simple_rmdir() call for the +			 * directory returned with error because of idmap pipe +			 * inside. Thus now we have to remove this directory +			 * here. +			 */ +			if (rpc_rmdir(parent)) +				printk(KERN_ERR "NFS: %s: failed to remove " +					"clnt dir!\n", __func__); +		} +		break; +	default: +		printk(KERN_ERR "NFS: %s: unknown event: %ld\n", __func__, +			event); +		return -ENOTSUPP; +	} +	return err; +} + +static struct nfs_client *nfs_get_client_for_event(struct net *net, int event) +{ +	struct nfs_net *nn = net_generic(net, nfs_net_id); +	struct dentry *cl_dentry; +	struct nfs_client *clp; + +	spin_lock(&nn->nfs_client_lock); +	list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) { +		if (clp->rpc_ops != &nfs_v4_clientops) +			continue; +		cl_dentry = clp->cl_idmap->idmap_pipe->dentry; +		if (((event == RPC_PIPEFS_MOUNT) && cl_dentry) || +		    ((event == RPC_PIPEFS_UMOUNT) && !cl_dentry)) +			continue; +		atomic_inc(&clp->cl_count); +		spin_unlock(&nn->nfs_client_lock); +		return clp; +	} +	spin_unlock(&nn->nfs_client_lock); +	return NULL;  } -static inline struct idmap_hashent * -idmap_id_hash(struct idmap_hashtable* h, __u32 id) +static int rpc_pipefs_event(struct notifier_block *nb, unsigned long event, +			    void *ptr)  { -	return &h->h_entries[fnvhash32(&id, sizeof(id)) % IDMAP_HASH_SZ]; -} +	struct super_block *sb = ptr; +	struct nfs_client *clp; +	int error = 0; -static struct idmap_hashent * -idmap_lookup_id(struct idmap_hashtable *h, __u32 id) -{ -	struct idmap_hashent *he = idmap_id_hash(h, id); -	if (he->ih_id != id || he->ih_namelen == 0) -		return NULL; -	if (time_after(jiffies, he->ih_expires)) -		return NULL; -	return he; +	while ((clp = nfs_get_client_for_event(sb->s_fs_info, event))) { +		error = __rpc_pipefs_event(clp, event, sb); +		nfs_put_client(clp); +		if (error) +			break; +	} +	return error;  } -/* - * Routines for allocating new entries in the hashtable. - * For now, we just have 1 entry per bucket, so it's all - * pretty trivial. - */ -static inline struct idmap_hashent * -idmap_alloc_name(struct idmap_hashtable *h, char *name, size_t len) -{ -	return idmap_name_hash(h, name, len); -} +#define PIPEFS_NFS_PRIO		1 + +static struct notifier_block nfs_idmap_block = { +	.notifier_call	= rpc_pipefs_event, +	.priority	= SUNRPC_PIPEFS_NFS_PRIO, +}; -static inline struct idmap_hashent * -idmap_alloc_id(struct idmap_hashtable *h, __u32 id) +int nfs_idmap_init(void)  { -	return idmap_id_hash(h, id); +	int ret; +	ret = nfs_idmap_init_keyring(); +	if (ret != 0) +		goto out; +	ret = rpc_pipefs_notifier_register(&nfs_idmap_block); +	if (ret != 0) +		nfs_idmap_quit_keyring(); +out: +	return ret;  } -static void -idmap_update_entry(struct idmap_hashent *he, const char *name, -		size_t namelen, __u32 id) +void nfs_idmap_quit(void)  { -	he->ih_id = id; -	memcpy(he->ih_name, name, namelen); -	he->ih_name[namelen] = '\0'; -	he->ih_namelen = namelen; -	he->ih_expires = jiffies + nfs_idmap_cache_timeout; +	rpc_pipefs_notifier_unregister(&nfs_idmap_block); +	nfs_idmap_quit_keyring();  } -/* - * Name -> ID - */ -static int -nfs_idmap_id(struct idmap *idmap, struct idmap_hashtable *h, -		const char *name, size_t namelen, __u32 *id) +static int nfs_idmap_prepare_message(char *desc, struct idmap_msg *im, +				     struct rpc_pipe_msg *msg)  { -	struct rpc_pipe_msg msg; -	struct idmap_msg *im; -	struct idmap_hashent *he; -	DECLARE_WAITQUEUE(wq, current); -	int ret = -EIO; - -	im = &idmap->idmap_im; - -	/* -	 * String sanity checks -	 * Note that the userland daemon expects NUL terminated strings -	 */ -	for (;;) { -		if (namelen == 0) -			return -EINVAL; -		if (name[namelen-1] != '\0') -			break; -		namelen--; -	} -	if (namelen >= IDMAP_NAMESZ) -		return -EINVAL; +	substring_t substr; +	int token, ret; -	mutex_lock(&idmap->idmap_lock); -	mutex_lock(&idmap->idmap_im_lock); - -	he = idmap_lookup_name(h, name, namelen); -	if (he != NULL) { -		*id = he->ih_id; -		ret = 0; -		goto out; -	} +	memset(im,  0, sizeof(*im)); +	memset(msg, 0, sizeof(*msg)); -	memset(im, 0, sizeof(*im)); -	memcpy(im->im_name, name, namelen); +	im->im_type = IDMAP_TYPE_GROUP; +	token = match_token(desc, nfs_idmap_tokens, &substr); -	im->im_type = h->h_type; -	im->im_conv = IDMAP_CONV_NAMETOID; +	switch (token) { +	case Opt_find_uid: +		im->im_type = IDMAP_TYPE_USER; +	case Opt_find_gid: +		im->im_conv = IDMAP_CONV_NAMETOID; +		ret = match_strlcpy(im->im_name, &substr, IDMAP_NAMESZ); +		break; -	memset(&msg, 0, sizeof(msg)); -	msg.data = im; -	msg.len = sizeof(*im); +	case Opt_find_user: +		im->im_type = IDMAP_TYPE_USER; +	case Opt_find_group: +		im->im_conv = IDMAP_CONV_IDTONAME; +		ret = match_int(&substr, &im->im_id); +		break; -	add_wait_queue(&idmap->idmap_wq, &wq); -	if (rpc_queue_upcall(idmap->idmap_dentry->d_inode, &msg) < 0) { -		remove_wait_queue(&idmap->idmap_wq, &wq); +	default: +		ret = -EINVAL;  		goto out;  	} -	set_current_state(TASK_UNINTERRUPTIBLE); -	mutex_unlock(&idmap->idmap_im_lock); -	schedule(); -	__set_current_state(TASK_RUNNING); -	remove_wait_queue(&idmap->idmap_wq, &wq); -	mutex_lock(&idmap->idmap_im_lock); +	msg->data = im; +	msg->len  = sizeof(struct idmap_msg); -	if (im->im_status & IDMAP_STATUS_SUCCESS) { -		*id = im->im_id; -		ret = 0; -	} - - out: -	memset(im, 0, sizeof(*im)); -	mutex_unlock(&idmap->idmap_im_lock); -	mutex_unlock(&idmap->idmap_lock); +out:  	return ret;  } -/* - * ID -> Name - */ -static int -nfs_idmap_name(struct idmap *idmap, struct idmap_hashtable *h, -		__u32 id, char *name) +static int nfs_idmap_legacy_upcall(struct key_construction *cons, +				   const char *op, +				   void *aux)  { -	struct rpc_pipe_msg msg; +	struct rpc_pipe_msg *msg;  	struct idmap_msg *im; -	struct idmap_hashent *he; -	DECLARE_WAITQUEUE(wq, current); -	int ret = -EIO; -	unsigned int len; - -	im = &idmap->idmap_im; +	struct idmap *idmap = (struct idmap *)aux; +	struct key *key = cons->key; +	int ret; -	mutex_lock(&idmap->idmap_lock); -	mutex_lock(&idmap->idmap_im_lock); +	/* msg and im are freed in idmap_pipe_destroy_msg */ +	msg = kmalloc(sizeof(*msg), GFP_KERNEL); +	if (IS_ERR(msg)) { +		ret = PTR_ERR(msg); +		goto out0; +	} -	he = idmap_lookup_id(h, id); -	if (he) { -		memcpy(name, he->ih_name, he->ih_namelen); -		ret = he->ih_namelen; -		goto out; +	im = kmalloc(sizeof(*im), GFP_KERNEL); +	if (IS_ERR(im)) { +		ret = PTR_ERR(im); +		goto out1;  	} -	memset(im, 0, sizeof(*im)); -	im->im_type = h->h_type; -	im->im_conv = IDMAP_CONV_IDTONAME; -	im->im_id = id; +	ret = nfs_idmap_prepare_message(key->description, im, msg); +	if (ret < 0) +		goto out2; -	memset(&msg, 0, sizeof(msg)); -	msg.data = im; -	msg.len = sizeof(*im); +	idmap->idmap_key_cons = cons; -	add_wait_queue(&idmap->idmap_wq, &wq); +	ret = rpc_queue_upcall(idmap->idmap_pipe, msg); +	if (ret < 0) +		goto out2; -	if (rpc_queue_upcall(idmap->idmap_dentry->d_inode, &msg) < 0) { -		remove_wait_queue(&idmap->idmap_wq, &wq); -		goto out; -	} +	return ret; + +out2: +	kfree(im); +out1: +	kfree(msg); +out0: +	key_revoke(cons->key); +	key_revoke(cons->authkey); +	return ret; +} + +static int nfs_idmap_instantiate(struct key *key, struct key *authkey, char *data) +{ +	return key_instantiate_and_link(key, data, strlen(data) + 1, +					id_resolver_cache->thread_keyring, +					authkey); +} -	set_current_state(TASK_UNINTERRUPTIBLE); -	mutex_unlock(&idmap->idmap_im_lock); -	schedule(); -	__set_current_state(TASK_RUNNING); -	remove_wait_queue(&idmap->idmap_wq, &wq); -	mutex_lock(&idmap->idmap_im_lock); - -	if (im->im_status & IDMAP_STATUS_SUCCESS) { -		if ((len = strnlen(im->im_name, IDMAP_NAMESZ)) == 0) -			goto out; -		memcpy(name, im->im_name, len); -		ret = len; +static int nfs_idmap_read_message(struct idmap_msg *im, struct key *key, struct key *authkey) +{ +	char id_str[NFS_UINT_MAXLEN]; +	int ret = -EINVAL; + +	switch (im->im_conv) { +	case IDMAP_CONV_NAMETOID: +		sprintf(id_str, "%d", im->im_id); +		ret = nfs_idmap_instantiate(key, authkey, id_str); +		break; +	case IDMAP_CONV_IDTONAME: +		ret = nfs_idmap_instantiate(key, authkey, im->im_name); +		break;  	} - out: -	memset(im, 0, sizeof(*im)); -	mutex_unlock(&idmap->idmap_im_lock); -	mutex_unlock(&idmap->idmap_lock);  	return ret;  } @@ -681,115 +703,51 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)  {  	struct rpc_inode *rpci = RPC_I(filp->f_path.dentry->d_inode);  	struct idmap *idmap = (struct idmap *)rpci->private; -	struct idmap_msg im_in, *im = &idmap->idmap_im; -	struct idmap_hashtable *h; -	struct idmap_hashent *he = NULL; +	struct key_construction *cons = idmap->idmap_key_cons; +	struct idmap_msg im;  	size_t namelen_in;  	int ret; -	if (mlen != sizeof(im_in)) -		return -ENOSPC; - -	if (copy_from_user(&im_in, src, mlen) != 0) -		return -EFAULT; - -	mutex_lock(&idmap->idmap_im_lock); - -	ret = mlen; -	im->im_status = im_in.im_status; -	/* If we got an error, terminate now, and wake up pending upcalls */ -	if (!(im_in.im_status & IDMAP_STATUS_SUCCESS)) { -		wake_up(&idmap->idmap_wq); +	if (mlen != sizeof(im)) { +		ret = -ENOSPC;  		goto out;  	} -	/* Sanity checking of strings */ -	ret = -EINVAL; -	namelen_in = strnlen(im_in.im_name, IDMAP_NAMESZ); -	if (namelen_in == 0 || namelen_in == IDMAP_NAMESZ) +	if (copy_from_user(&im, src, mlen) != 0) { +		ret = -EFAULT;  		goto out; +	} -	switch (im_in.im_type) { -		case IDMAP_TYPE_USER: -			h = &idmap->idmap_user_hash; -			break; -		case IDMAP_TYPE_GROUP: -			h = &idmap->idmap_group_hash; -			break; -		default: -			goto out; +	if (!(im.im_status & IDMAP_STATUS_SUCCESS)) { +		ret = mlen; +		complete_request_key(idmap->idmap_key_cons, -ENOKEY); +		goto out_incomplete;  	} -	switch (im_in.im_conv) { -	case IDMAP_CONV_IDTONAME: -		/* Did we match the current upcall? */ -		if (im->im_conv == IDMAP_CONV_IDTONAME -				&& im->im_type == im_in.im_type -				&& im->im_id == im_in.im_id) { -			/* Yes: copy string, including the terminating '\0'  */ -			memcpy(im->im_name, im_in.im_name, namelen_in); -			im->im_name[namelen_in] = '\0'; -			wake_up(&idmap->idmap_wq); -		} -		he = idmap_alloc_id(h, im_in.im_id); -		break; -	case IDMAP_CONV_NAMETOID: -		/* Did we match the current upcall? */ -		if (im->im_conv == IDMAP_CONV_NAMETOID -				&& im->im_type == im_in.im_type -				&& strnlen(im->im_name, IDMAP_NAMESZ) == namelen_in -				&& memcmp(im->im_name, im_in.im_name, namelen_in) == 0) { -			im->im_id = im_in.im_id; -			wake_up(&idmap->idmap_wq); -		} -		he = idmap_alloc_name(h, im_in.im_name, namelen_in); -		break; -	default: +	namelen_in = strnlen(im.im_name, IDMAP_NAMESZ); +	if (namelen_in == 0 || namelen_in == IDMAP_NAMESZ) { +		ret = -EINVAL;  		goto out;  	} -	/* If the entry is valid, also copy it to the cache */ -	if (he != NULL) -		idmap_update_entry(he, im_in.im_name, namelen_in, im_in.im_id); -	ret = mlen; +	ret = nfs_idmap_read_message(&im, cons->key, cons->authkey); +	if (ret >= 0) { +		key_set_timeout(cons->key, nfs_idmap_cache_timeout); +		ret = mlen; +	} +  out: -	mutex_unlock(&idmap->idmap_im_lock); +	complete_request_key(idmap->idmap_key_cons, ret); +out_incomplete:  	return ret;  }  static void  idmap_pipe_destroy_msg(struct rpc_pipe_msg *msg)  { -	struct idmap_msg *im = msg->data; -	struct idmap *idmap = container_of(im, struct idmap, idmap_im);  - -	if (msg->errno >= 0) -		return; -	mutex_lock(&idmap->idmap_im_lock); -	im->im_status = IDMAP_STATUS_LOOKUPFAIL; -	wake_up(&idmap->idmap_wq); -	mutex_unlock(&idmap->idmap_im_lock); -} - -/*  - * Fowler/Noll/Vo hash - *    http://www.isthe.com/chongo/tech/comp/fnv/ - */ - -#define FNV_P_32 ((unsigned int)0x01000193) /* 16777619 */ -#define FNV_1_32 ((unsigned int)0x811c9dc5) /* 2166136261 */ - -static unsigned int fnvhash32(const void *buf, size_t buflen) -{ -	const unsigned char *p, *end = (const unsigned char *)buf + buflen; -	unsigned int hash = FNV_1_32; - -	for (p = buf; p < end; p++) { -		hash *= FNV_P_32; -		hash ^= (unsigned int)*p; -	} - -	return hash; +	/* Free memory allocated in nfs_idmap_legacy_upcall() */ +	kfree(msg->data); +	kfree(msg);  }  int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *uid) @@ -798,16 +756,16 @@ int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_  	if (nfs_map_string_to_numeric(name, namelen, uid))  		return 0; -	return nfs_idmap_id(idmap, &idmap->idmap_user_hash, name, namelen, uid); +	return nfs_idmap_lookup_id(name, namelen, "uid", uid, idmap);  } -int nfs_map_group_to_gid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *uid) +int nfs_map_group_to_gid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *gid)  {  	struct idmap *idmap = server->nfs_client->cl_idmap; -	if (nfs_map_string_to_numeric(name, namelen, uid)) +	if (nfs_map_string_to_numeric(name, namelen, gid))  		return 0; -	return nfs_idmap_id(idmap, &idmap->idmap_group_hash, name, namelen, uid); +	return nfs_idmap_lookup_id(name, namelen, "gid", gid, idmap);  }  int nfs_map_uid_to_name(const struct nfs_server *server, __u32 uid, char *buf, size_t buflen) @@ -816,21 +774,19 @@ int nfs_map_uid_to_name(const struct nfs_server *server, __u32 uid, char *buf, s  	int ret = -EINVAL;  	if (!(server->caps & NFS_CAP_UIDGID_NOMAP)) -		ret = nfs_idmap_name(idmap, &idmap->idmap_user_hash, uid, buf); +		ret = nfs_idmap_lookup_name(uid, "user", buf, buflen, idmap);  	if (ret < 0)  		ret = nfs_map_numeric_to_string(uid, buf, buflen);  	return ret;  } -int nfs_map_gid_to_group(const struct nfs_server *server, __u32 uid, char *buf, size_t buflen) +int nfs_map_gid_to_group(const struct nfs_server *server, __u32 gid, char *buf, size_t buflen)  {  	struct idmap *idmap = server->nfs_client->cl_idmap;  	int ret = -EINVAL;  	if (!(server->caps & NFS_CAP_UIDGID_NOMAP)) -		ret = nfs_idmap_name(idmap, &idmap->idmap_group_hash, uid, buf); +		ret = nfs_idmap_lookup_name(gid, "group", buf, buflen, idmap);  	if (ret < 0) -		ret = nfs_map_numeric_to_string(uid, buf, buflen); +		ret = nfs_map_numeric_to_string(gid, buf, buflen);  	return ret;  } - -#endif /* CONFIG_NFS_USE_NEW_IDMAPPER */ | 
