xref: /titanic_51/usr/src/cmd/fs.d/autofs/autod_readdir.c (revision 3bfb48feb84bb78924286a801c68f80198912fa7)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
539d3e169Sevanl  * Common Development and Distribution License (the "License").
639d3e169Sevanl  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*3bfb48feSsemery  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*
277c478bd9Sstevel@tonic-gate  *	autod_readdir.c
287c478bd9Sstevel@tonic-gate  */
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate #include <stdio.h>
337c478bd9Sstevel@tonic-gate #include <ctype.h>
347c478bd9Sstevel@tonic-gate #include <string.h>
357c478bd9Sstevel@tonic-gate #include <syslog.h>
367c478bd9Sstevel@tonic-gate #include <sys/types.h>
377c478bd9Sstevel@tonic-gate #include <sys/param.h>
387c478bd9Sstevel@tonic-gate #include <errno.h>
397c478bd9Sstevel@tonic-gate #include <pwd.h>
407c478bd9Sstevel@tonic-gate #include <locale.h>
417c478bd9Sstevel@tonic-gate #include <stdlib.h>
427c478bd9Sstevel@tonic-gate #include <unistd.h>
437c478bd9Sstevel@tonic-gate #include <assert.h>
447c478bd9Sstevel@tonic-gate #include <fcntl.h>
457c478bd9Sstevel@tonic-gate #include "automount.h"
467c478bd9Sstevel@tonic-gate 
4739d3e169Sevanl static void build_dir_entry_list(struct autofs_rddir_cache *rdcp,
487c478bd9Sstevel@tonic-gate 				struct dir_entry *list);
4939d3e169Sevanl static int autofs_rddir_cache_enter(char *map, ulong_t bucket_size,
5039d3e169Sevanl 				struct autofs_rddir_cache **rdcpp);
5139d3e169Sevanl int autofs_rddir_cache_lookup(char *map, struct autofs_rddir_cache **rdcpp);
5239d3e169Sevanl static int autofs_rddir_cache_delete(struct autofs_rddir_cache *rdcp);
5339d3e169Sevanl static int create_dirents(struct autofs_rddir_cache *rdcp, ulong_t offset,
547c478bd9Sstevel@tonic-gate 				autofs_rddirres *res);
557c478bd9Sstevel@tonic-gate struct dir_entry *rddir_entry_lookup(char *name, struct dir_entry *list);
567c478bd9Sstevel@tonic-gate static void free_offset_tbl(struct off_tbl *head);
577c478bd9Sstevel@tonic-gate static void free_dir_list(struct dir_entry *head);
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate #define	OFFSET_BUCKET_SIZE	100
607c478bd9Sstevel@tonic-gate 
6139d3e169Sevanl rwlock_t autofs_rddir_cache_lock;		/* readdir cache lock */
6239d3e169Sevanl struct autofs_rddir_cache *rddir_head;		/* readdir cache head */
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate int
65*3bfb48feSsemery do_readdir(autofs_rddirargs *rda, autofs_rddirres *rd)
667c478bd9Sstevel@tonic-gate {
677c478bd9Sstevel@tonic-gate 	struct dir_entry *list = NULL, *l;
6839d3e169Sevanl 	struct autofs_rddir_cache *rdcp = NULL;
697c478bd9Sstevel@tonic-gate 	int error;
707c478bd9Sstevel@tonic-gate 	int cache_time = RDDIR_CACHE_TIME;
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate 	if (automountd_nobrowse) {
737c478bd9Sstevel@tonic-gate 		/*
747c478bd9Sstevel@tonic-gate 		 * Browsability was disabled return an empty list.
757c478bd9Sstevel@tonic-gate 		 */
767c478bd9Sstevel@tonic-gate 		rd->rd_status = AUTOFS_OK;
777c478bd9Sstevel@tonic-gate 		rd->rd_rddir.rddir_size = 0;
787c478bd9Sstevel@tonic-gate 		rd->rd_rddir.rddir_eof = 1;
797c478bd9Sstevel@tonic-gate 		rd->rd_rddir.rddir_entries = NULL;
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate 		return (0);
827c478bd9Sstevel@tonic-gate 	}
837c478bd9Sstevel@tonic-gate 
8439d3e169Sevanl 	rw_rdlock(&autofs_rddir_cache_lock);
8539d3e169Sevanl 	error = autofs_rddir_cache_lookup(rda->rda_map, &rdcp);
867c478bd9Sstevel@tonic-gate 	if (error) {
8739d3e169Sevanl 		rw_unlock(&autofs_rddir_cache_lock);
8839d3e169Sevanl 		rw_wrlock(&autofs_rddir_cache_lock);
8939d3e169Sevanl 		error = autofs_rddir_cache_lookup(rda->rda_map, &rdcp);
907c478bd9Sstevel@tonic-gate 		if (error) {
917c478bd9Sstevel@tonic-gate 			if (trace > 2)
927c478bd9Sstevel@tonic-gate 				trace_prt(1,
937c478bd9Sstevel@tonic-gate 				"map %s not found, adding...\n", rda->rda_map);
947c478bd9Sstevel@tonic-gate 			/*
957c478bd9Sstevel@tonic-gate 			 * entry doesn't exist, add it.
967c478bd9Sstevel@tonic-gate 			 */
9739d3e169Sevanl 			error = autofs_rddir_cache_enter(rda->rda_map,
987c478bd9Sstevel@tonic-gate 					OFFSET_BUCKET_SIZE, &rdcp);
997c478bd9Sstevel@tonic-gate 		}
1007c478bd9Sstevel@tonic-gate 	}
10139d3e169Sevanl 	rw_unlock(&autofs_rddir_cache_lock);
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate 	if (error)
1047c478bd9Sstevel@tonic-gate 		return (error);
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate 	assert(rdcp != NULL);
1077c478bd9Sstevel@tonic-gate 	assert(rdcp->in_use);
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate 	if (!rdcp->full) {
1107c478bd9Sstevel@tonic-gate 		rw_wrlock(&rdcp->rwlock);
1117c478bd9Sstevel@tonic-gate 		if (!rdcp->full) {
1127c478bd9Sstevel@tonic-gate 			/*
1137c478bd9Sstevel@tonic-gate 			 * cache entry hasn't been filled up, do it now.
1147c478bd9Sstevel@tonic-gate 			 */
1157c478bd9Sstevel@tonic-gate 			char *stack[STACKSIZ];
1167c478bd9Sstevel@tonic-gate 			char **stkptr;
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate 			/*
1197c478bd9Sstevel@tonic-gate 			 * Initialize the stack of open files
1207c478bd9Sstevel@tonic-gate 			 * for this thread
1217c478bd9Sstevel@tonic-gate 			 */
1227c478bd9Sstevel@tonic-gate 			stack_op(INIT, NULL, stack, &stkptr);
1237c478bd9Sstevel@tonic-gate 			(void) getmapkeys(rda->rda_map, &list, &error,
124*3bfb48feSsemery 			    &cache_time, stack, &stkptr, rda->uid);
1257c478bd9Sstevel@tonic-gate 			if (!error)
1267c478bd9Sstevel@tonic-gate 				build_dir_entry_list(rdcp, list);
1277c478bd9Sstevel@tonic-gate 			else if (list) {
1287c478bd9Sstevel@tonic-gate 				free_dir_list(list);
1297c478bd9Sstevel@tonic-gate 				list = NULL;
1307c478bd9Sstevel@tonic-gate 			}
1317c478bd9Sstevel@tonic-gate 		}
1327c478bd9Sstevel@tonic-gate 	} else
1337c478bd9Sstevel@tonic-gate 		rw_rdlock(&rdcp->rwlock);
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate 	rd->rd_bufsize = rda->rda_count;
1367c478bd9Sstevel@tonic-gate 	if (!error) {
1377c478bd9Sstevel@tonic-gate 		error = create_dirents(rdcp, rda->rda_offset, rd);
1387c478bd9Sstevel@tonic-gate 		if (error) {
1397c478bd9Sstevel@tonic-gate 			if (rdcp->offtp) {
1407c478bd9Sstevel@tonic-gate 				free_offset_tbl(rdcp->offtp);
1417c478bd9Sstevel@tonic-gate 				rdcp->offtp = NULL;
1427c478bd9Sstevel@tonic-gate 			}
1437c478bd9Sstevel@tonic-gate 			if (rdcp->entp) {
1447c478bd9Sstevel@tonic-gate 				free_dir_list(rdcp->entp);
1457c478bd9Sstevel@tonic-gate 				rdcp->entp = NULL;
1467c478bd9Sstevel@tonic-gate 			}
1477c478bd9Sstevel@tonic-gate 			rdcp->full = 0;
1487c478bd9Sstevel@tonic-gate 			list = NULL;
1497c478bd9Sstevel@tonic-gate 		}
1507c478bd9Sstevel@tonic-gate 	}
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate 	if (trace > 2) {
1537c478bd9Sstevel@tonic-gate 		/*
1547c478bd9Sstevel@tonic-gate 		 * print this list only once
1557c478bd9Sstevel@tonic-gate 		 */
1567c478bd9Sstevel@tonic-gate 		for (l = list; l != NULL; l = l->next)
1577c478bd9Sstevel@tonic-gate 			trace_prt(0, "%s\n", l->name);
1587c478bd9Sstevel@tonic-gate 		trace_prt(0, "\n");
1597c478bd9Sstevel@tonic-gate 	}
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate 	if (!error) {
1627c478bd9Sstevel@tonic-gate 		rd->rd_status = AUTOFS_OK;
1637c478bd9Sstevel@tonic-gate 		if (cache_time) {
1647c478bd9Sstevel@tonic-gate 			/*
1657c478bd9Sstevel@tonic-gate 			 * keep list of entries for up to
1667c478bd9Sstevel@tonic-gate 			 * 'cache_time' seconds
1677c478bd9Sstevel@tonic-gate 			 */
1687c478bd9Sstevel@tonic-gate 			rdcp->ttl = time((time_t *)NULL) + cache_time;
1697c478bd9Sstevel@tonic-gate 		} else {
1707c478bd9Sstevel@tonic-gate 			/*
1717c478bd9Sstevel@tonic-gate 			 * the underlying name service indicated not
1727c478bd9Sstevel@tonic-gate 			 * to cache contents.
1737c478bd9Sstevel@tonic-gate 			 */
1747c478bd9Sstevel@tonic-gate 			if (rdcp->offtp) {
1757c478bd9Sstevel@tonic-gate 				free_offset_tbl(rdcp->offtp);
1767c478bd9Sstevel@tonic-gate 				rdcp->offtp = NULL;
1777c478bd9Sstevel@tonic-gate 			}
1787c478bd9Sstevel@tonic-gate 			if (rdcp->entp) {
1797c478bd9Sstevel@tonic-gate 				free_dir_list(rdcp->entp);
1807c478bd9Sstevel@tonic-gate 				rdcp->entp = NULL;
1817c478bd9Sstevel@tonic-gate 			}
1827c478bd9Sstevel@tonic-gate 			rdcp->full = 0;
1837c478bd9Sstevel@tonic-gate 		}
1847c478bd9Sstevel@tonic-gate 	} else {
1857c478bd9Sstevel@tonic-gate 		/*
1867c478bd9Sstevel@tonic-gate 		 * return an empty list
1877c478bd9Sstevel@tonic-gate 		 */
1887c478bd9Sstevel@tonic-gate 		rd->rd_rddir.rddir_size = 0;
1897c478bd9Sstevel@tonic-gate 		rd->rd_rddir.rddir_eof = 1;
1907c478bd9Sstevel@tonic-gate 		rd->rd_rddir.rddir_entries = NULL;
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 		/*
1937c478bd9Sstevel@tonic-gate 		 * Invalidate cache and set error
1947c478bd9Sstevel@tonic-gate 		 */
1957c478bd9Sstevel@tonic-gate 		switch (error) {
1967c478bd9Sstevel@tonic-gate 		case ENOENT:
1977c478bd9Sstevel@tonic-gate 			rd->rd_status = AUTOFS_NOENT;
1987c478bd9Sstevel@tonic-gate 			break;
1997c478bd9Sstevel@tonic-gate 		case ENOMEM:
2007c478bd9Sstevel@tonic-gate 			rd->rd_status = AUTOFS_NOMEM;
2017c478bd9Sstevel@tonic-gate 			break;
2027c478bd9Sstevel@tonic-gate 		default:
2037c478bd9Sstevel@tonic-gate 			rd->rd_status = AUTOFS_ECOMM;
2047c478bd9Sstevel@tonic-gate 		}
2057c478bd9Sstevel@tonic-gate 	}
2067c478bd9Sstevel@tonic-gate 	rw_unlock(&rdcp->rwlock);
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate 	mutex_lock(&rdcp->lock);
2097c478bd9Sstevel@tonic-gate 	rdcp->in_use--;
2107c478bd9Sstevel@tonic-gate 	mutex_unlock(&rdcp->lock);
2117c478bd9Sstevel@tonic-gate 
2127c478bd9Sstevel@tonic-gate 	assert(rdcp->in_use >= 0);
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate 	return (error);
2157c478bd9Sstevel@tonic-gate }
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate #define	roundtoint(x)	(((x) + sizeof (int) - 1) & ~(sizeof (int) - 1))
2187c478bd9Sstevel@tonic-gate #define	DIRENT64_RECLEN(namelen)	\
2197c478bd9Sstevel@tonic-gate 	(((int)(((dirent64_t *)0)->d_name) + 1 + (namelen) + 7) & ~ 7)
2207c478bd9Sstevel@tonic-gate 
2217c478bd9Sstevel@tonic-gate static int
22239d3e169Sevanl create_dirents(
22339d3e169Sevanl 	struct autofs_rddir_cache *rdcp,
22439d3e169Sevanl 	ulong_t offset,
22539d3e169Sevanl 	autofs_rddirres *res)
2267c478bd9Sstevel@tonic-gate {
2277c478bd9Sstevel@tonic-gate 	uint_t total_bytes_wanted;
2287c478bd9Sstevel@tonic-gate 	int bufsize;
2297c478bd9Sstevel@tonic-gate 	ushort_t this_reclen;
2307c478bd9Sstevel@tonic-gate 	int outcount = 0;
2317c478bd9Sstevel@tonic-gate 	int namelen;
2327c478bd9Sstevel@tonic-gate 	struct dir_entry *list = NULL, *l, *nl;
2337c478bd9Sstevel@tonic-gate 	struct dirent64 *dp;
2347c478bd9Sstevel@tonic-gate 	char *outbuf;
2357c478bd9Sstevel@tonic-gate 	struct off_tbl *offtp, *next = NULL;
2367c478bd9Sstevel@tonic-gate 	int this_bucket = 0;
2377c478bd9Sstevel@tonic-gate 	int error = 0;
2387c478bd9Sstevel@tonic-gate 	int x = 0, y = 0;
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 	assert(RW_LOCK_HELD(&rdcp->rwlock));
2417c478bd9Sstevel@tonic-gate 	for (offtp = rdcp->offtp; offtp != NULL; offtp = next) {
2427c478bd9Sstevel@tonic-gate 		x++;
2437c478bd9Sstevel@tonic-gate 		next = offtp->next;
2447c478bd9Sstevel@tonic-gate 		this_bucket = (next == NULL);
2457c478bd9Sstevel@tonic-gate 		if (!this_bucket)
2467c478bd9Sstevel@tonic-gate 			this_bucket = (offset < next->offset);
2477c478bd9Sstevel@tonic-gate 		if (this_bucket) {
2487c478bd9Sstevel@tonic-gate 			/*
2497c478bd9Sstevel@tonic-gate 			 * has to be in this bucket
2507c478bd9Sstevel@tonic-gate 			 */
2517c478bd9Sstevel@tonic-gate 			assert(offset >= offtp->offset);
2527c478bd9Sstevel@tonic-gate 			list = offtp->first;
2537c478bd9Sstevel@tonic-gate 			break;
2547c478bd9Sstevel@tonic-gate 		}
2557c478bd9Sstevel@tonic-gate 		/*
2567c478bd9Sstevel@tonic-gate 		 * loop to look in next bucket
2577c478bd9Sstevel@tonic-gate 		 */
2587c478bd9Sstevel@tonic-gate 	}
2597c478bd9Sstevel@tonic-gate 
2607c478bd9Sstevel@tonic-gate 	for (l = list; l != NULL && l->offset < offset; l = l->next)
2617c478bd9Sstevel@tonic-gate 		y++;
2627c478bd9Sstevel@tonic-gate 
2637c478bd9Sstevel@tonic-gate 	if (l == NULL) {
2647c478bd9Sstevel@tonic-gate 		/*
2657c478bd9Sstevel@tonic-gate 		 * reached end of directory
2667c478bd9Sstevel@tonic-gate 		 */
2677c478bd9Sstevel@tonic-gate 		error = 0;
2687c478bd9Sstevel@tonic-gate 		goto empty;
2697c478bd9Sstevel@tonic-gate 	}
2707c478bd9Sstevel@tonic-gate 
2717c478bd9Sstevel@tonic-gate 	if (trace > 2)
2727c478bd9Sstevel@tonic-gate 		trace_prt(1, "%s: offset searches (%d, %d)\n", rdcp->map, x, y);
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate 	total_bytes_wanted = res->rd_bufsize;
2757c478bd9Sstevel@tonic-gate 	bufsize = total_bytes_wanted + sizeof (struct dirent64);
2767c478bd9Sstevel@tonic-gate 	outbuf = malloc(bufsize);
2777c478bd9Sstevel@tonic-gate 	if (outbuf == NULL) {
2787c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "memory allocation error\n");
2797c478bd9Sstevel@tonic-gate 		error = ENOMEM;
2807c478bd9Sstevel@tonic-gate 		goto empty;
2817c478bd9Sstevel@tonic-gate 	}
2827c478bd9Sstevel@tonic-gate 	memset(outbuf, 0, bufsize);
2837c478bd9Sstevel@tonic-gate 	/* LINTED pointer alignment */
2847c478bd9Sstevel@tonic-gate 	dp = (struct dirent64 *)outbuf;
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate 	while (l) {
2877c478bd9Sstevel@tonic-gate 		nl = l->next;
2887c478bd9Sstevel@tonic-gate 		namelen = strlen(l->name);
2897c478bd9Sstevel@tonic-gate 		this_reclen = DIRENT64_RECLEN(namelen);
2907c478bd9Sstevel@tonic-gate 		if (outcount + this_reclen > total_bytes_wanted) {
2917c478bd9Sstevel@tonic-gate 			break;
2927c478bd9Sstevel@tonic-gate 		}
2937c478bd9Sstevel@tonic-gate 		dp->d_ino = (ino64_t)l->nodeid;
2947c478bd9Sstevel@tonic-gate 		if (nl) {
2957c478bd9Sstevel@tonic-gate 			/*
2967c478bd9Sstevel@tonic-gate 			 * get the next elements offset
2977c478bd9Sstevel@tonic-gate 			 */
2987c478bd9Sstevel@tonic-gate 			dp->d_off = (off64_t)nl->offset;
2997c478bd9Sstevel@tonic-gate 		} else {
3007c478bd9Sstevel@tonic-gate 			/*
3017c478bd9Sstevel@tonic-gate 			 * This is the last element
3027c478bd9Sstevel@tonic-gate 			 * make offset one plus the current.
3037c478bd9Sstevel@tonic-gate 			 */
3047c478bd9Sstevel@tonic-gate 			dp->d_off = (off64_t)l->offset + 1;
3057c478bd9Sstevel@tonic-gate 		}
3067c478bd9Sstevel@tonic-gate 		(void) strcpy(dp->d_name, l->name);
3077c478bd9Sstevel@tonic-gate 		dp->d_reclen = (ushort_t)this_reclen;
3087c478bd9Sstevel@tonic-gate 		outcount += dp->d_reclen;
3097c478bd9Sstevel@tonic-gate 		dp = (struct dirent64 *)((int)dp + dp->d_reclen);
3107c478bd9Sstevel@tonic-gate 		assert(outcount <= total_bytes_wanted);
3117c478bd9Sstevel@tonic-gate 		l = l->next;
3127c478bd9Sstevel@tonic-gate 	}
3137c478bd9Sstevel@tonic-gate 
3147c478bd9Sstevel@tonic-gate 	res->rd_rddir.rddir_size = (long)outcount;
3157c478bd9Sstevel@tonic-gate 	if (outcount > 0) {
3167c478bd9Sstevel@tonic-gate 		/*
3177c478bd9Sstevel@tonic-gate 		 * have some entries
3187c478bd9Sstevel@tonic-gate 		 */
3197c478bd9Sstevel@tonic-gate 		res->rd_rddir.rddir_eof = (l == NULL);
3207c478bd9Sstevel@tonic-gate 		/* LINTED pointer alignment */
3217c478bd9Sstevel@tonic-gate 		res->rd_rddir.rddir_entries = (struct dirent64 *)outbuf;
3227c478bd9Sstevel@tonic-gate 		error = 0;
3237c478bd9Sstevel@tonic-gate 	} else {
3247c478bd9Sstevel@tonic-gate 		/*
3257c478bd9Sstevel@tonic-gate 		 * total_bytes_wanted is not large enough for one
3267c478bd9Sstevel@tonic-gate 		 * directory entry
3277c478bd9Sstevel@tonic-gate 		 */
3287c478bd9Sstevel@tonic-gate 		res->rd_rddir.rddir_eof = 0;
3297c478bd9Sstevel@tonic-gate 		res->rd_rddir.rddir_entries = NULL;
3307c478bd9Sstevel@tonic-gate 		free(outbuf);
3317c478bd9Sstevel@tonic-gate 		error = EIO;
3327c478bd9Sstevel@tonic-gate 	}
3337c478bd9Sstevel@tonic-gate 	return (error);
3347c478bd9Sstevel@tonic-gate 
33539d3e169Sevanl empty:
33639d3e169Sevanl 	res->rd_rddir.rddir_size = 0L;
3377c478bd9Sstevel@tonic-gate 	res->rd_rddir.rddir_eof = TRUE;
3387c478bd9Sstevel@tonic-gate 	res->rd_rddir.rddir_entries = NULL;
3397c478bd9Sstevel@tonic-gate 	return (error);
3407c478bd9Sstevel@tonic-gate }
3417c478bd9Sstevel@tonic-gate 
3427c478bd9Sstevel@tonic-gate 
3437c478bd9Sstevel@tonic-gate /*
3447c478bd9Sstevel@tonic-gate  * add new entry to cache for 'map'
3457c478bd9Sstevel@tonic-gate  */
3467c478bd9Sstevel@tonic-gate static int
34739d3e169Sevanl autofs_rddir_cache_enter(
34839d3e169Sevanl 	char *map,
34939d3e169Sevanl 	ulong_t bucket_size,
35039d3e169Sevanl 	struct autofs_rddir_cache **rdcpp)
3517c478bd9Sstevel@tonic-gate {
35239d3e169Sevanl 	struct autofs_rddir_cache *p;
35339d3e169Sevanl 	assert(RW_LOCK_HELD(&autofs_rddir_cache_lock));
3547c478bd9Sstevel@tonic-gate 
3557c478bd9Sstevel@tonic-gate 	/*
3567c478bd9Sstevel@tonic-gate 	 * Add to front of the list at this time
3577c478bd9Sstevel@tonic-gate 	 */
35839d3e169Sevanl 	p = (struct autofs_rddir_cache *)malloc(sizeof (*p));
3597c478bd9Sstevel@tonic-gate 	if (p == NULL) {
3607c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR,
36139d3e169Sevanl 			"autofs_rddir_cache_enter: memory allocation failed\n");
3627c478bd9Sstevel@tonic-gate 		return (ENOMEM);
3637c478bd9Sstevel@tonic-gate 	}
3647c478bd9Sstevel@tonic-gate 	memset((char *)p, 0, sizeof (*p));
3657c478bd9Sstevel@tonic-gate 
3667c478bd9Sstevel@tonic-gate 	p->map = malloc(strlen(map) + 1);
3677c478bd9Sstevel@tonic-gate 	if (p->map == NULL) {
3687c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR,
36939d3e169Sevanl 			"autofs_rddir_cache_enter: memory allocation failed\n");
3707c478bd9Sstevel@tonic-gate 		free(p);
3717c478bd9Sstevel@tonic-gate 		return (ENOMEM);
3727c478bd9Sstevel@tonic-gate 	}
3737c478bd9Sstevel@tonic-gate 	strcpy(p->map, map);
3747c478bd9Sstevel@tonic-gate 
3757c478bd9Sstevel@tonic-gate 	p->bucket_size = bucket_size;
3767c478bd9Sstevel@tonic-gate 	/*
3777c478bd9Sstevel@tonic-gate 	 * no need to grab mutex lock since I haven't yet made the
3787c478bd9Sstevel@tonic-gate 	 * node visible to the list
3797c478bd9Sstevel@tonic-gate 	 */
3807c478bd9Sstevel@tonic-gate 	p->in_use = 1;
3817c478bd9Sstevel@tonic-gate 	(void) rwlock_init(&p->rwlock, USYNC_THREAD, NULL);
3827c478bd9Sstevel@tonic-gate 	(void) mutex_init(&p->lock, USYNC_THREAD, NULL);
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate 	if (rddir_head == NULL)
3857c478bd9Sstevel@tonic-gate 		rddir_head = p;
3867c478bd9Sstevel@tonic-gate 	else {
3877c478bd9Sstevel@tonic-gate 		p->next = rddir_head;
3887c478bd9Sstevel@tonic-gate 		rddir_head = p;
3897c478bd9Sstevel@tonic-gate 	}
3907c478bd9Sstevel@tonic-gate 	*rdcpp = p;
3917c478bd9Sstevel@tonic-gate 
3927c478bd9Sstevel@tonic-gate 	return (0);
3937c478bd9Sstevel@tonic-gate }
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate /*
3967c478bd9Sstevel@tonic-gate  * find 'map' in readdir cache
3977c478bd9Sstevel@tonic-gate  */
3987c478bd9Sstevel@tonic-gate int
39939d3e169Sevanl autofs_rddir_cache_lookup(char *map, struct autofs_rddir_cache **rdcpp)
4007c478bd9Sstevel@tonic-gate {
40139d3e169Sevanl 	struct autofs_rddir_cache *p;
4027c478bd9Sstevel@tonic-gate 
40339d3e169Sevanl 	assert(RW_LOCK_HELD(&autofs_rddir_cache_lock));
4047c478bd9Sstevel@tonic-gate 	for (p = rddir_head; p != NULL; p = p->next) {
4057c478bd9Sstevel@tonic-gate 		if (strcmp(p->map, map) == 0) {
4067c478bd9Sstevel@tonic-gate 			/*
4077c478bd9Sstevel@tonic-gate 			 * found matching entry
4087c478bd9Sstevel@tonic-gate 			 */
4097c478bd9Sstevel@tonic-gate 			*rdcpp = p;
4107c478bd9Sstevel@tonic-gate 			mutex_lock(&p->lock);
4117c478bd9Sstevel@tonic-gate 			p->in_use++;
4127c478bd9Sstevel@tonic-gate 			mutex_unlock(&p->lock);
4137c478bd9Sstevel@tonic-gate 			return (0);
4147c478bd9Sstevel@tonic-gate 		}
4157c478bd9Sstevel@tonic-gate 	}
4167c478bd9Sstevel@tonic-gate 	/*
4177c478bd9Sstevel@tonic-gate 	 * didn't find entry
4187c478bd9Sstevel@tonic-gate 	 */
4197c478bd9Sstevel@tonic-gate 	return (ENOENT);
4207c478bd9Sstevel@tonic-gate }
4217c478bd9Sstevel@tonic-gate 
4227c478bd9Sstevel@tonic-gate /*
4237c478bd9Sstevel@tonic-gate  * free the offset table
4247c478bd9Sstevel@tonic-gate  */
4257c478bd9Sstevel@tonic-gate static void
4267c478bd9Sstevel@tonic-gate free_offset_tbl(struct off_tbl *head)
4277c478bd9Sstevel@tonic-gate {
4287c478bd9Sstevel@tonic-gate 	struct off_tbl *p, *next = NULL;
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate 	for (p = head; p != NULL; p = next) {
4317c478bd9Sstevel@tonic-gate 		next = p->next;
4327c478bd9Sstevel@tonic-gate 		free(p);
4337c478bd9Sstevel@tonic-gate 	}
4347c478bd9Sstevel@tonic-gate }
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate /*
4377c478bd9Sstevel@tonic-gate  * free the directory entries
4387c478bd9Sstevel@tonic-gate  */
4397c478bd9Sstevel@tonic-gate static void
4407c478bd9Sstevel@tonic-gate free_dir_list(struct dir_entry *head)
4417c478bd9Sstevel@tonic-gate {
4427c478bd9Sstevel@tonic-gate 	struct dir_entry *p, *next = NULL;
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate 	for (p = head; p != NULL; p = next) {
4457c478bd9Sstevel@tonic-gate 		next = p->next;
4467c478bd9Sstevel@tonic-gate 		assert(p->name);
4477c478bd9Sstevel@tonic-gate 		free(p->name);
4487c478bd9Sstevel@tonic-gate 		free(p);
4497c478bd9Sstevel@tonic-gate 	}
4507c478bd9Sstevel@tonic-gate }
4517c478bd9Sstevel@tonic-gate 
4527c478bd9Sstevel@tonic-gate static void
45339d3e169Sevanl autofs_rddir_cache_entry_free(struct autofs_rddir_cache *p)
4547c478bd9Sstevel@tonic-gate {
45539d3e169Sevanl 	assert(RW_LOCK_HELD(&autofs_rddir_cache_lock));
4567c478bd9Sstevel@tonic-gate 	assert(!p->in_use);
4577c478bd9Sstevel@tonic-gate 	if (p->map)
4587c478bd9Sstevel@tonic-gate 		free(p->map);
4597c478bd9Sstevel@tonic-gate 	if (p->offtp)
4607c478bd9Sstevel@tonic-gate 		free_offset_tbl(p->offtp);
4617c478bd9Sstevel@tonic-gate 	if (p->entp)
4627c478bd9Sstevel@tonic-gate 		free_dir_list(p->entp);
4637c478bd9Sstevel@tonic-gate 	free(p);
4647c478bd9Sstevel@tonic-gate }
4657c478bd9Sstevel@tonic-gate 
4667c478bd9Sstevel@tonic-gate /*
4677c478bd9Sstevel@tonic-gate  * Remove entry from the rddircache
46839d3e169Sevanl  * the caller must own the autofs_rddir_cache_lock.
4697c478bd9Sstevel@tonic-gate  */
4707c478bd9Sstevel@tonic-gate static int
47139d3e169Sevanl autofs_rddir_cache_delete(struct autofs_rddir_cache *rdcp)
4727c478bd9Sstevel@tonic-gate {
47339d3e169Sevanl 	struct autofs_rddir_cache *p, *prev;
4747c478bd9Sstevel@tonic-gate 
47539d3e169Sevanl 	assert(RW_LOCK_HELD(&autofs_rddir_cache_lock));
4767c478bd9Sstevel@tonic-gate 	/*
4777c478bd9Sstevel@tonic-gate 	 * Search cache for entry
4787c478bd9Sstevel@tonic-gate 	 */
4797c478bd9Sstevel@tonic-gate 	prev = NULL;
4807c478bd9Sstevel@tonic-gate 	for (p = rddir_head; p != NULL; p = p->next) {
4817c478bd9Sstevel@tonic-gate 		if (p == rdcp) {
4827c478bd9Sstevel@tonic-gate 			/*
4837c478bd9Sstevel@tonic-gate 			 * entry found, remove from list if not in use
4847c478bd9Sstevel@tonic-gate 			 */
4857c478bd9Sstevel@tonic-gate 			if (p->in_use)
4867c478bd9Sstevel@tonic-gate 				return (EBUSY);
4877c478bd9Sstevel@tonic-gate 			if (prev)
4887c478bd9Sstevel@tonic-gate 				prev->next = p->next;
4897c478bd9Sstevel@tonic-gate 			else
4907c478bd9Sstevel@tonic-gate 				rddir_head = p->next;
49139d3e169Sevanl 			autofs_rddir_cache_entry_free(p);
4927c478bd9Sstevel@tonic-gate 			return (0);
4937c478bd9Sstevel@tonic-gate 		}
4947c478bd9Sstevel@tonic-gate 		prev = p;
4957c478bd9Sstevel@tonic-gate 	}
4967c478bd9Sstevel@tonic-gate 	syslog(LOG_ERR, "Couldn't find entry %x in cache\n", p);
4977c478bd9Sstevel@tonic-gate 	return (ENOENT);
4987c478bd9Sstevel@tonic-gate }
4997c478bd9Sstevel@tonic-gate 
5007c478bd9Sstevel@tonic-gate /*
5017c478bd9Sstevel@tonic-gate  * Return entry that matches name, NULL otherwise.
5027c478bd9Sstevel@tonic-gate  * Assumes the readers lock for this list has been grabed.
5037c478bd9Sstevel@tonic-gate  */
5047c478bd9Sstevel@tonic-gate struct dir_entry *
5057c478bd9Sstevel@tonic-gate rddir_entry_lookup(char *name, struct dir_entry *list)
5067c478bd9Sstevel@tonic-gate {
5077c478bd9Sstevel@tonic-gate 	return (btree_lookup(list, name));
5087c478bd9Sstevel@tonic-gate }
5097c478bd9Sstevel@tonic-gate 
5107c478bd9Sstevel@tonic-gate static void
51139d3e169Sevanl build_dir_entry_list(struct autofs_rddir_cache *rdcp, struct dir_entry *list)
5127c478bd9Sstevel@tonic-gate {
5137c478bd9Sstevel@tonic-gate 	struct dir_entry *p;
5147c478bd9Sstevel@tonic-gate 	ulong_t offset = AUTOFS_DAEMONCOOKIE, offset_list = AUTOFS_DAEMONCOOKIE;
5157c478bd9Sstevel@tonic-gate 	struct off_tbl *offtp, *last = NULL;
5167c478bd9Sstevel@tonic-gate 	ino_t inonum = 4;
5177c478bd9Sstevel@tonic-gate 
5187c478bd9Sstevel@tonic-gate 	assert(RW_LOCK_HELD(&rdcp->rwlock));
5197c478bd9Sstevel@tonic-gate 	assert(rdcp->entp == NULL);
5207c478bd9Sstevel@tonic-gate 	rdcp->entp = list;
5217c478bd9Sstevel@tonic-gate 	for (p = list; p != NULL; p = p->next) {
5227c478bd9Sstevel@tonic-gate 		p->nodeid = inonum;
5237c478bd9Sstevel@tonic-gate 		p->offset = offset;
5247c478bd9Sstevel@tonic-gate 		if (offset >= offset_list) {
5257c478bd9Sstevel@tonic-gate 			/*
5267c478bd9Sstevel@tonic-gate 			 * add node to index table
5277c478bd9Sstevel@tonic-gate 			 */
5287c478bd9Sstevel@tonic-gate 			offtp = (struct off_tbl *)
5297c478bd9Sstevel@tonic-gate 				malloc(sizeof (struct off_tbl));
5307c478bd9Sstevel@tonic-gate 			if (offtp != NULL) {
5317c478bd9Sstevel@tonic-gate 				offtp->offset = offset;
5327c478bd9Sstevel@tonic-gate 				offtp->first = p;
5337c478bd9Sstevel@tonic-gate 				offtp->next = NULL;
5347c478bd9Sstevel@tonic-gate 				offset_list += rdcp->bucket_size;
5357c478bd9Sstevel@tonic-gate 			} else {
5367c478bd9Sstevel@tonic-gate 				syslog(LOG_ERR,
5377c478bd9Sstevel@tonic-gate "WARNING: build_dir_entry_list: could not add offset to index table\n");
5387c478bd9Sstevel@tonic-gate 				continue;
5397c478bd9Sstevel@tonic-gate 			}
5407c478bd9Sstevel@tonic-gate 			/*
5417c478bd9Sstevel@tonic-gate 			 * add to cache
5427c478bd9Sstevel@tonic-gate 			 */
5437c478bd9Sstevel@tonic-gate 			if (rdcp->offtp == NULL)
5447c478bd9Sstevel@tonic-gate 				rdcp->offtp = offtp;
5457c478bd9Sstevel@tonic-gate 			else
5467c478bd9Sstevel@tonic-gate 				last->next = offtp;
5477c478bd9Sstevel@tonic-gate 			last = offtp;
5487c478bd9Sstevel@tonic-gate 		}
5497c478bd9Sstevel@tonic-gate 		offset++;
5507c478bd9Sstevel@tonic-gate 		inonum += 2;		/* use even numbers in daemon */
5517c478bd9Sstevel@tonic-gate 	}
5527c478bd9Sstevel@tonic-gate 	rdcp->full = 1;
5537c478bd9Sstevel@tonic-gate }
5547c478bd9Sstevel@tonic-gate 
5557c478bd9Sstevel@tonic-gate mutex_t cleanup_lock;
5567c478bd9Sstevel@tonic-gate cond_t cleanup_start_cv;
5577c478bd9Sstevel@tonic-gate cond_t cleanup_done_cv;
5587c478bd9Sstevel@tonic-gate 
5597c478bd9Sstevel@tonic-gate /*
5607c478bd9Sstevel@tonic-gate  * cache cleanup thread starting point
5617c478bd9Sstevel@tonic-gate  */
5627c478bd9Sstevel@tonic-gate void
5637c478bd9Sstevel@tonic-gate cache_cleanup(void)
5647c478bd9Sstevel@tonic-gate {
5657c478bd9Sstevel@tonic-gate 	timestruc_t reltime;
56639d3e169Sevanl 	struct autofs_rddir_cache *p, *next = NULL;
5677c478bd9Sstevel@tonic-gate 	int error;
5687c478bd9Sstevel@tonic-gate 
5697c478bd9Sstevel@tonic-gate 	mutex_init(&cleanup_lock, USYNC_THREAD, NULL);
5707c478bd9Sstevel@tonic-gate 	cond_init(&cleanup_start_cv, USYNC_THREAD, NULL);
5717c478bd9Sstevel@tonic-gate 	cond_init(&cleanup_done_cv, USYNC_THREAD, NULL);
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate 	mutex_lock(&cleanup_lock);
5747c478bd9Sstevel@tonic-gate 	for (;;) {
5757c478bd9Sstevel@tonic-gate 		reltime.tv_sec = RDDIR_CACHE_TIME/2;
5767c478bd9Sstevel@tonic-gate 		reltime.tv_nsec = 0;
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 		/*
5797c478bd9Sstevel@tonic-gate 		 * delay RDDIR_CACHE_TIME seconds, or until some other thread
5807c478bd9Sstevel@tonic-gate 		 * requests that I cleanup the caches
5817c478bd9Sstevel@tonic-gate 		 */
5827c478bd9Sstevel@tonic-gate 		if (error = cond_reltimedwait(
5837c478bd9Sstevel@tonic-gate 		    &cleanup_start_cv, &cleanup_lock, &reltime)) {
5847c478bd9Sstevel@tonic-gate 			if (error != ETIME) {
5857c478bd9Sstevel@tonic-gate 				if (trace > 1)
5867c478bd9Sstevel@tonic-gate 					trace_prt(1,
5877c478bd9Sstevel@tonic-gate 					"cleanup thread wakeup (%d)\n", error);
5887c478bd9Sstevel@tonic-gate 				continue;
5897c478bd9Sstevel@tonic-gate 			}
5907c478bd9Sstevel@tonic-gate 		}
5917c478bd9Sstevel@tonic-gate 		mutex_unlock(&cleanup_lock);
5927c478bd9Sstevel@tonic-gate 
5937c478bd9Sstevel@tonic-gate 		/*
5947c478bd9Sstevel@tonic-gate 		 * Perform the cache cleanup
5957c478bd9Sstevel@tonic-gate 		 */
59639d3e169Sevanl 		rw_wrlock(&autofs_rddir_cache_lock);
5977c478bd9Sstevel@tonic-gate 		for (p = rddir_head; p != NULL; p = next) {
5987c478bd9Sstevel@tonic-gate 			next = p->next;
5997c478bd9Sstevel@tonic-gate 			if (p->in_use > 0) {
6007c478bd9Sstevel@tonic-gate 				/*
6017c478bd9Sstevel@tonic-gate 				 * cache entry busy, skip it
6027c478bd9Sstevel@tonic-gate 				 */
6037c478bd9Sstevel@tonic-gate 				if (trace > 1) {
6047c478bd9Sstevel@tonic-gate 					trace_prt(1,
6057c478bd9Sstevel@tonic-gate 					"%s cache in use\n", p->map);
6067c478bd9Sstevel@tonic-gate 				}
6077c478bd9Sstevel@tonic-gate 				continue;
6087c478bd9Sstevel@tonic-gate 			}
6097c478bd9Sstevel@tonic-gate 			/*
6107c478bd9Sstevel@tonic-gate 			 * Cache entry is not in use, and nobody can grab a
61139d3e169Sevanl 			 * new reference since I'm holding the
61239d3e169Sevanl 			 * autofs_rddir_cache_lock
6137c478bd9Sstevel@tonic-gate 			 */
6147c478bd9Sstevel@tonic-gate 
6157c478bd9Sstevel@tonic-gate 			/*
6167c478bd9Sstevel@tonic-gate 			 * error will be zero if some thread signaled us asking
6177c478bd9Sstevel@tonic-gate 			 * that the caches be freed. In such case, free caches
6187c478bd9Sstevel@tonic-gate 			 * even if they're still valid and nobody is referencing
6197c478bd9Sstevel@tonic-gate 			 * them at this time. Otherwise, free caches only
6207c478bd9Sstevel@tonic-gate 			 * if their time to live (ttl) has expired.
6217c478bd9Sstevel@tonic-gate 			 */
6227c478bd9Sstevel@tonic-gate 			if (error == ETIME && (p->ttl > time((time_t *)NULL))) {
6237c478bd9Sstevel@tonic-gate 				/*
6247c478bd9Sstevel@tonic-gate 				 * Scheduled cache cleanup, if cache is still
6257c478bd9Sstevel@tonic-gate 				 * valid don't free.
6267c478bd9Sstevel@tonic-gate 				 */
6277c478bd9Sstevel@tonic-gate 				if (trace > 1) {
6287c478bd9Sstevel@tonic-gate 					trace_prt(1,
6297c478bd9Sstevel@tonic-gate 					"%s cache still valid\n", p->map);
6307c478bd9Sstevel@tonic-gate 				}
6317c478bd9Sstevel@tonic-gate 				continue;
6327c478bd9Sstevel@tonic-gate 			}
6337c478bd9Sstevel@tonic-gate 			if (trace > 1)
6347c478bd9Sstevel@tonic-gate 				trace_prt(1, "%s freeing cache\n", p->map);
6357c478bd9Sstevel@tonic-gate 			assert(!p->in_use);
63639d3e169Sevanl 			error = autofs_rddir_cache_delete(p);
6377c478bd9Sstevel@tonic-gate 			assert(!error);
6387c478bd9Sstevel@tonic-gate 		}
63939d3e169Sevanl 		rw_unlock(&autofs_rddir_cache_lock);
6407c478bd9Sstevel@tonic-gate 
6417c478bd9Sstevel@tonic-gate 		/*
6427c478bd9Sstevel@tonic-gate 		 * wakeup the thread/threads waiting for the
6437c478bd9Sstevel@tonic-gate 		 * cleanup to finish
6447c478bd9Sstevel@tonic-gate 		 */
6457c478bd9Sstevel@tonic-gate 		mutex_lock(&cleanup_lock);
6467c478bd9Sstevel@tonic-gate 		cond_broadcast(&cleanup_done_cv);
6477c478bd9Sstevel@tonic-gate 	}
6487c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
6497c478bd9Sstevel@tonic-gate }
650