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 /fs/nfs/idmap.c | |
parent | aa510da5bfe1dfe263215fd0e05dac96e738a782 (diff) |
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>
Diffstat (limited to 'fs/nfs/idmap.c')
-rw-r--r-- | fs/nfs/idmap.c | 211 |
1 files changed, 209 insertions, 2 deletions
diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 21a84d45916f..dec47ed8b6b9 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c | |||
@@ -34,6 +34,212 @@ | |||
34 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 34 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
35 | */ | 35 | */ |
36 | 36 | ||
37 | #ifdef CONFIG_NFS_USE_NEW_IDMAPPER | ||
38 | |||
39 | #include <linux/slab.h> | ||
40 | #include <linux/cred.h> | ||
41 | #include <linux/nfs_idmap.h> | ||
42 | #include <linux/keyctl.h> | ||
43 | #include <linux/key-type.h> | ||
44 | #include <linux/rcupdate.h> | ||
45 | #include <linux/kernel.h> | ||
46 | #include <linux/err.h> | ||
47 | |||
48 | #include <keys/user-type.h> | ||
49 | |||
50 | #define NFS_UINT_MAXLEN 11 | ||
51 | |||
52 | const struct cred *id_resolver_cache; | ||
53 | |||
54 | struct key_type key_type_id_resolver = { | ||
55 | .name = "id_resolver", | ||
56 | .instantiate = user_instantiate, | ||
57 | .match = user_match, | ||
58 | .revoke = user_revoke, | ||
59 | .destroy = user_destroy, | ||
60 | .describe = user_describe, | ||
61 | .read = user_read, | ||
62 | }; | ||
63 | |||
64 | int nfs_idmap_init(void) | ||
65 | { | ||
66 | struct cred *cred; | ||
67 | struct key *keyring; | ||
68 | int ret = 0; | ||
69 | |||
70 | printk(KERN_NOTICE "Registering the %s key type\n", key_type_id_resolver.name); | ||
71 | |||
72 | cred = prepare_kernel_cred(NULL); | ||
73 | if (!cred) | ||
74 | return -ENOMEM; | ||
75 | |||
76 | keyring = key_alloc(&key_type_keyring, ".id_resolver", 0, 0, cred, | ||
77 | (KEY_POS_ALL & ~KEY_POS_SETATTR) | | ||
78 | KEY_USR_VIEW | KEY_USR_READ, | ||
79 | KEY_ALLOC_NOT_IN_QUOTA); | ||
80 | if (IS_ERR(keyring)) { | ||
81 | ret = PTR_ERR(keyring); | ||
82 | goto failed_put_cred; | ||
83 | } | ||
84 | |||
85 | ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL); | ||
86 | if (ret < 0) | ||
87 | goto failed_put_key; | ||
88 | |||
89 | ret = register_key_type(&key_type_id_resolver); | ||
90 | if (ret < 0) | ||
91 | goto failed_put_key; | ||
92 | |||
93 | cred->thread_keyring = keyring; | ||
94 | cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; | ||
95 | id_resolver_cache = cred; | ||
96 | return 0; | ||
97 | |||
98 | failed_put_key: | ||
99 | key_put(keyring); | ||
100 | failed_put_cred: | ||
101 | put_cred(cred); | ||
102 | return ret; | ||
103 | } | ||
104 | |||
105 | void nfs_idmap_quit(void) | ||
106 | { | ||
107 | key_revoke(id_resolver_cache->thread_keyring); | ||
108 | unregister_key_type(&key_type_id_resolver); | ||
109 | put_cred(id_resolver_cache); | ||
110 | } | ||
111 | |||
112 | /* | ||
113 | * Assemble the description to pass to request_key() | ||
114 | * This function will allocate a new string and update dest to point | ||
115 | * at it. The caller is responsible for freeing dest. | ||
116 | * | ||
117 | * On error 0 is returned. Otherwise, the length of dest is returned. | ||
118 | */ | ||
119 | static ssize_t nfs_idmap_get_desc(const char *name, size_t namelen, | ||
120 | const char *type, size_t typelen, char **desc) | ||
121 | { | ||
122 | char *cp; | ||
123 | size_t desclen = typelen + namelen + 2; | ||
124 | |||
125 | *desc = kmalloc(desclen, GFP_KERNEL); | ||
126 | if (!desc) | ||
127 | return -ENOMEM; | ||
128 | |||
129 | cp = *desc; | ||
130 | memcpy(cp, type, typelen); | ||
131 | cp += typelen; | ||
132 | *cp++ = ':'; | ||
133 | |||
134 | memcpy(cp, name, namelen); | ||
135 | cp += namelen; | ||
136 | *cp = '\0'; | ||
137 | return desclen; | ||
138 | } | ||
139 | |||
140 | static ssize_t nfs_idmap_request_key(const char *name, size_t namelen, | ||
141 | const char *type, void *data, size_t data_size) | ||
142 | { | ||
143 | const struct cred *saved_cred; | ||
144 | struct key *rkey; | ||
145 | char *desc; | ||
146 | struct user_key_payload *payload; | ||
147 | ssize_t ret; | ||
148 | |||
149 | ret = nfs_idmap_get_desc(name, namelen, type, strlen(type), &desc); | ||
150 | if (ret <= 0) | ||
151 | goto out; | ||
152 | |||
153 | saved_cred = override_creds(id_resolver_cache); | ||
154 | rkey = request_key(&key_type_id_resolver, desc, ""); | ||
155 | revert_creds(saved_cred); | ||
156 | kfree(desc); | ||
157 | if (IS_ERR(rkey)) { | ||
158 | ret = PTR_ERR(rkey); | ||
159 | goto out; | ||
160 | } | ||
161 | |||
162 | rcu_read_lock(); | ||
163 | rkey->perm |= KEY_USR_VIEW; | ||
164 | |||
165 | ret = key_validate(rkey); | ||
166 | if (ret < 0) | ||
167 | goto out_up; | ||
168 | |||
169 | payload = rcu_dereference(rkey->payload.data); | ||
170 | if (IS_ERR_OR_NULL(payload)) { | ||
171 | ret = PTR_ERR(payload); | ||
172 | goto out_up; | ||
173 | } | ||
174 | |||
175 | ret = payload->datalen; | ||
176 | if (ret > 0 && ret <= data_size) | ||
177 | memcpy(data, payload->data, ret); | ||
178 | else | ||
179 | ret = -EINVAL; | ||
180 | |||
181 | out_up: | ||
182 | rcu_read_unlock(); | ||
183 | key_put(rkey); | ||
184 | out: | ||
185 | return ret; | ||
186 | } | ||
187 | |||
188 | |||
189 | /* ID -> Name */ | ||
190 | static ssize_t nfs_idmap_lookup_name(__u32 id, const char *type, char *buf, size_t buflen) | ||
191 | { | ||
192 | char id_str[NFS_UINT_MAXLEN]; | ||
193 | int id_len; | ||
194 | ssize_t ret; | ||
195 | |||
196 | id_len = snprintf(id_str, sizeof(id_str), "%u", id); | ||
197 | ret = nfs_idmap_request_key(id_str, id_len, type, buf, buflen); | ||
198 | if (ret < 0) | ||
199 | return -EINVAL; | ||
200 | return ret; | ||
201 | } | ||
202 | |||
203 | /* Name -> ID */ | ||
204 | static int nfs_idmap_lookup_id(const char *name, size_t namelen, | ||
205 | const char *type, __u32 *id) | ||
206 | { | ||
207 | char id_str[NFS_UINT_MAXLEN]; | ||
208 | long id_long; | ||
209 | ssize_t data_size; | ||
210 | int ret = 0; | ||
211 | |||
212 | data_size = nfs_idmap_request_key(name, namelen, type, id_str, NFS_UINT_MAXLEN); | ||
213 | if (data_size <= 0) { | ||
214 | ret = -EINVAL; | ||
215 | } else { | ||
216 | ret = strict_strtol(id_str, 10, &id_long); | ||
217 | *id = (__u32)id_long; | ||
218 | } | ||
219 | return ret; | ||
220 | } | ||
221 | |||
222 | int nfs_map_name_to_uid(struct nfs_client *clp, const char *name, size_t namelen, __u32 *uid) | ||
223 | { | ||
224 | return nfs_idmap_lookup_id(name, namelen, "uid", uid); | ||
225 | } | ||
226 | |||
227 | int nfs_map_group_to_gid(struct nfs_client *clp, const char *name, size_t namelen, __u32 *gid) | ||
228 | { | ||
229 | return nfs_idmap_lookup_id(name, namelen, "gid", gid); | ||
230 | } | ||
231 | |||
232 | int nfs_map_uid_to_name(struct nfs_client *clp, __u32 uid, char *buf, size_t buflen) | ||
233 | { | ||
234 | return nfs_idmap_lookup_name(uid, "user", buf, buflen); | ||
235 | } | ||
236 | int nfs_map_gid_to_group(struct nfs_client *clp, __u32 gid, char *buf, size_t buflen) | ||
237 | { | ||
238 | return nfs_idmap_lookup_name(gid, "group", buf, buflen); | ||
239 | } | ||
240 | |||
241 | #else /* CONFIG_NFS_USE_IDMAPPER not defined */ | ||
242 | |||
37 | #include <linux/module.h> | 243 | #include <linux/module.h> |
38 | #include <linux/mutex.h> | 244 | #include <linux/mutex.h> |
39 | #include <linux/init.h> | 245 | #include <linux/init.h> |
@@ -503,16 +709,17 @@ int nfs_map_group_to_gid(struct nfs_client *clp, const char *name, size_t namele | |||
503 | return nfs_idmap_id(idmap, &idmap->idmap_group_hash, name, namelen, uid); | 709 | return nfs_idmap_id(idmap, &idmap->idmap_group_hash, name, namelen, uid); |
504 | } | 710 | } |
505 | 711 | ||
506 | int nfs_map_uid_to_name(struct nfs_client *clp, __u32 uid, char *buf) | 712 | int nfs_map_uid_to_name(struct nfs_client *clp, __u32 uid, char *buf, size_t buflen) |
507 | { | 713 | { |
508 | struct idmap *idmap = clp->cl_idmap; | 714 | struct idmap *idmap = clp->cl_idmap; |
509 | 715 | ||
510 | return nfs_idmap_name(idmap, &idmap->idmap_user_hash, uid, buf); | 716 | return nfs_idmap_name(idmap, &idmap->idmap_user_hash, uid, buf); |
511 | } | 717 | } |
512 | int nfs_map_gid_to_group(struct nfs_client *clp, __u32 uid, char *buf) | 718 | int nfs_map_gid_to_group(struct nfs_client *clp, __u32 uid, char *buf, size_t buflen) |
513 | { | 719 | { |
514 | struct idmap *idmap = clp->cl_idmap; | 720 | struct idmap *idmap = clp->cl_idmap; |
515 | 721 | ||
516 | return nfs_idmap_name(idmap, &idmap->idmap_group_hash, uid, buf); | 722 | return nfs_idmap_name(idmap, &idmap->idmap_group_hash, uid, buf); |
517 | } | 723 | } |
518 | 724 | ||
725 | #endif /* CONFIG_NFS_USE_NEW_IDMAPPER */ | ||