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