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