Home | History | Annotate | Download | only in nfs
      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