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