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 #include <stdio.h>
317c478bd9Sstevel@tonic-gate #include <ctype.h>
327c478bd9Sstevel@tonic-gate #include <string.h>
337c478bd9Sstevel@tonic-gate #include <syslog.h>
347c478bd9Sstevel@tonic-gate #include <sys/types.h>
357c478bd9Sstevel@tonic-gate #include <sys/param.h>
367c478bd9Sstevel@tonic-gate #include <errno.h>
377c478bd9Sstevel@tonic-gate #include <pwd.h>
387c478bd9Sstevel@tonic-gate #include <locale.h>
397c478bd9Sstevel@tonic-gate #include <stdlib.h>
407c478bd9Sstevel@tonic-gate #include <unistd.h>
417c478bd9Sstevel@tonic-gate #include <assert.h>
427c478bd9Sstevel@tonic-gate #include <fcntl.h>
437c478bd9Sstevel@tonic-gate #include "automount.h"
447c478bd9Sstevel@tonic-gate 
4539d3e169Sevanl static void build_dir_entry_list(struct autofs_rddir_cache *rdcp,
467c478bd9Sstevel@tonic-gate 				struct dir_entry *list);
4739d3e169Sevanl static int autofs_rddir_cache_enter(char *map, ulong_t bucket_size,
4839d3e169Sevanl 				struct autofs_rddir_cache **rdcpp);
4939d3e169Sevanl int autofs_rddir_cache_lookup(char *map, struct autofs_rddir_cache **rdcpp);
5039d3e169Sevanl static int autofs_rddir_cache_delete(struct autofs_rddir_cache *rdcp);
5139d3e169Sevanl static int create_dirents(struct autofs_rddir_cache *rdcp, ulong_t offset,
527c478bd9Sstevel@tonic-gate 				autofs_rddirres *res);
537c478bd9Sstevel@tonic-gate struct dir_entry *rddir_entry_lookup(char *name, struct dir_entry *list);
547c478bd9Sstevel@tonic-gate static void free_offset_tbl(struct off_tbl *head);
557c478bd9Sstevel@tonic-gate static void free_dir_list(struct dir_entry *head);
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate #define	OFFSET_BUCKET_SIZE	100
587c478bd9Sstevel@tonic-gate 
5939d3e169Sevanl rwlock_t autofs_rddir_cache_lock;		/* readdir cache lock */
6039d3e169Sevanl struct autofs_rddir_cache *rddir_head;		/* readdir cache head */
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate int
do_readdir(autofs_rddirargs * rda,autofs_rddirres * rd)63*3bfb48feSsemery do_readdir(autofs_rddirargs *rda, autofs_rddirres *rd)
647c478bd9Sstevel@tonic-gate {
657c478bd9Sstevel@tonic-gate 	struct dir_entry *list = NULL, *l;
6639d3e169Sevanl 	struct autofs_rddir_cache *rdcp = NULL;
677c478bd9Sstevel@tonic-gate 	int error;
687c478bd9Sstevel@tonic-gate 	int cache_time = RDDIR_CACHE_TIME;
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate 	if (automountd_nobrowse) {
717c478bd9Sstevel@tonic-gate 		/*
727c478bd9Sstevel@tonic-gate 		 * Browsability was disabled return an empty list.
737c478bd9Sstevel@tonic-gate 		 */
747c478bd9Sstevel@tonic-gate 		rd->rd_status = AUTOFS_OK;
757c478bd9Sstevel@tonic-gate 		rd->rd_rddir.rddir_size = 0;
767c478bd9Sstevel@tonic-gate 		rd->rd_rddir.rddir_eof = 1;
777c478bd9Sstevel@tonic-gate 		rd->rd_rddir.rddir_entries = NULL;
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate 		return (0);
807c478bd9Sstevel@tonic-gate 	}
817c478bd9Sstevel@tonic-gate 
8239d3e169Sevanl 	rw_rdlock(&autofs_rddir_cache_lock);
8339d3e169Sevanl 	error = autofs_rddir_cache_lookup(rda->rda_map, &rdcp);
847c478bd9Sstevel@tonic-gate 	if (error) {
8539d3e169Sevanl 		rw_unlock(&autofs_rddir_cache_lock);
8639d3e169Sevanl 		rw_wrlock(&autofs_rddir_cache_lock);
8739d3e169Sevanl 		error = autofs_rddir_cache_lookup(rda->rda_map, &rdcp);
887c478bd9Sstevel@tonic-gate 		if (error) {
897c478bd9Sstevel@tonic-gate 			if (trace > 2)
907c478bd9Sstevel@tonic-gate 				trace_prt(1,
917c478bd9Sstevel@tonic-gate 				"map %s not found, adding...\n", rda->rda_map);
927c478bd9Sstevel@tonic-gate 			/*
937c478bd9Sstevel@tonic-gate 			 * entry doesn't exist, add it.
947c478bd9Sstevel@tonic-gate 			 */
9539d3e169Sevanl 			error = autofs_rddir_cache_enter(rda->rda_map,
967c478bd9Sstevel@tonic-gate 					OFFSET_BUCKET_SIZE, &rdcp);
977c478bd9Sstevel@tonic-gate 		}
987c478bd9Sstevel@tonic-gate 	}
9939d3e169Sevanl 	rw_unlock(&autofs_rddir_cache_lock);
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate 	if (error)
1027c478bd9Sstevel@tonic-gate 		return (error);
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate 	assert(rdcp != NULL);
1057c478bd9Sstevel@tonic-gate 	assert(rdcp->in_use);
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate 	if (!rdcp->full) {
1087c478bd9Sstevel@tonic-gate 		rw_wrlock(&rdcp->rwlock);
1097c478bd9Sstevel@tonic-gate 		if (!rdcp->full) {
1107c478bd9Sstevel@tonic-gate 			/*
1117c478bd9Sstevel@tonic-gate 			 * cache entry hasn't been filled up, do it now.
1127c478bd9Sstevel@tonic-gate 			 */
1137c478bd9Sstevel@tonic-gate 			char *stack[STACKSIZ];
1147c478bd9Sstevel@tonic-gate 			char **stkptr;
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate 			/*
1177c478bd9Sstevel@tonic-gate 			 * Initialize the stack of open files
1187c478bd9Sstevel@tonic-gate 			 * for this thread
1197c478bd9Sstevel@tonic-gate 			 */
1207c478bd9Sstevel@tonic-gate 			stack_op(INIT, NULL, stack, &stkptr);
1217c478bd9Sstevel@tonic-gate 			(void) getmapkeys(rda->rda_map, &list, &error,
122*3bfb48feSsemery 			    &cache_time, stack, &stkptr, rda->uid);
1237c478bd9Sstevel@tonic-gate 			if (!error)
1247c478bd9Sstevel@tonic-gate 				build_dir_entry_list(rdcp, list);
1257c478bd9Sstevel@tonic-gate 			else if (list) {
1267c478bd9Sstevel@tonic-gate 				free_dir_list(list);
1277c478bd9Sstevel@tonic-gate 				list = NULL;
1287c478bd9Sstevel@tonic-gate 			}
1297c478bd9Sstevel@tonic-gate 		}
1307c478bd9Sstevel@tonic-gate 	} else
1317c478bd9Sstevel@tonic-gate 		rw_rdlock(&rdcp->rwlock);
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate 	rd->rd_bufsize = rda->rda_count;
1347c478bd9Sstevel@tonic-gate 	if (!error) {
1357c478bd9Sstevel@tonic-gate 		error = create_dirents(rdcp, rda->rda_offset, rd);
1367c478bd9Sstevel@tonic-gate 		if (error) {
1377c478bd9Sstevel@tonic-gate 			if (rdcp->offtp) {
1387c478bd9Sstevel@tonic-gate 				free_offset_tbl(rdcp->offtp);
1397c478bd9Sstevel@tonic-gate 				rdcp->offtp = NULL;
1407c478bd9Sstevel@tonic-gate 			}
1417c478bd9Sstevel@tonic-gate 			if (rdcp->entp) {
1427c478bd9Sstevel@tonic-gate 				free_dir_list(rdcp->entp);
1437c478bd9Sstevel@tonic-gate 				rdcp->entp = NULL;
1447c478bd9Sstevel@tonic-gate 			}
1457c478bd9Sstevel@tonic-gate 			rdcp->full = 0;
1467c478bd9Sstevel@tonic-gate 			list = NULL;
1477c478bd9Sstevel@tonic-gate 		}
1487c478bd9Sstevel@tonic-gate 	}
1497c478bd9Sstevel@tonic-gate 
1507c478bd9Sstevel@tonic-gate 	if (trace > 2) {
1517c478bd9Sstevel@tonic-gate 		/*
1527c478bd9Sstevel@tonic-gate 		 * print this list only once
1537c478bd9Sstevel@tonic-gate 		 */
1547c478bd9Sstevel@tonic-gate 		for (l = list; l != NULL; l = l->next)
1557c478bd9Sstevel@tonic-gate 			trace_prt(0, "%s\n", l->name);
1567c478bd9Sstevel@tonic-gate 		trace_prt(0, "\n");
1577c478bd9Sstevel@tonic-gate 	}
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate 	if (!error) {
1607c478bd9Sstevel@tonic-gate 		rd->rd_status = AUTOFS_OK;
1617c478bd9Sstevel@tonic-gate 		if (cache_time) {
1627c478bd9Sstevel@tonic-gate 			/*
1637c478bd9Sstevel@tonic-gate 			 * keep list of entries for up to
1647c478bd9Sstevel@tonic-gate 			 * 'cache_time' seconds
1657c478bd9Sstevel@tonic-gate 			 */
1667c478bd9Sstevel@tonic-gate 			rdcp->ttl = time((time_t *)NULL) + cache_time;
1677c478bd9Sstevel@tonic-gate 		} else {
1687c478bd9Sstevel@tonic-gate 			/*
1697c478bd9Sstevel@tonic-gate 			 * the underlying name service indicated not
1707c478bd9Sstevel@tonic-gate 			 * to cache contents.
1717c478bd9Sstevel@tonic-gate 			 */
1727c478bd9Sstevel@tonic-gate 			if (rdcp->offtp) {
1737c478bd9Sstevel@tonic-gate 				free_offset_tbl(rdcp->offtp);
1747c478bd9Sstevel@tonic-gate 				rdcp->offtp = NULL;
1757c478bd9Sstevel@tonic-gate 			}
1767c478bd9Sstevel@tonic-gate 			if (rdcp->entp) {
1777c478bd9Sstevel@tonic-gate 				free_dir_list(rdcp->entp);
1787c478bd9Sstevel@tonic-gate 				rdcp->entp = NULL;
1797c478bd9Sstevel@tonic-gate 			}
1807c478bd9Sstevel@tonic-gate 			rdcp->full = 0;
1817c478bd9Sstevel@tonic-gate 		}
1827c478bd9Sstevel@tonic-gate 	} else {
1837c478bd9Sstevel@tonic-gate 		/*
1847c478bd9Sstevel@tonic-gate 		 * return an empty list
1857c478bd9Sstevel@tonic-gate 		 */
1867c478bd9Sstevel@tonic-gate 		rd->rd_rddir.rddir_size = 0;
1877c478bd9Sstevel@tonic-gate 		rd->rd_rddir.rddir_eof = 1;
1887c478bd9Sstevel@tonic-gate 		rd->rd_rddir.rddir_entries = NULL;
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate 		/*
1917c478bd9Sstevel@tonic-gate 		 * Invalidate cache and set error
1927c478bd9Sstevel@tonic-gate 		 */
1937c478bd9Sstevel@tonic-gate 		switch (error) {
1947c478bd9Sstevel@tonic-gate 		case ENOENT:
1957c478bd9Sstevel@tonic-gate 			rd->rd_status = AUTOFS_NOENT;
1967c478bd9Sstevel@tonic-gate 			break;
1977c478bd9Sstevel@tonic-gate 		case ENOMEM:
1987c478bd9Sstevel@tonic-gate 			rd->rd_status = AUTOFS_NOMEM;
1997c478bd9Sstevel@tonic-gate 			break;
2007c478bd9Sstevel@tonic-gate 		default:
2017c478bd9Sstevel@tonic-gate 			rd->rd_status = AUTOFS_ECOMM;
2027c478bd9Sstevel@tonic-gate 		}
2037c478bd9Sstevel@tonic-gate 	}
2047c478bd9Sstevel@tonic-gate 	rw_unlock(&rdcp->rwlock);
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate 	mutex_lock(&rdcp->lock);
2077c478bd9Sstevel@tonic-gate 	rdcp->in_use--;
2087c478bd9Sstevel@tonic-gate 	mutex_unlock(&rdcp->lock);
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate 	assert(rdcp->in_use >= 0);
2117c478bd9Sstevel@tonic-gate 
2127c478bd9Sstevel@tonic-gate 	return (error);
2137c478bd9Sstevel@tonic-gate }
2147c478bd9Sstevel@tonic-gate 
2157c478bd9Sstevel@tonic-gate #define	roundtoint(x)	(((x) + sizeof (int) - 1) & ~(sizeof (int) - 1))
2167c478bd9Sstevel@tonic-gate #define	DIRENT64_RECLEN(namelen)	\
2177c478bd9Sstevel@tonic-gate 	(((int)(((dirent64_t *)0)->d_name) + 1 + (namelen) + 7) & ~ 7)
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate static int
create_dirents(struct autofs_rddir_cache * rdcp,ulong_t offset,autofs_rddirres * res)22039d3e169Sevanl create_dirents(
22139d3e169Sevanl 	struct autofs_rddir_cache *rdcp,
22239d3e169Sevanl 	ulong_t offset,
22339d3e169Sevanl 	autofs_rddirres *res)
2247c478bd9Sstevel@tonic-gate {
2257c478bd9Sstevel@tonic-gate 	uint_t total_bytes_wanted;
2267c478bd9Sstevel@tonic-gate 	int bufsize;
2277c478bd9Sstevel@tonic-gate 	ushort_t this_reclen;
2287c478bd9Sstevel@tonic-gate 	int outcount = 0;
2297c478bd9Sstevel@tonic-gate 	int namelen;
2307c478bd9Sstevel@tonic-gate 	struct dir_entry *list = NULL, *l, *nl;
2317c478bd9Sstevel@tonic-gate 	struct dirent64 *dp;
2327c478bd9Sstevel@tonic-gate 	char *outbuf;
2337c478bd9Sstevel@tonic-gate 	struct off_tbl *offtp, *next = NULL;
2347c478bd9Sstevel@tonic-gate 	int this_bucket = 0;
2357c478bd9Sstevel@tonic-gate 	int error = 0;
2367c478bd9Sstevel@tonic-gate 	int x = 0, y = 0;
2377c478bd9Sstevel@tonic-gate 
2387c478bd9Sstevel@tonic-gate 	assert(RW_LOCK_HELD(&rdcp->rwlock));
2397c478bd9Sstevel@tonic-gate 	for (offtp = rdcp->offtp; offtp != NULL; offtp = next) {
2407c478bd9Sstevel@tonic-gate 		x++;
2417c478bd9Sstevel@tonic-gate 		next = offtp->next;
2427c478bd9Sstevel@tonic-gate 		this_bucket = (next == NULL);
2437c478bd9Sstevel@tonic-gate 		if (!this_bucket)
2447c478bd9Sstevel@tonic-gate 			this_bucket = (offset < next->offset);
2457c478bd9Sstevel@tonic-gate 		if (this_bucket) {
2467c478bd9Sstevel@tonic-gate 			/*
2477c478bd9Sstevel@tonic-gate 			 * has to be in this bucket
2487c478bd9Sstevel@tonic-gate 			 */
2497c478bd9Sstevel@tonic-gate 			assert(offset >= offtp->offset);
2507c478bd9Sstevel@tonic-gate 			list = offtp->first;
2517c478bd9Sstevel@tonic-gate 			break;
2527c478bd9Sstevel@tonic-gate 		}
2537c478bd9Sstevel@tonic-gate 		/*
2547c478bd9Sstevel@tonic-gate 		 * loop to look in next bucket
2557c478bd9Sstevel@tonic-gate 		 */
2567c478bd9Sstevel@tonic-gate 	}
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 	for (l = list; l != NULL && l->offset < offset; l = l->next)
2597c478bd9Sstevel@tonic-gate 		y++;
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate 	if (l == NULL) {
2627c478bd9Sstevel@tonic-gate 		/*
2637c478bd9Sstevel@tonic-gate 		 * reached end of directory
2647c478bd9Sstevel@tonic-gate 		 */
2657c478bd9Sstevel@tonic-gate 		error = 0;
2667c478bd9Sstevel@tonic-gate 		goto empty;
2677c478bd9Sstevel@tonic-gate 	}
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate 	if (trace > 2)
2707c478bd9Sstevel@tonic-gate 		trace_prt(1, "%s: offset searches (%d, %d)\n", rdcp->map, x, y);
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate 	total_bytes_wanted = res->rd_bufsize;
2737c478bd9Sstevel@tonic-gate 	bufsize = total_bytes_wanted + sizeof (struct dirent64);
2747c478bd9Sstevel@tonic-gate 	outbuf = malloc(bufsize);
2757c478bd9Sstevel@tonic-gate 	if (outbuf == NULL) {
2767c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "memory allocation error\n");
2777c478bd9Sstevel@tonic-gate 		error = ENOMEM;
2787c478bd9Sstevel@tonic-gate 		goto empty;
2797c478bd9Sstevel@tonic-gate 	}
2807c478bd9Sstevel@tonic-gate 	memset(outbuf, 0, bufsize);
2817c478bd9Sstevel@tonic-gate 	/* LINTED pointer alignment */
2827c478bd9Sstevel@tonic-gate 	dp = (struct dirent64 *)outbuf;
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate 	while (l) {
2857c478bd9Sstevel@tonic-gate 		nl = l->next;
2867c478bd9Sstevel@tonic-gate 		namelen = strlen(l->name);
2877c478bd9Sstevel@tonic-gate 		this_reclen = DIRENT64_RECLEN(namelen);
2887c478bd9Sstevel@tonic-gate 		if (outcount + this_reclen > total_bytes_wanted) {
2897c478bd9Sstevel@tonic-gate 			break;
2907c478bd9Sstevel@tonic-gate 		}
2917c478bd9Sstevel@tonic-gate 		dp->d_ino = (ino64_t)l->nodeid;
2927c478bd9Sstevel@tonic-gate 		if (nl) {
2937c478bd9Sstevel@tonic-gate 			/*
2947c478bd9Sstevel@tonic-gate 			 * get the next elements offset
2957c478bd9Sstevel@tonic-gate 			 */
2967c478bd9Sstevel@tonic-gate 			dp->d_off = (off64_t)nl->offset;
2977c478bd9Sstevel@tonic-gate 		} else {
2987c478bd9Sstevel@tonic-gate 			/*
2997c478bd9Sstevel@tonic-gate 			 * This is the last element
3007c478bd9Sstevel@tonic-gate 			 * make offset one plus the current.
3017c478bd9Sstevel@tonic-gate 			 */
3027c478bd9Sstevel@tonic-gate 			dp->d_off = (off64_t)l->offset + 1;
3037c478bd9Sstevel@tonic-gate 		}
3047c478bd9Sstevel@tonic-gate 		(void) strcpy(dp->d_name, l->name);
3057c478bd9Sstevel@tonic-gate 		dp->d_reclen = (ushort_t)this_reclen;
3067c478bd9Sstevel@tonic-gate 		outcount += dp->d_reclen;
3077c478bd9Sstevel@tonic-gate 		dp = (struct dirent64 *)((int)dp + dp->d_reclen);
3087c478bd9Sstevel@tonic-gate 		assert(outcount <= total_bytes_wanted);
3097c478bd9Sstevel@tonic-gate 		l = l->next;
3107c478bd9Sstevel@tonic-gate 	}
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate 	res->rd_rddir.rddir_size = (long)outcount;
3137c478bd9Sstevel@tonic-gate 	if (outcount > 0) {
3147c478bd9Sstevel@tonic-gate 		/*
3157c478bd9Sstevel@tonic-gate 		 * have some entries
3167c478bd9Sstevel@tonic-gate 		 */
3177c478bd9Sstevel@tonic-gate 		res->rd_rddir.rddir_eof = (l == NULL);
3187c478bd9Sstevel@tonic-gate 		/* LINTED pointer alignment */
3197c478bd9Sstevel@tonic-gate 		res->rd_rddir.rddir_entries = (struct dirent64 *)outbuf;
3207c478bd9Sstevel@tonic-gate 		error = 0;
3217c478bd9Sstevel@tonic-gate 	} else {
3227c478bd9Sstevel@tonic-gate 		/*
3237c478bd9Sstevel@tonic-gate 		 * total_bytes_wanted is not large enough for one
3247c478bd9Sstevel@tonic-gate 		 * directory entry
3257c478bd9Sstevel@tonic-gate 		 */
3267c478bd9Sstevel@tonic-gate 		res->rd_rddir.rddir_eof = 0;
3277c478bd9Sstevel@tonic-gate 		res->rd_rddir.rddir_entries = NULL;
3287c478bd9Sstevel@tonic-gate 		free(outbuf);
3297c478bd9Sstevel@tonic-gate 		error = EIO;
3307c478bd9Sstevel@tonic-gate 	}
3317c478bd9Sstevel@tonic-gate 	return (error);
3327c478bd9Sstevel@tonic-gate 
33339d3e169Sevanl empty:
33439d3e169Sevanl 	res->rd_rddir.rddir_size = 0L;
3357c478bd9Sstevel@tonic-gate 	res->rd_rddir.rddir_eof = TRUE;
3367c478bd9Sstevel@tonic-gate 	res->rd_rddir.rddir_entries = NULL;
3377c478bd9Sstevel@tonic-gate 	return (error);
3387c478bd9Sstevel@tonic-gate }
3397c478bd9Sstevel@tonic-gate 
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate /*
3427c478bd9Sstevel@tonic-gate  * add new entry to cache for 'map'
3437c478bd9Sstevel@tonic-gate  */
3447c478bd9Sstevel@tonic-gate static int
autofs_rddir_cache_enter(char * map,ulong_t bucket_size,struct autofs_rddir_cache ** rdcpp)34539d3e169Sevanl autofs_rddir_cache_enter(
34639d3e169Sevanl 	char *map,
34739d3e169Sevanl 	ulong_t bucket_size,
34839d3e169Sevanl 	struct autofs_rddir_cache **rdcpp)
3497c478bd9Sstevel@tonic-gate {
35039d3e169Sevanl 	struct autofs_rddir_cache *p;
35139d3e169Sevanl 	assert(RW_LOCK_HELD(&autofs_rddir_cache_lock));
3527c478bd9Sstevel@tonic-gate 
3537c478bd9Sstevel@tonic-gate 	/*
3547c478bd9Sstevel@tonic-gate 	 * Add to front of the list at this time
3557c478bd9Sstevel@tonic-gate 	 */
35639d3e169Sevanl 	p = (struct autofs_rddir_cache *)malloc(sizeof (*p));
3577c478bd9Sstevel@tonic-gate 	if (p == NULL) {
3587c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR,
35939d3e169Sevanl 			"autofs_rddir_cache_enter: memory allocation failed\n");
3607c478bd9Sstevel@tonic-gate 		return (ENOMEM);
3617c478bd9Sstevel@tonic-gate 	}
3627c478bd9Sstevel@tonic-gate 	memset((char *)p, 0, sizeof (*p));
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 	p->map = malloc(strlen(map) + 1);
3657c478bd9Sstevel@tonic-gate 	if (p->map == NULL) {
3667c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR,
36739d3e169Sevanl 			"autofs_rddir_cache_enter: memory allocation failed\n");
3687c478bd9Sstevel@tonic-gate 		free(p);
3697c478bd9Sstevel@tonic-gate 		return (ENOMEM);
3707c478bd9Sstevel@tonic-gate 	}
3717c478bd9Sstevel@tonic-gate 	strcpy(p->map, map);
3727c478bd9Sstevel@tonic-gate 
3737c478bd9Sstevel@tonic-gate 	p->bucket_size = bucket_size;
3747c478bd9Sstevel@tonic-gate 	/*
3757c478bd9Sstevel@tonic-gate 	 * no need to grab mutex lock since I haven't yet made the
3767c478bd9Sstevel@tonic-gate 	 * node visible to the list
3777c478bd9Sstevel@tonic-gate 	 */
3787c478bd9Sstevel@tonic-gate 	p->in_use = 1;
3797c478bd9Sstevel@tonic-gate 	(void) rwlock_init(&p->rwlock, USYNC_THREAD, NULL);
3807c478bd9Sstevel@tonic-gate 	(void) mutex_init(&p->lock, USYNC_THREAD, NULL);
3817c478bd9Sstevel@tonic-gate 
3827c478bd9Sstevel@tonic-gate 	if (rddir_head == NULL)
3837c478bd9Sstevel@tonic-gate 		rddir_head = p;
3847c478bd9Sstevel@tonic-gate 	else {
3857c478bd9Sstevel@tonic-gate 		p->next = rddir_head;
3867c478bd9Sstevel@tonic-gate 		rddir_head = p;
3877c478bd9Sstevel@tonic-gate 	}
3887c478bd9Sstevel@tonic-gate 	*rdcpp = p;
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 	return (0);
3917c478bd9Sstevel@tonic-gate }
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate /*
3947c478bd9Sstevel@tonic-gate  * find 'map' in readdir cache
3957c478bd9Sstevel@tonic-gate  */
3967c478bd9Sstevel@tonic-gate int
autofs_rddir_cache_lookup(char * map,struct autofs_rddir_cache ** rdcpp)39739d3e169Sevanl autofs_rddir_cache_lookup(char *map, struct autofs_rddir_cache **rdcpp)
3987c478bd9Sstevel@tonic-gate {
39939d3e169Sevanl 	struct autofs_rddir_cache *p;
4007c478bd9Sstevel@tonic-gate 
40139d3e169Sevanl 	assert(RW_LOCK_HELD(&autofs_rddir_cache_lock));
4027c478bd9Sstevel@tonic-gate 	for (p = rddir_head; p != NULL; p = p->next) {
4037c478bd9Sstevel@tonic-gate 		if (strcmp(p->map, map) == 0) {
4047c478bd9Sstevel@tonic-gate 			/*
4057c478bd9Sstevel@tonic-gate 			 * found matching entry
4067c478bd9Sstevel@tonic-gate 			 */
4077c478bd9Sstevel@tonic-gate 			*rdcpp = p;
4087c478bd9Sstevel@tonic-gate 			mutex_lock(&p->lock);
4097c478bd9Sstevel@tonic-gate 			p->in_use++;
4107c478bd9Sstevel@tonic-gate 			mutex_unlock(&p->lock);
4117c478bd9Sstevel@tonic-gate 			return (0);
4127c478bd9Sstevel@tonic-gate 		}
4137c478bd9Sstevel@tonic-gate 	}
4147c478bd9Sstevel@tonic-gate 	/*
4157c478bd9Sstevel@tonic-gate 	 * didn't find entry
4167c478bd9Sstevel@tonic-gate 	 */
4177c478bd9Sstevel@tonic-gate 	return (ENOENT);
4187c478bd9Sstevel@tonic-gate }
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate /*
4217c478bd9Sstevel@tonic-gate  * free the offset table
4227c478bd9Sstevel@tonic-gate  */
4237c478bd9Sstevel@tonic-gate static void
free_offset_tbl(struct off_tbl * head)4247c478bd9Sstevel@tonic-gate free_offset_tbl(struct off_tbl *head)
4257c478bd9Sstevel@tonic-gate {
4267c478bd9Sstevel@tonic-gate 	struct off_tbl *p, *next = NULL;
4277c478bd9Sstevel@tonic-gate 
4287c478bd9Sstevel@tonic-gate 	for (p = head; p != NULL; p = next) {
4297c478bd9Sstevel@tonic-gate 		next = p->next;
4307c478bd9Sstevel@tonic-gate 		free(p);
4317c478bd9Sstevel@tonic-gate 	}
4327c478bd9Sstevel@tonic-gate }
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate /*
4357c478bd9Sstevel@tonic-gate  * free the directory entries
4367c478bd9Sstevel@tonic-gate  */
4377c478bd9Sstevel@tonic-gate static void
free_dir_list(struct dir_entry * head)4387c478bd9Sstevel@tonic-gate free_dir_list(struct dir_entry *head)
4397c478bd9Sstevel@tonic-gate {
4407c478bd9Sstevel@tonic-gate 	struct dir_entry *p, *next = NULL;
4417c478bd9Sstevel@tonic-gate 
4427c478bd9Sstevel@tonic-gate 	for (p = head; p != NULL; p = next) {
4437c478bd9Sstevel@tonic-gate 		next = p->next;
4447c478bd9Sstevel@tonic-gate 		assert(p->name);
4457c478bd9Sstevel@tonic-gate 		free(p->name);
4467c478bd9Sstevel@tonic-gate 		free(p);
4477c478bd9Sstevel@tonic-gate 	}
4487c478bd9Sstevel@tonic-gate }
4497c478bd9Sstevel@tonic-gate 
4507c478bd9Sstevel@tonic-gate static void
autofs_rddir_cache_entry_free(struct autofs_rddir_cache * p)45139d3e169Sevanl autofs_rddir_cache_entry_free(struct autofs_rddir_cache *p)
4527c478bd9Sstevel@tonic-gate {
45339d3e169Sevanl 	assert(RW_LOCK_HELD(&autofs_rddir_cache_lock));
4547c478bd9Sstevel@tonic-gate 	assert(!p->in_use);
4557c478bd9Sstevel@tonic-gate 	if (p->map)
4567c478bd9Sstevel@tonic-gate 		free(p->map);
4577c478bd9Sstevel@tonic-gate 	if (p->offtp)
4587c478bd9Sstevel@tonic-gate 		free_offset_tbl(p->offtp);
4597c478bd9Sstevel@tonic-gate 	if (p->entp)
4607c478bd9Sstevel@tonic-gate 		free_dir_list(p->entp);
4617c478bd9Sstevel@tonic-gate 	free(p);
4627c478bd9Sstevel@tonic-gate }
4637c478bd9Sstevel@tonic-gate 
4647c478bd9Sstevel@tonic-gate /*
4657c478bd9Sstevel@tonic-gate  * Remove entry from the rddircache
46639d3e169Sevanl  * the caller must own the autofs_rddir_cache_lock.
4677c478bd9Sstevel@tonic-gate  */
4687c478bd9Sstevel@tonic-gate static int
autofs_rddir_cache_delete(struct autofs_rddir_cache * rdcp)46939d3e169Sevanl autofs_rddir_cache_delete(struct autofs_rddir_cache *rdcp)
4707c478bd9Sstevel@tonic-gate {
47139d3e169Sevanl 	struct autofs_rddir_cache *p, *prev;
4727c478bd9Sstevel@tonic-gate 
47339d3e169Sevanl 	assert(RW_LOCK_HELD(&autofs_rddir_cache_lock));
4747c478bd9Sstevel@tonic-gate 	/*
4757c478bd9Sstevel@tonic-gate 	 * Search cache for entry
4767c478bd9Sstevel@tonic-gate 	 */
4777c478bd9Sstevel@tonic-gate 	prev = NULL;
4787c478bd9Sstevel@tonic-gate 	for (p = rddir_head; p != NULL; p = p->next) {
4797c478bd9Sstevel@tonic-gate 		if (p == rdcp) {
4807c478bd9Sstevel@tonic-gate 			/*
4817c478bd9Sstevel@tonic-gate 			 * entry found, remove from list if not in use
4827c478bd9Sstevel@tonic-gate 			 */
4837c478bd9Sstevel@tonic-gate 			if (p->in_use)
4847c478bd9Sstevel@tonic-gate 				return (EBUSY);
4857c478bd9Sstevel@tonic-gate 			if (prev)
4867c478bd9Sstevel@tonic-gate 				prev->next = p->next;
4877c478bd9Sstevel@tonic-gate 			else
4887c478bd9Sstevel@tonic-gate 				rddir_head = p->next;
48939d3e169Sevanl 			autofs_rddir_cache_entry_free(p);
4907c478bd9Sstevel@tonic-gate 			return (0);
4917c478bd9Sstevel@tonic-gate 		}
4927c478bd9Sstevel@tonic-gate 		prev = p;
4937c478bd9Sstevel@tonic-gate 	}
4947c478bd9Sstevel@tonic-gate 	syslog(LOG_ERR, "Couldn't find entry %x in cache\n", p);
4957c478bd9Sstevel@tonic-gate 	return (ENOENT);
4967c478bd9Sstevel@tonic-gate }
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate /*
4997c478bd9Sstevel@tonic-gate  * Return entry that matches name, NULL otherwise.
5007c478bd9Sstevel@tonic-gate  * Assumes the readers lock for this list has been grabed.
5017c478bd9Sstevel@tonic-gate  */
5027c478bd9Sstevel@tonic-gate struct dir_entry *
rddir_entry_lookup(char * name,struct dir_entry * list)5037c478bd9Sstevel@tonic-gate rddir_entry_lookup(char *name, struct dir_entry *list)
5047c478bd9Sstevel@tonic-gate {
5057c478bd9Sstevel@tonic-gate 	return (btree_lookup(list, name));
5067c478bd9Sstevel@tonic-gate }
5077c478bd9Sstevel@tonic-gate 
5087c478bd9Sstevel@tonic-gate static void
build_dir_entry_list(struct autofs_rddir_cache * rdcp,struct dir_entry * list)50939d3e169Sevanl build_dir_entry_list(struct autofs_rddir_cache *rdcp, struct dir_entry *list)
5107c478bd9Sstevel@tonic-gate {
5117c478bd9Sstevel@tonic-gate 	struct dir_entry *p;
5127c478bd9Sstevel@tonic-gate 	ulong_t offset = AUTOFS_DAEMONCOOKIE, offset_list = AUTOFS_DAEMONCOOKIE;
5137c478bd9Sstevel@tonic-gate 	struct off_tbl *offtp, *last = NULL;
5147c478bd9Sstevel@tonic-gate 	ino_t inonum = 4;
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 	assert(RW_LOCK_HELD(&rdcp->rwlock));
5177c478bd9Sstevel@tonic-gate 	assert(rdcp->entp == NULL);
5187c478bd9Sstevel@tonic-gate 	rdcp->entp = list;
5197c478bd9Sstevel@tonic-gate 	for (p = list; p != NULL; p = p->next) {
5207c478bd9Sstevel@tonic-gate 		p->nodeid = inonum;
5217c478bd9Sstevel@tonic-gate 		p->offset = offset;
5227c478bd9Sstevel@tonic-gate 		if (offset >= offset_list) {
5237c478bd9Sstevel@tonic-gate 			/*
5247c478bd9Sstevel@tonic-gate 			 * add node to index table
5257c478bd9Sstevel@tonic-gate 			 */
5267c478bd9Sstevel@tonic-gate 			offtp = (struct off_tbl *)
5277c478bd9Sstevel@tonic-gate 				malloc(sizeof (struct off_tbl));
5287c478bd9Sstevel@tonic-gate 			if (offtp != NULL) {
5297c478bd9Sstevel@tonic-gate 				offtp->offset = offset;
5307c478bd9Sstevel@tonic-gate 				offtp->first = p;
5317c478bd9Sstevel@tonic-gate 				offtp->next = NULL;
5327c478bd9Sstevel@tonic-gate 				offset_list += rdcp->bucket_size;
5337c478bd9Sstevel@tonic-gate 			} else {
5347c478bd9Sstevel@tonic-gate 				syslog(LOG_ERR,
5357c478bd9Sstevel@tonic-gate "WARNING: build_dir_entry_list: could not add offset to index table\n");
5367c478bd9Sstevel@tonic-gate 				continue;
5377c478bd9Sstevel@tonic-gate 			}
5387c478bd9Sstevel@tonic-gate 			/*
5397c478bd9Sstevel@tonic-gate 			 * add to cache
5407c478bd9Sstevel@tonic-gate 			 */
5417c478bd9Sstevel@tonic-gate 			if (rdcp->offtp == NULL)
5427c478bd9Sstevel@tonic-gate 				rdcp->offtp = offtp;
5437c478bd9Sstevel@tonic-gate 			else
5447c478bd9Sstevel@tonic-gate 				last->next = offtp;
5457c478bd9Sstevel@tonic-gate 			last = offtp;
5467c478bd9Sstevel@tonic-gate 		}
5477c478bd9Sstevel@tonic-gate 		offset++;
5487c478bd9Sstevel@tonic-gate 		inonum += 2;		/* use even numbers in daemon */
5497c478bd9Sstevel@tonic-gate 	}
5507c478bd9Sstevel@tonic-gate 	rdcp->full = 1;
5517c478bd9Sstevel@tonic-gate }
5527c478bd9Sstevel@tonic-gate 
5537c478bd9Sstevel@tonic-gate mutex_t cleanup_lock;
5547c478bd9Sstevel@tonic-gate cond_t cleanup_start_cv;
5557c478bd9Sstevel@tonic-gate cond_t cleanup_done_cv;
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate /*
5587c478bd9Sstevel@tonic-gate  * cache cleanup thread starting point
5597c478bd9Sstevel@tonic-gate  */
5607c478bd9Sstevel@tonic-gate void
cache_cleanup(void)5617c478bd9Sstevel@tonic-gate cache_cleanup(void)
5627c478bd9Sstevel@tonic-gate {
5637c478bd9Sstevel@tonic-gate 	timestruc_t reltime;
56439d3e169Sevanl 	struct autofs_rddir_cache *p, *next = NULL;
5657c478bd9Sstevel@tonic-gate 	int error;
5667c478bd9Sstevel@tonic-gate 
5677c478bd9Sstevel@tonic-gate 	mutex_init(&cleanup_lock, USYNC_THREAD, NULL);
5687c478bd9Sstevel@tonic-gate 	cond_init(&cleanup_start_cv, USYNC_THREAD, NULL);
5697c478bd9Sstevel@tonic-gate 	cond_init(&cleanup_done_cv, USYNC_THREAD, NULL);
5707c478bd9Sstevel@tonic-gate 
5717c478bd9Sstevel@tonic-gate 	mutex_lock(&cleanup_lock);
5727c478bd9Sstevel@tonic-gate 	for (;;) {
5737c478bd9Sstevel@tonic-gate 		reltime.tv_sec = RDDIR_CACHE_TIME/2;
5747c478bd9Sstevel@tonic-gate 		reltime.tv_nsec = 0;
5757c478bd9Sstevel@tonic-gate 
5767c478bd9Sstevel@tonic-gate 		/*
5777c478bd9Sstevel@tonic-gate 		 * delay RDDIR_CACHE_TIME seconds, or until some other thread
5787c478bd9Sstevel@tonic-gate 		 * requests that I cleanup the caches
5797c478bd9Sstevel@tonic-gate 		 */
5807c478bd9Sstevel@tonic-gate 		if (error = cond_reltimedwait(
5817c478bd9Sstevel@tonic-gate 		    &cleanup_start_cv, &cleanup_lock, &reltime)) {
5827c478bd9Sstevel@tonic-gate 			if (error != ETIME) {
5837c478bd9Sstevel@tonic-gate 				if (trace > 1)
5847c478bd9Sstevel@tonic-gate 					trace_prt(1,
5857c478bd9Sstevel@tonic-gate 					"cleanup thread wakeup (%d)\n", error);
5867c478bd9Sstevel@tonic-gate 				continue;
5877c478bd9Sstevel@tonic-gate 			}
5887c478bd9Sstevel@tonic-gate 		}
5897c478bd9Sstevel@tonic-gate 		mutex_unlock(&cleanup_lock);
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate 		/*
5927c478bd9Sstevel@tonic-gate 		 * Perform the cache cleanup
5937c478bd9Sstevel@tonic-gate 		 */
59439d3e169Sevanl 		rw_wrlock(&autofs_rddir_cache_lock);
5957c478bd9Sstevel@tonic-gate 		for (p = rddir_head; p != NULL; p = next) {
5967c478bd9Sstevel@tonic-gate 			next = p->next;
5977c478bd9Sstevel@tonic-gate 			if (p->in_use > 0) {
5987c478bd9Sstevel@tonic-gate 				/*
5997c478bd9Sstevel@tonic-gate 				 * cache entry busy, skip it
6007c478bd9Sstevel@tonic-gate 				 */
6017c478bd9Sstevel@tonic-gate 				if (trace > 1) {
6027c478bd9Sstevel@tonic-gate 					trace_prt(1,
6037c478bd9Sstevel@tonic-gate 					"%s cache in use\n", p->map);
6047c478bd9Sstevel@tonic-gate 				}
6057c478bd9Sstevel@tonic-gate 				continue;
6067c478bd9Sstevel@tonic-gate 			}
6077c478bd9Sstevel@tonic-gate 			/*
6087c478bd9Sstevel@tonic-gate 			 * Cache entry is not in use, and nobody can grab a
60939d3e169Sevanl 			 * new reference since I'm holding the
61039d3e169Sevanl 			 * autofs_rddir_cache_lock
6117c478bd9Sstevel@tonic-gate 			 */
6127c478bd9Sstevel@tonic-gate 
6137c478bd9Sstevel@tonic-gate 			/*
6147c478bd9Sstevel@tonic-gate 			 * error will be zero if some thread signaled us asking
6157c478bd9Sstevel@tonic-gate 			 * that the caches be freed. In such case, free caches
6167c478bd9Sstevel@tonic-gate 			 * even if they're still valid and nobody is referencing
6177c478bd9Sstevel@tonic-gate 			 * them at this time. Otherwise, free caches only
6187c478bd9Sstevel@tonic-gate 			 * if their time to live (ttl) has expired.
6197c478bd9Sstevel@tonic-gate 			 */
6207c478bd9Sstevel@tonic-gate 			if (error == ETIME && (p->ttl > time((time_t *)NULL))) {
6217c478bd9Sstevel@tonic-gate 				/*
6227c478bd9Sstevel@tonic-gate 				 * Scheduled cache cleanup, if cache is still
6237c478bd9Sstevel@tonic-gate 				 * valid don't free.
6247c478bd9Sstevel@tonic-gate 				 */
6257c478bd9Sstevel@tonic-gate 				if (trace > 1) {
6267c478bd9Sstevel@tonic-gate 					trace_prt(1,
6277c478bd9Sstevel@tonic-gate 					"%s cache still valid\n", p->map);
6287c478bd9Sstevel@tonic-gate 				}
6297c478bd9Sstevel@tonic-gate 				continue;
6307c478bd9Sstevel@tonic-gate 			}
6317c478bd9Sstevel@tonic-gate 			if (trace > 1)
6327c478bd9Sstevel@tonic-gate 				trace_prt(1, "%s freeing cache\n", p->map);
6337c478bd9Sstevel@tonic-gate 			assert(!p->in_use);
63439d3e169Sevanl 			error = autofs_rddir_cache_delete(p);
6357c478bd9Sstevel@tonic-gate 			assert(!error);
6367c478bd9Sstevel@tonic-gate 		}
63739d3e169Sevanl 		rw_unlock(&autofs_rddir_cache_lock);
6387c478bd9Sstevel@tonic-gate 
6397c478bd9Sstevel@tonic-gate 		/*
6407c478bd9Sstevel@tonic-gate 		 * wakeup the thread/threads waiting for the
6417c478bd9Sstevel@tonic-gate 		 * cleanup to finish
6427c478bd9Sstevel@tonic-gate 		 */
6437c478bd9Sstevel@tonic-gate 		mutex_lock(&cleanup_lock);
6447c478bd9Sstevel@tonic-gate 		cond_broadcast(&cleanup_done_cv);
6457c478bd9Sstevel@tonic-gate 	}
6467c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
6477c478bd9Sstevel@tonic-gate }
648