blob: e2d579d458f1fbb2101e617300b20c0950198a4e [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * fs/nfs/idmap.c
3 *
4 * UID and GID to name mapping for clients.
5 *
6 * Copyright (c) 2002 The Regents of the University of Michigan.
7 * All rights reserved.
8 *
9 * Marius Aamodt Eriksen <marius@umich.edu>
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 *
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
25 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
31 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
Trond Myklebust5cf36cf2011-02-22 15:44:31 -080036#include <linux/types.h>
37#include <linux/string.h>
38#include <linux/kernel.h>
39
40static int nfs_map_string_to_numeric(const char *name, size_t namelen, __u32 *res)
41{
42 unsigned long val;
43 char buf[16];
44
45 if (memchr(name, '@', namelen) != NULL || namelen >= sizeof(buf))
46 return 0;
47 memcpy(buf, name, namelen);
48 buf[namelen] = '\0';
49 if (strict_strtoul(buf, 0, &val) != 0)
50 return 0;
51 *res = val;
52 return 1;
53}
Linus Torvalds1da177e2005-04-16 15:20:36 -070054
Trond Myklebustf0b85162011-02-22 15:44:31 -080055static int nfs_map_numeric_to_string(__u32 id, char *buf, size_t buflen)
56{
57 return snprintf(buf, buflen, "%u", id);
58}
59
Bryan Schumaker955a8572010-09-29 15:41:49 -040060#ifdef CONFIG_NFS_USE_NEW_IDMAPPER
61
62#include <linux/slab.h>
63#include <linux/cred.h>
64#include <linux/nfs_idmap.h>
65#include <linux/keyctl.h>
66#include <linux/key-type.h>
67#include <linux/rcupdate.h>
Bryan Schumaker955a8572010-09-29 15:41:49 -040068#include <linux/err.h>
69
70#include <keys/user-type.h>
71
72#define NFS_UINT_MAXLEN 11
73
74const struct cred *id_resolver_cache;
75
76struct key_type key_type_id_resolver = {
77 .name = "id_resolver",
78 .instantiate = user_instantiate,
79 .match = user_match,
80 .revoke = user_revoke,
81 .destroy = user_destroy,
82 .describe = user_describe,
83 .read = user_read,
84};
85
86int nfs_idmap_init(void)
87{
88 struct cred *cred;
89 struct key *keyring;
90 int ret = 0;
91
92 printk(KERN_NOTICE "Registering the %s key type\n", key_type_id_resolver.name);
93
94 cred = prepare_kernel_cred(NULL);
95 if (!cred)
96 return -ENOMEM;
97
98 keyring = key_alloc(&key_type_keyring, ".id_resolver", 0, 0, cred,
99 (KEY_POS_ALL & ~KEY_POS_SETATTR) |
100 KEY_USR_VIEW | KEY_USR_READ,
101 KEY_ALLOC_NOT_IN_QUOTA);
102 if (IS_ERR(keyring)) {
103 ret = PTR_ERR(keyring);
104 goto failed_put_cred;
105 }
106
107 ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL);
108 if (ret < 0)
109 goto failed_put_key;
110
111 ret = register_key_type(&key_type_id_resolver);
112 if (ret < 0)
113 goto failed_put_key;
114
115 cred->thread_keyring = keyring;
116 cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
117 id_resolver_cache = cred;
118 return 0;
119
120failed_put_key:
121 key_put(keyring);
122failed_put_cred:
123 put_cred(cred);
124 return ret;
125}
126
127void nfs_idmap_quit(void)
128{
129 key_revoke(id_resolver_cache->thread_keyring);
130 unregister_key_type(&key_type_id_resolver);
131 put_cred(id_resolver_cache);
132}
133
134/*
135 * Assemble the description to pass to request_key()
136 * This function will allocate a new string and update dest to point
137 * at it. The caller is responsible for freeing dest.
138 *
139 * On error 0 is returned. Otherwise, the length of dest is returned.
140 */
141static ssize_t nfs_idmap_get_desc(const char *name, size_t namelen,
142 const char *type, size_t typelen, char **desc)
143{
144 char *cp;
145 size_t desclen = typelen + namelen + 2;
146
147 *desc = kmalloc(desclen, GFP_KERNEL);
Dan Carpenter8f0d97b2010-10-28 08:05:57 +0200148 if (!*desc)
Bryan Schumaker955a8572010-09-29 15:41:49 -0400149 return -ENOMEM;
150
151 cp = *desc;
152 memcpy(cp, type, typelen);
153 cp += typelen;
154 *cp++ = ':';
155
156 memcpy(cp, name, namelen);
157 cp += namelen;
158 *cp = '\0';
159 return desclen;
160}
161
162static ssize_t nfs_idmap_request_key(const char *name, size_t namelen,
163 const char *type, void *data, size_t data_size)
164{
165 const struct cred *saved_cred;
166 struct key *rkey;
167 char *desc;
168 struct user_key_payload *payload;
169 ssize_t ret;
170
171 ret = nfs_idmap_get_desc(name, namelen, type, strlen(type), &desc);
172 if (ret <= 0)
173 goto out;
174
175 saved_cred = override_creds(id_resolver_cache);
176 rkey = request_key(&key_type_id_resolver, desc, "");
177 revert_creds(saved_cred);
178 kfree(desc);
179 if (IS_ERR(rkey)) {
180 ret = PTR_ERR(rkey);
181 goto out;
182 }
183
184 rcu_read_lock();
185 rkey->perm |= KEY_USR_VIEW;
186
187 ret = key_validate(rkey);
188 if (ret < 0)
189 goto out_up;
190
191 payload = rcu_dereference(rkey->payload.data);
192 if (IS_ERR_OR_NULL(payload)) {
193 ret = PTR_ERR(payload);
194 goto out_up;
195 }
196
197 ret = payload->datalen;
198 if (ret > 0 && ret <= data_size)
199 memcpy(data, payload->data, ret);
200 else
201 ret = -EINVAL;
202
203out_up:
204 rcu_read_unlock();
205 key_put(rkey);
206out:
207 return ret;
208}
209
210
211/* ID -> Name */
212static ssize_t nfs_idmap_lookup_name(__u32 id, const char *type, char *buf, size_t buflen)
213{
214 char id_str[NFS_UINT_MAXLEN];
215 int id_len;
216 ssize_t ret;
217
218 id_len = snprintf(id_str, sizeof(id_str), "%u", id);
219 ret = nfs_idmap_request_key(id_str, id_len, type, buf, buflen);
220 if (ret < 0)
221 return -EINVAL;
222 return ret;
223}
224
225/* Name -> ID */
226static int nfs_idmap_lookup_id(const char *name, size_t namelen,
227 const char *type, __u32 *id)
228{
229 char id_str[NFS_UINT_MAXLEN];
230 long id_long;
231 ssize_t data_size;
232 int ret = 0;
233
234 data_size = nfs_idmap_request_key(name, namelen, type, id_str, NFS_UINT_MAXLEN);
235 if (data_size <= 0) {
236 ret = -EINVAL;
237 } else {
238 ret = strict_strtol(id_str, 10, &id_long);
239 *id = (__u32)id_long;
240 }
241 return ret;
242}
243
Trond Myklebuste4fd72a2011-02-22 15:44:31 -0800244int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *uid)
Bryan Schumaker955a8572010-09-29 15:41:49 -0400245{
Trond Myklebust5cf36cf2011-02-22 15:44:31 -0800246 if (nfs_map_string_to_numeric(name, namelen, uid))
247 return 0;
Bryan Schumaker955a8572010-09-29 15:41:49 -0400248 return nfs_idmap_lookup_id(name, namelen, "uid", uid);
249}
250
Trond Myklebuste4fd72a2011-02-22 15:44:31 -0800251int nfs_map_group_to_gid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *gid)
Bryan Schumaker955a8572010-09-29 15:41:49 -0400252{
Trond Myklebust5cf36cf2011-02-22 15:44:31 -0800253 if (nfs_map_string_to_numeric(name, namelen, gid))
254 return 0;
Bryan Schumaker955a8572010-09-29 15:41:49 -0400255 return nfs_idmap_lookup_id(name, namelen, "gid", gid);
256}
257
Trond Myklebuste4fd72a2011-02-22 15:44:31 -0800258int nfs_map_uid_to_name(const struct nfs_server *server, __u32 uid, char *buf, size_t buflen)
Bryan Schumaker955a8572010-09-29 15:41:49 -0400259{
Trond Myklebustf0b85162011-02-22 15:44:31 -0800260 int ret;
261 ret = nfs_idmap_lookup_name(uid, "user", buf, buflen);
262 if (ret < 0)
263 ret = nfs_map_numeric_to_string(uid, buf, buflen);
264 return ret;
Bryan Schumaker955a8572010-09-29 15:41:49 -0400265}
Trond Myklebuste4fd72a2011-02-22 15:44:31 -0800266int nfs_map_gid_to_group(const struct nfs_server *server, __u32 gid, char *buf, size_t buflen)
Bryan Schumaker955a8572010-09-29 15:41:49 -0400267{
Trond Myklebustf0b85162011-02-22 15:44:31 -0800268 int ret;
269
270 ret = nfs_idmap_lookup_name(gid, "group", buf, buflen);
271 if (ret < 0)
272 ret = nfs_map_numeric_to_string(gid, buf, buflen);
273 return ret;
Bryan Schumaker955a8572010-09-29 15:41:49 -0400274}
275
J. Bruce Fields5f3e97c2010-12-21 23:49:34 +0000276#else /* CONFIG_NFS_USE_NEW_IDMAPPER not defined */
Bryan Schumaker955a8572010-09-29 15:41:49 -0400277
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278#include <linux/module.h>
Ingo Molnarc9d51282006-03-20 13:44:11 -0500279#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281#include <linux/slab.h>
282#include <linux/socket.h>
283#include <linux/in.h>
284#include <linux/sched.h>
285
286#include <linux/sunrpc/clnt.h>
287#include <linux/workqueue.h>
288#include <linux/sunrpc/rpc_pipe_fs.h>
289
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290#include <linux/nfs_fs.h>
291
292#include <linux/nfs_idmap.h>
Trond Myklebust4ce79712005-06-22 17:16:21 +0000293#include "nfs4_fs.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294
295#define IDMAP_HASH_SZ 128
296
Trond Myklebust58df0952006-01-03 09:55:57 +0100297/* Default cache timeout is 10 minutes */
298unsigned int nfs_idmap_cache_timeout = 600 * HZ;
299
David Howells7d4e2742006-08-22 20:06:07 -0400300static int param_set_idmap_timeout(const char *val, struct kernel_param *kp)
301{
302 char *endp;
303 int num = simple_strtol(val, &endp, 0);
304 int jif = num * HZ;
305 if (endp == val || *endp || num < 0 || jif < num)
306 return -EINVAL;
307 *((int *)kp->arg) = jif;
308 return 0;
309}
310
311module_param_call(idmap_cache_timeout, param_set_idmap_timeout, param_get_int,
312 &nfs_idmap_cache_timeout, 0644);
313
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314struct idmap_hashent {
Chuck Lever369af0f2007-12-20 14:54:35 -0500315 unsigned long ih_expires;
316 __u32 ih_id;
Chuck Leverd24aae42007-12-20 14:54:49 -0500317 size_t ih_namelen;
Chuck Lever369af0f2007-12-20 14:54:35 -0500318 char ih_name[IDMAP_NAMESZ];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319};
320
321struct idmap_hashtable {
Chuck Lever369af0f2007-12-20 14:54:35 -0500322 __u8 h_type;
323 struct idmap_hashent h_entries[IDMAP_HASH_SZ];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324};
325
326struct idmap {
Chuck Lever369af0f2007-12-20 14:54:35 -0500327 struct dentry *idmap_dentry;
328 wait_queue_head_t idmap_wq;
329 struct idmap_msg idmap_im;
330 struct mutex idmap_lock; /* Serializes upcalls */
331 struct mutex idmap_im_lock; /* Protects the hashtable */
332 struct idmap_hashtable idmap_user_hash;
333 struct idmap_hashtable idmap_group_hash;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334};
335
Chuck Lever369af0f2007-12-20 14:54:35 -0500336static ssize_t idmap_pipe_upcall(struct file *, struct rpc_pipe_msg *,
337 char __user *, size_t);
338static ssize_t idmap_pipe_downcall(struct file *, const char __user *,
339 size_t);
340static void idmap_pipe_destroy_msg(struct rpc_pipe_msg *);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341
342static unsigned int fnvhash32(const void *, size_t);
343
Trond Myklebustb693ba42009-08-09 15:14:15 -0400344static const struct rpc_pipe_ops idmap_upcall_ops = {
Chuck Lever369af0f2007-12-20 14:54:35 -0500345 .upcall = idmap_pipe_upcall,
346 .downcall = idmap_pipe_downcall,
347 .destroy_msg = idmap_pipe_destroy_msg,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348};
349
David Howellsb7162792006-08-22 20:06:09 -0400350int
David Howellsadfa6f92006-08-22 20:06:08 -0400351nfs_idmap_new(struct nfs_client *clp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352{
353 struct idmap *idmap;
David Howellsb7162792006-08-22 20:06:09 -0400354 int error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355
David Howells54ceac42006-08-22 20:06:13 -0400356 BUG_ON(clp->cl_idmap != NULL);
David Howellsb7162792006-08-22 20:06:09 -0400357
Chuck Lever369af0f2007-12-20 14:54:35 -0500358 idmap = kzalloc(sizeof(*idmap), GFP_KERNEL);
359 if (idmap == NULL)
360 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361
Trond Myklebust7d217ca2009-08-09 15:14:24 -0400362 idmap->idmap_dentry = rpc_mkpipe(clp->cl_rpcclient->cl_path.dentry,
363 "idmap", idmap, &idmap_upcall_ops, 0);
Chuck Lever369af0f2007-12-20 14:54:35 -0500364 if (IS_ERR(idmap->idmap_dentry)) {
David Howellsb7162792006-08-22 20:06:09 -0400365 error = PTR_ERR(idmap->idmap_dentry);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 kfree(idmap);
David Howellsb7162792006-08-22 20:06:09 -0400367 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 }
369
Chuck Lever369af0f2007-12-20 14:54:35 -0500370 mutex_init(&idmap->idmap_lock);
371 mutex_init(&idmap->idmap_im_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 init_waitqueue_head(&idmap->idmap_wq);
373 idmap->idmap_user_hash.h_type = IDMAP_TYPE_USER;
374 idmap->idmap_group_hash.h_type = IDMAP_TYPE_GROUP;
375
376 clp->cl_idmap = idmap;
David Howellsb7162792006-08-22 20:06:09 -0400377 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378}
379
380void
David Howellsadfa6f92006-08-22 20:06:08 -0400381nfs_idmap_delete(struct nfs_client *clp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382{
383 struct idmap *idmap = clp->cl_idmap;
384
385 if (!idmap)
386 return;
Trond Myklebust5d674762006-07-31 14:11:48 -0700387 rpc_unlink(idmap->idmap_dentry);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 clp->cl_idmap = NULL;
389 kfree(idmap);
390}
391
392/*
393 * Helper routines for manipulating the hashtable
394 */
395static inline struct idmap_hashent *
396idmap_name_hash(struct idmap_hashtable* h, const char *name, size_t len)
397{
398 return &h->h_entries[fnvhash32(name, len) % IDMAP_HASH_SZ];
399}
400
401static struct idmap_hashent *
402idmap_lookup_name(struct idmap_hashtable *h, const char *name, size_t len)
403{
404 struct idmap_hashent *he = idmap_name_hash(h, name, len);
405
406 if (he->ih_namelen != len || memcmp(he->ih_name, name, len) != 0)
407 return NULL;
Trond Myklebust58df0952006-01-03 09:55:57 +0100408 if (time_after(jiffies, he->ih_expires))
409 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 return he;
411}
412
413static inline struct idmap_hashent *
414idmap_id_hash(struct idmap_hashtable* h, __u32 id)
415{
416 return &h->h_entries[fnvhash32(&id, sizeof(id)) % IDMAP_HASH_SZ];
417}
418
419static struct idmap_hashent *
420idmap_lookup_id(struct idmap_hashtable *h, __u32 id)
421{
422 struct idmap_hashent *he = idmap_id_hash(h, id);
423 if (he->ih_id != id || he->ih_namelen == 0)
424 return NULL;
Trond Myklebust58df0952006-01-03 09:55:57 +0100425 if (time_after(jiffies, he->ih_expires))
426 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 return he;
428}
429
430/*
431 * Routines for allocating new entries in the hashtable.
432 * For now, we just have 1 entry per bucket, so it's all
433 * pretty trivial.
434 */
435static inline struct idmap_hashent *
Chuck Leverd24aae42007-12-20 14:54:49 -0500436idmap_alloc_name(struct idmap_hashtable *h, char *name, size_t len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437{
438 return idmap_name_hash(h, name, len);
439}
440
441static inline struct idmap_hashent *
442idmap_alloc_id(struct idmap_hashtable *h, __u32 id)
443{
444 return idmap_id_hash(h, id);
445}
446
447static void
448idmap_update_entry(struct idmap_hashent *he, const char *name,
449 size_t namelen, __u32 id)
450{
451 he->ih_id = id;
452 memcpy(he->ih_name, name, namelen);
453 he->ih_name[namelen] = '\0';
454 he->ih_namelen = namelen;
Trond Myklebust58df0952006-01-03 09:55:57 +0100455 he->ih_expires = jiffies + nfs_idmap_cache_timeout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456}
457
458/*
459 * Name -> ID
460 */
461static int
462nfs_idmap_id(struct idmap *idmap, struct idmap_hashtable *h,
463 const char *name, size_t namelen, __u32 *id)
464{
465 struct rpc_pipe_msg msg;
466 struct idmap_msg *im;
467 struct idmap_hashent *he;
468 DECLARE_WAITQUEUE(wq, current);
469 int ret = -EIO;
470
471 im = &idmap->idmap_im;
472
473 /*
474 * String sanity checks
475 * Note that the userland daemon expects NUL terminated strings
476 */
477 for (;;) {
478 if (namelen == 0)
479 return -EINVAL;
480 if (name[namelen-1] != '\0')
481 break;
482 namelen--;
483 }
484 if (namelen >= IDMAP_NAMESZ)
485 return -EINVAL;
486
Ingo Molnarc9d51282006-03-20 13:44:11 -0500487 mutex_lock(&idmap->idmap_lock);
488 mutex_lock(&idmap->idmap_im_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489
490 he = idmap_lookup_name(h, name, namelen);
491 if (he != NULL) {
492 *id = he->ih_id;
493 ret = 0;
494 goto out;
495 }
496
497 memset(im, 0, sizeof(*im));
498 memcpy(im->im_name, name, namelen);
499
500 im->im_type = h->h_type;
501 im->im_conv = IDMAP_CONV_NAMETOID;
502
503 memset(&msg, 0, sizeof(msg));
504 msg.data = im;
505 msg.len = sizeof(*im);
506
507 add_wait_queue(&idmap->idmap_wq, &wq);
508 if (rpc_queue_upcall(idmap->idmap_dentry->d_inode, &msg) < 0) {
509 remove_wait_queue(&idmap->idmap_wq, &wq);
510 goto out;
511 }
512
513 set_current_state(TASK_UNINTERRUPTIBLE);
Ingo Molnarc9d51282006-03-20 13:44:11 -0500514 mutex_unlock(&idmap->idmap_im_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 schedule();
Milind Arun Choudharyfee7f232007-04-26 00:29:03 -0700516 __set_current_state(TASK_RUNNING);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 remove_wait_queue(&idmap->idmap_wq, &wq);
Ingo Molnarc9d51282006-03-20 13:44:11 -0500518 mutex_lock(&idmap->idmap_im_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519
520 if (im->im_status & IDMAP_STATUS_SUCCESS) {
521 *id = im->im_id;
522 ret = 0;
523 }
524
525 out:
526 memset(im, 0, sizeof(*im));
Ingo Molnarc9d51282006-03-20 13:44:11 -0500527 mutex_unlock(&idmap->idmap_im_lock);
528 mutex_unlock(&idmap->idmap_lock);
Chuck Lever369af0f2007-12-20 14:54:35 -0500529 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530}
531
532/*
533 * ID -> Name
534 */
535static int
536nfs_idmap_name(struct idmap *idmap, struct idmap_hashtable *h,
537 __u32 id, char *name)
538{
539 struct rpc_pipe_msg msg;
540 struct idmap_msg *im;
541 struct idmap_hashent *he;
542 DECLARE_WAITQUEUE(wq, current);
543 int ret = -EIO;
544 unsigned int len;
545
546 im = &idmap->idmap_im;
547
Ingo Molnarc9d51282006-03-20 13:44:11 -0500548 mutex_lock(&idmap->idmap_lock);
549 mutex_lock(&idmap->idmap_im_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550
551 he = idmap_lookup_id(h, id);
Harvey Harrison90dc7d22008-02-20 13:03:05 -0800552 if (he) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 memcpy(name, he->ih_name, he->ih_namelen);
554 ret = he->ih_namelen;
555 goto out;
556 }
557
558 memset(im, 0, sizeof(*im));
559 im->im_type = h->h_type;
560 im->im_conv = IDMAP_CONV_IDTONAME;
561 im->im_id = id;
562
563 memset(&msg, 0, sizeof(msg));
564 msg.data = im;
565 msg.len = sizeof(*im);
566
567 add_wait_queue(&idmap->idmap_wq, &wq);
568
569 if (rpc_queue_upcall(idmap->idmap_dentry->d_inode, &msg) < 0) {
570 remove_wait_queue(&idmap->idmap_wq, &wq);
571 goto out;
572 }
573
574 set_current_state(TASK_UNINTERRUPTIBLE);
Ingo Molnarc9d51282006-03-20 13:44:11 -0500575 mutex_unlock(&idmap->idmap_im_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 schedule();
Milind Arun Choudharyfee7f232007-04-26 00:29:03 -0700577 __set_current_state(TASK_RUNNING);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 remove_wait_queue(&idmap->idmap_wq, &wq);
Ingo Molnarc9d51282006-03-20 13:44:11 -0500579 mutex_lock(&idmap->idmap_im_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580
581 if (im->im_status & IDMAP_STATUS_SUCCESS) {
582 if ((len = strnlen(im->im_name, IDMAP_NAMESZ)) == 0)
583 goto out;
584 memcpy(name, im->im_name, len);
585 ret = len;
586 }
587
588 out:
589 memset(im, 0, sizeof(*im));
Ingo Molnarc9d51282006-03-20 13:44:11 -0500590 mutex_unlock(&idmap->idmap_im_lock);
591 mutex_unlock(&idmap->idmap_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 return ret;
593}
594
595/* RPC pipefs upcall/downcall routines */
596static ssize_t
597idmap_pipe_upcall(struct file *filp, struct rpc_pipe_msg *msg,
Chuck Lever369af0f2007-12-20 14:54:35 -0500598 char __user *dst, size_t buflen)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599{
Chuck Lever369af0f2007-12-20 14:54:35 -0500600 char *data = (char *)msg->data + msg->copied;
Chuck Levera661b772007-12-20 14:54:42 -0500601 size_t mlen = min(msg->len, buflen);
602 unsigned long left;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603
Chuck Lever369af0f2007-12-20 14:54:35 -0500604 left = copy_to_user(dst, data, mlen);
Chuck Levera661b772007-12-20 14:54:42 -0500605 if (left == mlen) {
606 msg->errno = -EFAULT;
607 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608 }
Chuck Levera661b772007-12-20 14:54:42 -0500609
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 mlen -= left;
611 msg->copied += mlen;
612 msg->errno = 0;
Chuck Lever369af0f2007-12-20 14:54:35 -0500613 return mlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614}
615
616static ssize_t
617idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
618{
Chuck Lever369af0f2007-12-20 14:54:35 -0500619 struct rpc_inode *rpci = RPC_I(filp->f_path.dentry->d_inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 struct idmap *idmap = (struct idmap *)rpci->private;
621 struct idmap_msg im_in, *im = &idmap->idmap_im;
622 struct idmap_hashtable *h;
623 struct idmap_hashent *he = NULL;
Chuck Leverd24aae42007-12-20 14:54:49 -0500624 size_t namelen_in;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 int ret;
626
Chuck Lever369af0f2007-12-20 14:54:35 -0500627 if (mlen != sizeof(im_in))
628 return -ENOSPC;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629
Chuck Lever369af0f2007-12-20 14:54:35 -0500630 if (copy_from_user(&im_in, src, mlen) != 0)
631 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632
Ingo Molnarc9d51282006-03-20 13:44:11 -0500633 mutex_lock(&idmap->idmap_im_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634
635 ret = mlen;
636 im->im_status = im_in.im_status;
637 /* If we got an error, terminate now, and wake up pending upcalls */
638 if (!(im_in.im_status & IDMAP_STATUS_SUCCESS)) {
639 wake_up(&idmap->idmap_wq);
640 goto out;
641 }
642
643 /* Sanity checking of strings */
644 ret = -EINVAL;
645 namelen_in = strnlen(im_in.im_name, IDMAP_NAMESZ);
646 if (namelen_in == 0 || namelen_in == IDMAP_NAMESZ)
647 goto out;
648
649 switch (im_in.im_type) {
650 case IDMAP_TYPE_USER:
651 h = &idmap->idmap_user_hash;
652 break;
653 case IDMAP_TYPE_GROUP:
654 h = &idmap->idmap_group_hash;
655 break;
656 default:
657 goto out;
658 }
659
660 switch (im_in.im_conv) {
661 case IDMAP_CONV_IDTONAME:
662 /* Did we match the current upcall? */
663 if (im->im_conv == IDMAP_CONV_IDTONAME
664 && im->im_type == im_in.im_type
665 && im->im_id == im_in.im_id) {
666 /* Yes: copy string, including the terminating '\0' */
667 memcpy(im->im_name, im_in.im_name, namelen_in);
668 im->im_name[namelen_in] = '\0';
669 wake_up(&idmap->idmap_wq);
670 }
671 he = idmap_alloc_id(h, im_in.im_id);
672 break;
673 case IDMAP_CONV_NAMETOID:
674 /* Did we match the current upcall? */
675 if (im->im_conv == IDMAP_CONV_NAMETOID
676 && im->im_type == im_in.im_type
677 && strnlen(im->im_name, IDMAP_NAMESZ) == namelen_in
678 && memcmp(im->im_name, im_in.im_name, namelen_in) == 0) {
679 im->im_id = im_in.im_id;
680 wake_up(&idmap->idmap_wq);
681 }
682 he = idmap_alloc_name(h, im_in.im_name, namelen_in);
683 break;
684 default:
685 goto out;
686 }
687
688 /* If the entry is valid, also copy it to the cache */
689 if (he != NULL)
690 idmap_update_entry(he, im_in.im_name, namelen_in, im_in.im_id);
691 ret = mlen;
692out:
Ingo Molnarc9d51282006-03-20 13:44:11 -0500693 mutex_unlock(&idmap->idmap_im_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 return ret;
695}
696
Adrian Bunk75c96f82005-05-05 16:16:09 -0700697static void
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698idmap_pipe_destroy_msg(struct rpc_pipe_msg *msg)
699{
700 struct idmap_msg *im = msg->data;
701 struct idmap *idmap = container_of(im, struct idmap, idmap_im);
702
703 if (msg->errno >= 0)
704 return;
Ingo Molnarc9d51282006-03-20 13:44:11 -0500705 mutex_lock(&idmap->idmap_im_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 im->im_status = IDMAP_STATUS_LOOKUPFAIL;
707 wake_up(&idmap->idmap_wq);
Ingo Molnarc9d51282006-03-20 13:44:11 -0500708 mutex_unlock(&idmap->idmap_im_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709}
710
711/*
712 * Fowler/Noll/Vo hash
713 * http://www.isthe.com/chongo/tech/comp/fnv/
714 */
715
716#define FNV_P_32 ((unsigned int)0x01000193) /* 16777619 */
717#define FNV_1_32 ((unsigned int)0x811c9dc5) /* 2166136261 */
718
719static unsigned int fnvhash32(const void *buf, size_t buflen)
720{
721 const unsigned char *p, *end = (const unsigned char *)buf + buflen;
722 unsigned int hash = FNV_1_32;
723
724 for (p = buf; p < end; p++) {
725 hash *= FNV_P_32;
726 hash ^= (unsigned int)*p;
727 }
728
Chuck Lever369af0f2007-12-20 14:54:35 -0500729 return hash;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730}
731
Trond Myklebuste4fd72a2011-02-22 15:44:31 -0800732int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *uid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733{
Trond Myklebuste4fd72a2011-02-22 15:44:31 -0800734 struct idmap *idmap = server->nfs_client->cl_idmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735
Trond Myklebust5cf36cf2011-02-22 15:44:31 -0800736 if (nfs_map_string_to_numeric(name, namelen, uid))
737 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 return nfs_idmap_id(idmap, &idmap->idmap_user_hash, name, namelen, uid);
739}
740
Trond Myklebuste4fd72a2011-02-22 15:44:31 -0800741int nfs_map_group_to_gid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *uid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742{
Trond Myklebuste4fd72a2011-02-22 15:44:31 -0800743 struct idmap *idmap = server->nfs_client->cl_idmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744
Trond Myklebust5cf36cf2011-02-22 15:44:31 -0800745 if (nfs_map_string_to_numeric(name, namelen, uid))
746 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 return nfs_idmap_id(idmap, &idmap->idmap_group_hash, name, namelen, uid);
748}
749
Trond Myklebuste4fd72a2011-02-22 15:44:31 -0800750int nfs_map_uid_to_name(const struct nfs_server *server, __u32 uid, char *buf, size_t buflen)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751{
Trond Myklebuste4fd72a2011-02-22 15:44:31 -0800752 struct idmap *idmap = server->nfs_client->cl_idmap;
Trond Myklebustf0b85162011-02-22 15:44:31 -0800753 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754
Trond Myklebustf0b85162011-02-22 15:44:31 -0800755 ret = nfs_idmap_name(idmap, &idmap->idmap_user_hash, uid, buf);
756 if (ret < 0)
757 ret = nfs_map_numeric_to_string(uid, buf, buflen);
758 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759}
Trond Myklebuste4fd72a2011-02-22 15:44:31 -0800760int nfs_map_gid_to_group(const struct nfs_server *server, __u32 uid, char *buf, size_t buflen)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761{
Trond Myklebuste4fd72a2011-02-22 15:44:31 -0800762 struct idmap *idmap = server->nfs_client->cl_idmap;
Trond Myklebustf0b85162011-02-22 15:44:31 -0800763 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764
Trond Myklebustf0b85162011-02-22 15:44:31 -0800765 ret = nfs_idmap_name(idmap, &idmap->idmap_group_hash, uid, buf);
766 if (ret < 0)
767 ret = nfs_map_numeric_to_string(uid, buf, buflen);
768 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769}
770
Bryan Schumaker955a8572010-09-29 15:41:49 -0400771#endif /* CONFIG_NFS_USE_NEW_IDMAPPER */