xref: /illumos-gate/usr/src/uts/common/fs/nfs/nfs4_idmap.c (revision 7c478bd9)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * There are well defined policies for mapping uid and gid values to and
31  * from utf8 strings, as specified in RFC 3530. The protocol ops that are
32  * most significantly affected by any changes in policy are GETATTR and
33  * SETATTR, as these have different behavior depending on whether the id
34  * mapping code is executing on the client or server. Thus, the following
35  * rules represents the latest incantation of the id mapping policies.
36  *
37  * 1) For the case in which the nfsmapid(1m) daemon has _never_ been
38  *    started, the policy is to _always_ work with stringified uid's
39  *    and gid's
40  *
41  * 2) For the case in which the nfsmapid(1m) daemon _was_ started but
42  *    has either died or become unresponsive, the mapping policies are
43  *    as follows:
44  *
45  *                      Server                             Client
46  *         .-------------------------------.---------------------------------.
47  *         |                               |                                 |
48  *         | . Respond to req by replying  | . If attr string does not have  |
49  *         |   success and map the [u/g]id |   '@' sign, attempt to decode   |
50  *         |   into its literal id string  |   a stringified id; map to      |
51  *         |                               |   *ID_NOBODY if not an encoded  |
52  *         |                               |   id.                           |
53  *         |                               |                                 |
54  * GETATTR |                               | . If attr string _does_ have    |
55  *         |                               |   '@' sign			     |
56  *         |                               |   Map to *ID_NOBODY on failure. |
57  *         |                               |                                 |
58  *         | nfs_idmap_*id_str             | nfs_idmap_str_*id               |
59  *         +-------------------------------+---------------------------------+
60  *         |                               |                                 |
61  *         | . Respond to req by returning | . _Must_ map the user's passed  |
62  *         |  ECOMM, which will be mapped  |  in [u/g]id into it's network   |
63  *         |  to NFS4ERR_DELAY to clnt     |  attr string, so contact the    |
64  *         |                               |  daemon, retrying forever if    |
65  *         |   Server must not allow the   |  necessary, unless interrupted  |
66  * SETATTR |   mapping to *ID_NOBODY upon  |                                 |
67  *         |   lack of communication with  |   Client _should_ specify the   |
68  *         |   the daemon, which could     |   correct attr string for a     |
69  *         |   result in the file being    |   SETATTR operation, otherwise  |
70  *         |   inadvertently given away !  |   it can also result in the     |
71  *         |                               |   file being inadvertently      |
72  *         |                               |   given away !                  |
73  *         |                               |                                 |
74  *         | nfs_idmap_str_*id             |   nfs_idmap_*id_str             |
75  *         `-------------------------------'---------------------------------'
76  *
77  * 3) Lastly, in order to leverage better cache utilization whenever
78  *    communication with nfsmapid(1m) is currently hindered, cache
79  *    entry eviction is throttled whenever nfsidmap_daemon_dh == NULL.
80  *
81  *
82  *  Server-side behavior for upcall communication errors
83  *  ====================================================
84  *
85  *   GETATTR - Server-side GETATTR *id to attr string conversion policies
86  *             for unresponsive/dead nfsmapid(1m) daemon
87  *
88  *	a) If the *id is *ID_NOBODY, the string "nobody" is returned
89  *
90  *	b) If the *id is not *ID_NOBODY _and_ the nfsmapid(1m) daemon
91  *	   _is_ operational, the daemon is contacted to convert the
92  *	   [u/g]id into a string of type "[user/group]@domain"
93  *
94  *	c) If the nfsmapid(1m) daemon has died or has become unresponsive,
95  *	   the server returns status == NFS4_OK for the GETATTR operation,
96  *	   and returns a strigified [u/g]id to let the client map it into
97  *	   the appropriate value.
98  *
99  *   SETATTR - Server-side SETATTR attr string to *id conversion policies
100  *             for unresponsive/dead nfsmapid(1m) daemon
101  *
102  *	a) If the otw string is a stringified uid (ie. does _not_ contain
103  *	   an '@' sign and is of the form "12345") then the literal uid is
104  *	   decoded and it is used to perform the mapping.
105  *
106  *	b) If, on the other hand, the otw string _is_ of the form
107  *	   "[user/group]@domain" and problems arise contacting nfsmapid(1m),
108  *	   the SETATTR operation _must_ fail w/NFS4ERR_DELAY, as the server
109  *	   cannot default to *ID_NOBODY, which would allow a file to be
110  *	   given away by setting it's owner or owner_group to "nobody".
111  */
112 #include <sys/param.h>
113 #include <sys/errno.h>
114 #include <sys/disp.h>
115 #include <sys/vfs.h>
116 #include <sys/vnode.h>
117 #include <sys/cred.h>
118 #include <sys/cmn_err.h>
119 #include <sys/systm.h>
120 #include <sys/kmem.h>
121 #include <sys/pathname.h>
122 #include <sys/utsname.h>
123 #include <sys/debug.h>
124 #include <sys/sysmacros.h>
125 #include <sys/list.h>
126 #include <sys/sunddi.h>
127 #include <sys/dnlc.h>
128 #include <sys/sdt.h>
129 #include <nfs/nfs4.h>
130 #include <nfs/rnode4.h>
131 #include <nfs/nfsid_map.h>
132 #include <nfs/nfs4_idmap_impl.h>
133 #include <nfs/nfssys.h>
134 
135 /*
136  * Truly global modular globals
137  */
138 static zone_key_t		nfsidmap_zone_key;
139 static list_t			nfsidmap_globals_list;
140 static kmutex_t			nfsidmap_globals_lock;
141 static kmem_cache_t		*nfsidmap_cache;
142 static uint_t			pkp_tab[NFSID_CACHE_ANCHORS];
143 static int			nfs4_idcache_tout;
144 
145 /*
146  * Some useful macros
147  */
148 #define		MOD2(a, pow_of_2)	a & (pow_of_2 - 1)
149 #define		_CACHE_TOUT		60*60		/* secs in 1 hour */
150 #define		TIMEOUT(x)		(gethrestime_sec() > \
151 					(x + nfs4_idcache_tout))
152 
153 /*
154  * Max length of valid id string including the trailing null
155  */
156 #define		_MAXIDSTRLEN		11
157 
158 /*
159  * Pearson's string hash
160  *
161  * See: Communications of the ACM, June 1990 Vol 33 pp 677-680
162  * http://www.acm.org/pubs/citations/journals/cacm/1990-33-6/p677-pearson
163  */
164 #define		PS_HASH(msg, hash, len)					\
165 {                                                                       \
166 	uint_t		key = 0x12345678;	/* arbitrary value */	\
167 	int		i;						\
168                                                                         \
169 	(hash) = MOD2((key + (len)), NFSID_CACHE_ANCHORS);		\
170                                                                         \
171 	for (i = 0; i < ((len) - 1); i++) {				\
172 		(hash) = MOD2(((hash) + (msg)[i]), NFSID_CACHE_ANCHORS); \
173 		(hash) = pkp_tab[(hash)];				\
174 	}                                                               \
175 }
176 
177 #define		ID_HASH(id, hash)					\
178 {									\
179 	(hash) = MOD2(((id) ^ NFSID_CACHE_ANCHORS), NFSID_CACHE_ANCHORS); \
180 }
181 
182 /*
183  * Prototypes
184  */
185 
186 static void	*nfs_idmap_init_zone(zoneid_t);
187 static void	 nfs_idmap_fini_zone(zoneid_t, void *);
188 
189 static int	 is_stringified_id(utf8string *);
190 static void	 init_pkp_tab(void);
191 static void	 nfs_idmap_i2s_literal(uid_t, utf8string *);
192 static int	 nfs_idmap_s2i_literal(utf8string *, uid_t *, int);
193 static void	 nfs_idmap_reclaim(void *);
194 static void	 nfs_idmap_cache_reclaim(idmap_cache_info_t *);
195 static void	 nfs_idmap_cache_create(idmap_cache_info_t *, const char *);
196 static void	 nfs_idmap_cache_destroy(idmap_cache_info_t *);
197 static void	 nfs_idmap_cache_flush(idmap_cache_info_t *);
198 
199 static uint_t	 nfs_idmap_cache_s2i_lkup(idmap_cache_info_t *, utf8string *,
200 			uint_t *, uid_t *);
201 
202 static uint_t	 nfs_idmap_cache_i2s_lkup(idmap_cache_info_t *, uid_t,
203 			uint_t *, utf8string *);
204 
205 static void	 nfs_idmap_cache_s2i_insert(idmap_cache_info_t *, uid_t,
206 			utf8string *, hash_stat, uint_t);
207 
208 static void	 nfs_idmap_cache_i2s_insert(idmap_cache_info_t *, uid_t,
209 			utf8string *, hash_stat, uint_t);
210 
211 static void	 nfs_idmap_cache_rment(nfsidmap_t *);
212 
213 /*
214  * Initialization routine for NFSv4 id mapping
215  */
216 void
217 nfs_idmap_init(void)
218 {
219 	/*
220 	 * Initialize Pearson's Table
221 	 */
222 	init_pkp_tab();
223 	/*
224 	 * Initialize the kmem cache
225 	 */
226 	nfsidmap_cache = kmem_cache_create("NFS_idmap_cache",
227 	    sizeof (nfsidmap_t), 0, NULL, NULL, nfs_idmap_reclaim, NULL,
228 	    NULL, 0);
229 	/*
230 	 * If not set in "/etc/system", set to default value
231 	 */
232 	if (!nfs4_idcache_tout)
233 		nfs4_idcache_tout = _CACHE_TOUT;
234 	/*
235 	 * Initialize the list of nfsidmap_globals
236 	 */
237 	mutex_init(&nfsidmap_globals_lock, NULL, MUTEX_DEFAULT, NULL);
238 	list_create(&nfsidmap_globals_list, sizeof (struct nfsidmap_globals),
239 	    offsetof(struct nfsidmap_globals, nig_link));
240 	/*
241 	 * Initialize the zone_key_t for per-zone idmaps
242 	 */
243 	zone_key_create(&nfsidmap_zone_key, nfs_idmap_init_zone, NULL,
244 	    nfs_idmap_fini_zone);
245 }
246 
247 /*
248  * Called only when module was not loaded properly
249  */
250 void
251 nfs_idmap_fini(void)
252 {
253 	(void) zone_key_delete(nfsidmap_zone_key);
254 	list_destroy(&nfsidmap_globals_list);
255 	mutex_destroy(&nfsidmap_globals_lock);
256 	kmem_cache_destroy(nfsidmap_cache);
257 }
258 
259 /*ARGSUSED*/
260 static void *
261 nfs_idmap_init_zone(zoneid_t zoneid)
262 {
263 	struct nfsidmap_globals *nig;
264 
265 	nig = kmem_alloc(sizeof (*nig), KM_SLEEP);
266 	nig->nig_msg_done = 0;
267 	mutex_init(&nig->nfsidmap_daemon_lock, NULL, MUTEX_DEFAULT, NULL);
268 
269 	/*
270 	 * nfsidmap certainly isn't running.
271 	 */
272 	nig->nfsidmap_pid = NOPID;
273 	nig->nfsidmap_daemon_dh = NULL;
274 
275 	/*
276 	 * Create the idmap caches
277 	 */
278 	nfs_idmap_cache_create(&nig->u2s_ci, "u2s_cache");
279 	nig->u2s_ci.nfsidmap_daemon_dh = &nig->nfsidmap_daemon_dh;
280 	nfs_idmap_cache_create(&nig->s2u_ci, "s2u_cache");
281 	nig->s2u_ci.nfsidmap_daemon_dh = &nig->nfsidmap_daemon_dh;
282 	nfs_idmap_cache_create(&nig->g2s_ci, "g2s_cache");
283 	nig->g2s_ci.nfsidmap_daemon_dh = &nig->nfsidmap_daemon_dh;
284 	nfs_idmap_cache_create(&nig->s2g_ci, "s2g_cache");
285 	nig->s2g_ci.nfsidmap_daemon_dh = &nig->nfsidmap_daemon_dh;
286 
287 	/*
288 	 * Add to global list.
289 	 */
290 	mutex_enter(&nfsidmap_globals_lock);
291 	list_insert_head(&nfsidmap_globals_list, nig);
292 	mutex_exit(&nfsidmap_globals_lock);
293 
294 	return (nig);
295 }
296 
297 /*ARGSUSED*/
298 static void
299 nfs_idmap_fini_zone(zoneid_t zoneid, void *arg)
300 {
301 	struct nfsidmap_globals *nig = arg;
302 
303 	/*
304 	 * Remove from list.
305 	 */
306 	mutex_enter(&nfsidmap_globals_lock);
307 	list_remove(&nfsidmap_globals_list, nig);
308 	/*
309 	 * Destroy the idmap caches
310 	 */
311 	nfs_idmap_cache_destroy(&nig->u2s_ci);
312 	nfs_idmap_cache_destroy(&nig->s2u_ci);
313 	nfs_idmap_cache_destroy(&nig->g2s_ci);
314 	nfs_idmap_cache_destroy(&nig->s2g_ci);
315 	mutex_exit(&nfsidmap_globals_lock);
316 	/*
317 	 * Cleanup
318 	 */
319 	if (nig->nfsidmap_daemon_dh)
320 		door_ki_rele(nig->nfsidmap_daemon_dh);
321 	mutex_destroy(&nig->nfsidmap_daemon_lock);
322 	kmem_free(nig, sizeof (*nig));
323 }
324 
325 /*
326  * Convert a user utf-8 string identifier into its local uid.
327  */
328 int
329 nfs_idmap_str_uid(utf8string *u8s, uid_t *uid, bool_t isserver)
330 {
331 	int			error;
332 	uint_t			hashno = 0;
333 	const char		*whoami = "nfs_idmap_str_uid";
334 	struct nfsidmap_globals *nig;
335 	struct mapid_arg	*mapargp;
336 	struct mapid_res	mapres;
337 	struct mapid_res	*mapresp = &mapres;
338 	struct mapid_res	*resp = mapresp;
339 	door_arg_t		door_args;
340 	door_handle_t		dh;
341 
342 	nig = zone_getspecific(nfsidmap_zone_key, curproc->p_zone);
343 	ASSERT(nig != NULL);
344 
345 	if (!u8s || !u8s->utf8string_val || u8s->utf8string_len == 0 ||
346 			(u8s->utf8string_val[0] == '\0')) {
347 		*uid = UID_NOBODY;
348 		return (isserver ? EINVAL : 0);
349 	}
350 
351 	/*
352 	 * If "nobody", just short circuit and bail
353 	 */
354 	if (bcmp(u8s->utf8string_val, "nobody", 6) == 0) {
355 		*uid = UID_NOBODY;
356 		return (0);
357 	}
358 
359 	/*
360 	 * Start-off with upcalls disabled, and once nfsmapid(1m) is up and
361 	 * running, we'll leverage it's first flush to let the kernel know
362 	 * when it's up and available to perform mappings. Also, on client
363 	 * only, be smarter about when to issue upcalls by checking the
364 	 * string for existence of an '@' sign. If no '@' sign, then we just
365 	 * make our best effort to decode the string ourselves.
366 	 */
367 retry:
368 	mutex_enter(&nig->nfsidmap_daemon_lock);
369 	dh = nig->nfsidmap_daemon_dh;
370 	if (dh)
371 		door_ki_hold(dh);
372 	mutex_exit(&nig->nfsidmap_daemon_lock);
373 
374 	if (dh == NULL || nig->nfsidmap_pid == curproc->p_pid ||
375 			(!utf8_strchr(u8s, '@') && !isserver)) {
376 		if (dh)
377 			door_ki_rele(dh);
378 		error = nfs_idmap_s2i_literal(u8s, uid, isserver);
379 		/*
380 		 * If we get a numeric value, but we only do so because
381 		 * we are nfsmapid, return ENOTSUP to indicate a valid
382 		 * response, but not to cache it.
383 		 */
384 		if (!error && nig->nfsidmap_pid == curproc->p_pid)
385 			return (ENOTSUP);
386 		return (error);
387 	}
388 
389 	/* cache hit */
390 	if (nfs_idmap_cache_s2i_lkup(&nig->s2u_ci, u8s, &hashno, uid)) {
391 		door_ki_rele(dh);
392 		return (0);
393 	}
394 
395 	/* cache miss */
396 	mapargp = kmem_alloc(MAPID_ARG_LEN(u8s->utf8string_len), KM_SLEEP);
397 	mapargp->cmd = NFSMAPID_STR_UID;
398 	mapargp->u_arg.len = u8s->utf8string_len;
399 	(void) bcopy(u8s->utf8string_val, mapargp->str, mapargp->u_arg.len);
400 	mapargp->str[mapargp->u_arg.len] = '\0';
401 
402 	door_args.data_ptr = (char *)mapargp;
403 	door_args.data_size = MAPID_ARG_LEN(mapargp->u_arg.len);
404 	door_args.desc_ptr = NULL;
405 	door_args.desc_num = 0;
406 	door_args.rbuf = (char *)mapresp;
407 	door_args.rsize = sizeof (struct mapid_res);
408 
409 	error = door_ki_upcall(dh, &door_args);
410 	if (!error) {
411 		resp = (struct mapid_res *)door_args.rbuf;
412 
413 		/* Should never provide daemon with bad args */
414 		ASSERT(resp->status != NFSMAPID_INVALID);
415 
416 		switch (resp->status) {
417 		case NFSMAPID_OK:
418 			/*
419 			 * Valid mapping. Cache it.
420 			 */
421 			*uid = resp->u_res.uid;
422 			nfs_idmap_cache_s2i_insert(&nig->s2u_ci, *uid,
423 			    u8s, HQ_HASH_HINT, hashno);
424 			break;
425 
426 		case NFSMAPID_NUMSTR:
427 			/*
428 			 * string came in as stringified id. Don't cache !
429 			 *
430 			 * nfsmapid(1m) semantics have changed in order to
431 			 * support diskless clients. Thus, for stringified
432 			 * id's that have passwd/group entries, we'll go
433 			 * ahead and map them, returning no error.
434 			 */
435 			*uid = resp->u_res.uid;
436 			break;
437 
438 		case NFSMAPID_BADDOMAIN:
439 			/*
440 			 * Make the offending "user@domain" string readily
441 			 * available to D scripts that enable the probe.
442 			 */
443 			DTRACE_PROBE1(nfs4__str__uid, char *, mapargp->str);
444 			/* FALLTHROUGH */
445 
446 		case NFSMAPID_INVALID:
447 		case NFSMAPID_UNMAPPABLE:
448 		case NFSMAPID_INTERNAL:
449 		case NFSMAPID_BADID:
450 		case NFSMAPID_NOTFOUND:
451 		default:
452 			/*
453 			 * For now, treat all of these errors as equal.
454 			 *
455 			 * Return error on the server side, then the
456 			 * server returns NFS4_BADOWNER to the client.
457 			 * On client side, just map to UID_NOBODY.
458 			 */
459 			if (isserver)
460 				error = EPERM;
461 			else
462 				*uid = UID_NOBODY;
463 			break;
464 		}
465 		kmem_free(mapargp, MAPID_ARG_LEN(u8s->utf8string_len));
466 		if (resp != mapresp)
467 			kmem_free(door_args.rbuf, door_args.rsize);
468 		door_ki_rele(dh);
469 		return (error);
470 	}
471 
472 	kmem_free(mapargp, MAPID_ARG_LEN(u8s->utf8string_len));
473 	/*
474 	 * We got some door error
475 	 */
476 	switch (error) {
477 	case EINTR:
478 		/*
479 		 * If we took an interrupt we have to bail out.
480 		 */
481 		if (ttolwp(curthread) && ISSIG(curthread, JUSTLOOKING)) {
482 			door_ki_rele(dh);
483 			return (EINTR);
484 		}
485 
486 		/*
487 		 * We may have gotten EINTR for other reasons like the
488 		 * door being revoked on us, instead of trying to
489 		 * extract this out of the door handle, sleep
490 		 * and try again, if still revoked we will get EBADF
491 		 * next time through.
492 		 */
493 		/* FALLTHROUGH */
494 	case EAGAIN:    /* process may be forking */
495 		door_ki_rele(dh);
496 		/*
497 		 * Back off for a bit
498 		 */
499 		delay(hz);
500 		goto retry;
501 	default:	/* Unknown must be fatal */
502 	case EBADF:	/* Invalid door */
503 	case EINVAL:	/* Not a door, wrong target */
504 		/*
505 		 * A fatal door error, if our failing door handle is the
506 		 * current door handle, clean up our state and
507 		 * mark the server dead.
508 		 */
509 		mutex_enter(&nig->nfsidmap_daemon_lock);
510 		if (dh == nig->nfsidmap_daemon_dh) {
511 			door_ki_rele(nig->nfsidmap_daemon_dh);
512 			nig->nfsidmap_daemon_dh = NULL;
513 		}
514 		mutex_exit(&nig->nfsidmap_daemon_lock);
515 		door_ki_rele(dh);
516 
517 		if (isserver)
518 			return (ECOMM);
519 
520 		/*
521 		 * Note: We've already done optimizations above to check
522 		 *	 for '@' sign, so if we can't comm w/nfsmapid, we
523 		 *	 _know_ this _can't_ be a stringified uid.
524 		 */
525 		if (!nig->nig_msg_done) {
526 			zcmn_err(getzoneid(), CE_WARN,
527 			    "!%s: Can't communicate with mapping daemon "
528 			    "nfsmapid", whoami);
529 
530 			nig->nig_msg_done = 1;
531 		}
532 		*uid = UID_NOBODY;
533 		return (0);
534 	}
535 	/* NOTREACHED */
536 }
537 
538 /*
539  * Convert a uid into its utf-8 string representation.
540  */
541 int
542 nfs_idmap_uid_str(uid_t uid, utf8string *u8s, bool_t isserver)
543 {
544 	int			error;
545 	uint_t			hashno = 0;
546 	const char		*whoami = "nfs_idmap_uid_str";
547 	struct nfsidmap_globals	*nig;
548 	struct mapid_arg	maparg;
549 	struct mapid_res	mapres;
550 	struct mapid_res	*mapresp = &mapres;
551 	struct mapid_res	*resp = mapresp;
552 	door_arg_t		door_args;
553 	door_handle_t		dh;
554 
555 	nig = zone_getspecific(nfsidmap_zone_key, curproc->p_zone);
556 	ASSERT(nig != NULL);
557 
558 	/*
559 	 * If the supplied uid is "nobody", then we don't look at the
560 	 * cache, since we DON'T cache it in the u2s_cache. We cannot
561 	 * tell two strings apart from caching the same uid.
562 	 */
563 	if (uid == UID_NOBODY) {
564 		(void) str_to_utf8("nobody", u8s);
565 		return (0);
566 	}
567 
568 	/*
569 	 * Start-off with upcalls disabled, and once nfsmapid(1m) is
570 	 * up and running, we'll leverage it's first flush to let the
571 	 * kernel know when it's up and available to perform mappings.
572 	 * We fall back to answering with stringified uid's.
573 	 */
574 retry:
575 	mutex_enter(&nig->nfsidmap_daemon_lock);
576 	dh = nig->nfsidmap_daemon_dh;
577 	if (dh)
578 		door_ki_hold(dh);
579 	mutex_exit(&nig->nfsidmap_daemon_lock);
580 
581 	if (dh == NULL || nig->nfsidmap_pid == curproc->p_pid) {
582 		if (dh)
583 			door_ki_rele(dh);
584 		nfs_idmap_i2s_literal(uid, u8s);
585 		return (0);
586 	}
587 
588 	/* cache hit */
589 	if (nfs_idmap_cache_i2s_lkup(&nig->u2s_ci, uid, &hashno, u8s)) {
590 		door_ki_rele(dh);
591 		return (0);
592 	}
593 
594 	/* cache miss */
595 	maparg.cmd = NFSMAPID_UID_STR;
596 	maparg.u_arg.uid = uid;
597 
598 	door_args.data_ptr = (char *)&maparg;
599 	door_args.data_size = sizeof (struct mapid_arg);
600 	door_args.desc_ptr = NULL;
601 	door_args.desc_num = 0;
602 	door_args.rbuf = (char *)mapresp;
603 	door_args.rsize = sizeof (struct mapid_res);
604 
605 	error = door_ki_upcall(dh, &door_args);
606 	if (!error) {
607 		resp = (struct mapid_res *)door_args.rbuf;
608 
609 		/* Should never provide daemon with bad args */
610 		ASSERT(resp->status != NFSMAPID_INVALID);
611 
612 		switch (resp->status) {
613 		case NFSMAPID_OK:
614 			/*
615 			 * We now have a valid result from the
616 			 * user-land daemon, so cache the result (if need be).
617 			 * Load return value first then do the caches.
618 			 */
619 			(void) str_to_utf8(resp->str, u8s);
620 			nfs_idmap_cache_i2s_insert(&nig->u2s_ci, uid,
621 				u8s, HQ_HASH_HINT, hashno);
622 			break;
623 
624 		case NFSMAPID_INVALID:
625 		case NFSMAPID_UNMAPPABLE:
626 		case NFSMAPID_INTERNAL:
627 		case NFSMAPID_BADDOMAIN:
628 		case NFSMAPID_BADID:
629 		case NFSMAPID_NOTFOUND:
630 		default:
631 			/*
632 			 * For now, treat all of these errors as equal.
633 			 */
634 			error = EPERM;
635 			break;
636 		}
637 
638 		if (resp != mapresp)
639 			kmem_free(door_args.rbuf, door_args.rsize);
640 		door_ki_rele(dh);
641 		return (error);
642 	}
643 
644 	/*
645 	 * We got some door error
646 	 */
647 	switch (error) {
648 	case EINTR:
649 		/*
650 		 * If we took an interrupt we have to bail out.
651 		 */
652 		if (ttolwp(curthread) && ISSIG(curthread, JUSTLOOKING)) {
653 			door_ki_rele(dh);
654 			return (EINTR);
655 		}
656 
657 		/*
658 		 * We may have gotten EINTR for other reasons like the
659 		 * door being revoked on us, instead of trying to
660 		 * extract this out of the door handle, sleep
661 		 * and try again, if still revoked we will get EBADF
662 		 * next time through.
663 		 */
664 		/* FALLTHROUGH */
665 	case EAGAIN:    /* process may be forking */
666 		door_ki_rele(dh);
667 		/*
668 		 * Back off for a bit
669 		 */
670 		delay(hz);
671 		goto retry;
672 	default:	/* Unknown must be fatal */
673 	case EBADF:	/* Invalid door */
674 	case EINVAL:	/* Not a door, wrong target */
675 		/*
676 		 * A fatal door error, if our failing door handle is the
677 		 * current door handle, clean up our state and
678 		 * mark the server dead.
679 		 */
680 		mutex_enter(&nig->nfsidmap_daemon_lock);
681 		if (dh == nig->nfsidmap_daemon_dh) {
682 			door_ki_rele(nig->nfsidmap_daemon_dh);
683 			nig->nfsidmap_daemon_dh = NULL;
684 		}
685 		mutex_exit(&nig->nfsidmap_daemon_lock);
686 		door_ki_rele(dh);
687 
688 		/*
689 		 * Log error on client-side only
690 		 */
691 		if (!nig->nig_msg_done && !isserver) {
692 			zcmn_err(getzoneid(), CE_WARN,
693 			    "!%s: Can't communicate with mapping daemon "
694 			    "nfsmapid", whoami);
695 
696 			nig->nig_msg_done = 1;
697 		}
698 		nfs_idmap_i2s_literal(uid, u8s);
699 		return (0);
700 	}
701 	/* NOTREACHED */
702 }
703 
704 /*
705  * Convert a group utf-8 string identifier into its local gid.
706  */
707 int
708 nfs_idmap_str_gid(utf8string *u8s, gid_t *gid, bool_t isserver)
709 {
710 	int			error;
711 	uint_t			hashno = 0;
712 	const char		*whoami = "nfs_idmap_str_gid";
713 	struct nfsidmap_globals *nig;
714 	struct mapid_arg	*mapargp;
715 	struct mapid_res	mapres;
716 	struct mapid_res	*mapresp = &mapres;
717 	struct mapid_res	*resp = mapresp;
718 	door_arg_t		door_args;
719 	door_handle_t		dh;
720 
721 	nig = zone_getspecific(nfsidmap_zone_key, curproc->p_zone);
722 	ASSERT(nig != NULL);
723 
724 	if (!u8s || !u8s->utf8string_val || u8s->utf8string_len == 0 ||
725 			(u8s->utf8string_val[0] == '\0')) {
726 		*gid = GID_NOBODY;
727 		return (isserver ? EINVAL : 0);
728 	}
729 
730 	/*
731 	 * If "nobody", just short circuit and bail
732 	 */
733 	if (bcmp(u8s->utf8string_val, "nobody", 6) == 0) {
734 		*gid = GID_NOBODY;
735 		return (0);
736 	}
737 
738 	/*
739 	 * Start-off with upcalls disabled, and once nfsmapid(1m) is up and
740 	 * running, we'll leverage it's first flush to let the kernel know
741 	 * when it's up and available to perform mappings. Also, on client
742 	 * only, be smarter about when to issue upcalls by checking the
743 	 * string for existence of an '@' sign. If no '@' sign, then we just
744 	 * make our best effort to decode the string ourselves.
745 	 */
746 retry:
747 	mutex_enter(&nig->nfsidmap_daemon_lock);
748 	dh = nig->nfsidmap_daemon_dh;
749 	if (dh)
750 		door_ki_hold(dh);
751 	mutex_exit(&nig->nfsidmap_daemon_lock);
752 
753 	if (dh == NULL || nig->nfsidmap_pid == curproc->p_pid ||
754 			(!utf8_strchr(u8s, '@') && !isserver)) {
755 		if (dh)
756 			door_ki_rele(dh);
757 		error = nfs_idmap_s2i_literal(u8s, gid, isserver);
758 		/*
759 		 * If we get a numeric value, but we only do so because
760 		 * we are nfsmapid, return ENOTSUP to indicate a valid
761 		 * response, but not to cache it.
762 		 */
763 		if (!error && nig->nfsidmap_pid == curproc->p_pid)
764 			return (ENOTSUP);
765 		return (error);
766 	}
767 
768 	/* cache hit */
769 	if (nfs_idmap_cache_s2i_lkup(&nig->s2g_ci, u8s, &hashno, gid)) {
770 		door_ki_rele(dh);
771 		return (0);
772 	}
773 
774 	/* cache miss */
775 	mapargp = kmem_alloc(MAPID_ARG_LEN(u8s->utf8string_len), KM_SLEEP);
776 	mapargp->cmd = NFSMAPID_STR_GID;
777 	mapargp->u_arg.len = u8s->utf8string_len;
778 	(void) bcopy(u8s->utf8string_val, mapargp->str, mapargp->u_arg.len);
779 	mapargp->str[mapargp->u_arg.len] = '\0';
780 
781 	door_args.data_ptr = (char *)mapargp;
782 	door_args.data_size = MAPID_ARG_LEN(mapargp->u_arg.len);
783 	door_args.desc_ptr = NULL;
784 	door_args.desc_num = 0;
785 	door_args.rbuf = (char *)mapresp;
786 	door_args.rsize = sizeof (struct mapid_res);
787 
788 	error = door_ki_upcall(dh, &door_args);
789 	if (!error) {
790 		resp = (struct mapid_res *)door_args.rbuf;
791 
792 		/* Should never provide daemon with bad args */
793 		ASSERT(resp->status != NFSMAPID_INVALID);
794 
795 		switch (resp->status) {
796 		case NFSMAPID_OK:
797 			/*
798 			 * Valid mapping. Cache it.
799 			 */
800 			*gid = resp->u_res.gid;
801 			error = 0;
802 			nfs_idmap_cache_s2i_insert(&nig->s2g_ci, *gid,
803 			    u8s, HQ_HASH_HINT, hashno);
804 			break;
805 
806 		case NFSMAPID_NUMSTR:
807 			/*
808 			 * string came in as stringified id. Don't cache !
809 			 *
810 			 * nfsmapid(1m) semantics have changed in order to
811 			 * support diskless clients. Thus, for stringified
812 			 * id's that have passwd/group entries, we'll go
813 			 * ahead and map them, returning no error.
814 			 */
815 			*gid = resp->u_res.gid;
816 			break;
817 
818 		case NFSMAPID_BADDOMAIN:
819 			/*
820 			 * Make the offending "group@domain" string readily
821 			 * available to D scripts that enable the probe.
822 			 */
823 			DTRACE_PROBE1(nfs4__str__gid, char *, mapargp->str);
824 			/* FALLTHROUGH */
825 
826 		case NFSMAPID_INVALID:
827 		case NFSMAPID_UNMAPPABLE:
828 		case NFSMAPID_INTERNAL:
829 		case NFSMAPID_BADID:
830 		case NFSMAPID_NOTFOUND:
831 		default:
832 			/*
833 			 * For now, treat all of these errors as equal.
834 			 *
835 			 * Return error on the server side, then the
836 			 * server returns NFS4_BADOWNER to the client.
837 			 * On client side, just map to GID_NOBODY.
838 			 */
839 			if (isserver)
840 				error = EPERM;
841 			else
842 				*gid = GID_NOBODY;
843 			break;
844 		}
845 		kmem_free(mapargp, MAPID_ARG_LEN(u8s->utf8string_len));
846 		if (resp != mapresp)
847 			kmem_free(door_args.rbuf, door_args.rsize);
848 		door_ki_rele(dh);
849 		return (error);
850 	}
851 
852 	kmem_free(mapargp, MAPID_ARG_LEN(u8s->utf8string_len));
853 	/*
854 	 * We got some door error
855 	 */
856 	switch (error) {
857 	case EINTR:
858 		/*
859 		 * If we took an interrupt we have to bail out.
860 		 */
861 		if (ttolwp(curthread) && ISSIG(curthread, JUSTLOOKING)) {
862 			door_ki_rele(dh);
863 			return (EINTR);
864 		}
865 
866 		/*
867 		 * We may have gotten EINTR for other reasons like the
868 		 * door being revoked on us, instead of trying to
869 		 * extract this out of the door handle, sleep
870 		 * and try again, if still revoked we will get EBADF
871 		 * next time through.
872 		 */
873 		/* FALLTHROUGH */
874 	case EAGAIN:    /* process may be forking */
875 		door_ki_rele(dh);
876 		/*
877 		 * Back off for a bit
878 		 */
879 		delay(hz);
880 		goto retry;
881 	default:	/* Unknown must be fatal */
882 	case EBADF:	/* Invalid door */
883 	case EINVAL:	/* Not a door, wrong target */
884 		/*
885 		 * A fatal door error, clean up our state and
886 		 * mark the server dead.
887 		 */
888 
889 		mutex_enter(&nig->nfsidmap_daemon_lock);
890 		if (dh == nig->nfsidmap_daemon_dh) {
891 			door_ki_rele(nig->nfsidmap_daemon_dh);
892 			nig->nfsidmap_daemon_dh = NULL;
893 		}
894 		mutex_exit(&nig->nfsidmap_daemon_lock);
895 		door_ki_rele(dh);
896 
897 		if (isserver)
898 			return (ECOMM);
899 
900 		/*
901 		 * Note: We've already done optimizations above to check
902 		 *	 for '@' sign, so if we can't comm w/nfsmapid, we
903 		 *	 _know_ this _can't_ be a stringified gid.
904 		 */
905 		if (!nig->nig_msg_done) {
906 			zcmn_err(getzoneid(), CE_WARN,
907 			    "!%s: Can't communicate with mapping daemon "
908 			    "nfsmapid", whoami);
909 
910 			nig->nig_msg_done = 1;
911 		}
912 		*gid = GID_NOBODY;
913 		return (0);
914 	}
915 	/* NOTREACHED */
916 }
917 
918 /*
919  * Convert a gid into its utf-8 string representation.
920  */
921 int
922 nfs_idmap_gid_str(gid_t gid, utf8string *u8s, bool_t isserver)
923 {
924 	int			error;
925 	uint_t			hashno = 0;
926 	const char		*whoami = "nfs_idmap_gid_str";
927 	struct nfsidmap_globals	*nig;
928 	struct mapid_arg	maparg;
929 	struct mapid_res	mapres;
930 	struct mapid_res	*mapresp = &mapres;
931 	struct mapid_res	*resp = mapresp;
932 	door_arg_t		door_args;
933 	door_handle_t		dh;
934 
935 	nig = zone_getspecific(nfsidmap_zone_key, curproc->p_zone);
936 	ASSERT(nig != NULL);
937 
938 	/*
939 	 * If the supplied gid is "nobody", then we don't look at the
940 	 * cache, since we DON'T cache it in the u2s_cache. We cannot
941 	 * tell two strings apart from caching the same gid.
942 	 */
943 	if (gid == GID_NOBODY) {
944 		(void) str_to_utf8("nobody", u8s);
945 		return (0);
946 	}
947 
948 	/*
949 	 * Start-off with upcalls disabled, and once nfsmapid(1m) is
950 	 * up and running, we'll leverage it's first flush to let the
951 	 * kernel know when it's up and available to perform mappings.
952 	 * We fall back to answering with stringified gid's.
953 	 */
954 retry:
955 	mutex_enter(&nig->nfsidmap_daemon_lock);
956 	dh = nig->nfsidmap_daemon_dh;
957 	if (dh)
958 		door_ki_hold(dh);
959 	mutex_exit(&nig->nfsidmap_daemon_lock);
960 
961 	if (dh == NULL || nig->nfsidmap_pid == curproc->p_pid) {
962 		if (dh)
963 			door_ki_rele(dh);
964 		nfs_idmap_i2s_literal(gid, u8s);
965 		return (0);
966 	}
967 
968 	/* cache hit */
969 	if (nfs_idmap_cache_i2s_lkup(&nig->g2s_ci, gid, &hashno, u8s)) {
970 		door_ki_rele(dh);
971 		return (0);
972 	}
973 
974 	/* cache miss */
975 	maparg.cmd = NFSMAPID_GID_STR;
976 	maparg.u_arg.gid = gid;
977 
978 	door_args.data_ptr = (char *)&maparg;
979 	door_args.data_size = sizeof (struct mapid_arg);
980 	door_args.desc_ptr = NULL;
981 	door_args.desc_num = 0;
982 	door_args.rbuf = (char *)mapresp;
983 	door_args.rsize = sizeof (struct mapid_res);
984 
985 	error = door_ki_upcall(dh, &door_args);
986 	if (!error) {
987 		resp = (struct mapid_res *)door_args.rbuf;
988 
989 		/* Should never provide daemon with bad args */
990 		ASSERT(resp->status != NFSMAPID_INVALID);
991 
992 		switch (resp->status) {
993 		case NFSMAPID_OK:
994 			/*
995 			 * We now have a valid result from the
996 			 * user-land daemon, so cache the result (if need be).
997 			 * Load return value first then do the caches.
998 			 */
999 			(void) str_to_utf8(resp->str, u8s);
1000 			nfs_idmap_cache_i2s_insert(&nig->g2s_ci, gid,
1001 				u8s, HQ_HASH_HINT, hashno);
1002 			break;
1003 
1004 		case NFSMAPID_INVALID:
1005 		case NFSMAPID_UNMAPPABLE:
1006 		case NFSMAPID_INTERNAL:
1007 		case NFSMAPID_BADDOMAIN:
1008 		case NFSMAPID_BADID:
1009 		case NFSMAPID_NOTFOUND:
1010 		default:
1011 			/*
1012 			 * For now, treat all of these errors as equal.
1013 			 */
1014 			error = EPERM;
1015 			break;
1016 		}
1017 
1018 		if (resp != mapresp)
1019 			kmem_free(door_args.rbuf, door_args.rsize);
1020 		door_ki_rele(dh);
1021 		return (error);
1022 	}
1023 
1024 	/*
1025 	 * We got some door error
1026 	 */
1027 	switch (error) {
1028 	case EINTR:
1029 		/*
1030 		 * If we took an interrupt we have to bail out.
1031 		 */
1032 		if (ttolwp(curthread) && ISSIG(curthread, JUSTLOOKING)) {
1033 			door_ki_rele(dh);
1034 			return (EINTR);
1035 		}
1036 
1037 		/*
1038 		 * We may have gotten EINTR for other reasons like the
1039 		 * door being revoked on us, instead of trying to
1040 		 * extract this out of the door handle, sleep
1041 		 * and try again, if still revoked we will get EBADF
1042 		 * next time through.
1043 		 */
1044 		/* FALLTHROUGH */
1045 	case EAGAIN:    /* process may be forking */
1046 		door_ki_rele(dh);
1047 		/*
1048 		 * Back off for a bit
1049 		 */
1050 		delay(hz);
1051 		goto retry;
1052 	default:	/* Unknown must be fatal */
1053 	case EBADF:	/* Invalid door */
1054 	case EINVAL:	/* Not a door, wrong target */
1055 		/*
1056 		 * A fatal door error, if our failing door handle is the
1057 		 * current door handle, clean up our state and
1058 		 * mark the server dead.
1059 		 */
1060 		mutex_enter(&nig->nfsidmap_daemon_lock);
1061 		if (dh == nig->nfsidmap_daemon_dh) {
1062 			door_ki_rele(nig->nfsidmap_daemon_dh);
1063 			nig->nfsidmap_daemon_dh = NULL;
1064 		}
1065 		door_ki_rele(dh);
1066 		mutex_exit(&nig->nfsidmap_daemon_lock);
1067 
1068 		/*
1069 		 * Log error on client-side only
1070 		 */
1071 		if (!nig->nig_msg_done && !isserver) {
1072 			zcmn_err(getzoneid(), CE_WARN,
1073 			    "!%s: Can't communicate with mapping daemon "
1074 			    "nfsmapid", whoami);
1075 
1076 			nig->nig_msg_done = 1;
1077 		}
1078 		nfs_idmap_i2s_literal(gid, u8s);
1079 		return (0);
1080 	}
1081 	/* NOTREACHED */
1082 }
1083 
1084 /* -- idmap cache management -- */
1085 
1086 /*
1087  * Cache creation and initialization routine
1088  */
1089 static void
1090 nfs_idmap_cache_create(idmap_cache_info_t *cip, const char *name)
1091 {
1092 	int		 i;
1093 	nfsidhq_t	*hq = NULL;
1094 
1095 	cip->table = kmem_alloc((NFSID_CACHE_ANCHORS * sizeof (nfsidhq_t)),
1096 				KM_SLEEP);
1097 
1098 	for (i = 0, hq = cip->table; i < NFSID_CACHE_ANCHORS; i++, hq++) {
1099 		hq->hq_que_forw = hq;
1100 		hq->hq_que_back = hq;
1101 		mutex_init(&(hq->hq_lock), NULL, MUTEX_DEFAULT, NULL);
1102 	}
1103 	cip->name = name;
1104 }
1105 
1106 /*
1107  * Cache destruction routine
1108  *
1109  * Ops per hash queue
1110  *
1111  * - dequeue cache entries
1112  * - release string storage per entry
1113  * - release cache entry storage
1114  * - destroy HQ lock when HQ is empty
1115  * - once all HQ's empty, release HQ storage
1116  */
1117 static void
1118 nfs_idmap_cache_destroy(idmap_cache_info_t *cip)
1119 {
1120 	int		 i;
1121 	nfsidhq_t	*hq;
1122 
1123 	ASSERT(MUTEX_HELD(&nfsidmap_globals_lock));
1124 	nfs_idmap_cache_flush(cip);
1125 	/*
1126 	 * We can safely destroy per-queue locks since the only
1127 	 * other entity that could be mucking with this table is the
1128 	 * kmem reaper thread which does everything under
1129 	 * nfsidmap_globals_lock (which we're holding).
1130 	 */
1131 	for (i = 0, hq = cip->table; i < NFSID_CACHE_ANCHORS; i++, hq++)
1132 		mutex_destroy(&(hq->hq_lock));
1133 	kmem_free(cip->table, NFSID_CACHE_ANCHORS * sizeof (nfsidhq_t));
1134 }
1135 
1136 void
1137 nfs_idmap_args(struct nfsidmap_args *idmp)
1138 {
1139 	struct nfsidmap_globals *nig;
1140 
1141 	nig = zone_getspecific(nfsidmap_zone_key, curproc->p_zone);
1142 	ASSERT(nig != NULL);
1143 
1144 	nfs_idmap_cache_flush(&nig->u2s_ci);
1145 	nfs_idmap_cache_flush(&nig->s2u_ci);
1146 	nfs_idmap_cache_flush(&nig->g2s_ci);
1147 	nfs_idmap_cache_flush(&nig->s2g_ci);
1148 
1149 	/*
1150 	 * nfsmapid(1m) up and running; enable upcalls
1151 	 * State:
1152 	 *	0	Just flush caches
1153 	 *	1	Re-establish door knob
1154 	 */
1155 	if (idmp->state) {
1156 		/*
1157 		 * When reestablishing the nfsmapid we need to
1158 		 * not only purge the idmap cache but also
1159 		 * the dnlc as it will have cached uid/gid's.
1160 		 * While heavyweight, this should almost never happen
1161 		 */
1162 		dnlc_purge();
1163 
1164 		/*
1165 		 * Invalidate the attrs of all rnodes to force new uid and gids
1166 		 */
1167 		nfs4_rnode_invalidate(NULL);
1168 
1169 		mutex_enter(&nig->nfsidmap_daemon_lock);
1170 		if (nig->nfsidmap_daemon_dh)
1171 			door_ki_rele(nig->nfsidmap_daemon_dh);
1172 		nig->nfsidmap_daemon_dh = door_ki_lookup(idmp->did);
1173 		nig->nfsidmap_pid = curproc->p_pid;
1174 		nig->nig_msg_done = 0;
1175 		mutex_exit(&nig->nfsidmap_daemon_lock);
1176 	}
1177 }
1178 
1179 /*
1180  * Cache flush routine
1181  *
1182  *	The only serialization required it to hold the hash chain lock
1183  *	when destroying cache entries.  There is no need to prevent access
1184  *	to all hash chains while flushing.  It is possible that (valid)
1185  *	entries could be cached in later hash chains after we start flushing.
1186  *	It is unfortunate that the entry will be instantly destroyed, but
1187  *	it isn't a major concern.  This is only a cache.  It'll be repopulated.
1188  *
1189  * Ops per hash queue
1190  *
1191  * - dequeue cache entries
1192  * - release string storage per entry
1193  * - release cache entry storage
1194  */
1195 static void
1196 nfs_idmap_cache_flush(idmap_cache_info_t *cip)
1197 {
1198 	int		 i;
1199 	nfsidmap_t	*p, *next;
1200 	nfsidhq_t	*hq;
1201 
1202 	for (i = 0, hq = cip->table; i < NFSID_CACHE_ANCHORS; i++, hq++) {
1203 
1204 		mutex_enter(&(hq->hq_lock));
1205 
1206 		/*
1207 		 * remove list from hash header so we can release
1208 		 * the lock early.
1209 		 */
1210 		p = hq->hq_lru_forw;
1211 		hq->hq_que_forw = hq;
1212 		hq->hq_que_back = hq;
1213 
1214 		mutex_exit(&(hq->hq_lock));
1215 
1216 		/*
1217 		 * Iterate over the orphan'd list and free all elements.
1218 		 * There's no need to bother with remque since we're
1219 		 * freeing the entire list.
1220 		 */
1221 		while (p != (nfsidmap_t *)hq) {
1222 			next = p->id_forw;
1223 			if (p->id_val != 0)
1224 				kmem_free(p->id_val, p->id_len);
1225 			kmem_cache_free(nfsidmap_cache, p);
1226 			p = next;
1227 		}
1228 
1229 	}
1230 }
1231 
1232 static void
1233 nfs_idmap_cache_reclaim(idmap_cache_info_t *cip)
1234 {
1235 	nfsidhq_t		*hq;
1236 	nfsidmap_t		*pprev = NULL;
1237 	int			 i;
1238 	nfsidmap_t		*p;
1239 
1240 	ASSERT(cip != NULL && cip->table != NULL);
1241 
1242 	/*
1243 	 * If the daemon is down, do not flush anything
1244 	 */
1245 	if ((*cip->nfsidmap_daemon_dh) == NULL)
1246 		return;
1247 
1248 	for (i = 0, hq = cip->table; i < NFSID_CACHE_ANCHORS; i++, hq++) {
1249 		if (!mutex_tryenter(&(hq->hq_lock)))
1250 			continue;
1251 
1252 		/*
1253 		 * Start at end of list and work backwards since LRU
1254 		 */
1255 		for (p = hq->hq_lru_back; p != (nfsidmap_t *)hq; p = pprev) {
1256 			pprev = p->id_back;
1257 
1258 			/*
1259 			 * List is LRU. If trailing end does not
1260 			 * contain stale entries, then no need to
1261 			 * continue.
1262 			 */
1263 			if (!TIMEOUT(p->id_time))
1264 				break;
1265 
1266 			nfs_idmap_cache_rment(p);
1267 		}
1268 		mutex_exit(&(hq->hq_lock));
1269 	}
1270 }
1271 
1272 /*
1273  * Callback reclaim function for VM.  We reap timed-out entries from all hash
1274  * tables in all zones.
1275  */
1276 /* ARGSUSED */
1277 void
1278 nfs_idmap_reclaim(void *arg)
1279 {
1280 	struct nfsidmap_globals *nig;
1281 
1282 	mutex_enter(&nfsidmap_globals_lock);
1283 	for (nig = list_head(&nfsidmap_globals_list); nig != NULL;
1284 	    nig = list_next(&nfsidmap_globals_list, nig)) {
1285 		nfs_idmap_cache_reclaim(&nig->u2s_ci);
1286 		nfs_idmap_cache_reclaim(&nig->s2u_ci);
1287 		nfs_idmap_cache_reclaim(&nig->g2s_ci);
1288 		nfs_idmap_cache_reclaim(&nig->s2g_ci);
1289 	}
1290 	mutex_exit(&nfsidmap_globals_lock);
1291 }
1292 
1293 /*
1294  * Search the specified cache for the existence of the specified utf-8
1295  * string. If found, the corresponding mapping is returned in id_buf and
1296  * the cache entry is updated to the head of the LRU list. The computed
1297  * hash queue number, is returned in hashno.
1298  */
1299 static uint_t
1300 nfs_idmap_cache_s2i_lkup(idmap_cache_info_t *cip,	/* cache info ptr */
1301 			utf8string *u8s,	/* utf8 string to resolve */
1302 			uint_t *hashno,		/* hash number, retval */
1303 			uid_t *id_buf)		/* if found, id for u8s */
1304 {
1305 	nfsidmap_t	*p;
1306 	nfsidmap_t	*pnext;
1307 	nfsidhq_t	*hq;
1308 	uint_t		 hash;
1309 	char		*rqst_c_str;
1310 	uint_t		 rqst_len;
1311 	uint_t		 found_stat = 0;
1312 
1313 	if ((rqst_c_str = utf8_to_str(u8s, &rqst_len, NULL)) == NULL) {
1314 		/*
1315 		 * Illegal string, return not found.
1316 		 */
1317 		return (0);
1318 	}
1319 
1320 	/*
1321 	 * Compute hash queue
1322 	 */
1323 	PS_HASH(rqst_c_str, hash, rqst_len - 1);
1324 	*hashno = hash;
1325 	hq = &cip->table[hash];
1326 
1327 	/*
1328 	 * Look for the entry in the HQ
1329 	 */
1330 	mutex_enter(&(hq->hq_lock));
1331 	for (p = hq->hq_lru_forw; p != (nfsidmap_t *)hq; p = pnext) {
1332 
1333 		pnext = p->id_forw;
1334 
1335 		/*
1336 		 * Check entry for staleness first, as user's id
1337 		 * may have changed and may need to be remapped.
1338 		 * Note that we don't evict entries from the cache
1339 		 * if we're having trouble contacting nfsmapid(1m)
1340 		 */
1341 		if (TIMEOUT(p->id_time) && (*cip->nfsidmap_daemon_dh) != NULL) {
1342 			nfs_idmap_cache_rment(p);
1343 			continue;
1344 		}
1345 
1346 		/*
1347 		 * Compare equal length strings
1348 		 */
1349 		if (p->id_len == (rqst_len - 1)) {
1350 			if (bcmp(p->id_val, rqst_c_str, (rqst_len - 1)) == 0) {
1351 				/*
1352 				 * Found it. Update it and load return value.
1353 				 */
1354 				*id_buf = p->id_no;
1355 				remque(p);
1356 				insque(p, hq);
1357 				p->id_time = gethrestime_sec();
1358 
1359 				found_stat = 1;
1360 				break;
1361 			}
1362 		}
1363 	}
1364 	mutex_exit(&(hq->hq_lock));
1365 
1366 	if (rqst_c_str != NULL)
1367 		kmem_free(rqst_c_str, rqst_len);
1368 
1369 	return (found_stat);
1370 }
1371 
1372 /*
1373  * Search the specified cache for the existence of the specified utf8
1374  * string, as it may have been inserted before this instance got a chance
1375  * to do it. If NOT found, then a new entry is allocated for the specified
1376  * cache, and inserted. The hash queue number is obtained from hash_number
1377  * if the behavior is HQ_HASH_HINT, or computed otherwise.
1378  */
1379 static void
1380 nfs_idmap_cache_s2i_insert(idmap_cache_info_t *cip,	/* cache info ptr */
1381 			uid_t id,		/* id result from upcall */
1382 			utf8string *u8s,	/* utf8 string to resolve */
1383 			hash_stat behavior,	/* hash algorithm behavior */
1384 			uint_t hash_number)	/* hash number iff hint */
1385 {
1386 	uint_t			 hashno;
1387 	char			*c_str;
1388 	nfsidhq_t		*hq;
1389 	nfsidmap_t		*newp;
1390 	nfsidmap_t		*p;
1391 	nfsidmap_t		*pnext;
1392 	uint_t			 c_len;
1393 
1394 	/*
1395 	 * This shouldn't fail, since already successful at lkup.
1396 	 * So, if it does happen, just drop the request-to-insert
1397 	 * on the floor.
1398 	 */
1399 	if ((c_str = utf8_to_str(u8s, &c_len, NULL)) == NULL)
1400 		return;
1401 
1402 	/*
1403 	 * Obtain correct hash queue to insert new entry in
1404 	 */
1405 	switch (behavior) {
1406 		case HQ_HASH_HINT:
1407 			hashno = hash_number;
1408 			break;
1409 
1410 		case HQ_HASH_FIND:
1411 		default:
1412 			PS_HASH(c_str, hashno, c_len - 1);
1413 			break;
1414 	}
1415 	hq = &cip->table[hashno];
1416 
1417 
1418 	/*
1419 	 * Look for an existing entry in the cache. If one exists
1420 	 * update it, and return. Otherwise, allocate a new cache
1421 	 * entry, initialize it and insert it.
1422 	 */
1423 	mutex_enter(&(hq->hq_lock));
1424 	for (p = hq->hq_lru_forw; p != (nfsidmap_t *)hq; p = pnext) {
1425 
1426 		pnext = p->id_forw;
1427 
1428 		/*
1429 		 * Check entry for staleness first, as user's id
1430 		 * may have changed and may need to be remapped.
1431 		 * Note that we don't evict entries from the cache
1432 		 * if we're having trouble contacting nfsmapid(1m)
1433 		 */
1434 		if (TIMEOUT(p->id_time) && (*cip->nfsidmap_daemon_dh) != NULL) {
1435 			nfs_idmap_cache_rment(p);
1436 			continue;
1437 		}
1438 
1439 		/*
1440 		 * Compare equal length strings
1441 		 */
1442 		if (p->id_len == (c_len - 1)) {
1443 			if (bcmp(p->id_val, c_str, (c_len - 1)) == 0) {
1444 				/*
1445 				 * Move to front, and update time.
1446 				 */
1447 				remque(p);
1448 				insque(p, hq);
1449 				p->id_time = gethrestime_sec();
1450 
1451 				mutex_exit(&(hq->hq_lock));
1452 				kmem_free(c_str, c_len);
1453 				return;
1454 			}
1455 		}
1456 	}
1457 
1458 	/*
1459 	 * Not found ! Alloc, init and insert new entry
1460 	 */
1461 	newp = kmem_cache_alloc(nfsidmap_cache, KM_SLEEP);
1462 	newp->id_len = u8s->utf8string_len;
1463 	newp->id_val = kmem_alloc(u8s->utf8string_len, KM_SLEEP);
1464 	bcopy(u8s->utf8string_val, newp->id_val, u8s->utf8string_len);
1465 	newp->id_no = id;
1466 	newp->id_time = gethrestime_sec();
1467 	insque(newp, hq);
1468 
1469 	mutex_exit(&(hq->hq_lock));
1470 	kmem_free(c_str, c_len);
1471 }
1472 
1473 /*
1474  * Search the specified cache for the existence of the specified id.
1475  * If found, the corresponding mapping is returned in u8s and the
1476  * cache entry is updated to the head of the LRU list. The computed
1477  * hash queue number, is returned in hashno.
1478  */
1479 static uint_t
1480 nfs_idmap_cache_i2s_lkup(idmap_cache_info_t *cip,   /* cache info ptr */
1481 			uid_t id,		    /* id to resolve */
1482 			uint_t *hashno,		    /* hash number, retval */
1483 			utf8string *u8s)	/* if found, utf8 str for id */
1484 {
1485 	uint_t			 found_stat = 0;
1486 	nfsidmap_t		*p;
1487 	nfsidmap_t		*pnext;
1488 	nfsidhq_t		*hq;
1489 	uint_t			 hash;
1490 
1491 	/*
1492 	 * Compute hash queue
1493 	 */
1494 	ID_HASH(id, hash);
1495 	*hashno = hash;
1496 	hq = &cip->table[hash];
1497 
1498 	/*
1499 	 * Look for the entry in the HQ
1500 	 */
1501 	mutex_enter(&(hq->hq_lock));
1502 	for (p = hq->hq_lru_forw; p != (nfsidmap_t *)hq; p = pnext) {
1503 
1504 		pnext = p->id_forw;
1505 
1506 		/*
1507 		 * Check entry for staleness first, as user's id
1508 		 * may have changed and may need to be remapped.
1509 		 * Note that we don't evict entries from the cache
1510 		 * if we're having trouble contacting nfsmapid(1m)
1511 		 */
1512 		if (TIMEOUT(p->id_time) && (*cip->nfsidmap_daemon_dh) != NULL) {
1513 			nfs_idmap_cache_rment(p);
1514 			continue;
1515 		}
1516 
1517 		if (p->id_no == id) {
1518 
1519 			/*
1520 			 * Found it. Load return value and move to head
1521 			 */
1522 			ASSERT(u8s->utf8string_val == NULL);
1523 			u8s->utf8string_len = p->id_len;
1524 			u8s->utf8string_val = kmem_alloc(p->id_len, KM_SLEEP);
1525 			bcopy(p->id_val, u8s->utf8string_val, p->id_len);
1526 
1527 			remque(p);
1528 			insque(p, hq);
1529 			p->id_time = gethrestime_sec();
1530 
1531 			found_stat = 1;
1532 			break;
1533 		}
1534 	}
1535 	mutex_exit(&(hq->hq_lock));
1536 
1537 	return (found_stat);
1538 }
1539 
1540 /*
1541  * Search the specified cache for the existence of the specified id,
1542  * as it may have been inserted before this instance got a chance to
1543  * do it. If NOT found, then a new entry is allocated for the specified
1544  * cache, and inserted. The hash queue number is obtained from hash_number
1545  * if the behavior is HQ_HASH_HINT, or computed otherwise.
1546  */
1547 static void
1548 nfs_idmap_cache_i2s_insert(idmap_cache_info_t *cip, /* cache info ptr */
1549 			uid_t id,		    /* id to resolve */
1550 			utf8string *u8s,	/* utf8 result from upcall */
1551 			hash_stat behavior,	/* has algorithm behavior */
1552 			uint_t hash_number)	/* hash number iff hint */
1553 {
1554 	uint_t		 hashno;
1555 	nfsidhq_t	*hq;
1556 	nfsidmap_t	*newp;
1557 	nfsidmap_t	*pnext;
1558 	nfsidmap_t	*p;
1559 
1560 
1561 	/*
1562 	 * Obtain correct hash queue to insert new entry in
1563 	 */
1564 	switch (behavior) {
1565 		case HQ_HASH_HINT:
1566 			hashno = hash_number;
1567 			break;
1568 
1569 		case HQ_HASH_FIND:
1570 		default:
1571 			ID_HASH(id, hashno);
1572 			break;
1573 	}
1574 	hq = &cip->table[hashno];
1575 
1576 
1577 	/*
1578 	 * Look for an existing entry in the cache. If one exists
1579 	 * update it, and return. Otherwise, allocate a new cache
1580 	 * entry, initialize and insert it.
1581 	 */
1582 	mutex_enter(&(hq->hq_lock));
1583 	for (p = hq->hq_lru_forw; p != (nfsidmap_t *)hq; p = pnext) {
1584 
1585 		pnext = p->id_forw;
1586 
1587 		/*
1588 		 * Check entry for staleness first, as user's id
1589 		 * may have changed and may need to be remapped.
1590 		 * Note that we don't evict entries from the cache
1591 		 * if we're having trouble contacting nfsmapid(1m)
1592 		 */
1593 		if (TIMEOUT(p->id_time) && (*cip->nfsidmap_daemon_dh) != NULL) {
1594 			nfs_idmap_cache_rment(p);
1595 			continue;
1596 		}
1597 
1598 
1599 		if ((p->id_no == id) && (p->id_len == u8s->utf8string_len)) {
1600 			/*
1601 			 * Found It ! Move to front, and update time.
1602 			 */
1603 			remque(p);
1604 			insque(p, hq);
1605 			p->id_time = gethrestime_sec();
1606 
1607 			mutex_exit(&(hq->hq_lock));
1608 			return;
1609 		}
1610 	}
1611 
1612 	/*
1613 	 * Not found ! Alloc, init and insert new entry
1614 	 */
1615 	newp = kmem_cache_alloc(nfsidmap_cache, KM_SLEEP);
1616 	newp->id_len = u8s->utf8string_len;
1617 	newp->id_val = kmem_alloc(u8s->utf8string_len, KM_SLEEP);
1618 	bcopy(u8s->utf8string_val, newp->id_val, u8s->utf8string_len);
1619 	newp->id_no = id;
1620 	newp->id_time = gethrestime_sec();
1621 	insque(newp, hq);
1622 
1623 	mutex_exit(&(hq->hq_lock));
1624 }
1625 
1626 /*
1627  * Remove and free one cache entry
1628  */
1629 static void
1630 nfs_idmap_cache_rment(nfsidmap_t *p)
1631 {
1632 	remque(p);
1633 	if (p->id_val != 0)
1634 		kmem_free(p->id_val, p->id_len);
1635 	kmem_cache_free(nfsidmap_cache, p);
1636 }
1637 
1638 #ifndef		UID_MAX
1639 #define		UID_MAX		2147483647		/* see limits.h */
1640 #endif
1641 
1642 #ifndef		isdigit
1643 #define		isdigit(c)	((c) >= '0' && (c) <= '9')
1644 #endif
1645 
1646 static int
1647 is_stringified_id(utf8string *u8s)
1648 {
1649 	int	i;
1650 
1651 	for (i = 0; i < u8s->utf8string_len; i++)
1652 		if (!isdigit(u8s->utf8string_val[i]))
1653 			return (0);
1654 	return (1);
1655 }
1656 
1657 int
1658 nfs_idmap_s2i_literal(utf8string *u8s, uid_t *id, int isserver)
1659 {
1660 	long	tmp;
1661 	int	convd;
1662 	char	ids[_MAXIDSTRLEN];
1663 
1664 	/*
1665 	 * "nobody" unless we can actually decode it.
1666 	 */
1667 	*id = UID_NOBODY;
1668 
1669 	/*
1670 	 * We're here because it has already been determined that the
1671 	 * string contains no '@' _or_ the nfsmapid daemon has yet to
1672 	 * be started.
1673 	 */
1674 	if (!is_stringified_id(u8s))
1675 		return (0);
1676 
1677 	/*
1678 	 * If utf8string_len is greater than _MAXIDSTRLEN-1, then the id
1679 	 * is going to be greater than UID_MAX. Return id of "nobody"
1680 	 * right away.
1681 	 */
1682 	if (u8s->utf8string_len >= _MAXIDSTRLEN)
1683 		return (isserver ? EPERM : 0);
1684 
1685 	/*
1686 	 * Make sure we pass a NULL terminated 'C' string to ddi_strtol
1687 	 */
1688 	bcopy(u8s->utf8string_val, ids, u8s->utf8string_len);
1689 	ids[u8s->utf8string_len] = '\0';
1690 	convd = ddi_strtol(ids, NULL, 10, &tmp);
1691 	if (convd == 0 && tmp >= 0 && tmp <= UID_MAX) {
1692 		*id = tmp;
1693 		return (0);
1694 	}
1695 	return (isserver ? EPERM : 0);
1696 }
1697 
1698 static void
1699 nfs_idmap_i2s_literal(uid_t id, utf8string *u8s)
1700 {
1701 	char	ids[_MAXIDSTRLEN];
1702 
1703 	(void) snprintf(ids, _MAXIDSTRLEN, "%d", id);
1704 	(void) str_to_utf8(ids, u8s);
1705 }
1706 
1707 /* -- Utility functions -- */
1708 
1709 /*
1710  * Initialize table in pseudo-random fashion
1711  * for use in Pearson's string hash algorithm.
1712  *
1713  * See: Communications of the ACM, June 1990 Vol 33 pp 677-680
1714  * http://www.acm.org/pubs/citations/journals/cacm/1990-33-6/p677-pearson
1715  */
1716 static void
1717 init_pkp_tab(void)
1718 {
1719 	int		i;
1720 	int		j;
1721 	int		k = 7;
1722 	uint_t		s;
1723 
1724 	for (i = 0; i < NFSID_CACHE_ANCHORS; i++)
1725 		pkp_tab[i] = i;
1726 
1727 	for (j = 0; j < 4; j++)
1728 		for (i = 0; i < NFSID_CACHE_ANCHORS; i++) {
1729 			s = pkp_tab[i];
1730 			k = MOD2((k + s), NFSID_CACHE_ANCHORS);
1731 			pkp_tab[i] = pkp_tab[k];
1732 			pkp_tab[k] = s;
1733 		}
1734 }
1735 
1736 char *
1737 utf8_strchr(utf8string *u8s, const char c)
1738 {
1739 	int	i;
1740 	char	*u8p = u8s->utf8string_val;
1741 	int	len = u8s->utf8string_len;
1742 
1743 	for (i = 0; i < len; i++)
1744 		if (u8p[i] == c)
1745 			return (&u8p[i]);
1746 	return (NULL);
1747 }
1748