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.
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.
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]
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
30 #pragma ident "%Z%%M% %I% %E% SMI"
36 #include <sys/types.h>
37 #include <sys/param.h>
45 #include "automount.h"
47 static void build_dir_entry_list(struct autofs_rddir_cache
*rdcp
,
48 struct dir_entry
*list
);
49 static int autofs_rddir_cache_enter(char *map
, ulong_t bucket_size
,
50 struct autofs_rddir_cache
**rdcpp
);
51 int autofs_rddir_cache_lookup(char *map
, struct autofs_rddir_cache
**rdcpp
);
52 static int autofs_rddir_cache_delete(struct autofs_rddir_cache
*rdcp
);
53 static int create_dirents(struct autofs_rddir_cache
*rdcp
, ulong_t offset
,
54 autofs_rddirres
*res
);
55 struct dir_entry
*rddir_entry_lookup(char *name
, struct dir_entry
*list
);
56 static void free_offset_tbl(struct off_tbl
*head
);
57 static void free_dir_list(struct dir_entry
*head
);
59 #define OFFSET_BUCKET_SIZE 100
61 rwlock_t autofs_rddir_cache_lock
; /* readdir cache lock */
62 struct autofs_rddir_cache
*rddir_head
; /* readdir cache head */
65 do_readdir(autofs_rddirargs
*rda
, autofs_rddirres
*rd
)
67 struct dir_entry
*list
= NULL
, *l
;
68 struct autofs_rddir_cache
*rdcp
= NULL
;
70 int cache_time
= RDDIR_CACHE_TIME
;
72 if (automountd_nobrowse
) {
74 * Browsability was disabled return an empty list.
76 rd
->rd_status
= AUTOFS_OK
;
77 rd
->rd_rddir
.rddir_size
= 0;
78 rd
->rd_rddir
.rddir_eof
= 1;
79 rd
->rd_rddir
.rddir_entries
= NULL
;
84 rw_rdlock(&autofs_rddir_cache_lock
);
85 error
= autofs_rddir_cache_lookup(rda
->rda_map
, &rdcp
);
87 rw_unlock(&autofs_rddir_cache_lock
);
88 rw_wrlock(&autofs_rddir_cache_lock
);
89 error
= autofs_rddir_cache_lookup(rda
->rda_map
, &rdcp
);
93 "map %s not found, adding...\n", rda
->rda_map
);
95 * entry doesn't exist, add it.
97 error
= autofs_rddir_cache_enter(rda
->rda_map
,
98 OFFSET_BUCKET_SIZE
, &rdcp
);
101 rw_unlock(&autofs_rddir_cache_lock
);
106 assert(rdcp
!= NULL
);
107 assert(rdcp
->in_use
);
110 rw_wrlock(&rdcp
->rwlock
);
113 * cache entry hasn't been filled up, do it now.
115 char *stack
[STACKSIZ
];
119 * Initialize the stack of open files
122 stack_op(INIT
, NULL
, stack
, &stkptr
);
123 (void) getmapkeys(rda
->rda_map
, &list
, &error
,
124 &cache_time
, stack
, &stkptr
, rda
->uid
);
126 build_dir_entry_list(rdcp
, list
);
133 rw_rdlock(&rdcp
->rwlock
);
135 rd
->rd_bufsize
= rda
->rda_count
;
137 error
= create_dirents(rdcp
, rda
->rda_offset
, rd
);
140 free_offset_tbl(rdcp
->offtp
);
144 free_dir_list(rdcp
->entp
);
154 * print this list only once
156 for (l
= list
; l
!= NULL
; l
= l
->next
)
157 trace_prt(0, "%s\n", l
->name
);
162 rd
->rd_status
= AUTOFS_OK
;
165 * keep list of entries for up to
166 * 'cache_time' seconds
168 rdcp
->ttl
= time((time_t *)NULL
) + cache_time
;
171 * the underlying name service indicated not
175 free_offset_tbl(rdcp
->offtp
);
179 free_dir_list(rdcp
->entp
);
186 * return an empty list
188 rd
->rd_rddir
.rddir_size
= 0;
189 rd
->rd_rddir
.rddir_eof
= 1;
190 rd
->rd_rddir
.rddir_entries
= NULL
;
193 * Invalidate cache and set error
197 rd
->rd_status
= AUTOFS_NOENT
;
200 rd
->rd_status
= AUTOFS_NOMEM
;
203 rd
->rd_status
= AUTOFS_ECOMM
;
206 rw_unlock(&rdcp
->rwlock
);
208 mutex_lock(&rdcp
->lock
);
210 mutex_unlock(&rdcp
->lock
);
212 assert(rdcp
->in_use
>= 0);
217 #define roundtoint(x) (((x) + sizeof (int) - 1) & ~(sizeof (int) - 1))
218 #define DIRENT64_RECLEN(namelen) \
219 (((int)(((dirent64_t *)0)->d_name) + 1 + (namelen) + 7) & ~ 7)
223 struct autofs_rddir_cache
*rdcp
,
225 autofs_rddirres
*res
)
227 uint_t total_bytes_wanted
;
229 ushort_t this_reclen
;
232 struct dir_entry
*list
= NULL
, *l
, *nl
;
235 struct off_tbl
*offtp
, *next
= NULL
;
240 assert(RW_LOCK_HELD(&rdcp
->rwlock
));
241 for (offtp
= rdcp
->offtp
; offtp
!= NULL
; offtp
= next
) {
244 this_bucket
= (next
== NULL
);
246 this_bucket
= (offset
< next
->offset
);
249 * has to be in this bucket
251 assert(offset
>= offtp
->offset
);
256 * loop to look in next bucket
260 for (l
= list
; l
!= NULL
&& l
->offset
< offset
; l
= l
->next
)
265 * reached end of directory
272 trace_prt(1, "%s: offset searches (%d, %d)\n", rdcp
->map
, x
, y
);
274 total_bytes_wanted
= res
->rd_bufsize
;
275 bufsize
= total_bytes_wanted
+ sizeof (struct dirent64
);
276 outbuf
= malloc(bufsize
);
277 if (outbuf
== NULL
) {
278 syslog(LOG_ERR
, "memory allocation error\n");
282 memset(outbuf
, 0, bufsize
);
283 /* LINTED pointer alignment */
284 dp
= (struct dirent64
*)outbuf
;
288 namelen
= strlen(l
->name
);
289 this_reclen
= DIRENT64_RECLEN(namelen
);
290 if (outcount
+ this_reclen
> total_bytes_wanted
) {
293 dp
->d_ino
= (ino64_t
)l
->nodeid
;
296 * get the next elements offset
298 dp
->d_off
= (off64_t
)nl
->offset
;
301 * This is the last element
302 * make offset one plus the current.
304 dp
->d_off
= (off64_t
)l
->offset
+ 1;
306 (void) strcpy(dp
->d_name
, l
->name
);
307 dp
->d_reclen
= (ushort_t
)this_reclen
;
308 outcount
+= dp
->d_reclen
;
309 dp
= (struct dirent64
*)((int)dp
+ dp
->d_reclen
);
310 assert(outcount
<= total_bytes_wanted
);
314 res
->rd_rddir
.rddir_size
= (long)outcount
;
319 res
->rd_rddir
.rddir_eof
= (l
== NULL
);
320 /* LINTED pointer alignment */
321 res
->rd_rddir
.rddir_entries
= (struct dirent64
*)outbuf
;
325 * total_bytes_wanted is not large enough for one
328 res
->rd_rddir
.rddir_eof
= 0;
329 res
->rd_rddir
.rddir_entries
= NULL
;
336 res
->rd_rddir
.rddir_size
= 0L;
337 res
->rd_rddir
.rddir_eof
= TRUE
;
338 res
->rd_rddir
.rddir_entries
= NULL
;
344 * add new entry to cache for 'map'
347 autofs_rddir_cache_enter(
350 struct autofs_rddir_cache
**rdcpp
)
352 struct autofs_rddir_cache
*p
;
353 assert(RW_LOCK_HELD(&autofs_rddir_cache_lock
));
356 * Add to front of the list at this time
358 p
= (struct autofs_rddir_cache
*)malloc(sizeof (*p
));
361 "autofs_rddir_cache_enter: memory allocation failed\n");
364 memset((char *)p
, 0, sizeof (*p
));
366 p
->map
= malloc(strlen(map
) + 1);
367 if (p
->map
== NULL
) {
369 "autofs_rddir_cache_enter: memory allocation failed\n");
375 p
->bucket_size
= bucket_size
;
377 * no need to grab mutex lock since I haven't yet made the
378 * node visible to the list
381 (void) rwlock_init(&p
->rwlock
, USYNC_THREAD
, NULL
);
382 (void) mutex_init(&p
->lock
, USYNC_THREAD
, NULL
);
384 if (rddir_head
== NULL
)
387 p
->next
= rddir_head
;
396 * find 'map' in readdir cache
399 autofs_rddir_cache_lookup(char *map
, struct autofs_rddir_cache
**rdcpp
)
401 struct autofs_rddir_cache
*p
;
403 assert(RW_LOCK_HELD(&autofs_rddir_cache_lock
));
404 for (p
= rddir_head
; p
!= NULL
; p
= p
->next
) {
405 if (strcmp(p
->map
, map
) == 0) {
407 * found matching entry
410 mutex_lock(&p
->lock
);
412 mutex_unlock(&p
->lock
);
423 * free the offset table
426 free_offset_tbl(struct off_tbl
*head
)
428 struct off_tbl
*p
, *next
= NULL
;
430 for (p
= head
; p
!= NULL
; p
= next
) {
437 * free the directory entries
440 free_dir_list(struct dir_entry
*head
)
442 struct dir_entry
*p
, *next
= NULL
;
444 for (p
= head
; p
!= NULL
; p
= next
) {
453 autofs_rddir_cache_entry_free(struct autofs_rddir_cache
*p
)
455 assert(RW_LOCK_HELD(&autofs_rddir_cache_lock
));
460 free_offset_tbl(p
->offtp
);
462 free_dir_list(p
->entp
);
467 * Remove entry from the rddircache
468 * the caller must own the autofs_rddir_cache_lock.
471 autofs_rddir_cache_delete(struct autofs_rddir_cache
*rdcp
)
473 struct autofs_rddir_cache
*p
, *prev
;
475 assert(RW_LOCK_HELD(&autofs_rddir_cache_lock
));
477 * Search cache for entry
480 for (p
= rddir_head
; p
!= NULL
; p
= p
->next
) {
483 * entry found, remove from list if not in use
488 prev
->next
= p
->next
;
490 rddir_head
= p
->next
;
491 autofs_rddir_cache_entry_free(p
);
496 syslog(LOG_ERR
, "Couldn't find entry %x in cache\n", p
);
501 * Return entry that matches name, NULL otherwise.
502 * Assumes the readers lock for this list has been grabed.
505 rddir_entry_lookup(char *name
, struct dir_entry
*list
)
507 return (btree_lookup(list
, name
));
511 build_dir_entry_list(struct autofs_rddir_cache
*rdcp
, struct dir_entry
*list
)
514 ulong_t offset
= AUTOFS_DAEMONCOOKIE
, offset_list
= AUTOFS_DAEMONCOOKIE
;
515 struct off_tbl
*offtp
, *last
= NULL
;
518 assert(RW_LOCK_HELD(&rdcp
->rwlock
));
519 assert(rdcp
->entp
== NULL
);
521 for (p
= list
; p
!= NULL
; p
= p
->next
) {
524 if (offset
>= offset_list
) {
526 * add node to index table
528 offtp
= (struct off_tbl
*)
529 malloc(sizeof (struct off_tbl
));
531 offtp
->offset
= offset
;
534 offset_list
+= rdcp
->bucket_size
;
537 "WARNING: build_dir_entry_list: could not add offset to index table\n");
543 if (rdcp
->offtp
== NULL
)
550 inonum
+= 2; /* use even numbers in daemon */
555 mutex_t cleanup_lock
;
556 cond_t cleanup_start_cv
;
557 cond_t cleanup_done_cv
;
560 * cache cleanup thread starting point
566 struct autofs_rddir_cache
*p
, *next
= NULL
;
569 mutex_init(&cleanup_lock
, USYNC_THREAD
, NULL
);
570 cond_init(&cleanup_start_cv
, USYNC_THREAD
, NULL
);
571 cond_init(&cleanup_done_cv
, USYNC_THREAD
, NULL
);
573 mutex_lock(&cleanup_lock
);
575 reltime
.tv_sec
= RDDIR_CACHE_TIME
/2;
579 * delay RDDIR_CACHE_TIME seconds, or until some other thread
580 * requests that I cleanup the caches
582 if (error
= cond_reltimedwait(
583 &cleanup_start_cv
, &cleanup_lock
, &reltime
)) {
584 if (error
!= ETIME
) {
587 "cleanup thread wakeup (%d)\n", error
);
591 mutex_unlock(&cleanup_lock
);
594 * Perform the cache cleanup
596 rw_wrlock(&autofs_rddir_cache_lock
);
597 for (p
= rddir_head
; p
!= NULL
; p
= next
) {
601 * cache entry busy, skip it
605 "%s cache in use\n", p
->map
);
610 * Cache entry is not in use, and nobody can grab a
611 * new reference since I'm holding the
612 * autofs_rddir_cache_lock
616 * error will be zero if some thread signaled us asking
617 * that the caches be freed. In such case, free caches
618 * even if they're still valid and nobody is referencing
619 * them at this time. Otherwise, free caches only
620 * if their time to live (ttl) has expired.
622 if (error
== ETIME
&& (p
->ttl
> time((time_t *)NULL
))) {
624 * Scheduled cache cleanup, if cache is still
629 "%s cache still valid\n", p
->map
);
634 trace_prt(1, "%s freeing cache\n", p
->map
);
636 error
= autofs_rddir_cache_delete(p
);
639 rw_unlock(&autofs_rddir_cache_lock
);
642 * wakeup the thread/threads waiting for the
645 mutex_lock(&cleanup_lock
);
646 cond_broadcast(&cleanup_done_cv
);