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 */
46extern	uintptr_t	space_fetch(char *key);
47extern	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
95typedef struct sid_prefix_node {
96	avl_node_t	avl_link;
97	const char 	*sid_prefix;
98} sid_prefix_node_t;
99
100
101typedef int (*avl_comp_fn)(const void*, const void*);
102
103
104struct sid_prefix_store {
105	struct avl_tree	tree;
106	krwlock_t	lock;
107};
108
109struct sid_prefix_store *kidmap_sid_prefix_store = NULL;
110
111
112
113static void
114kidmap_purge_sid2pid_cache(idmap_sid2pid_cache_t *cache, size_t limit);
115
116static void
117kidmap_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 */
123static char *
124kidmap_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
134static int
135kidmap_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
151static int
152kidmap_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
162static int
163kidmap_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
179void
180kidmap_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
208void
209kidmap_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
243void
244kidmap_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
263void
264kidmap_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
319int
320kidmap_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
349int
350kidmap_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
379int
380kidmap_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
417int
418kidmap_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
447int
448kidmap_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
477void
478kidmap_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
567void
568kidmap_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
655void
656kidmap_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
794static void
795kidmap_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
817static void
818kidmap_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
834void
835kidmap_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
861const char *
862kidmap_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