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