diff options
| author | Bryan Schumaker <bjschuma@netapp.com> | 2010-09-29 15:41:49 -0400 | 
|---|---|---|
| committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2010-10-07 18:48:49 -0400 | 
| commit | 955a857e062642cd3ebe1dc7bb38c0f85d8f8f17 (patch) | |
| tree | f95fc349c245c4a0a3f6f8fcc5bf02f36a756134 | |
| parent | aa510da5bfe1dfe263215fd0e05dac96e738a782 (diff) | |
| download | olio-linux-3.10-955a857e062642cd3ebe1dc7bb38c0f85d8f8f17.tar.xz olio-linux-3.10-955a857e062642cd3ebe1dc7bb38c0f85d8f8f17.zip  | |
NFS: new idmapper
This patch creates a new idmapper system that uses the request-key function to
place a call into userspace to map user and group ids to names.  The old
idmapper was single threaded, which prevented more than one request from running
at a single time.  This means that a user would have to wait for an upcall to
finish before accessing a cached result.
The upcall result is stored on a keyring of type id_resolver.  See the file
Documentation/filesystems/nfs/idmapper.txt for instructions.
Signed-off-by: Bryan Schumaker <bjschuma@netapp.com>
[Trond: fix up the return value of nfs_idmap_lookup_name and clean up code]
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
| -rw-r--r-- | Documentation/filesystems/nfs/00-INDEX | 2 | ||||
| -rw-r--r-- | Documentation/filesystems/nfs/idmapper.txt | 67 | ||||
| -rw-r--r-- | fs/nfs/Kconfig | 11 | ||||
| -rw-r--r-- | fs/nfs/idmap.c | 211 | ||||
| -rw-r--r-- | fs/nfs/inode.c | 7 | ||||
| -rw-r--r-- | fs/nfs/nfs4xdr.c | 4 | ||||
| -rw-r--r-- | fs/nfs/sysctl.c | 2 | ||||
| -rw-r--r-- | include/linux/nfs_idmap.h | 31 | 
8 files changed, 329 insertions, 6 deletions
diff --git a/Documentation/filesystems/nfs/00-INDEX b/Documentation/filesystems/nfs/00-INDEX index 2f68cd68876..3225a566211 100644 --- a/Documentation/filesystems/nfs/00-INDEX +++ b/Documentation/filesystems/nfs/00-INDEX @@ -14,3 +14,5 @@ nfsroot.txt  	- short guide on setting up a diskless box with NFS root filesystem.  rpc-cache.txt  	- introduction to the caching mechanisms in the sunrpc layer. +idmapper.txt +	- information for configuring request-keys to be used by idmapper diff --git a/Documentation/filesystems/nfs/idmapper.txt b/Documentation/filesystems/nfs/idmapper.txt new file mode 100644 index 00000000000..c3852041a21 --- /dev/null +++ b/Documentation/filesystems/nfs/idmapper.txt @@ -0,0 +1,67 @@ + +========= +ID Mapper +========= +Id mapper is used by NFS to translate user and group ids into names, and to +translate user and group names into ids.  Part of this translation involves +performing an upcall to userspace to request the information.  Id mapper will +user request-key to perform this upcall and cache the result.  The program +/usr/sbin/nfs.upcall should be called by request-key, and will perform the +translation and initialize a key with the resulting information. + + NFS_USE_NEW_IDMAPPER must be selected when configuring the kernel to use this + feature. + +=========== +Configuring +=========== +The file /etc/request-key.conf will need to be modified so /sbin/request-key can +direct the upcall.  The following line should be added: + +#OP	TYPE	DESCRIPTION	CALLOUT INFO	PROGRAM ARG1 ARG2 ARG3 ... +#======	=======	===============	===============	=============================== +create	id_resolver	*	*		/usr/sbin/nfs.upcall %k %d 600 + +This will direct all id_resolver requests to the program /usr/sbin/nfs.upcall. +The last parameter, 600, defines how many seconds into the future the key will +expire.  This parameter is optional for /usr/sbin/nfs.upcall.  When the timeout +is not specified, nfs.upcall will default to 600 seconds. + +id mapper uses for key descriptions: +	  uid:  Find the UID for the given user +	  gid:  Find the GID for the given group +	 user:  Find the user  name for the given UID +	group:  Find the group name for the given GID + +You can handle any of these individually, rather than using the generic upcall +program.  If you would like to use your own program for a uid lookup then you +would edit your request-key.conf so it look similar to this: + +#OP	TYPE	DESCRIPTION	CALLOUT INFO	PROGRAM ARG1 ARG2 ARG3 ... +#======	=======	===============	===============	=============================== +create	id_resolver	uid:*	*		/some/other/program  %k %d 600 +create	id_resolver	*	*		/usr/sbin/nfs.upcall %k %d 600 + +Notice that the new line was added above the line for the generic program. +request-key will find the first matching line and corresponding program.  In +this case, /some/other/program will handle all uid lookups and +/usr/sbin/nfs.upcall will handle gid, user, and group lookups. + +See <file:Documentation/keys-request-keys.txt> for more information about the +request-key function. + + +========== +nfs.upcall +========== +nfs.upcall is designed to be called by request-key, and should not be run "by +hand".  This program takes two arguments, a serialized key and a key +description.  The serialized key is first converted into a key_serial_t, and +then passed as an argument to keyctl_instantiate (both are part of keyutils.h). + +The actual lookups are performed by functions found in nfsidmap.h.  nfs.upcall +determines the correct function to call by looking at the first part of the +description string.  For example, a uid lookup description will appear as +"uid:user@domain". + +nfs.upcall will return 0 if the key was instantiated, and non-zero otherwise. diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index 6c2aad49d73..3f69752d6f1 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -116,3 +116,14 @@ config NFS_USE_KERNEL_DNS  	select DNS_RESOLVER  	select KEYS  	default y + +config NFS_USE_NEW_IDMAPPER +	bool "Use the new idmapper upcall routine" +	depends on NFS_V4 && KEYS +	help +	  Say Y here if you want NFS to use the new idmapper upcall functions. +	  You will need /sbin/request-key (usually provided by the keyutils +	  package).  For details, read +	  <file:Documentation/filesystems/nfs/idmapper.txt>. + +	  If you are unsure, say N. diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 21a84d45916..dec47ed8b6b 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -34,6 +34,212 @@   *  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/nfs_idmap.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> + +#define NFS_UINT_MAXLEN 11 + +const struct cred *id_resolver_cache; + +struct key_type key_type_id_resolver = { +	.name		= "id_resolver", +	.instantiate	= user_instantiate, +	.match		= user_match, +	.revoke		= user_revoke, +	.destroy	= user_destroy, +	.describe	= user_describe, +	.read		= user_read, +}; + +int nfs_idmap_init(void) +{ +	struct cred *cred; +	struct key *keyring; +	int ret = 0; + +	printk(KERN_NOTICE "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); +	if (IS_ERR(keyring)) { +		ret = PTR_ERR(keyring); +		goto failed_put_cred; +	} + +	ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL); +	if (ret < 0) +		goto failed_put_key; + +	ret = register_key_type(&key_type_id_resolver); +	if (ret < 0) +		goto failed_put_key; + +	cred->thread_keyring = keyring; +	cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; +	id_resolver_cache = cred; +	return 0; + +failed_put_key: +	key_put(keyring); +failed_put_cred: +	put_cred(cred); +	return ret; +} + +void nfs_idmap_quit(void) +{ +	key_revoke(id_resolver_cache->thread_keyring); +	unregister_key_type(&key_type_id_resolver); +	put_cred(id_resolver_cache); +} + +/* + * Assemble the description to pass to request_key() + * This function will allocate a new string and update dest to point + * at it.  The caller is responsible for freeing dest. + * + * On error 0 is returned.  Otherwise, the length of dest is returned. + */ +static ssize_t nfs_idmap_get_desc(const char *name, size_t namelen, +				const char *type, size_t typelen, char **desc) +{ +	char *cp; +	size_t desclen = typelen + namelen + 2; + +	*desc = kmalloc(desclen, GFP_KERNEL); +	if (!desc) +		return -ENOMEM; + +	cp = *desc; +	memcpy(cp, type, typelen); +	cp += typelen; +	*cp++ = ':'; + +	memcpy(cp, name, namelen); +	cp += namelen; +	*cp = '\0'; +	return desclen; +} + +static ssize_t nfs_idmap_request_key(const char *name, size_t namelen, +		const char *type, void *data, size_t data_size) +{ +	const struct cred *saved_cred; +	struct key *rkey; +	char *desc; +	struct user_key_payload *payload; +	ssize_t ret; + +	ret = nfs_idmap_get_desc(name, namelen, type, strlen(type), &desc); +	if (ret <= 0) +		goto out; + +	saved_cred = override_creds(id_resolver_cache); +	rkey = request_key(&key_type_id_resolver, desc, ""); +	revert_creds(saved_cred); +	kfree(desc); +	if (IS_ERR(rkey)) { +		ret = PTR_ERR(rkey); +		goto out; +	} + +	rcu_read_lock(); +	rkey->perm |= KEY_USR_VIEW; + +	ret = key_validate(rkey); +	if (ret < 0) +		goto out_up; + +	payload = rcu_dereference(rkey->payload.data); +	if (IS_ERR_OR_NULL(payload)) { +		ret = PTR_ERR(payload); +		goto out_up; +	} + +	ret = payload->datalen; +	if (ret > 0 && ret <= data_size) +		memcpy(data, payload->data, ret); +	else +		ret = -EINVAL; + +out_up: +	rcu_read_unlock(); +	key_put(rkey); +out: +	return ret; +} + + +/* ID -> Name */ +static ssize_t nfs_idmap_lookup_name(__u32 id, const char *type, char *buf, size_t buflen) +{ +	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); +	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) +{ +	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); +	if (data_size <= 0) { +		ret = -EINVAL; +	} else { +		ret = strict_strtol(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> @@ -503,16 +709,17 @@ int nfs_map_group_to_gid(struct nfs_client *clp, const char *name, size_t namele  	return nfs_idmap_id(idmap, &idmap->idmap_group_hash, name, namelen, uid);  } -int nfs_map_uid_to_name(struct nfs_client *clp, __u32 uid, char *buf) +int nfs_map_uid_to_name(struct nfs_client *clp, __u32 uid, char *buf, size_t buflen)  {  	struct idmap *idmap = clp->cl_idmap;  	return nfs_idmap_name(idmap, &idmap->idmap_user_hash, uid, buf);  } -int nfs_map_gid_to_group(struct nfs_client *clp, __u32 uid, char *buf) +int nfs_map_gid_to_group(struct nfs_client *clp, __u32 uid, char *buf, size_t buflen)  {  	struct idmap *idmap = clp->cl_idmap;  	return nfs_idmap_name(idmap, &idmap->idmap_group_hash, uid, buf);  } +#endif /* CONFIG_NFS_USE_NEW_IDMAPPER */ diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 18be041abd2..f2d2c801e0a 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -1526,6 +1526,10 @@ static int __init init_nfs_fs(void)  {  	int err; +	err = nfs_idmap_init(); +	if (err < 0) +		goto out9; +  	err = nfs_dns_resolver_init();  	if (err < 0)  		goto out8; @@ -1590,6 +1594,8 @@ out6:  out7:  	nfs_dns_resolver_destroy();  out8: +	nfs_idmap_quit(); +out9:  	return err;  } @@ -1602,6 +1608,7 @@ static void __exit exit_nfs_fs(void)  	nfs_destroy_nfspagecache();  	nfs_fscache_unregister();  	nfs_dns_resolver_destroy(); +	nfs_idmap_quit();  #ifdef CONFIG_PROC_FS  	rpc_proc_unregister("nfs");  #endif diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 3feace66b98..6ea5c9392fe 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -816,7 +816,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const  	if (iap->ia_valid & ATTR_MODE)  		len += 4;  	if (iap->ia_valid & ATTR_UID) { -		owner_namelen = nfs_map_uid_to_name(server->nfs_client, iap->ia_uid, owner_name); +		owner_namelen = nfs_map_uid_to_name(server->nfs_client, iap->ia_uid, owner_name, IDMAP_NAMESZ);  		if (owner_namelen < 0) {  			dprintk("nfs: couldn't resolve uid %d to string\n",  					iap->ia_uid); @@ -828,7 +828,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const  		len += 4 + (XDR_QUADLEN(owner_namelen) << 2);  	}  	if (iap->ia_valid & ATTR_GID) { -		owner_grouplen = nfs_map_gid_to_group(server->nfs_client, iap->ia_gid, owner_group); +		owner_grouplen = nfs_map_gid_to_group(server->nfs_client, iap->ia_gid, owner_group, IDMAP_NAMESZ);  		if (owner_grouplen < 0) {  			dprintk("nfs: couldn't resolve gid %d to string\n",  					iap->ia_gid); diff --git a/fs/nfs/sysctl.c b/fs/nfs/sysctl.c index ad4d2e787b2..978aaeb8a09 100644 --- a/fs/nfs/sysctl.c +++ b/fs/nfs/sysctl.c @@ -32,6 +32,7 @@ static ctl_table nfs_cb_sysctls[] = {  		.extra1 = (int *)&nfs_set_port_min,  		.extra2 = (int *)&nfs_set_port_max,  	}, +#ifndef CONFIG_NFS_USE_NEW_IDMAPPER  	{  		.procname = "idmap_cache_timeout",  		.data = &nfs_idmap_cache_timeout, @@ -39,6 +40,7 @@ static ctl_table nfs_cb_sysctls[] = {  		.mode = 0644,  		.proc_handler = proc_dointvec_jiffies,  	}, +#endif /* CONFIG_NFS_USE_NEW_IDMAPPER */  #endif  	{  		.procname	= "nfs_mountpoint_timeout", diff --git a/include/linux/nfs_idmap.h b/include/linux/nfs_idmap.h index 91a1c24e0cb..e8352dc5afb 100644 --- a/include/linux/nfs_idmap.h +++ b/include/linux/nfs_idmap.h @@ -66,13 +66,40 @@ struct idmap_msg {  /* Forward declaration to make this header independent of others */  struct nfs_client; +#ifdef CONFIG_NFS_USE_NEW_IDMAPPER + +int nfs_idmap_init(void); +void nfs_idmap_quit(void); + +static inline int nfs_idmap_new(struct nfs_client *clp) +{ +	return 0; +} + +static inline void nfs_idmap_delete(struct nfs_client *clp) +{ +} + +#else /* CONFIG_NFS_USE_NEW_IDMAPPER not set */ + +static inline int nfs_idmap_init(void) +{ +	return 0; +} + +static inline void nfs_idmap_quit(void) +{ +} +  int nfs_idmap_new(struct nfs_client *);  void nfs_idmap_delete(struct nfs_client *); +#endif /* CONFIG_NFS_USE_NEW_IDMAPPER */ +  int nfs_map_name_to_uid(struct nfs_client *, const char *, size_t, __u32 *);  int nfs_map_group_to_gid(struct nfs_client *, const char *, size_t, __u32 *); -int nfs_map_uid_to_name(struct nfs_client *, __u32, char *); -int nfs_map_gid_to_group(struct nfs_client *, __u32, char *); +int nfs_map_uid_to_name(struct nfs_client *, __u32, char *, size_t); +int nfs_map_gid_to_group(struct nfs_client *, __u32, char *, size_t);  extern unsigned int nfs_idmap_cache_timeout;  #endif /* __KERNEL__ */  |