xref: /illumos-gate/usr/src/uts/common/idmap/idmap_cache.c (revision 6faf52448e142b151fa3deade474be359e7c8698)
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 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Copyright 2023 RackTop Systems, Inc.
27  */
28 
29 /*
30  * Windows to Solaris Identity Mapping kernel API
31  * This module provides the kernel cache.
32  */
33 
34 
35 #include <sys/types.h>
36 #include <sys/avl.h>
37 #include <sys/systm.h>
38 #include <sys/sysmacros.h>
39 #include <sys/ksynch.h>
40 #include <sys/kidmap.h>
41 #include <rpcsvc/idmap_prot.h>
42 #include "kidmap_priv.h"
43 
44 
45 /*
46  * External functions
47  */
48 extern	uintptr_t	space_fetch(char *key);
49 extern	int		space_store(char *key, uintptr_t ptr);
50 
51 
52 /*
53  * Internal definitions and functions
54  */
55 
56 #define	CACHE_UID_TRIGGER_SIZE	4096
57 #define	CACHE_GID_TRIGGER_SIZE	2048
58 #define	CACHE_PID_TRIGGER_SIZE \
59 	(CACHE_UID_TRIGGER_SIZE + CACHE_GID_TRIGGER_SIZE)
60 
61 
62 #define	UNDEF_UID	((uid_t)-1)
63 #define	UNDEF_GID	((gid_t)-1)
64 #define	UNDEF_ISUSER	(-1)
65 
66 #define	CACHE_PURGE_INTERVAL	(60 * 3)
67 #define	CACHE_TTL		(60 * 10)
68 
69 
70 
71 #define	list_insert(head, ele)\
72 	do {\
73 		(ele)->flink = (head)->flink;\
74 		(head)->flink = (ele);\
75 		(ele)->blink = (ele)->flink->blink;\
76 		(ele)->flink->blink = (ele);\
77 	} while (0)
78 
79 
80 
81 #define	list_remove(ele)\
82 	do {\
83 		(ele)->flink->blink = (ele)->blink;\
84 		(ele)->blink->flink = (ele)->flink;\
85 	} while (0)
86 
87 
88 #define	list_move(head, ele) \
89 	do {\
90 		if ((head)->flink != (ele)) {\
91 			list_remove(ele);\
92 			list_insert(head, ele);\
93 		}\
94 	} while (0)
95 
96 
97 typedef struct sid_prefix_node {
98 	avl_node_t	avl_link;
99 	const char 	*sid_prefix;
100 } sid_prefix_node_t;
101 
102 
103 struct sid_prefix_store {
104 	struct avl_tree	tree;
105 	krwlock_t	lock;
106 };
107 
108 struct sid_prefix_store *kidmap_sid_prefix_store = NULL;
109 
110 
111 
112 static void
113 kidmap_purge_sid2pid_cache(idmap_sid2pid_cache_t *cache, size_t limit);
114 
115 static void
116 kidmap_purge_pid2sid_cache(idmap_pid2sid_cache_t *cache, size_t limit);
117 
118 
119 /*
120  * kidmap_strdup() copied from uts/common/fs/sockfs/nl7c.c
121  */
122 static char *
123 kidmap_strdup(const char *s)
124 {
125 	int	len = strlen(s) + 1;
126 	char	*ret = kmem_alloc(len, KM_SLEEP);
127 
128 	bcopy(s, ret, len);
129 	return (ret);
130 }
131 
132 
133 static int
134 kidmap_compare_sid(const void *p1, const void *p2)
135 {
136 	const sid2pid_t *entry1 = p1;
137 	const sid2pid_t *entry2 = p2;
138 	int64_t comp = ((int64_t)entry2->rid) - ((int64_t)entry1->rid);
139 
140 	if (comp == 0)
141 		comp = strcmp(entry2->sid_prefix, entry1->sid_prefix);
142 
143 	if (comp < 0)
144 		comp = -1;
145 	else if (comp > 0)
146 		comp = 1;
147 
148 	return ((int)comp);
149 }
150 
151 
152 static int
153 kidmap_compare_pid(const void *p1, const void *p2)
154 {
155 	const pid2sid_t *entry1 = p1;
156 	const pid2sid_t *entry2 = p2;
157 
158 	if (entry2->pid > entry1->pid)
159 		return (1);
160 	if (entry2->pid < entry1->pid)
161 		return (-1);
162 	return (0);
163 }
164 
165 
166 static int
167 kidmap_compare_sid_prefix(const void *p1, const void *p2)
168 {
169 	const sid_prefix_node_t *entry1 = p1;
170 	const sid_prefix_node_t *entry2 = p2;
171 	int comp;
172 
173 	comp = strcmp(entry2->sid_prefix, entry1->sid_prefix);
174 
175 	if (comp < 0)
176 		comp = -1;
177 	else if (comp > 0)
178 		comp = 1;
179 
180 	return (comp);
181 }
182 
183 
184 void
185 kidmap_cache_create(idmap_cache_t *cache)
186 {
187 	int i;
188 
189 	/*
190 	 * Create SID-2-PID hash table
191 	 */
192 	for (i = 0; i < KIDMAP_HASH_SIZE; i++) {
193 		idmap_sid2pid_cache_t *sid2pid_hb = &cache->sid2pid_hash[i];
194 
195 		avl_create(&sid2pid_hb->tree, kidmap_compare_sid,
196 		    sizeof (sid2pid_t), offsetof(sid2pid_t, avl_link));
197 		mutex_init(&sid2pid_hb->mutex, NULL, MUTEX_DEFAULT, NULL);
198 		sid2pid_hb->purge_time = 0;
199 		sid2pid_hb->head.flink = &sid2pid_hb->head;
200 		sid2pid_hb->head.blink = &sid2pid_hb->head;
201 		sid2pid_hb->uid_num = 0;
202 		sid2pid_hb->gid_num = 0;
203 		sid2pid_hb->pid_num = 0;
204 	}
205 
206 	/*
207 	 * Create UID-2-SID hash table
208 	 */
209 	for (i = 0; i < KIDMAP_HASH_SIZE; i++) {
210 		idmap_pid2sid_cache_t *uid2sid_hb = &cache->uid2sid_hash[i];
211 
212 		avl_create(&uid2sid_hb->tree, kidmap_compare_pid,
213 		    sizeof (pid2sid_t), offsetof(pid2sid_t, avl_link));
214 		mutex_init(&uid2sid_hb->mutex, NULL, MUTEX_DEFAULT, NULL);
215 		uid2sid_hb->purge_time = 0;
216 		uid2sid_hb->head.flink = &uid2sid_hb->head;
217 		uid2sid_hb->head.blink = &uid2sid_hb->head;
218 	}
219 
220 	/*
221 	 * Create GID-2-SID hash table
222 	 */
223 	for (i = 0; i < KIDMAP_HASH_SIZE; i++) {
224 		idmap_pid2sid_cache_t *gid2sid_hb  = &cache->gid2sid_hash[i];
225 
226 		avl_create(&gid2sid_hb->tree, kidmap_compare_pid,
227 		    sizeof (pid2sid_t), offsetof(pid2sid_t, avl_link));
228 		mutex_init(&gid2sid_hb->mutex, NULL, MUTEX_DEFAULT, NULL);
229 		gid2sid_hb->purge_time = 0;
230 		gid2sid_hb->head.flink = &gid2sid_hb->head;
231 		gid2sid_hb->head.blink = &gid2sid_hb->head;
232 	}
233 }
234 
235 
236 void
237 kidmap_cache_delete(idmap_cache_t *cache)
238 {
239 	void *cookie;
240 	int i;
241 
242 	for (i = 0; i < KIDMAP_HASH_SIZE; i++) {
243 		idmap_sid2pid_cache_t *sid2pid_hb = &cache->sid2pid_hash[i];
244 		sid2pid_t *sid2pid;
245 
246 		cookie = NULL;
247 		while ((sid2pid = avl_destroy_nodes(&sid2pid_hb->tree,
248 		    &cookie)) != NULL) {
249 			kmem_free(sid2pid, sizeof (sid2pid_t));
250 		}
251 		avl_destroy(&sid2pid_hb->tree);
252 		mutex_destroy(&sid2pid_hb->mutex);
253 	}
254 
255 	for (i = 0; i < KIDMAP_HASH_SIZE; i++) {
256 		idmap_pid2sid_cache_t *uid2sid_hb = &cache->uid2sid_hash[i];
257 		pid2sid_t *uid2sid;
258 
259 		cookie = NULL;
260 		while ((uid2sid = avl_destroy_nodes(&uid2sid_hb->tree,
261 		    &cookie)) != NULL) {
262 			kmem_free(uid2sid, sizeof (pid2sid_t));
263 		}
264 		avl_destroy(&uid2sid_hb->tree);
265 		mutex_destroy(&uid2sid_hb->mutex);
266 	}
267 
268 	for (i = 0; i < KIDMAP_HASH_SIZE; i++) {
269 		idmap_pid2sid_cache_t *gid2sid_hb = &cache->gid2sid_hash[i];
270 		pid2sid_t *gid2sid;
271 
272 		cookie = NULL;
273 		while ((gid2sid = avl_destroy_nodes(&gid2sid_hb->tree,
274 		    &cookie)) != NULL) {
275 			kmem_free(gid2sid, sizeof (pid2sid_t));
276 		}
277 		avl_destroy(&gid2sid_hb->tree);
278 		mutex_destroy(&gid2sid_hb->mutex);
279 	}
280 }
281 
282 
283 /*
284  * Get counts of cache entries
285  */
286 void
287 kidmap_cache_get_data(idmap_cache_t *cache, size_t *uidbysid, size_t *gidbysid,
288 	size_t *pidbysid, size_t *sidbyuid, size_t *sidbygid)
289 {
290 	int i;
291 
292 	*uidbysid = 0;
293 	*gidbysid = 0;
294 	*pidbysid = 0;
295 	*sidbyuid = 0;
296 	*sidbygid = 0;
297 
298 
299 	for (i = 0; i < KIDMAP_HASH_SIZE; i++) {
300 		idmap_sid2pid_cache_t *sid2pid_hb = &cache->sid2pid_hash[i];
301 
302 		mutex_enter(&sid2pid_hb->mutex);
303 		*uidbysid += sid2pid_hb->uid_num;
304 		*gidbysid += sid2pid_hb->gid_num;
305 		*pidbysid += sid2pid_hb->pid_num;
306 		mutex_exit(&sid2pid_hb->mutex);
307 	}
308 
309 	for (i = 0; i < KIDMAP_HASH_SIZE; i++) {
310 		idmap_pid2sid_cache_t *uid2sid_hb = &cache->uid2sid_hash[i];
311 
312 		mutex_enter(&uid2sid_hb->mutex);
313 		*sidbyuid += avl_numnodes(&uid2sid_hb->tree);
314 		mutex_exit(&uid2sid_hb->mutex);
315 	}
316 
317 	for (i = 0; i < KIDMAP_HASH_SIZE; i++) {
318 		idmap_pid2sid_cache_t *gid2sid_hb = &cache->gid2sid_hash[i];
319 
320 		mutex_enter(&gid2sid_hb->mutex);
321 		*sidbygid += avl_numnodes(&gid2sid_hb->tree);
322 		mutex_exit(&gid2sid_hb->mutex);
323 	}
324 }
325 
326 
327 void
328 kidmap_cache_purge(idmap_cache_t *cache)
329 {
330 	void *cookie;
331 	int i;
332 
333 	for (i = 0; i < KIDMAP_HASH_SIZE; i++) {
334 		idmap_sid2pid_cache_t *sid2pid_hb = &cache->sid2pid_hash[i];
335 		sid2pid_t *sid2pid;
336 
337 		mutex_enter(&sid2pid_hb->mutex);
338 		cookie = NULL;
339 		while ((sid2pid = avl_destroy_nodes(&sid2pid_hb->tree,
340 		    &cookie)) != NULL) {
341 			kmem_free(sid2pid, sizeof (sid2pid_t));
342 		}
343 		avl_destroy(&sid2pid_hb->tree);
344 		avl_create(&sid2pid_hb->tree, kidmap_compare_sid,
345 		    sizeof (sid2pid_t), offsetof(sid2pid_t, avl_link));
346 		sid2pid_hb->purge_time = 0;
347 		sid2pid_hb->head.flink = &sid2pid_hb->head;
348 		sid2pid_hb->head.blink = &sid2pid_hb->head;
349 		sid2pid_hb->uid_num = 0;
350 		sid2pid_hb->gid_num = 0;
351 		sid2pid_hb->pid_num = 0;
352 		mutex_exit(&sid2pid_hb->mutex);
353 	}
354 
355 	for (i = 0; i < KIDMAP_HASH_SIZE; i++) {
356 		idmap_pid2sid_cache_t *uid2sid_hb = &cache->uid2sid_hash[i];
357 		pid2sid_t *uid2sid;
358 
359 		mutex_enter(&uid2sid_hb->mutex);
360 		cookie = NULL;
361 		while ((uid2sid = avl_destroy_nodes(&uid2sid_hb->tree,
362 		    &cookie)) != NULL) {
363 			kmem_free(uid2sid, sizeof (pid2sid_t));
364 		}
365 		avl_destroy(&uid2sid_hb->tree);
366 		avl_create(&uid2sid_hb->tree, kidmap_compare_pid,
367 		    sizeof (pid2sid_t), offsetof(pid2sid_t, avl_link));
368 		uid2sid_hb->purge_time = 0;
369 		uid2sid_hb->head.flink = &uid2sid_hb->head;
370 		uid2sid_hb->head.blink = &uid2sid_hb->head;
371 		mutex_exit(&uid2sid_hb->mutex);
372 	}
373 
374 	for (i = 0; i < KIDMAP_HASH_SIZE; i++) {
375 		idmap_pid2sid_cache_t *gid2sid_hb = &cache->gid2sid_hash[i];
376 		pid2sid_t *gid2sid;
377 
378 		mutex_enter(&gid2sid_hb->mutex);
379 		cookie = NULL;
380 		while ((gid2sid = avl_destroy_nodes(&gid2sid_hb->tree,
381 		    &cookie)) != NULL) {
382 			kmem_free(gid2sid, sizeof (pid2sid_t));
383 		}
384 		avl_destroy(&gid2sid_hb->tree);
385 		avl_create(&gid2sid_hb->tree, kidmap_compare_pid,
386 		    sizeof (pid2sid_t), offsetof(pid2sid_t, avl_link));
387 		gid2sid_hb->purge_time = 0;
388 		gid2sid_hb->head.flink = &gid2sid_hb->head;
389 		gid2sid_hb->head.blink = &gid2sid_hb->head;
390 		mutex_exit(&gid2sid_hb->mutex);
391 	}
392 }
393 
394 
395 int
396 kidmap_cache_lookup_uidbysid(idmap_cache_t *cache, const char *sid_prefix,
397 			uint32_t rid, uid_t *uid)
398 {
399 	sid2pid_t	entry;
400 	sid2pid_t	*result;
401 	avl_index_t	where;
402 	int		status = IDMAP_ERR_NOMAPPING;
403 	int		idx = (rid & KIDMAP_HASH_MASK);
404 	idmap_sid2pid_cache_t *sid2pid_hb = &cache->sid2pid_hash[idx];
405 	time_t		now = gethrestime_sec();
406 
407 	entry.sid_prefix = sid_prefix;
408 	entry.rid = rid;
409 
410 	mutex_enter(&sid2pid_hb->mutex);
411 
412 	result = avl_find(&sid2pid_hb->tree, &entry, &where);
413 	if (result != NULL) {
414 		list_move(&sid2pid_hb->head, result);
415 		if (result->uid != UNDEF_UID && result->uid_ttl > now) {
416 			*uid = result->uid;
417 			status = IDMAP_SUCCESS;
418 		}
419 	}
420 
421 	mutex_exit(&sid2pid_hb->mutex);
422 
423 	return (status);
424 }
425 
426 
427 int
428 kidmap_cache_lookup_gidbysid(idmap_cache_t *cache, const char *sid_prefix,
429 			uint32_t rid, gid_t *gid)
430 {
431 	sid2pid_t	entry;
432 	sid2pid_t	*result;
433 	avl_index_t	where;
434 	int		status = IDMAP_ERR_NOMAPPING;
435 	int		idx = (rid & KIDMAP_HASH_MASK);
436 	idmap_sid2pid_cache_t *sid2pid_hb = &cache->sid2pid_hash[idx];
437 	time_t		now = gethrestime_sec();
438 
439 	entry.sid_prefix = sid_prefix;
440 	entry.rid = rid;
441 
442 	mutex_enter(&sid2pid_hb->mutex);
443 
444 	result = avl_find(&sid2pid_hb->tree, &entry, &where);
445 	if (result != NULL) {
446 		list_move(&sid2pid_hb->head, result);
447 		if (result->gid != UNDEF_GID && result->gid_ttl > now) {
448 			*gid = result->gid;
449 			status = IDMAP_SUCCESS;
450 		}
451 	}
452 
453 	mutex_exit(&sid2pid_hb->mutex);
454 
455 	return (status);
456 }
457 
458 
459 int
460 kidmap_cache_lookup_pidbysid(idmap_cache_t *cache, const char *sid_prefix,
461 			uint32_t rid, uid_t *pid, int *is_user)
462 {
463 	sid2pid_t	entry;
464 	sid2pid_t	*result;
465 	avl_index_t	where;
466 	int		status = IDMAP_ERR_NOMAPPING;
467 	int		idx = (rid & KIDMAP_HASH_MASK);
468 	idmap_sid2pid_cache_t *sid2pid_hb = &cache->sid2pid_hash[idx];
469 	time_t		now = gethrestime_sec();
470 
471 	entry.sid_prefix = sid_prefix;
472 	entry.rid = rid;
473 
474 	mutex_enter(&sid2pid_hb->mutex);
475 
476 	result = avl_find(&sid2pid_hb->tree, &entry, &where);
477 	if (result != NULL) {
478 		list_move(&sid2pid_hb->head, result);
479 		if (result->is_user != UNDEF_ISUSER) {
480 			if (result->is_user && result->uid_ttl > now) {
481 				*pid = result->uid;
482 				*is_user = result->is_user;
483 				status = IDMAP_SUCCESS;
484 			} else if (!result->is_user && result->gid_ttl > now) {
485 				*pid = result->gid;
486 				*is_user = result->is_user;
487 				status = IDMAP_SUCCESS;
488 			}
489 		}
490 	}
491 
492 	mutex_exit(&sid2pid_hb->mutex);
493 
494 	return (status);
495 }
496 
497 
498 
499 int
500 kidmap_cache_lookup_sidbyuid(idmap_cache_t *cache, const char **sid_prefix,
501 			uint32_t *rid, uid_t uid)
502 {
503 	pid2sid_t	entry;
504 	pid2sid_t	*result;
505 	avl_index_t	where;
506 	int		status = IDMAP_ERR_NOMAPPING;
507 	int		idx = (uid & KIDMAP_HASH_MASK);
508 	idmap_pid2sid_cache_t *uid2sid_hb = &cache->uid2sid_hash[idx];
509 	time_t		now = gethrestime_sec();
510 
511 	entry.pid = uid;
512 
513 	mutex_enter(&uid2sid_hb->mutex);
514 
515 	result = avl_find(&uid2sid_hb->tree, &entry, &where);
516 	if (result != NULL) {
517 		list_move(&uid2sid_hb->head, result);
518 		if (result->ttl > now) {
519 			*sid_prefix = result->sid_prefix;
520 			*rid = result->rid;
521 			status = IDMAP_SUCCESS;
522 		}
523 	}
524 
525 	mutex_exit(&uid2sid_hb->mutex);
526 
527 	return (status);
528 }
529 
530 
531 int
532 kidmap_cache_lookup_sidbygid(idmap_cache_t *cache, const char **sid_prefix,
533 			uint32_t *rid, gid_t gid)
534 {
535 	pid2sid_t	entry;
536 	pid2sid_t	*result;
537 	avl_index_t	where;
538 	int		status = IDMAP_ERR_NOMAPPING;
539 	int		idx = (gid & KIDMAP_HASH_MASK);
540 	idmap_pid2sid_cache_t *gid2sid_hb = &cache->gid2sid_hash[idx];
541 	time_t		now = gethrestime_sec();
542 
543 	entry.pid = gid;
544 
545 	mutex_enter(&gid2sid_hb->mutex);
546 
547 	result = avl_find(&gid2sid_hb->tree, &entry, &where);
548 	if (result != NULL) {
549 		list_move(&gid2sid_hb->head, result);
550 		if (result->ttl > now) {
551 			*sid_prefix = result->sid_prefix;
552 			*rid = result->rid;
553 			status = IDMAP_SUCCESS;
554 		}
555 	}
556 
557 	mutex_exit(&gid2sid_hb->mutex);
558 
559 	return (status);
560 }
561 
562 
563 void
564 kidmap_cache_add_sid2uid(idmap_cache_t *cache, const char *sid_prefix,
565 			uint32_t rid, uid_t uid, int direction)
566 
567 {
568 	avl_index_t	where;
569 	time_t		ttl = CACHE_TTL + gethrestime_sec();
570 
571 
572 	if (direction == IDMAP_DIRECTION_BI ||
573 	    direction == IDMAP_DIRECTION_W2U) {
574 		sid2pid_t	find;
575 		sid2pid_t	*result;
576 		sid2pid_t	*new;
577 		idmap_sid2pid_cache_t *sid2pid_hb =
578 		    &cache->sid2pid_hash[rid & KIDMAP_HASH_MASK];
579 
580 		find.sid_prefix = sid_prefix;
581 		find.rid = rid;
582 
583 		mutex_enter(&sid2pid_hb->mutex);
584 
585 		result = avl_find(&sid2pid_hb->tree, &find, &where);
586 		if (result) {
587 			if (result->uid == UNDEF_UID)
588 				sid2pid_hb->uid_num++;
589 			result->uid = uid;
590 			result->uid_ttl = ttl;
591 		} else {
592 			new = kmem_alloc(sizeof (sid2pid_t), KM_SLEEP);
593 			new->sid_prefix = sid_prefix;
594 			new->rid = rid;
595 			new->uid = uid;
596 			new->uid_ttl = ttl;
597 			new->gid = UNDEF_GID;
598 			new->gid_ttl = 0;
599 			new->is_user = UNDEF_ISUSER; /* Unknown */
600 			sid2pid_hb->uid_num++;
601 
602 			list_insert(&sid2pid_hb->head, new);
603 			avl_insert(&sid2pid_hb->tree, new, where);
604 		}
605 
606 		if ((avl_numnodes(&sid2pid_hb->tree) >
607 		    CACHE_PID_TRIGGER_SIZE) &&
608 		    (sid2pid_hb->purge_time + CACHE_PURGE_INTERVAL <
609 		    gethrestime_sec()))
610 			kidmap_purge_sid2pid_cache(sid2pid_hb,
611 			    CACHE_PID_TRIGGER_SIZE);
612 
613 		mutex_exit(&sid2pid_hb->mutex);
614 	}
615 
616 	if (direction == IDMAP_DIRECTION_BI ||
617 	    direction == IDMAP_DIRECTION_U2W) {
618 		pid2sid_t	find;
619 		pid2sid_t	*result;
620 		pid2sid_t	*new;
621 		idmap_pid2sid_cache_t *uid2sid_hb =
622 		    &cache->uid2sid_hash[uid & KIDMAP_HASH_MASK];
623 
624 		find.pid = uid;
625 
626 		mutex_enter(&uid2sid_hb->mutex);
627 
628 		result = avl_find(&uid2sid_hb->tree, &find, &where);
629 		if (result) {
630 			result->sid_prefix = sid_prefix;
631 			result->rid = rid;
632 			result->ttl = ttl;
633 		} else {
634 			new = kmem_alloc(sizeof (pid2sid_t), KM_SLEEP);
635 			new->sid_prefix = sid_prefix;
636 			new->rid = rid;
637 			new->pid = uid;
638 			new->ttl = ttl;
639 
640 			list_insert(&uid2sid_hb->head, new);
641 			avl_insert(&uid2sid_hb->tree, new, where);
642 		}
643 
644 		if ((avl_numnodes(&uid2sid_hb->tree) >
645 		    CACHE_UID_TRIGGER_SIZE) &&
646 		    (uid2sid_hb->purge_time + CACHE_PURGE_INTERVAL <
647 		    gethrestime_sec()))
648 			kidmap_purge_pid2sid_cache(uid2sid_hb,
649 			    CACHE_UID_TRIGGER_SIZE);
650 
651 		mutex_exit(&uid2sid_hb->mutex);
652 	}
653 }
654 
655 
656 
657 void
658 kidmap_cache_add_sid2gid(idmap_cache_t *cache, const char *sid_prefix,
659 			uint32_t rid, gid_t gid, int direction)
660 {
661 	avl_index_t	where;
662 	time_t		ttl = CACHE_TTL + gethrestime_sec();
663 
664 
665 	if (direction == IDMAP_DIRECTION_BI ||
666 	    direction == IDMAP_DIRECTION_W2U) {
667 		sid2pid_t	find;
668 		sid2pid_t	*result;
669 		sid2pid_t	*new;
670 		idmap_sid2pid_cache_t *sid2pid_hb =
671 		    &cache->sid2pid_hash[rid & KIDMAP_HASH_MASK];
672 
673 		find.sid_prefix = sid_prefix;
674 		find.rid = rid;
675 
676 		mutex_enter(&sid2pid_hb->mutex);
677 
678 		result = avl_find(&sid2pid_hb->tree, &find, &where);
679 		if (result) {
680 			if (result->gid == UNDEF_GID)
681 				sid2pid_hb->gid_num++;
682 			result->gid = gid;
683 			result->gid_ttl = ttl;
684 		} else {
685 			new = kmem_alloc(sizeof (sid2pid_t), KM_SLEEP);
686 			new->sid_prefix = sid_prefix;
687 			new->rid = rid;
688 			new->uid = UNDEF_UID;
689 			new->uid_ttl = 0;
690 			new->gid = gid;
691 			new->gid_ttl = ttl;
692 			new->is_user = UNDEF_ISUSER; /* Unknown */
693 			sid2pid_hb->gid_num++;
694 
695 			list_insert(&sid2pid_hb->head, new);
696 			avl_insert(&sid2pid_hb->tree, new, where);
697 		}
698 
699 		if ((avl_numnodes(&sid2pid_hb->tree) >
700 		    CACHE_PID_TRIGGER_SIZE) &&
701 		    (sid2pid_hb->purge_time + CACHE_PURGE_INTERVAL <
702 		    gethrestime_sec()))
703 			kidmap_purge_sid2pid_cache(sid2pid_hb,
704 			    CACHE_PID_TRIGGER_SIZE);
705 
706 		mutex_exit(&sid2pid_hb->mutex);
707 	}
708 
709 	if (direction == IDMAP_DIRECTION_BI ||
710 	    direction == IDMAP_DIRECTION_U2W) {
711 		pid2sid_t	find;
712 		pid2sid_t	*result;
713 		pid2sid_t	*new;
714 		idmap_pid2sid_cache_t *gid2sid_hb =
715 		    &cache->gid2sid_hash[gid & KIDMAP_HASH_MASK];
716 
717 		find.pid = gid;
718 
719 		mutex_enter(&gid2sid_hb->mutex);
720 
721 		result = avl_find(&gid2sid_hb->tree, &find, &where);
722 		if (result) {
723 			result->sid_prefix = sid_prefix;
724 			result->rid = rid;
725 			result->ttl = ttl;
726 		} else {
727 			new = kmem_alloc(sizeof (pid2sid_t), KM_SLEEP);
728 			new->sid_prefix = sid_prefix;
729 			new->rid = rid;
730 			new->pid = gid;
731 			new->ttl = ttl;
732 
733 			list_insert(&gid2sid_hb->head, new);
734 			avl_insert(&gid2sid_hb->tree, new, where);
735 		}
736 
737 		if ((avl_numnodes(&gid2sid_hb->tree) >
738 		    CACHE_GID_TRIGGER_SIZE) &&
739 		    (gid2sid_hb->purge_time + CACHE_PURGE_INTERVAL <
740 		    gethrestime_sec()))
741 			kidmap_purge_pid2sid_cache(gid2sid_hb,
742 			    CACHE_GID_TRIGGER_SIZE);
743 
744 		mutex_exit(&gid2sid_hb->mutex);
745 	}
746 }
747 
748 
749 void
750 kidmap_cache_add_sid2pid(idmap_cache_t *cache, const char *sid_prefix,
751 			uint32_t rid, uid_t pid, int is_user, int direction)
752 {
753 	avl_index_t	where;
754 	time_t		ttl = CACHE_TTL + gethrestime_sec();
755 
756 
757 	if (direction == IDMAP_DIRECTION_BI ||
758 	    direction == IDMAP_DIRECTION_W2U) {
759 		sid2pid_t	find;
760 		sid2pid_t	*result;
761 		sid2pid_t	*new;
762 		idmap_sid2pid_cache_t *sid2pid_hb =
763 		    &cache->sid2pid_hash[rid & KIDMAP_HASH_MASK];
764 
765 		find.sid_prefix = sid_prefix;
766 		find.rid = rid;
767 
768 		mutex_enter(&sid2pid_hb->mutex);
769 
770 		result = avl_find(&sid2pid_hb->tree, &find, &where);
771 		if (result) {
772 			if (result->is_user == UNDEF_ISUSER)
773 				sid2pid_hb->pid_num++;
774 			result->is_user = is_user;
775 			if (is_user) {
776 				if (result->uid == UNDEF_UID)
777 					sid2pid_hb->uid_num++;
778 				result->uid = pid;
779 				result->uid_ttl = ttl;
780 			} else {
781 				if (result->gid == UNDEF_GID)
782 					sid2pid_hb->gid_num++;
783 				result->gid = pid;
784 				result->gid_ttl = ttl;
785 			}
786 		} else {
787 			new = kmem_alloc(sizeof (sid2pid_t), KM_SLEEP);
788 			new->sid_prefix = sid_prefix;
789 			new->rid = rid;
790 			new->is_user = is_user;
791 			if (is_user) {
792 				new->uid = pid;
793 				new->uid_ttl = ttl;
794 				new->gid = UNDEF_GID;
795 				new->gid_ttl = 0;
796 				sid2pid_hb->uid_num++;
797 			} else {
798 				new->uid = UNDEF_UID;
799 				new->uid_ttl = 0;
800 				new->gid = pid;
801 				new->gid_ttl = ttl;
802 				sid2pid_hb->gid_num++;
803 			}
804 			sid2pid_hb->pid_num++;
805 
806 			list_insert(&sid2pid_hb->head, new);
807 			avl_insert(&sid2pid_hb->tree, new, where);
808 		}
809 
810 		if ((avl_numnodes(&sid2pid_hb->tree) >
811 		    CACHE_PID_TRIGGER_SIZE) &&
812 		    (sid2pid_hb->purge_time + CACHE_PURGE_INTERVAL <
813 		    gethrestime_sec()))
814 			kidmap_purge_sid2pid_cache(sid2pid_hb,
815 			    CACHE_PID_TRIGGER_SIZE);
816 
817 		mutex_exit(&sid2pid_hb->mutex);
818 	}
819 
820 	if (direction == IDMAP_DIRECTION_BI ||
821 	    direction == IDMAP_DIRECTION_U2W) {
822 		pid2sid_t	find;
823 		pid2sid_t	*result;
824 		pid2sid_t	*new;
825 		int		idx = pid & KIDMAP_HASH_MASK;
826 
827 		find.pid = pid;
828 		if (is_user) {
829 			idmap_pid2sid_cache_t *uid2sid_hb =
830 				&cache->uid2sid_hash[idx];
831 
832 			mutex_enter(&uid2sid_hb->mutex);
833 
834 			result = avl_find(&uid2sid_hb->tree, &find, &where);
835 			if (result) {
836 				result->sid_prefix = sid_prefix;
837 				result->rid = rid;
838 				result->ttl = ttl;
839 			} else {
840 				new = kmem_alloc(sizeof (pid2sid_t), KM_SLEEP);
841 				new->sid_prefix = sid_prefix;
842 				new->rid = rid;
843 				new->pid = pid;
844 				new->ttl = ttl;
845 
846 				list_insert(&uid2sid_hb->head, new);
847 				avl_insert(&uid2sid_hb->tree, new, where);
848 			}
849 
850 			if ((avl_numnodes(&uid2sid_hb->tree) >
851 			    CACHE_UID_TRIGGER_SIZE) &&
852 			    (uid2sid_hb->purge_time +
853 			    CACHE_PURGE_INTERVAL <
854 			    gethrestime_sec()))
855 				kidmap_purge_pid2sid_cache(uid2sid_hb,
856 				    CACHE_UID_TRIGGER_SIZE);
857 
858 			mutex_exit(&uid2sid_hb->mutex);
859 		} else {
860 			idmap_pid2sid_cache_t *gid2sid_hb =
861 			    &cache->gid2sid_hash[idx];
862 
863 			mutex_enter(&gid2sid_hb->mutex);
864 
865 			result = avl_find(&gid2sid_hb->tree, &find, &where);
866 			if (result) {
867 				result->sid_prefix = sid_prefix;
868 				result->rid = rid;
869 				result->ttl = ttl;
870 			} else {
871 				new = kmem_alloc(sizeof (pid2sid_t), KM_SLEEP);
872 				new->sid_prefix = sid_prefix;
873 				new->rid = rid;
874 				new->pid = pid;
875 				new->ttl = ttl;
876 
877 				list_insert(&gid2sid_hb->head, new);
878 				avl_insert(&gid2sid_hb->tree, new, where);
879 			}
880 
881 			if ((avl_numnodes(&gid2sid_hb->tree) >
882 			    CACHE_GID_TRIGGER_SIZE) &&
883 			    (gid2sid_hb->purge_time +
884 			    CACHE_PURGE_INTERVAL < gethrestime_sec()))
885 				kidmap_purge_pid2sid_cache(gid2sid_hb,
886 				    CACHE_GID_TRIGGER_SIZE);
887 
888 			mutex_exit(&gid2sid_hb->mutex);
889 		}
890 	}
891 }
892 
893 
894 
895 
896 
897 static void
898 kidmap_purge_sid2pid_cache(idmap_sid2pid_cache_t *cache, size_t limit)
899 {
900 	time_t		now = gethrestime_sec();
901 	sid2pid_t	*item;
902 
903 	while (avl_numnodes(&cache->tree) > limit) {
904 		/* Remove least recently used */
905 		item = cache->head.blink;
906 		list_remove(item);
907 		avl_remove(&cache->tree, item);
908 		if (item->uid != UNDEF_UID)
909 			cache->uid_num--;
910 		if (item->gid != UNDEF_GID)
911 			cache->gid_num--;
912 		if (item->is_user != UNDEF_ISUSER)
913 			cache->pid_num--;
914 		kmem_free(item, sizeof (sid2pid_t));
915 	}
916 	cache->purge_time = now;
917 }
918 
919 
920 static void
921 kidmap_purge_pid2sid_cache(idmap_pid2sid_cache_t *cache, size_t limit)
922 {
923 	time_t		now = gethrestime_sec();
924 	pid2sid_t	*item;
925 
926 	while (avl_numnodes(&cache->tree) > limit) {
927 		/* Remove least recently used */
928 		item = cache->head.blink;
929 		list_remove(item);
930 		avl_remove(&cache->tree, item);
931 		kmem_free(item, sizeof (pid2sid_t));
932 	}
933 	cache->purge_time = now;
934 }
935 
936 
937 void
938 kidmap_sid_prefix_store_init(void)
939 {
940 	kidmap_sid_prefix_store = (struct sid_prefix_store *)
941 	    space_fetch("SUNW,idmap_sid_prefix");
942 	if (kidmap_sid_prefix_store == NULL) {
943 		kidmap_sid_prefix_store = kmem_alloc(
944 		    sizeof (struct sid_prefix_store), KM_SLEEP);
945 		rw_init(&kidmap_sid_prefix_store->lock, NULL, RW_DRIVER, NULL);
946 		avl_create(&kidmap_sid_prefix_store->tree,
947 		    kidmap_compare_sid_prefix,
948 		    sizeof (sid_prefix_node_t),
949 		    offsetof(sid_prefix_node_t, avl_link));
950 		(void) space_store("SUNW,idmap_sid_prefix",
951 		    (uintptr_t)kidmap_sid_prefix_store);
952 	} else {
953 		/*
954 		 * The AVL comparison function must be re-initialised on
955 		 * re-load because may not be loaded into the same
956 		 * address space.
957 		 */
958 		kidmap_sid_prefix_store->tree.avl_compar =
959 		    kidmap_compare_sid_prefix;
960 	}
961 }
962 
963 
964 const char *
965 kidmap_find_sid_prefix(const char *sid_prefix) {
966 	sid_prefix_node_t 	find;
967 	sid_prefix_node_t	*result;
968 	sid_prefix_node_t 	*new;
969 	avl_index_t		where;
970 
971 	if (sid_prefix == NULL || *sid_prefix == '\0')
972 		return (NULL);
973 
974 	find.sid_prefix = sid_prefix;
975 
976 	rw_enter(&kidmap_sid_prefix_store->lock, RW_READER);
977 
978 	result = avl_find(&kidmap_sid_prefix_store->tree, &find, &where);
979 
980 	if (result) {
981 		rw_exit(&kidmap_sid_prefix_store->lock);
982 		return (result->sid_prefix);
983 	}
984 
985 	if (rw_tryupgrade(&kidmap_sid_prefix_store->lock) == 0) {
986 		/*
987 		 * Could not upgrade lock so release lock
988 		 * and acquire the write lock
989 		 */
990 		rw_exit(&kidmap_sid_prefix_store->lock);
991 		rw_enter(&kidmap_sid_prefix_store->lock, RW_WRITER);
992 
993 		result = avl_find(&kidmap_sid_prefix_store->tree,
994 			&find, &where);
995 		if (result) {
996 			rw_exit(&kidmap_sid_prefix_store->lock);
997 			return (result->sid_prefix);
998 		}
999 	}
1000 
1001 	new = kmem_alloc(sizeof (sid_prefix_node_t), KM_SLEEP);
1002 	new->sid_prefix = kidmap_strdup(sid_prefix);
1003 	avl_insert(&kidmap_sid_prefix_store->tree, new, where);
1004 	rw_exit(&kidmap_sid_prefix_store->lock);
1005 
1006 	return (new->sid_prefix);
1007 }
1008