diff options
Diffstat (limited to 'fs/nfs/idmap.c')
| -rw-r--r-- | fs/nfs/idmap.c | 890 | 
1 files changed, 476 insertions, 414 deletions
diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 4e2d9b6b138..567983d2c0e 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -33,25 +33,146 @@   *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS   *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.   */ - -#ifdef CONFIG_NFS_USE_NEW_IDMAPPER - -#include <linux/slab.h> -#include <linux/cred.h> +#include <linux/types.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 <linux/rcupdate.h> -#include <linux/kernel.h> -#include <linux/err.h> -  #include <keys/user-type.h> +#include <linux/module.h> + +#include "internal.h" +#include "netns.h" +#include "nfs4trace.h"  #define NFS_UINT_MAXLEN 11 -const struct cred *id_resolver_cache; +static const struct cred *id_resolver_cache; +static struct key_type key_type_id_resolver_legacy; + +struct idmap_legacy_upcalldata { +	struct rpc_pipe_msg pipe_msg; +	struct idmap_msg idmap_msg; +	struct key_construction	*key_cons; +	struct idmap *idmap; +}; + +struct idmap { +	struct rpc_pipe_dir_object idmap_pdo; +	struct rpc_pipe		*idmap_pipe; +	struct idmap_legacy_upcalldata *idmap_upcall_data; +	struct mutex		idmap_mutex; +}; + +/** + * nfs_fattr_init_names - initialise the nfs_fattr owner_name/group_name fields + * @fattr: fully initialised struct nfs_fattr + * @owner_name: owner name string cache + * @group_name: group name string cache + */ +void nfs_fattr_init_names(struct nfs_fattr *fattr, +		struct nfs4_string *owner_name, +		struct nfs4_string *group_name) +{ +	fattr->owner_name = owner_name; +	fattr->group_name = group_name; +} + +static void nfs_fattr_free_owner_name(struct nfs_fattr *fattr) +{ +	fattr->valid &= ~NFS_ATTR_FATTR_OWNER_NAME; +	kfree(fattr->owner_name->data); +} + +static void nfs_fattr_free_group_name(struct nfs_fattr *fattr) +{ +	fattr->valid &= ~NFS_ATTR_FATTR_GROUP_NAME; +	kfree(fattr->group_name->data); +} + +static bool nfs_fattr_map_owner_name(struct nfs_server *server, struct nfs_fattr *fattr) +{ +	struct nfs4_string *owner = fattr->owner_name; +	kuid_t uid; + +	if (!(fattr->valid & NFS_ATTR_FATTR_OWNER_NAME)) +		return false; +	if (nfs_map_name_to_uid(server, owner->data, owner->len, &uid) == 0) { +		fattr->uid = uid; +		fattr->valid |= NFS_ATTR_FATTR_OWNER; +	} +	return true; +} + +static bool nfs_fattr_map_group_name(struct nfs_server *server, struct nfs_fattr *fattr) +{ +	struct nfs4_string *group = fattr->group_name; +	kgid_t gid; + +	if (!(fattr->valid & NFS_ATTR_FATTR_GROUP_NAME)) +		return false; +	if (nfs_map_group_to_gid(server, group->data, group->len, &gid) == 0) { +		fattr->gid = gid; +		fattr->valid |= NFS_ATTR_FATTR_GROUP; +	} +	return true; +} + +/** + * nfs_fattr_free_names - free up the NFSv4 owner and group strings + * @fattr: a fully initialised nfs_fattr structure + */ +void nfs_fattr_free_names(struct nfs_fattr *fattr) +{ +	if (fattr->valid & NFS_ATTR_FATTR_OWNER_NAME) +		nfs_fattr_free_owner_name(fattr); +	if (fattr->valid & NFS_ATTR_FATTR_GROUP_NAME) +		nfs_fattr_free_group_name(fattr); +} + +/** + * nfs_fattr_map_and_free_names - map owner/group strings into uid/gid and free + * @server: pointer to the filesystem nfs_server structure + * @fattr: a fully initialised nfs_fattr structure + * + * This helper maps the cached NFSv4 owner/group strings in fattr into + * their numeric uid/gid equivalents, and then frees the cached strings. + */ +void nfs_fattr_map_and_free_names(struct nfs_server *server, struct nfs_fattr *fattr) +{ +	if (nfs_fattr_map_owner_name(server, fattr)) +		nfs_fattr_free_owner_name(fattr); +	if (nfs_fattr_map_group_name(server, fattr)) +		nfs_fattr_free_group_name(fattr); +} + +static int nfs_map_string_to_numeric(const char *name, size_t namelen, __u32 *res) +{ +	unsigned long val; +	char buf[16]; + +	if (memchr(name, '@', namelen) != NULL || namelen >= sizeof(buf)) +		return 0; +	memcpy(buf, name, namelen); +	buf[namelen] = '\0'; +	if (kstrtoul(buf, 0, &val) != 0) +		return 0; +	*res = val; +	return 1; +} + +static int nfs_map_numeric_to_string(__u32 id, char *buf, size_t buflen) +{ +	return snprintf(buf, buflen, "%u", id); +} -struct key_type key_type_id_resolver = { +static struct key_type key_type_id_resolver = {  	.name		= "id_resolver",  	.instantiate	= user_instantiate,  	.match		= user_match, @@ -61,40 +182,45 @@ 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)  		return -ENOMEM; -	keyring = key_alloc(&key_type_keyring, ".id_resolver", 0, 0, cred, -			     (KEY_POS_ALL & ~KEY_POS_SETATTR) | -			     KEY_USR_VIEW | KEY_USR_READ, -			     KEY_ALLOC_NOT_IN_QUOTA); +	keyring = keyring_alloc(".id_resolver", +				GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, +				(KEY_POS_ALL & ~KEY_POS_SETATTR) | +				KEY_USR_VIEW | KEY_USR_READ, +				KEY_ALLOC_NOT_IN_QUOTA, NULL);  	if (IS_ERR(keyring)) {  		ret = PTR_ERR(keyring);  		goto failed_put_cred;  	} -	ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL); +	ret = register_key_type(&key_type_id_resolver);  	if (ret < 0)  		goto failed_put_key; -	ret = register_key_type(&key_type_id_resolver); +	ret = register_key_type(&key_type_id_resolver_legacy);  	if (ret < 0) -		goto failed_put_key; +		goto failed_reg_legacy; +	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;  	return 0; +failed_reg_legacy: +	unregister_key_type(&key_type_id_resolver);  failed_put_key:  	key_put(keyring);  failed_put_cred: @@ -102,10 +228,11 @@ 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); +	unregister_key_type(&key_type_id_resolver_legacy);  	put_cred(id_resolver_cache);  } @@ -137,23 +264,42 @@ 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 struct key *nfs_idmap_request_key(const char *name, size_t namelen, +					 const char *type, struct idmap *idmap)  { -	const struct cred *saved_cred; -	struct key *rkey;  	char *desc; -	struct user_key_payload *payload; +	struct key *rkey;  	ssize_t ret;  	ret = nfs_idmap_get_desc(name, namelen, type, strlen(type), &desc);  	if (ret <= 0) -		goto out; +		return ERR_PTR(ret); -	saved_cred = override_creds(id_resolver_cache);  	rkey = request_key(&key_type_id_resolver, desc, ""); -	revert_creds(saved_cred); +	if (IS_ERR(rkey)) { +		mutex_lock(&idmap->idmap_mutex); +		rkey = request_key_with_auxdata(&key_type_id_resolver_legacy, +						desc, "", 0, idmap); +		mutex_unlock(&idmap->idmap_mutex); +	} +  	kfree(desc); +	return rkey; +} + +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) +{ +	const struct cred *saved_cred; +	struct key *rkey; +	struct user_key_payload *payload; +	ssize_t ret; + +	saved_cred = override_creds(id_resolver_cache); +	rkey = nfs_idmap_request_key(name, namelen, type, idmap); +	revert_creds(saved_cred); +  	if (IS_ERR(rkey)) {  		ret = PTR_ERR(rkey);  		goto out; @@ -166,7 +312,7 @@ static ssize_t nfs_idmap_request_key(const char *name, size_t namelen,  	if (ret < 0)  		goto out_up; -	payload = rcu_dereference(rkey->payload.data); +	payload = rcu_dereference(rkey->payload.rcudata);  	if (IS_ERR_OR_NULL(payload)) {  		ret = PTR_ERR(payload);  		goto out_up; @@ -185,162 +331,145 @@ out:  	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 { -		ret = strict_strtol(id_str, 10, &id_long); +		ret = kstrtol(id_str, 10, &id_long);  		*id = (__u32)id_long;  	}  	return ret;  } -int nfs_map_name_to_uid(struct nfs_client *clp, const char *name, size_t namelen, __u32 *uid) -{ -	return nfs_idmap_lookup_id(name, namelen, "uid", uid); -} - -int nfs_map_group_to_gid(struct nfs_client *clp, const char *name, size_t namelen, __u32 *gid) -{ -	return nfs_idmap_lookup_id(name, namelen, "gid", gid); -} - -int nfs_map_uid_to_name(struct nfs_client *clp, __u32 uid, char *buf, size_t buflen) -{ -	return nfs_idmap_lookup_name(uid, "user", buf, buflen); -} -int nfs_map_gid_to_group(struct nfs_client *clp, __u32 gid, char *buf, size_t buflen) -{ -	return nfs_idmap_lookup_name(gid, "group", buf, buflen); -} - -#else  /* CONFIG_NFS_USE_IDMAPPER not defined */ - -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/init.h> -#include <linux/types.h> -#include <linux/slab.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> +/* idmap classic begins here */ -#include <linux/nfs_idmap.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]; +enum { +	Opt_find_uid, Opt_find_gid, Opt_find_user, Opt_find_group, Opt_find_err  }; -struct idmap_hashtable { -	__u8			h_type; -	struct idmap_hashent	h_entries[IDMAP_HASH_SZ]; +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 }  }; -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 ssize_t idmap_pipe_upcall(struct file *, struct rpc_pipe_msg *, -				 char __user *, size_t); +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_release_pipe(struct inode *);  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		= idmap_pipe_upcall, +	.upcall		= rpc_pipe_generic_upcall,  	.downcall	= idmap_pipe_downcall, +	.release_pipe	= idmap_release_pipe,  	.destroy_msg	= idmap_pipe_destroy_msg,  }; +static struct key_type key_type_id_resolver_legacy = { +	.name		= "id_legacy", +	.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_pipe_destroy(struct dentry *dir, +		struct rpc_pipe_dir_object *pdo) +{ +	struct idmap *idmap = pdo->pdo_data; +	struct rpc_pipe *pipe = idmap->idmap_pipe; + +	if (pipe->dentry) { +		rpc_unlink(pipe->dentry); +		pipe->dentry = NULL; +	} +} + +static int nfs_idmap_pipe_create(struct dentry *dir, +		struct rpc_pipe_dir_object *pdo) +{ +	struct idmap *idmap = pdo->pdo_data; +	struct rpc_pipe *pipe = idmap->idmap_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 const struct rpc_pipe_dir_object_ops nfs_idmap_pipe_dir_object_ops = { +	.create = nfs_idmap_pipe_create, +	.destroy = nfs_idmap_pipe_destroy, +}; +  int  nfs_idmap_new(struct nfs_client *clp)  {  	struct idmap *idmap; +	struct rpc_pipe *pipe;  	int error; -	BUG_ON(clp->cl_idmap != NULL); -  	idmap = kzalloc(sizeof(*idmap), GFP_KERNEL);  	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); -		kfree(idmap); -		return error; +	rpc_init_pipe_dir_object(&idmap->idmap_pdo, +			&nfs_idmap_pipe_dir_object_ops, +			idmap); + +	pipe = rpc_mkpipe_data(&idmap_upcall_ops, 0); +	if (IS_ERR(pipe)) { +		error = PTR_ERR(pipe); +		goto err;  	} +	idmap->idmap_pipe = pipe; +	mutex_init(&idmap->idmap_mutex); -	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 = rpc_add_pipe_dir_object(clp->cl_net, +			&clp->cl_rpcclient->cl_pipedir_objects, +			&idmap->idmap_pdo); +	if (error) +		goto err_destroy_pipe;  	clp->cl_idmap = idmap;  	return 0; +err_destroy_pipe: +	rpc_destroy_pipe_data(idmap->idmap_pipe); +err: +	kfree(idmap); +	return error;  }  void @@ -350,376 +479,309 @@ nfs_idmap_delete(struct nfs_client *clp)  	if (!idmap)  		return; -	rpc_unlink(idmap->idmap_dentry);  	clp->cl_idmap = NULL; +	rpc_remove_pipe_dir_object(clp->cl_net, +			&clp->cl_rpcclient->cl_pipedir_objects, +			&idmap->idmap_pdo); +	rpc_destroy_pipe_data(idmap->idmap_pipe);  	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) +int nfs_idmap_init(void)  { -	return &h->h_entries[fnvhash32(name, len) % IDMAP_HASH_SZ]; +	int ret; +	ret = nfs_idmap_init_keyring(); +	if (ret != 0) +		goto out; +out: +	return ret;  } -static struct idmap_hashent * -idmap_lookup_name(struct idmap_hashtable *h, const char *name, size_t len) +void nfs_idmap_quit(void)  { -	struct idmap_hashent *he = idmap_name_hash(h, name, len); - -	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; +	nfs_idmap_quit_keyring();  } -static inline struct idmap_hashent * -idmap_id_hash(struct idmap_hashtable* h, __u32 id) +static int nfs_idmap_prepare_message(char *desc, struct idmap *idmap, +				     struct idmap_msg *im, +				     struct rpc_pipe_msg *msg)  { -	return &h->h_entries[fnvhash32(&id, sizeof(id)) % IDMAP_HASH_SZ]; -} +	substring_t substr; +	int token, ret; + +	im->im_type = IDMAP_TYPE_GROUP; +	token = match_token(desc, nfs_idmap_tokens, &substr); + +	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; -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; +	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; + +	default: +		ret = -EINVAL; +		goto out; +	} + +	msg->data = im; +	msg->len  = sizeof(struct idmap_msg); + +out: +	return ret;  } -/* - * 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) +static bool +nfs_idmap_prepare_pipe_upcall(struct idmap *idmap, +		struct idmap_legacy_upcalldata *data)  { -	return idmap_name_hash(h, name, len); +	if (idmap->idmap_upcall_data != NULL) { +		WARN_ON_ONCE(1); +		return false; +	} +	idmap->idmap_upcall_data = data; +	return true;  } -static inline struct idmap_hashent * -idmap_alloc_id(struct idmap_hashtable *h, __u32 id) +static void +nfs_idmap_complete_pipe_upcall_locked(struct idmap *idmap, int ret)  { -	return idmap_id_hash(h, id); +	struct key_construction *cons = idmap->idmap_upcall_data->key_cons; + +	kfree(idmap->idmap_upcall_data); +	idmap->idmap_upcall_data = NULL; +	complete_request_key(cons, ret);  }  static void -idmap_update_entry(struct idmap_hashent *he, const char *name, -		size_t namelen, __u32 id) +nfs_idmap_abort_pipe_upcall(struct idmap *idmap, int ret)  { -	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; +	if (idmap->idmap_upcall_data != NULL) +		nfs_idmap_complete_pipe_upcall_locked(idmap, ret);  } -/* - * 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_legacy_upcall(struct key_construction *cons, +				   const char *op, +				   void *aux)  { -	struct rpc_pipe_msg msg; +	struct idmap_legacy_upcalldata *data; +	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; - -	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)); -	memcpy(im->im_name, name, namelen); +	struct idmap *idmap = (struct idmap *)aux; +	struct key *key = cons->key; +	int ret = -ENOMEM; -	im->im_type = h->h_type; -	im->im_conv = IDMAP_CONV_NAMETOID; +	/* msg and im are freed in idmap_pipe_destroy_msg */ +	data = kzalloc(sizeof(*data), GFP_KERNEL); +	if (!data) +		goto out1; -	memset(&msg, 0, sizeof(msg)); -	msg.data = im; -	msg.len = sizeof(*im); +	msg = &data->pipe_msg; +	im = &data->idmap_msg; +	data->idmap = idmap; +	data->key_cons = cons; -	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); -		goto out; -	} +	ret = nfs_idmap_prepare_message(key->description, idmap, im, msg); +	if (ret < 0) +		goto out2; -	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); +	ret = -EAGAIN; +	if (!nfs_idmap_prepare_pipe_upcall(idmap, data)) +		goto out2; -	if (im->im_status & IDMAP_STATUS_SUCCESS) { -		*id = im->im_id; -		ret = 0; -	} +	ret = rpc_queue_upcall(idmap->idmap_pipe, msg); +	if (ret < 0) +		nfs_idmap_abort_pipe_upcall(idmap, ret); - out: -	memset(im, 0, sizeof(*im)); -	mutex_unlock(&idmap->idmap_im_lock); -	mutex_unlock(&idmap->idmap_lock); +	return ret; +out2: +	kfree(data); +out1: +	complete_request_key(cons, ret);  	return ret;  } -/* - * ID -> Name - */ -static int -nfs_idmap_name(struct idmap *idmap, struct idmap_hashtable *h, -		__u32 id, char *name) +static int nfs_idmap_instantiate(struct key *key, struct key *authkey, char *data, size_t datalen)  { -	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; - -	mutex_lock(&idmap->idmap_lock); -	mutex_lock(&idmap->idmap_im_lock); - -	he = idmap_lookup_id(h, id); -	if (he) { -		memcpy(name, he->ih_name, he->ih_namelen); -		ret = he->ih_namelen; -		goto out; -	} - -	memset(im, 0, sizeof(*im)); -	im->im_type = h->h_type; -	im->im_conv = IDMAP_CONV_IDTONAME; -	im->im_id = id; - -	memset(&msg, 0, sizeof(msg)); -	msg.data = im; -	msg.len = sizeof(*im); - -	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); -		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); - -	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; -	} - - out: -	memset(im, 0, sizeof(*im)); -	mutex_unlock(&idmap->idmap_im_lock); -	mutex_unlock(&idmap->idmap_lock); -	return ret; +	return key_instantiate_and_link(key, data, datalen, +					id_resolver_cache->thread_keyring, +					authkey);  } -/* RPC pipefs upcall/downcall routines */ -static ssize_t -idmap_pipe_upcall(struct file *filp, struct rpc_pipe_msg *msg, -		  char __user *dst, size_t buflen) +static int nfs_idmap_read_and_verify_message(struct idmap_msg *im, +		struct idmap_msg *upcall, +		struct key *key, struct key *authkey)  { -	char *data = (char *)msg->data + msg->copied; -	size_t mlen = min(msg->len, buflen); -	unsigned long left; +	char id_str[NFS_UINT_MAXLEN]; +	size_t len; +	int ret = -ENOKEY; -	left = copy_to_user(dst, data, mlen); -	if (left == mlen) { -		msg->errno = -EFAULT; -		return -EFAULT; +	/* ret = -ENOKEY */ +	if (upcall->im_type != im->im_type || upcall->im_conv != im->im_conv) +		goto out; +	switch (im->im_conv) { +	case IDMAP_CONV_NAMETOID: +		if (strcmp(upcall->im_name, im->im_name) != 0) +			break; +		/* Note: here we store the NUL terminator too */ +		len = sprintf(id_str, "%d", im->im_id) + 1; +		ret = nfs_idmap_instantiate(key, authkey, id_str, len); +		break; +	case IDMAP_CONV_IDTONAME: +		if (upcall->im_id != im->im_id) +			break; +		len = strlen(im->im_name); +		ret = nfs_idmap_instantiate(key, authkey, im->im_name, len); +		break; +	default: +		ret = -EINVAL;  	} - -	mlen -= left; -	msg->copied += mlen; -	msg->errno = 0; -	return mlen; +out: +	return ret;  }  static ssize_t  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 rpc_inode *rpci = RPC_I(file_inode(filp));  	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; +	struct idmap_msg im;  	size_t namelen_in; -	int ret; +	int ret = -ENOKEY; -	if (mlen != sizeof(im_in)) -		return -ENOSPC; - -	if (copy_from_user(&im_in, src, mlen) != 0) -		return -EFAULT; +	/* If instantiation is successful, anyone waiting for key construction +	 * will have been woken up and someone else may now have used +	 * idmap_key_cons - so after this point we may no longer touch it. +	 */ +	if (idmap->idmap_upcall_data == NULL) +		goto out_noupcall; -	mutex_lock(&idmap->idmap_im_lock); +	cons = idmap->idmap_upcall_data->key_cons; -	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 = -ENOKEY; +		goto out;  	} -	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; +} + +	ret = nfs_idmap_read_and_verify_message(&im, +			&idmap->idmap_upcall_data->idmap_msg, +			cons->key, cons->authkey); +	if (ret >= 0) { +		key_set_timeout(cons->key, nfs_idmap_cache_timeout); +		ret = mlen;  	} -	/* 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;  out: -	mutex_unlock(&idmap->idmap_im_lock); +	nfs_idmap_complete_pipe_upcall_locked(idmap, ret); +out_noupcall:  	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);  +	struct idmap_legacy_upcalldata *data = container_of(msg, +			struct idmap_legacy_upcalldata, +			pipe_msg); +	struct idmap *idmap = data->idmap; -	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); +	if (msg->errno) +		nfs_idmap_abort_pipe_upcall(idmap, msg->errno);  } -/*  - * 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) +static void +idmap_release_pipe(struct inode *inode)  { -	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; -	} +	struct rpc_inode *rpci = RPC_I(inode); +	struct idmap *idmap = (struct idmap *)rpci->private; -	return hash; +	nfs_idmap_abort_pipe_upcall(idmap, -EPIPE);  } -int nfs_map_name_to_uid(struct nfs_client *clp, const char *name, size_t namelen, __u32 *uid) +int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, kuid_t *uid)  { -	struct idmap *idmap = clp->cl_idmap; +	struct idmap *idmap = server->nfs_client->cl_idmap; +	__u32 id = -1; +	int ret = 0; -	return nfs_idmap_id(idmap, &idmap->idmap_user_hash, name, namelen, uid); +	if (!nfs_map_string_to_numeric(name, namelen, &id)) +		ret = nfs_idmap_lookup_id(name, namelen, "uid", &id, idmap); +	if (ret == 0) { +		*uid = make_kuid(&init_user_ns, id); +		if (!uid_valid(*uid)) +			ret = -ERANGE; +	} +	trace_nfs4_map_name_to_uid(name, namelen, id, ret); +	return ret;  } -int nfs_map_group_to_gid(struct nfs_client *clp, 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, kgid_t *gid)  { -	struct idmap *idmap = clp->cl_idmap; +	struct idmap *idmap = server->nfs_client->cl_idmap; +	__u32 id = -1; +	int ret = 0; -	return nfs_idmap_id(idmap, &idmap->idmap_group_hash, name, namelen, uid); +	if (!nfs_map_string_to_numeric(name, namelen, &id)) +		ret = nfs_idmap_lookup_id(name, namelen, "gid", &id, idmap); +	if (ret == 0) { +		*gid = make_kgid(&init_user_ns, id); +		if (!gid_valid(*gid)) +			ret = -ERANGE; +	} +	trace_nfs4_map_group_to_gid(name, namelen, id, ret); +	return ret;  } -int nfs_map_uid_to_name(struct nfs_client *clp, __u32 uid, char *buf, size_t buflen) +int nfs_map_uid_to_name(const struct nfs_server *server, kuid_t uid, char *buf, size_t buflen)  { -	struct idmap *idmap = clp->cl_idmap; +	struct idmap *idmap = server->nfs_client->cl_idmap; +	int ret = -EINVAL; +	__u32 id; -	return nfs_idmap_name(idmap, &idmap->idmap_user_hash, uid, buf); +	id = from_kuid(&init_user_ns, uid); +	if (!(server->caps & NFS_CAP_UIDGID_NOMAP)) +		ret = nfs_idmap_lookup_name(id, "user", buf, buflen, idmap); +	if (ret < 0) +		ret = nfs_map_numeric_to_string(id, buf, buflen); +	trace_nfs4_map_uid_to_name(buf, ret, id, ret); +	return ret;  } -int nfs_map_gid_to_group(struct nfs_client *clp, __u32 uid, char *buf, size_t buflen) +int nfs_map_gid_to_group(const struct nfs_server *server, kgid_t gid, char *buf, size_t buflen)  { -	struct idmap *idmap = clp->cl_idmap; +	struct idmap *idmap = server->nfs_client->cl_idmap; +	int ret = -EINVAL; +	__u32 id; -	return nfs_idmap_name(idmap, &idmap->idmap_group_hash, uid, buf); +	id = from_kgid(&init_user_ns, gid); +	if (!(server->caps & NFS_CAP_UIDGID_NOMAP)) +		ret = nfs_idmap_lookup_name(id, "group", buf, buflen, idmap); +	if (ret < 0) +		ret = nfs_map_numeric_to_string(id, buf, buflen); +	trace_nfs4_map_gid_to_group(buf, ret, id, ret); +	return ret;  } - -#endif /* CONFIG_NFS_USE_NEW_IDMAPPER */  | 
