xref: /titanic_50/usr/src/uts/common/idmap/idmap_cache.c (revision ab4a9beb2e4d596be0b3288c7d92919e27781b57)
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 2007 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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
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 "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_TRIGGER_SIZE	8192
57 #define	CACHE_PURGE_INTERVAL	(60 * 3)
58 
59 typedef struct sid_prefix_node {
60 	avl_node_t	avl_link;
61 	const char 	*sid_prefix;
62 } sid_prefix_node_t;
63 
64 
65 typedef struct entry {
66 	avl_node_t	avl_link;
67 	const char 	*sid_prefix;
68 	uint32_t	rid;
69 	uid_t		pid;
70 	int		is_user;
71 	time_t		ttl;
72 } entry_t;
73 
74 typedef int (*avl_comp_fn)(const void*, const void*);
75 
76 
77 struct sid_prefix_store {
78 	struct avl_tree	tree;
79 	krwlock_t	lock;
80 };
81 
82 struct sid_prefix_store *kidmap_sid_prefix_store = NULL;
83 
84 
85 
86 static void
87 kidmap_cache_purge_avl(idmap_avl_cache_t *cache);
88 
89 /*
90  * kidmap_strdup() copied from uts/common/fs/sockfs/nl7c.c
91  */
92 static char *
93 kidmap_strdup(const char *s)
94 {
95 	int	len = strlen(s) + 1;
96 	char	*ret = kmem_alloc(len, KM_SLEEP);
97 
98 	bcopy(s, ret, len);
99 	return (ret);
100 }
101 
102 
103 static int
104 kidmap_compare_sid(const entry_t *entry1, const entry_t *entry2)
105 {
106 	int comp = entry2->rid - entry1->rid;
107 
108 	if (comp == 0)
109 		comp = strcmp(entry2->sid_prefix, entry1->sid_prefix);
110 
111 	if (comp < 0)
112 		comp = -1;
113 	else if (comp > 0)
114 		comp = 1;
115 
116 	return (comp);
117 }
118 
119 
120 static int
121 kidmap_compare_pid(const entry_t *entry1, const entry_t *entry2)
122 {
123 	int comp = entry2->pid - entry1->pid;
124 
125 	if (comp == 0)
126 		comp = entry2->is_user - entry1->is_user;
127 
128 	if (comp < 0)
129 		comp = -1;
130 	else if (comp > 0)
131 		comp = 1;
132 
133 	return (comp);
134 }
135 
136 
137 static int
138 kidmap_compare_sid_prefix(const sid_prefix_node_t *entry1,
139 			const sid_prefix_node_t *entry2)
140 {
141 	int comp;
142 
143 	comp = strcmp(entry2->sid_prefix, entry1->sid_prefix);
144 
145 	if (comp < 0)
146 		comp = -1;
147 	else if (comp > 0)
148 		comp = 1;
149 
150 	return (comp);
151 }
152 
153 
154 void
155 kidmap_cache_create(idmap_cache_t *cache)
156 {
157 	typedef int (*comp)(const void*, const void*);
158 
159 	rw_init(&cache->sid.lock, NULL, RW_DRIVER, NULL);
160 	avl_create(&cache->sid.tree, (avl_comp_fn)kidmap_compare_sid,
161 	    sizeof (entry_t), offsetof(entry_t, avl_link));
162 	mutex_init(&cache->sid.mutex, NULL, MUTEX_DEFAULT, NULL);
163 	cache->sid.state = CACHE_CREATED;
164 	cache->sid.purge_time = 0;
165 
166 	rw_init(&cache->pid.lock, NULL, RW_DRIVER, NULL);
167 	avl_create(&cache->pid.tree, (avl_comp_fn)kidmap_compare_pid,
168 	    sizeof (entry_t), offsetof(entry_t, avl_link));
169 	mutex_init(&cache->pid.mutex, NULL, MUTEX_DEFAULT, NULL);
170 	cache->pid.state = CACHE_CREATED;
171 	cache->pid.purge_time = 0;
172 }
173 
174 
175 void
176 kidmap_cache_delete(idmap_cache_t *cache)
177 {
178 	entry_t *entry;
179 	void *cookie;
180 
181 	cookie = NULL;
182 	while ((entry = avl_destroy_nodes(&cache->pid.tree, &cookie))
183 	    != NULL) {
184 		kmem_free(entry, sizeof (entry_t));
185 	}
186 	avl_destroy(&cache->pid.tree);
187 	rw_destroy(&cache->pid.lock);
188 	mutex_destroy(&cache->pid.mutex);
189 
190 	cookie = NULL;
191 	while ((entry = avl_destroy_nodes(&cache->sid.tree, &cookie))
192 	    != NULL) {
193 		kmem_free(entry, sizeof (entry_t));
194 	}
195 	avl_destroy(&cache->sid.tree);
196 	rw_destroy(&cache->sid.lock);
197 	mutex_destroy(&cache->sid.mutex);
198 }
199 
200 
201 int
202 kidmap_cache_lookupbypid(idmap_cache_t *cache, const char **sid_prefix,
203 			uint32_t *rid, uid_t pid, int is_user)
204 
205 {
206 	entry_t		entry;
207 	entry_t		*result;
208 	avl_index_t	where;
209 	int		status;
210 	time_t		now = gethrestime_sec();
211 
212 	entry.pid = pid;
213 	entry.is_user = is_user;
214 
215 	rw_enter(&cache->pid.lock, RW_READER);
216 
217 	result = avl_find(&cache->pid.tree, &entry, &where);
218 
219 	if (result && result->ttl > now) {
220 		*sid_prefix = result->sid_prefix;
221 		*rid = result->rid;
222 		status = IDMAP_SUCCESS;
223 	} else
224 		status = IDMAP_ERR_NOMAPPING;
225 
226 	rw_exit(&cache->pid.lock);
227 
228 	return (status);
229 }
230 
231 
232 int
233 kidmap_cache_lookupbysid(idmap_cache_t *cache, const char *sid_prefix,
234 			uint32_t rid, uid_t *pid, int *is_user)
235 {
236 	entry_t		entry;
237 	entry_t		*result;
238 	avl_index_t	where;
239 	int		status;
240 	time_t		now = gethrestime_sec();
241 
242 	entry.sid_prefix = sid_prefix;
243 	entry.rid = rid;
244 
245 	rw_enter(&cache->sid.lock, RW_READER);
246 
247 	result = avl_find(&cache->sid.tree, &entry, &where);
248 
249 	if (result && result->ttl > now) {
250 		*pid = result->pid;
251 		*is_user = result->is_user;
252 		status = IDMAP_SUCCESS;
253 	} else
254 		status = IDMAP_ERR_NOMAPPING;
255 
256 	rw_exit(&cache->sid.lock);
257 
258 	return (status);
259 }
260 
261 
262 void
263 kidmap_cache_addbypid(idmap_cache_t *cache, const char *sid_prefix,
264 			uint32_t rid, uid_t pid, int is_user, time_t ttl)
265 {
266 	entry_t		find;
267 	entry_t		*result;
268 	entry_t		*new;
269 	avl_index_t	where;
270 	int		purge_required = FALSE;
271 
272 	find.pid = pid;
273 	find.is_user = is_user;
274 
275 	rw_enter(&cache->pid.lock, RW_WRITER);
276 	result = avl_find(&cache->pid.tree, &find, &where);
277 
278 	if (result) {
279 		result->sid_prefix = sid_prefix;
280 		result->rid = rid;
281 		result->ttl = ttl;
282 	} else {
283 		new = kmem_alloc(sizeof (entry_t), KM_SLEEP);
284 		new->pid = pid;
285 		new->is_user = is_user;
286 		new->sid_prefix = sid_prefix;
287 		new->rid = rid;
288 		new->ttl = ttl;
289 
290 		avl_insert(&cache->pid.tree, new, where);
291 		if ((avl_numnodes(&cache->pid.tree) > CACHE_TRIGGER_SIZE) &&
292 		    (cache->pid.purge_time + CACHE_PURGE_INTERVAL <
293 		    gethrestime_sec()))
294 			purge_required = TRUE;
295 	}
296 
297 	rw_exit(&cache->pid.lock);
298 
299 	if (purge_required)
300 		kidmap_cache_purge_avl(&cache->pid);
301 }
302 
303 
304 void
305 kidmap_cache_addbysid(idmap_cache_t *cache, const char *sid_prefix,
306 			uint32_t rid, uid_t pid, int is_user, time_t ttl)
307 
308 {
309 	entry_t find;
310 	entry_t *result;
311 	entry_t *new;
312 	avl_index_t where;
313 	int purge_required = FALSE;
314 
315 	find.sid_prefix = sid_prefix;
316 	find.rid = rid;
317 
318 	rw_enter(&cache->sid.lock, RW_WRITER);
319 	result = avl_find(&cache->sid.tree, &find, &where);
320 
321 	if (result) {
322 		result->pid = pid;
323 		result->is_user = is_user;
324 		result->ttl = ttl;
325 	} else {
326 		new = kmem_alloc(sizeof (entry_t), KM_SLEEP);
327 		new->pid = pid;
328 		new->is_user = is_user;
329 		new->sid_prefix = sid_prefix;
330 		new->rid = rid;
331 		new->ttl = ttl;
332 
333 		avl_insert(&cache->sid.tree, new, where);
334 
335 		if ((avl_numnodes(&cache->sid.tree) > CACHE_TRIGGER_SIZE) &&
336 		    (cache->sid.purge_time + CACHE_PURGE_INTERVAL <
337 		    gethrestime_sec()))
338 			purge_required = TRUE;
339 	}
340 
341 	rw_exit(&cache->sid.lock);
342 
343 	if (purge_required)
344 		kidmap_cache_purge_avl(&cache->sid);
345 }
346 
347 
348 static void
349 kidmap_cache_purge_avl(idmap_avl_cache_t *cache)
350 {
351 	time_t		now = gethrestime_sec();
352 	entry_t		*curr;
353 	entry_t		*prev = NULL;
354 
355 	mutex_enter(&cache->mutex);
356 	if (cache->state != CACHE_CREATED) {
357 			mutex_exit(&cache->mutex);
358 			return;
359 	}
360 	cache->state = CACHE_PURGING;
361 	mutex_exit(&cache->mutex);
362 
363 	rw_enter(&cache->lock, RW_READER);
364 	curr = avl_first(&cache->tree);
365 	while (curr != NULL) {
366 		if (curr->ttl < now) {
367 			/* Old entry to remove - we need a write lock */
368 			if (rw_tryupgrade(&cache->lock) == 0) {
369 				/*
370 				 * Could not upgrade lock so release lock
371 				 * and aquire the write lock. It is valid to
372 				 * release abd re-aquire the lock as there
373 				 * can only be one purge routine running on an
374 				 * avl tree and no other routine removes
375 				 * entries.
376 				 */
377 				rw_exit(&cache->lock);
378 				rw_enter(&cache->lock, RW_WRITER);
379 			}
380 			/* Old entry to remove */
381 			avl_remove(&cache->tree, curr);
382 			rw_downgrade(&cache->lock);
383 
384 			curr = prev;
385 			if (curr == NULL) {
386 				/* We removed the first entery */
387 				curr = avl_first(&cache->tree);
388 				continue;
389 			}
390 		}
391 		prev = curr;
392 		curr = AVL_NEXT(&cache->tree, curr);
393 	}
394 	rw_exit(&cache->lock);
395 
396 	mutex_enter(&cache->mutex);
397 	cache->state = CACHE_CREATED;
398 	cache->purge_time = now;
399 	mutex_exit(&cache->mutex);
400 }
401 
402 void
403 kidmap_sid_prefix_store_init(void)
404 {
405 	kidmap_sid_prefix_store = (struct sid_prefix_store *)
406 	    space_fetch("SUNW,idmap_sid_prefix");
407 	if (kidmap_sid_prefix_store == NULL) {
408 		kidmap_sid_prefix_store = kmem_alloc(
409 		    sizeof (struct sid_prefix_store), KM_SLEEP);
410 		rw_init(&kidmap_sid_prefix_store->lock, NULL, RW_DRIVER, NULL);
411 		avl_create(&kidmap_sid_prefix_store->tree,
412 		    (avl_comp_fn)kidmap_compare_sid_prefix,
413 		    sizeof (sid_prefix_node_t),
414 		    offsetof(sid_prefix_node_t, avl_link));
415 		(void) space_store("SUNW,idmap_sid_prefix",
416 		    (uintptr_t)kidmap_sid_prefix_store);
417 	} else {
418 		/*
419 		 * The AVL comparison function must be re-initialised on
420 		 * re-load because may not be loaded into the same
421 		 * address space.
422 		 */
423 		kidmap_sid_prefix_store->tree.avl_compar =
424 		    (avl_comp_fn)kidmap_compare_sid_prefix;
425 	}
426 }
427 
428 
429 const char *
430 kidmap_find_sid_prefix(const char *sid_prefix) {
431 	sid_prefix_node_t 	find;
432 	sid_prefix_node_t	*result;
433 	sid_prefix_node_t 	*new;
434 	avl_index_t		where;
435 
436 	if (sid_prefix == NULL || *sid_prefix == '\0')
437 		return (NULL);
438 
439 	find.sid_prefix = sid_prefix;
440 
441 
442 	rw_enter(&kidmap_sid_prefix_store->lock, RW_READER);
443 
444 	result = avl_find(&kidmap_sid_prefix_store->tree, &find, &where);
445 
446 	if (result) {
447 		rw_exit(&kidmap_sid_prefix_store->lock);
448 		return (result->sid_prefix);
449 	}
450 
451 	if (rw_tryupgrade(&kidmap_sid_prefix_store->lock) == 0) {
452 		/*
453 		 * Could not upgrade lock so release lock
454 		 * and aquire the write lock
455 		 */
456 		rw_exit(&kidmap_sid_prefix_store->lock);
457 		rw_enter(&kidmap_sid_prefix_store->lock, RW_WRITER);
458 
459 		result = avl_find(&kidmap_sid_prefix_store->tree,
460 			&find, &where);
461 		if (result) {
462 			rw_exit(&kidmap_sid_prefix_store->lock);
463 			return (result->sid_prefix);
464 		}
465 	}
466 
467 	new = kmem_alloc(sizeof (sid_prefix_node_t), KM_SLEEP);
468 	new->sid_prefix = kidmap_strdup(sid_prefix);
469 	avl_insert(&kidmap_sid_prefix_store->tree, new, where);
470 	rw_exit(&kidmap_sid_prefix_store->lock);
471 
472 	return (new->sid_prefix);
473 }
474