8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / fs.d / autofs / autod_readdir.c
blobdb774ea789f21943d2cdf193b4289b553fed5a19
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * autod_readdir.c
30 #pragma ident "%Z%%M% %I% %E% SMI"
32 #include <stdio.h>
33 #include <ctype.h>
34 #include <string.h>
35 #include <syslog.h>
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <errno.h>
39 #include <pwd.h>
40 #include <locale.h>
41 #include <stdlib.h>
42 #include <unistd.h>
43 #include <assert.h>
44 #include <fcntl.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 */
64 int
65 do_readdir(autofs_rddirargs *rda, autofs_rddirres *rd)
67 struct dir_entry *list = NULL, *l;
68 struct autofs_rddir_cache *rdcp = NULL;
69 int error;
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;
81 return (0);
84 rw_rdlock(&autofs_rddir_cache_lock);
85 error = autofs_rddir_cache_lookup(rda->rda_map, &rdcp);
86 if (error) {
87 rw_unlock(&autofs_rddir_cache_lock);
88 rw_wrlock(&autofs_rddir_cache_lock);
89 error = autofs_rddir_cache_lookup(rda->rda_map, &rdcp);
90 if (error) {
91 if (trace > 2)
92 trace_prt(1,
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);
103 if (error)
104 return (error);
106 assert(rdcp != NULL);
107 assert(rdcp->in_use);
109 if (!rdcp->full) {
110 rw_wrlock(&rdcp->rwlock);
111 if (!rdcp->full) {
113 * cache entry hasn't been filled up, do it now.
115 char *stack[STACKSIZ];
116 char **stkptr;
119 * Initialize the stack of open files
120 * for this thread
122 stack_op(INIT, NULL, stack, &stkptr);
123 (void) getmapkeys(rda->rda_map, &list, &error,
124 &cache_time, stack, &stkptr, rda->uid);
125 if (!error)
126 build_dir_entry_list(rdcp, list);
127 else if (list) {
128 free_dir_list(list);
129 list = NULL;
132 } else
133 rw_rdlock(&rdcp->rwlock);
135 rd->rd_bufsize = rda->rda_count;
136 if (!error) {
137 error = create_dirents(rdcp, rda->rda_offset, rd);
138 if (error) {
139 if (rdcp->offtp) {
140 free_offset_tbl(rdcp->offtp);
141 rdcp->offtp = NULL;
143 if (rdcp->entp) {
144 free_dir_list(rdcp->entp);
145 rdcp->entp = NULL;
147 rdcp->full = 0;
148 list = NULL;
152 if (trace > 2) {
154 * print this list only once
156 for (l = list; l != NULL; l = l->next)
157 trace_prt(0, "%s\n", l->name);
158 trace_prt(0, "\n");
161 if (!error) {
162 rd->rd_status = AUTOFS_OK;
163 if (cache_time) {
165 * keep list of entries for up to
166 * 'cache_time' seconds
168 rdcp->ttl = time((time_t *)NULL) + cache_time;
169 } else {
171 * the underlying name service indicated not
172 * to cache contents.
174 if (rdcp->offtp) {
175 free_offset_tbl(rdcp->offtp);
176 rdcp->offtp = NULL;
178 if (rdcp->entp) {
179 free_dir_list(rdcp->entp);
180 rdcp->entp = NULL;
182 rdcp->full = 0;
184 } else {
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
195 switch (error) {
196 case ENOENT:
197 rd->rd_status = AUTOFS_NOENT;
198 break;
199 case ENOMEM:
200 rd->rd_status = AUTOFS_NOMEM;
201 break;
202 default:
203 rd->rd_status = AUTOFS_ECOMM;
206 rw_unlock(&rdcp->rwlock);
208 mutex_lock(&rdcp->lock);
209 rdcp->in_use--;
210 mutex_unlock(&rdcp->lock);
212 assert(rdcp->in_use >= 0);
214 return (error);
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)
221 static int
222 create_dirents(
223 struct autofs_rddir_cache *rdcp,
224 ulong_t offset,
225 autofs_rddirres *res)
227 uint_t total_bytes_wanted;
228 int bufsize;
229 ushort_t this_reclen;
230 int outcount = 0;
231 int namelen;
232 struct dir_entry *list = NULL, *l, *nl;
233 struct dirent64 *dp;
234 char *outbuf;
235 struct off_tbl *offtp, *next = NULL;
236 int this_bucket = 0;
237 int error = 0;
238 int x = 0, y = 0;
240 assert(RW_LOCK_HELD(&rdcp->rwlock));
241 for (offtp = rdcp->offtp; offtp != NULL; offtp = next) {
242 x++;
243 next = offtp->next;
244 this_bucket = (next == NULL);
245 if (!this_bucket)
246 this_bucket = (offset < next->offset);
247 if (this_bucket) {
249 * has to be in this bucket
251 assert(offset >= offtp->offset);
252 list = offtp->first;
253 break;
256 * loop to look in next bucket
260 for (l = list; l != NULL && l->offset < offset; l = l->next)
261 y++;
263 if (l == NULL) {
265 * reached end of directory
267 error = 0;
268 goto empty;
271 if (trace > 2)
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");
279 error = ENOMEM;
280 goto empty;
282 memset(outbuf, 0, bufsize);
283 /* LINTED pointer alignment */
284 dp = (struct dirent64 *)outbuf;
286 while (l) {
287 nl = l->next;
288 namelen = strlen(l->name);
289 this_reclen = DIRENT64_RECLEN(namelen);
290 if (outcount + this_reclen > total_bytes_wanted) {
291 break;
293 dp->d_ino = (ino64_t)l->nodeid;
294 if (nl) {
296 * get the next elements offset
298 dp->d_off = (off64_t)nl->offset;
299 } else {
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);
311 l = l->next;
314 res->rd_rddir.rddir_size = (long)outcount;
315 if (outcount > 0) {
317 * have some entries
319 res->rd_rddir.rddir_eof = (l == NULL);
320 /* LINTED pointer alignment */
321 res->rd_rddir.rddir_entries = (struct dirent64 *)outbuf;
322 error = 0;
323 } else {
325 * total_bytes_wanted is not large enough for one
326 * directory entry
328 res->rd_rddir.rddir_eof = 0;
329 res->rd_rddir.rddir_entries = NULL;
330 free(outbuf);
331 error = EIO;
333 return (error);
335 empty:
336 res->rd_rddir.rddir_size = 0L;
337 res->rd_rddir.rddir_eof = TRUE;
338 res->rd_rddir.rddir_entries = NULL;
339 return (error);
344 * add new entry to cache for 'map'
346 static int
347 autofs_rddir_cache_enter(
348 char *map,
349 ulong_t bucket_size,
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));
359 if (p == NULL) {
360 syslog(LOG_ERR,
361 "autofs_rddir_cache_enter: memory allocation failed\n");
362 return (ENOMEM);
364 memset((char *)p, 0, sizeof (*p));
366 p->map = malloc(strlen(map) + 1);
367 if (p->map == NULL) {
368 syslog(LOG_ERR,
369 "autofs_rddir_cache_enter: memory allocation failed\n");
370 free(p);
371 return (ENOMEM);
373 strcpy(p->map, map);
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
380 p->in_use = 1;
381 (void) rwlock_init(&p->rwlock, USYNC_THREAD, NULL);
382 (void) mutex_init(&p->lock, USYNC_THREAD, NULL);
384 if (rddir_head == NULL)
385 rddir_head = p;
386 else {
387 p->next = rddir_head;
388 rddir_head = p;
390 *rdcpp = p;
392 return (0);
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
409 *rdcpp = p;
410 mutex_lock(&p->lock);
411 p->in_use++;
412 mutex_unlock(&p->lock);
413 return (0);
417 * didn't find entry
419 return (ENOENT);
423 * free the offset table
425 static void
426 free_offset_tbl(struct off_tbl *head)
428 struct off_tbl *p, *next = NULL;
430 for (p = head; p != NULL; p = next) {
431 next = p->next;
432 free(p);
437 * free the directory entries
439 static void
440 free_dir_list(struct dir_entry *head)
442 struct dir_entry *p, *next = NULL;
444 for (p = head; p != NULL; p = next) {
445 next = p->next;
446 assert(p->name);
447 free(p->name);
448 free(p);
452 static void
453 autofs_rddir_cache_entry_free(struct autofs_rddir_cache *p)
455 assert(RW_LOCK_HELD(&autofs_rddir_cache_lock));
456 assert(!p->in_use);
457 if (p->map)
458 free(p->map);
459 if (p->offtp)
460 free_offset_tbl(p->offtp);
461 if (p->entp)
462 free_dir_list(p->entp);
463 free(p);
467 * Remove entry from the rddircache
468 * the caller must own the autofs_rddir_cache_lock.
470 static int
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
479 prev = NULL;
480 for (p = rddir_head; p != NULL; p = p->next) {
481 if (p == rdcp) {
483 * entry found, remove from list if not in use
485 if (p->in_use)
486 return (EBUSY);
487 if (prev)
488 prev->next = p->next;
489 else
490 rddir_head = p->next;
491 autofs_rddir_cache_entry_free(p);
492 return (0);
494 prev = p;
496 syslog(LOG_ERR, "Couldn't find entry %x in cache\n", p);
497 return (ENOENT);
501 * Return entry that matches name, NULL otherwise.
502 * Assumes the readers lock for this list has been grabed.
504 struct dir_entry *
505 rddir_entry_lookup(char *name, struct dir_entry *list)
507 return (btree_lookup(list, name));
510 static void
511 build_dir_entry_list(struct autofs_rddir_cache *rdcp, struct dir_entry *list)
513 struct dir_entry *p;
514 ulong_t offset = AUTOFS_DAEMONCOOKIE, offset_list = AUTOFS_DAEMONCOOKIE;
515 struct off_tbl *offtp, *last = NULL;
516 ino_t inonum = 4;
518 assert(RW_LOCK_HELD(&rdcp->rwlock));
519 assert(rdcp->entp == NULL);
520 rdcp->entp = list;
521 for (p = list; p != NULL; p = p->next) {
522 p->nodeid = inonum;
523 p->offset = offset;
524 if (offset >= offset_list) {
526 * add node to index table
528 offtp = (struct off_tbl *)
529 malloc(sizeof (struct off_tbl));
530 if (offtp != NULL) {
531 offtp->offset = offset;
532 offtp->first = p;
533 offtp->next = NULL;
534 offset_list += rdcp->bucket_size;
535 } else {
536 syslog(LOG_ERR,
537 "WARNING: build_dir_entry_list: could not add offset to index table\n");
538 continue;
541 * add to cache
543 if (rdcp->offtp == NULL)
544 rdcp->offtp = offtp;
545 else
546 last->next = offtp;
547 last = offtp;
549 offset++;
550 inonum += 2; /* use even numbers in daemon */
552 rdcp->full = 1;
555 mutex_t cleanup_lock;
556 cond_t cleanup_start_cv;
557 cond_t cleanup_done_cv;
560 * cache cleanup thread starting point
562 void
563 cache_cleanup(void)
565 timestruc_t reltime;
566 struct autofs_rddir_cache *p, *next = NULL;
567 int error;
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);
574 for (;;) {
575 reltime.tv_sec = RDDIR_CACHE_TIME/2;
576 reltime.tv_nsec = 0;
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) {
585 if (trace > 1)
586 trace_prt(1,
587 "cleanup thread wakeup (%d)\n", error);
588 continue;
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) {
598 next = p->next;
599 if (p->in_use > 0) {
601 * cache entry busy, skip it
603 if (trace > 1) {
604 trace_prt(1,
605 "%s cache in use\n", p->map);
607 continue;
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
625 * valid don't free.
627 if (trace > 1) {
628 trace_prt(1,
629 "%s cache still valid\n", p->map);
631 continue;
633 if (trace > 1)
634 trace_prt(1, "%s freeing cache\n", p->map);
635 assert(!p->in_use);
636 error = autofs_rddir_cache_delete(p);
637 assert(!error);
639 rw_unlock(&autofs_rddir_cache_lock);
642 * wakeup the thread/threads waiting for the
643 * cleanup to finish
645 mutex_lock(&cleanup_lock);
646 cond_broadcast(&cleanup_done_cv);
648 /* NOTREACHED */