xref: /onnv-gate/usr/src/cmd/fs.d/autofs/autod_readdir.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate /*
28*0Sstevel@tonic-gate  *	autod_readdir.c
29*0Sstevel@tonic-gate  */
30*0Sstevel@tonic-gate 
31*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
32*0Sstevel@tonic-gate 
33*0Sstevel@tonic-gate #include <stdio.h>
34*0Sstevel@tonic-gate #include <ctype.h>
35*0Sstevel@tonic-gate #include <string.h>
36*0Sstevel@tonic-gate #include <syslog.h>
37*0Sstevel@tonic-gate #include <sys/types.h>
38*0Sstevel@tonic-gate #include <sys/param.h>
39*0Sstevel@tonic-gate #include <errno.h>
40*0Sstevel@tonic-gate #include <pwd.h>
41*0Sstevel@tonic-gate #include <locale.h>
42*0Sstevel@tonic-gate #include <stdlib.h>
43*0Sstevel@tonic-gate #include <unistd.h>
44*0Sstevel@tonic-gate #include <assert.h>
45*0Sstevel@tonic-gate #include <fcntl.h>
46*0Sstevel@tonic-gate #include "automount.h"
47*0Sstevel@tonic-gate 
48*0Sstevel@tonic-gate static void build_dir_entry_list(struct rddir_cache *rdcp,
49*0Sstevel@tonic-gate 				struct dir_entry *list);
50*0Sstevel@tonic-gate static int rddir_cache_enter(char *map, ulong_t bucket_size,
51*0Sstevel@tonic-gate 				struct rddir_cache **rdcpp);
52*0Sstevel@tonic-gate int rddir_cache_lookup(char *map, struct rddir_cache **rdcpp);
53*0Sstevel@tonic-gate static int rddir_cache_delete(struct rddir_cache *rdcp);
54*0Sstevel@tonic-gate static int create_dirents(struct rddir_cache *rdcp, ulong_t offset,
55*0Sstevel@tonic-gate 				autofs_rddirres *res);
56*0Sstevel@tonic-gate struct dir_entry *rddir_entry_lookup(char *name, struct dir_entry *list);
57*0Sstevel@tonic-gate static void free_offset_tbl(struct off_tbl *head);
58*0Sstevel@tonic-gate static void free_dir_list(struct dir_entry *head);
59*0Sstevel@tonic-gate 
60*0Sstevel@tonic-gate #define	OFFSET_BUCKET_SIZE	100
61*0Sstevel@tonic-gate 
62*0Sstevel@tonic-gate rwlock_t rddir_cache_lock;		/* readdir cache lock */
63*0Sstevel@tonic-gate struct rddir_cache *rddir_head;		/* readdir cache head */
64*0Sstevel@tonic-gate 
65*0Sstevel@tonic-gate int
66*0Sstevel@tonic-gate do_readdir(struct autofs_rddirargs *rda, struct autofs_rddirres *rd,
67*0Sstevel@tonic-gate     struct authunix_parms *cred)
68*0Sstevel@tonic-gate {
69*0Sstevel@tonic-gate 	struct dir_entry *list = NULL, *l;
70*0Sstevel@tonic-gate 	struct rddir_cache *rdcp = NULL;
71*0Sstevel@tonic-gate 	int error;
72*0Sstevel@tonic-gate 	int cache_time = RDDIR_CACHE_TIME;
73*0Sstevel@tonic-gate 
74*0Sstevel@tonic-gate 	if (automountd_nobrowse) {
75*0Sstevel@tonic-gate 		/*
76*0Sstevel@tonic-gate 		 * Browsability was disabled return an empty list.
77*0Sstevel@tonic-gate 		 */
78*0Sstevel@tonic-gate 		rd->rd_status = AUTOFS_OK;
79*0Sstevel@tonic-gate 		rd->rd_rddir.rddir_size = 0;
80*0Sstevel@tonic-gate 		rd->rd_rddir.rddir_eof = 1;
81*0Sstevel@tonic-gate 		rd->rd_rddir.rddir_entries = NULL;
82*0Sstevel@tonic-gate 
83*0Sstevel@tonic-gate 		return (0);
84*0Sstevel@tonic-gate 	}
85*0Sstevel@tonic-gate 
86*0Sstevel@tonic-gate 	rw_rdlock(&rddir_cache_lock);
87*0Sstevel@tonic-gate 	error = rddir_cache_lookup(rda->rda_map, &rdcp);
88*0Sstevel@tonic-gate 	if (error) {
89*0Sstevel@tonic-gate 		rw_unlock(&rddir_cache_lock);
90*0Sstevel@tonic-gate 		rw_wrlock(&rddir_cache_lock);
91*0Sstevel@tonic-gate 		error = rddir_cache_lookup(rda->rda_map, &rdcp);
92*0Sstevel@tonic-gate 		if (error) {
93*0Sstevel@tonic-gate 			if (trace > 2)
94*0Sstevel@tonic-gate 				trace_prt(1,
95*0Sstevel@tonic-gate 				"map %s not found, adding...\n", rda->rda_map);
96*0Sstevel@tonic-gate 			/*
97*0Sstevel@tonic-gate 			 * entry doesn't exist, add it.
98*0Sstevel@tonic-gate 			 */
99*0Sstevel@tonic-gate 			error = rddir_cache_enter(rda->rda_map,
100*0Sstevel@tonic-gate 					OFFSET_BUCKET_SIZE, &rdcp);
101*0Sstevel@tonic-gate 		}
102*0Sstevel@tonic-gate 	}
103*0Sstevel@tonic-gate 	rw_unlock(&rddir_cache_lock);
104*0Sstevel@tonic-gate 
105*0Sstevel@tonic-gate 	if (error)
106*0Sstevel@tonic-gate 		return (error);
107*0Sstevel@tonic-gate 
108*0Sstevel@tonic-gate 	assert(rdcp != NULL);
109*0Sstevel@tonic-gate 	assert(rdcp->in_use);
110*0Sstevel@tonic-gate 
111*0Sstevel@tonic-gate 	if (!rdcp->full) {
112*0Sstevel@tonic-gate 		rw_wrlock(&rdcp->rwlock);
113*0Sstevel@tonic-gate 		if (!rdcp->full) {
114*0Sstevel@tonic-gate 			/*
115*0Sstevel@tonic-gate 			 * cache entry hasn't been filled up, do it now.
116*0Sstevel@tonic-gate 			 */
117*0Sstevel@tonic-gate 			char *stack[STACKSIZ];
118*0Sstevel@tonic-gate 			char **stkptr;
119*0Sstevel@tonic-gate 
120*0Sstevel@tonic-gate 			/*
121*0Sstevel@tonic-gate 			 * Initialize the stack of open files
122*0Sstevel@tonic-gate 			 * for this thread
123*0Sstevel@tonic-gate 			 */
124*0Sstevel@tonic-gate 			stack_op(INIT, NULL, stack, &stkptr);
125*0Sstevel@tonic-gate 			(void) getmapkeys(rda->rda_map, &list, &error,
126*0Sstevel@tonic-gate 			    &cache_time, stack, &stkptr, cred->aup_uid);
127*0Sstevel@tonic-gate 			if (!error)
128*0Sstevel@tonic-gate 				build_dir_entry_list(rdcp, list);
129*0Sstevel@tonic-gate 			else if (list) {
130*0Sstevel@tonic-gate 				free_dir_list(list);
131*0Sstevel@tonic-gate 				list = NULL;
132*0Sstevel@tonic-gate 			}
133*0Sstevel@tonic-gate 		}
134*0Sstevel@tonic-gate 	} else
135*0Sstevel@tonic-gate 		rw_rdlock(&rdcp->rwlock);
136*0Sstevel@tonic-gate 
137*0Sstevel@tonic-gate 	rd->rd_bufsize = rda->rda_count;
138*0Sstevel@tonic-gate 	if (!error) {
139*0Sstevel@tonic-gate 		error = create_dirents(rdcp, rda->rda_offset, rd);
140*0Sstevel@tonic-gate 		if (error) {
141*0Sstevel@tonic-gate 			if (rdcp->offtp) {
142*0Sstevel@tonic-gate 				free_offset_tbl(rdcp->offtp);
143*0Sstevel@tonic-gate 				rdcp->offtp = NULL;
144*0Sstevel@tonic-gate 			}
145*0Sstevel@tonic-gate 			if (rdcp->entp) {
146*0Sstevel@tonic-gate 				free_dir_list(rdcp->entp);
147*0Sstevel@tonic-gate 				rdcp->entp = NULL;
148*0Sstevel@tonic-gate 			}
149*0Sstevel@tonic-gate 			rdcp->full = 0;
150*0Sstevel@tonic-gate 			list = NULL;
151*0Sstevel@tonic-gate 		}
152*0Sstevel@tonic-gate 	}
153*0Sstevel@tonic-gate 
154*0Sstevel@tonic-gate 	if (trace > 2) {
155*0Sstevel@tonic-gate 		/*
156*0Sstevel@tonic-gate 		 * print this list only once
157*0Sstevel@tonic-gate 		 */
158*0Sstevel@tonic-gate 		for (l = list; l != NULL; l = l->next)
159*0Sstevel@tonic-gate 			trace_prt(0, "%s\n", l->name);
160*0Sstevel@tonic-gate 		trace_prt(0, "\n");
161*0Sstevel@tonic-gate 	}
162*0Sstevel@tonic-gate 
163*0Sstevel@tonic-gate 	if (!error) {
164*0Sstevel@tonic-gate 		rd->rd_status = AUTOFS_OK;
165*0Sstevel@tonic-gate 		if (cache_time) {
166*0Sstevel@tonic-gate 			/*
167*0Sstevel@tonic-gate 			 * keep list of entries for up to
168*0Sstevel@tonic-gate 			 * 'cache_time' seconds
169*0Sstevel@tonic-gate 			 */
170*0Sstevel@tonic-gate 			rdcp->ttl = time((time_t *)NULL) + cache_time;
171*0Sstevel@tonic-gate 		} else {
172*0Sstevel@tonic-gate 			/*
173*0Sstevel@tonic-gate 			 * the underlying name service indicated not
174*0Sstevel@tonic-gate 			 * to cache contents.
175*0Sstevel@tonic-gate 			 */
176*0Sstevel@tonic-gate 			if (rdcp->offtp) {
177*0Sstevel@tonic-gate 				free_offset_tbl(rdcp->offtp);
178*0Sstevel@tonic-gate 				rdcp->offtp = NULL;
179*0Sstevel@tonic-gate 			}
180*0Sstevel@tonic-gate 			if (rdcp->entp) {
181*0Sstevel@tonic-gate 				free_dir_list(rdcp->entp);
182*0Sstevel@tonic-gate 				rdcp->entp = NULL;
183*0Sstevel@tonic-gate 			}
184*0Sstevel@tonic-gate 			rdcp->full = 0;
185*0Sstevel@tonic-gate 		}
186*0Sstevel@tonic-gate 	} else {
187*0Sstevel@tonic-gate 		/*
188*0Sstevel@tonic-gate 		 * return an empty list
189*0Sstevel@tonic-gate 		 */
190*0Sstevel@tonic-gate 		rd->rd_rddir.rddir_size = 0;
191*0Sstevel@tonic-gate 		rd->rd_rddir.rddir_eof = 1;
192*0Sstevel@tonic-gate 		rd->rd_rddir.rddir_entries = NULL;
193*0Sstevel@tonic-gate 
194*0Sstevel@tonic-gate 		/*
195*0Sstevel@tonic-gate 		 * Invalidate cache and set error
196*0Sstevel@tonic-gate 		 */
197*0Sstevel@tonic-gate 		switch (error) {
198*0Sstevel@tonic-gate 		case ENOENT:
199*0Sstevel@tonic-gate 			rd->rd_status = AUTOFS_NOENT;
200*0Sstevel@tonic-gate 			break;
201*0Sstevel@tonic-gate 		case ENOMEM:
202*0Sstevel@tonic-gate 			rd->rd_status = AUTOFS_NOMEM;
203*0Sstevel@tonic-gate 			break;
204*0Sstevel@tonic-gate 		default:
205*0Sstevel@tonic-gate 			rd->rd_status = AUTOFS_ECOMM;
206*0Sstevel@tonic-gate 		}
207*0Sstevel@tonic-gate 	}
208*0Sstevel@tonic-gate 	rw_unlock(&rdcp->rwlock);
209*0Sstevel@tonic-gate 
210*0Sstevel@tonic-gate 	mutex_lock(&rdcp->lock);
211*0Sstevel@tonic-gate 	rdcp->in_use--;
212*0Sstevel@tonic-gate 	mutex_unlock(&rdcp->lock);
213*0Sstevel@tonic-gate 
214*0Sstevel@tonic-gate 	assert(rdcp->in_use >= 0);
215*0Sstevel@tonic-gate 
216*0Sstevel@tonic-gate 	return (error);
217*0Sstevel@tonic-gate }
218*0Sstevel@tonic-gate 
219*0Sstevel@tonic-gate #define	roundtoint(x)	(((x) + sizeof (int) - 1) & ~(sizeof (int) - 1))
220*0Sstevel@tonic-gate #define	DIRENT64_RECLEN(namelen)	\
221*0Sstevel@tonic-gate 	(((int)(((dirent64_t *)0)->d_name) + 1 + (namelen) + 7) & ~ 7)
222*0Sstevel@tonic-gate 
223*0Sstevel@tonic-gate static int
224*0Sstevel@tonic-gate create_dirents(struct rddir_cache *rdcp, ulong_t offset, autofs_rddirres *res)
225*0Sstevel@tonic-gate {
226*0Sstevel@tonic-gate 	uint_t total_bytes_wanted;
227*0Sstevel@tonic-gate 	int bufsize;
228*0Sstevel@tonic-gate 	ushort_t this_reclen;
229*0Sstevel@tonic-gate 	int outcount = 0;
230*0Sstevel@tonic-gate 	int namelen;
231*0Sstevel@tonic-gate 	struct dir_entry *list = NULL, *l, *nl;
232*0Sstevel@tonic-gate 	struct dirent64 *dp;
233*0Sstevel@tonic-gate 	char *outbuf;
234*0Sstevel@tonic-gate 	struct off_tbl *offtp, *next = NULL;
235*0Sstevel@tonic-gate 	int this_bucket = 0;
236*0Sstevel@tonic-gate 	int error = 0;
237*0Sstevel@tonic-gate 	int x = 0, y = 0;
238*0Sstevel@tonic-gate 
239*0Sstevel@tonic-gate 	assert(RW_LOCK_HELD(&rdcp->rwlock));
240*0Sstevel@tonic-gate 	for (offtp = rdcp->offtp; offtp != NULL; offtp = next) {
241*0Sstevel@tonic-gate 		x++;
242*0Sstevel@tonic-gate 		next = offtp->next;
243*0Sstevel@tonic-gate 		this_bucket = (next == NULL);
244*0Sstevel@tonic-gate 		if (!this_bucket)
245*0Sstevel@tonic-gate 			this_bucket = (offset < next->offset);
246*0Sstevel@tonic-gate 		if (this_bucket) {
247*0Sstevel@tonic-gate 			/*
248*0Sstevel@tonic-gate 			 * has to be in this bucket
249*0Sstevel@tonic-gate 			 */
250*0Sstevel@tonic-gate 			assert(offset >= offtp->offset);
251*0Sstevel@tonic-gate 			list = offtp->first;
252*0Sstevel@tonic-gate 			break;
253*0Sstevel@tonic-gate 		}
254*0Sstevel@tonic-gate 		/*
255*0Sstevel@tonic-gate 		 * loop to look in next bucket
256*0Sstevel@tonic-gate 		 */
257*0Sstevel@tonic-gate 	}
258*0Sstevel@tonic-gate 
259*0Sstevel@tonic-gate 	for (l = list; l != NULL && l->offset < offset; l = l->next)
260*0Sstevel@tonic-gate 		y++;
261*0Sstevel@tonic-gate 
262*0Sstevel@tonic-gate 	if (l == NULL) {
263*0Sstevel@tonic-gate 		/*
264*0Sstevel@tonic-gate 		 * reached end of directory
265*0Sstevel@tonic-gate 		 */
266*0Sstevel@tonic-gate 		error = 0;
267*0Sstevel@tonic-gate 		goto empty;
268*0Sstevel@tonic-gate 	}
269*0Sstevel@tonic-gate 
270*0Sstevel@tonic-gate 	if (trace > 2)
271*0Sstevel@tonic-gate 		trace_prt(1, "%s: offset searches (%d, %d)\n", rdcp->map, x, y);
272*0Sstevel@tonic-gate 
273*0Sstevel@tonic-gate 	total_bytes_wanted = res->rd_bufsize;
274*0Sstevel@tonic-gate 	bufsize = total_bytes_wanted + sizeof (struct dirent64);
275*0Sstevel@tonic-gate 	outbuf = malloc(bufsize);
276*0Sstevel@tonic-gate 	if (outbuf == NULL) {
277*0Sstevel@tonic-gate 		syslog(LOG_ERR, "memory allocation error\n");
278*0Sstevel@tonic-gate 		error = ENOMEM;
279*0Sstevel@tonic-gate 		goto empty;
280*0Sstevel@tonic-gate 	}
281*0Sstevel@tonic-gate 	memset(outbuf, 0, bufsize);
282*0Sstevel@tonic-gate 	/* LINTED pointer alignment */
283*0Sstevel@tonic-gate 	dp = (struct dirent64 *)outbuf;
284*0Sstevel@tonic-gate 
285*0Sstevel@tonic-gate 	while (l) {
286*0Sstevel@tonic-gate 		nl = l->next;
287*0Sstevel@tonic-gate 		namelen = strlen(l->name);
288*0Sstevel@tonic-gate 		this_reclen = DIRENT64_RECLEN(namelen);
289*0Sstevel@tonic-gate 		if (outcount + this_reclen > total_bytes_wanted) {
290*0Sstevel@tonic-gate 			break;
291*0Sstevel@tonic-gate 		}
292*0Sstevel@tonic-gate 		dp->d_ino = (ino64_t)l->nodeid;
293*0Sstevel@tonic-gate 		if (nl) {
294*0Sstevel@tonic-gate 			/*
295*0Sstevel@tonic-gate 			 * get the next elements offset
296*0Sstevel@tonic-gate 			 */
297*0Sstevel@tonic-gate 			dp->d_off = (off64_t)nl->offset;
298*0Sstevel@tonic-gate 		} else {
299*0Sstevel@tonic-gate 			/*
300*0Sstevel@tonic-gate 			 * This is the last element
301*0Sstevel@tonic-gate 			 * make offset one plus the current.
302*0Sstevel@tonic-gate 			 */
303*0Sstevel@tonic-gate 			dp->d_off = (off64_t)l->offset + 1;
304*0Sstevel@tonic-gate 		}
305*0Sstevel@tonic-gate 		(void) strcpy(dp->d_name, l->name);
306*0Sstevel@tonic-gate 		dp->d_reclen = (ushort_t)this_reclen;
307*0Sstevel@tonic-gate 		outcount += dp->d_reclen;
308*0Sstevel@tonic-gate 		dp = (struct dirent64 *)((int)dp + dp->d_reclen);
309*0Sstevel@tonic-gate 		assert(outcount <= total_bytes_wanted);
310*0Sstevel@tonic-gate 		l = l->next;
311*0Sstevel@tonic-gate 	}
312*0Sstevel@tonic-gate 
313*0Sstevel@tonic-gate 	res->rd_rddir.rddir_size = (long)outcount;
314*0Sstevel@tonic-gate 	if (outcount > 0) {
315*0Sstevel@tonic-gate 		/*
316*0Sstevel@tonic-gate 		 * have some entries
317*0Sstevel@tonic-gate 		 */
318*0Sstevel@tonic-gate 		res->rd_rddir.rddir_eof = (l == NULL);
319*0Sstevel@tonic-gate 		/* LINTED pointer alignment */
320*0Sstevel@tonic-gate 		res->rd_rddir.rddir_entries = (struct dirent64 *)outbuf;
321*0Sstevel@tonic-gate 		error = 0;
322*0Sstevel@tonic-gate 	} else {
323*0Sstevel@tonic-gate 		/*
324*0Sstevel@tonic-gate 		 * total_bytes_wanted is not large enough for one
325*0Sstevel@tonic-gate 		 * directory entry
326*0Sstevel@tonic-gate 		 */
327*0Sstevel@tonic-gate 		res->rd_rddir.rddir_eof = 0;
328*0Sstevel@tonic-gate 		res->rd_rddir.rddir_entries = NULL;
329*0Sstevel@tonic-gate 		free(outbuf);
330*0Sstevel@tonic-gate 		error = EIO;
331*0Sstevel@tonic-gate 	}
332*0Sstevel@tonic-gate 	return (error);
333*0Sstevel@tonic-gate 
334*0Sstevel@tonic-gate empty:	res->rd_rddir.rddir_size = (long)0;
335*0Sstevel@tonic-gate 	res->rd_rddir.rddir_eof = TRUE;
336*0Sstevel@tonic-gate 	res->rd_rddir.rddir_entries = NULL;
337*0Sstevel@tonic-gate 	return (error);
338*0Sstevel@tonic-gate }
339*0Sstevel@tonic-gate 
340*0Sstevel@tonic-gate 
341*0Sstevel@tonic-gate /*
342*0Sstevel@tonic-gate  * add new entry to cache for 'map'
343*0Sstevel@tonic-gate  */
344*0Sstevel@tonic-gate static int
345*0Sstevel@tonic-gate rddir_cache_enter(char *map, ulong_t bucket_size, struct rddir_cache **rdcpp)
346*0Sstevel@tonic-gate {
347*0Sstevel@tonic-gate 	struct rddir_cache *p;
348*0Sstevel@tonic-gate 	assert(RW_LOCK_HELD(&rddir_cache_lock));
349*0Sstevel@tonic-gate 
350*0Sstevel@tonic-gate 	/*
351*0Sstevel@tonic-gate 	 * Add to front of the list at this time
352*0Sstevel@tonic-gate 	 */
353*0Sstevel@tonic-gate 	p = (struct rddir_cache *)malloc(sizeof (*p));
354*0Sstevel@tonic-gate 	if (p == NULL) {
355*0Sstevel@tonic-gate 		syslog(LOG_ERR,
356*0Sstevel@tonic-gate 			"rddir_cache_enter: memory allocation failed\n");
357*0Sstevel@tonic-gate 		return (ENOMEM);
358*0Sstevel@tonic-gate 	}
359*0Sstevel@tonic-gate 	memset((char *)p, 0, sizeof (*p));
360*0Sstevel@tonic-gate 
361*0Sstevel@tonic-gate 	p->map = malloc(strlen(map) + 1);
362*0Sstevel@tonic-gate 	if (p->map == NULL) {
363*0Sstevel@tonic-gate 		syslog(LOG_ERR,
364*0Sstevel@tonic-gate 			"rddir_cache_enter: memory allocation failed\n");
365*0Sstevel@tonic-gate 		free(p);
366*0Sstevel@tonic-gate 		return (ENOMEM);
367*0Sstevel@tonic-gate 	}
368*0Sstevel@tonic-gate 	strcpy(p->map, map);
369*0Sstevel@tonic-gate 
370*0Sstevel@tonic-gate 	p->bucket_size = bucket_size;
371*0Sstevel@tonic-gate 	/*
372*0Sstevel@tonic-gate 	 * no need to grab mutex lock since I haven't yet made the
373*0Sstevel@tonic-gate 	 * node visible to the list
374*0Sstevel@tonic-gate 	 */
375*0Sstevel@tonic-gate 	p->in_use = 1;
376*0Sstevel@tonic-gate 	(void) rwlock_init(&p->rwlock, USYNC_THREAD, NULL);
377*0Sstevel@tonic-gate 	(void) mutex_init(&p->lock, USYNC_THREAD, NULL);
378*0Sstevel@tonic-gate 
379*0Sstevel@tonic-gate 	if (rddir_head == NULL)
380*0Sstevel@tonic-gate 		rddir_head = p;
381*0Sstevel@tonic-gate 	else {
382*0Sstevel@tonic-gate 		p->next = rddir_head;
383*0Sstevel@tonic-gate 		rddir_head = p;
384*0Sstevel@tonic-gate 	}
385*0Sstevel@tonic-gate 	*rdcpp = p;
386*0Sstevel@tonic-gate 
387*0Sstevel@tonic-gate 	return (0);
388*0Sstevel@tonic-gate }
389*0Sstevel@tonic-gate 
390*0Sstevel@tonic-gate /*
391*0Sstevel@tonic-gate  * find 'map' in readdir cache
392*0Sstevel@tonic-gate  */
393*0Sstevel@tonic-gate int
394*0Sstevel@tonic-gate rddir_cache_lookup(char *map, struct rddir_cache **rdcpp)
395*0Sstevel@tonic-gate {
396*0Sstevel@tonic-gate 	struct rddir_cache *p;
397*0Sstevel@tonic-gate 
398*0Sstevel@tonic-gate 	assert(RW_LOCK_HELD(&rddir_cache_lock));
399*0Sstevel@tonic-gate 	for (p = rddir_head; p != NULL; p = p->next) {
400*0Sstevel@tonic-gate 		if (strcmp(p->map, map) == 0) {
401*0Sstevel@tonic-gate 			/*
402*0Sstevel@tonic-gate 			 * found matching entry
403*0Sstevel@tonic-gate 			 */
404*0Sstevel@tonic-gate 			*rdcpp = p;
405*0Sstevel@tonic-gate 			mutex_lock(&p->lock);
406*0Sstevel@tonic-gate 			p->in_use++;
407*0Sstevel@tonic-gate 			mutex_unlock(&p->lock);
408*0Sstevel@tonic-gate 			return (0);
409*0Sstevel@tonic-gate 		}
410*0Sstevel@tonic-gate 	}
411*0Sstevel@tonic-gate 	/*
412*0Sstevel@tonic-gate 	 * didn't find entry
413*0Sstevel@tonic-gate 	 */
414*0Sstevel@tonic-gate 	return (ENOENT);
415*0Sstevel@tonic-gate }
416*0Sstevel@tonic-gate 
417*0Sstevel@tonic-gate /*
418*0Sstevel@tonic-gate  * free the offset table
419*0Sstevel@tonic-gate  */
420*0Sstevel@tonic-gate static void
421*0Sstevel@tonic-gate free_offset_tbl(struct off_tbl *head)
422*0Sstevel@tonic-gate {
423*0Sstevel@tonic-gate 	struct off_tbl *p, *next = NULL;
424*0Sstevel@tonic-gate 
425*0Sstevel@tonic-gate 	for (p = head; p != NULL; p = next) {
426*0Sstevel@tonic-gate 		next = p->next;
427*0Sstevel@tonic-gate 		free(p);
428*0Sstevel@tonic-gate 	}
429*0Sstevel@tonic-gate }
430*0Sstevel@tonic-gate 
431*0Sstevel@tonic-gate /*
432*0Sstevel@tonic-gate  * free the directory entries
433*0Sstevel@tonic-gate  */
434*0Sstevel@tonic-gate static void
435*0Sstevel@tonic-gate free_dir_list(struct dir_entry *head)
436*0Sstevel@tonic-gate {
437*0Sstevel@tonic-gate 	struct dir_entry *p, *next = NULL;
438*0Sstevel@tonic-gate 
439*0Sstevel@tonic-gate 	for (p = head; p != NULL; p = next) {
440*0Sstevel@tonic-gate 		next = p->next;
441*0Sstevel@tonic-gate 		assert(p->name);
442*0Sstevel@tonic-gate 		free(p->name);
443*0Sstevel@tonic-gate 		free(p);
444*0Sstevel@tonic-gate 	}
445*0Sstevel@tonic-gate }
446*0Sstevel@tonic-gate 
447*0Sstevel@tonic-gate static void
448*0Sstevel@tonic-gate rddir_cache_entry_free(struct rddir_cache *p)
449*0Sstevel@tonic-gate {
450*0Sstevel@tonic-gate 	assert(RW_LOCK_HELD(&rddir_cache_lock));
451*0Sstevel@tonic-gate 	assert(!p->in_use);
452*0Sstevel@tonic-gate 	if (p->map)
453*0Sstevel@tonic-gate 		free(p->map);
454*0Sstevel@tonic-gate 	if (p->offtp)
455*0Sstevel@tonic-gate 		free_offset_tbl(p->offtp);
456*0Sstevel@tonic-gate 	if (p->entp)
457*0Sstevel@tonic-gate 		free_dir_list(p->entp);
458*0Sstevel@tonic-gate 	free(p);
459*0Sstevel@tonic-gate }
460*0Sstevel@tonic-gate 
461*0Sstevel@tonic-gate /*
462*0Sstevel@tonic-gate  * Remove entry from the rddircache
463*0Sstevel@tonic-gate  * the caller must own the rddir_cache_lock.
464*0Sstevel@tonic-gate  */
465*0Sstevel@tonic-gate static int
466*0Sstevel@tonic-gate rddir_cache_delete(struct rddir_cache *rdcp)
467*0Sstevel@tonic-gate {
468*0Sstevel@tonic-gate 	struct rddir_cache *p, *prev;
469*0Sstevel@tonic-gate 
470*0Sstevel@tonic-gate 	assert(RW_LOCK_HELD(&rddir_cache_lock));
471*0Sstevel@tonic-gate 	/*
472*0Sstevel@tonic-gate 	 * Search cache for entry
473*0Sstevel@tonic-gate 	 */
474*0Sstevel@tonic-gate 	prev = NULL;
475*0Sstevel@tonic-gate 	for (p = rddir_head; p != NULL; p = p->next) {
476*0Sstevel@tonic-gate 		if (p == rdcp) {
477*0Sstevel@tonic-gate 			/*
478*0Sstevel@tonic-gate 			 * entry found, remove from list if not in use
479*0Sstevel@tonic-gate 			 */
480*0Sstevel@tonic-gate 			if (p->in_use)
481*0Sstevel@tonic-gate 				return (EBUSY);
482*0Sstevel@tonic-gate 			if (prev)
483*0Sstevel@tonic-gate 				prev->next = p->next;
484*0Sstevel@tonic-gate 			else
485*0Sstevel@tonic-gate 				rddir_head = p->next;
486*0Sstevel@tonic-gate 			rddir_cache_entry_free(p);
487*0Sstevel@tonic-gate 			return (0);
488*0Sstevel@tonic-gate 		}
489*0Sstevel@tonic-gate 		prev = p;
490*0Sstevel@tonic-gate 	}
491*0Sstevel@tonic-gate 	syslog(LOG_ERR, "Couldn't find entry %x in cache\n", p);
492*0Sstevel@tonic-gate 	return (ENOENT);
493*0Sstevel@tonic-gate }
494*0Sstevel@tonic-gate 
495*0Sstevel@tonic-gate /*
496*0Sstevel@tonic-gate  * Return entry that matches name, NULL otherwise.
497*0Sstevel@tonic-gate  * Assumes the readers lock for this list has been grabed.
498*0Sstevel@tonic-gate  */
499*0Sstevel@tonic-gate struct dir_entry *
500*0Sstevel@tonic-gate rddir_entry_lookup(char *name, struct dir_entry *list)
501*0Sstevel@tonic-gate {
502*0Sstevel@tonic-gate 	return (btree_lookup(list, name));
503*0Sstevel@tonic-gate }
504*0Sstevel@tonic-gate 
505*0Sstevel@tonic-gate static void
506*0Sstevel@tonic-gate build_dir_entry_list(struct rddir_cache *rdcp, struct dir_entry *list)
507*0Sstevel@tonic-gate {
508*0Sstevel@tonic-gate 	struct dir_entry *p;
509*0Sstevel@tonic-gate 	ulong_t offset = AUTOFS_DAEMONCOOKIE, offset_list = AUTOFS_DAEMONCOOKIE;
510*0Sstevel@tonic-gate 	struct off_tbl *offtp, *last = NULL;
511*0Sstevel@tonic-gate 	ino_t inonum = 4;
512*0Sstevel@tonic-gate 
513*0Sstevel@tonic-gate 	assert(RW_LOCK_HELD(&rdcp->rwlock));
514*0Sstevel@tonic-gate 	assert(rdcp->entp == NULL);
515*0Sstevel@tonic-gate 	rdcp->entp = list;
516*0Sstevel@tonic-gate 	for (p = list; p != NULL; p = p->next) {
517*0Sstevel@tonic-gate 		p->nodeid = inonum;
518*0Sstevel@tonic-gate 		p->offset = offset;
519*0Sstevel@tonic-gate 		if (offset >= offset_list) {
520*0Sstevel@tonic-gate 			/*
521*0Sstevel@tonic-gate 			 * add node to index table
522*0Sstevel@tonic-gate 			 */
523*0Sstevel@tonic-gate 			offtp = (struct off_tbl *)
524*0Sstevel@tonic-gate 				malloc(sizeof (struct off_tbl));
525*0Sstevel@tonic-gate 			if (offtp != NULL) {
526*0Sstevel@tonic-gate 				offtp->offset = offset;
527*0Sstevel@tonic-gate 				offtp->first = p;
528*0Sstevel@tonic-gate 				offtp->next = NULL;
529*0Sstevel@tonic-gate 				offset_list += rdcp->bucket_size;
530*0Sstevel@tonic-gate 			} else {
531*0Sstevel@tonic-gate 				syslog(LOG_ERR,
532*0Sstevel@tonic-gate "WARNING: build_dir_entry_list: could not add offset to index table\n");
533*0Sstevel@tonic-gate 				continue;
534*0Sstevel@tonic-gate 			}
535*0Sstevel@tonic-gate 			/*
536*0Sstevel@tonic-gate 			 * add to cache
537*0Sstevel@tonic-gate 			 */
538*0Sstevel@tonic-gate 			if (rdcp->offtp == NULL)
539*0Sstevel@tonic-gate 				rdcp->offtp = offtp;
540*0Sstevel@tonic-gate 			else
541*0Sstevel@tonic-gate 				last->next = offtp;
542*0Sstevel@tonic-gate 			last = offtp;
543*0Sstevel@tonic-gate 		}
544*0Sstevel@tonic-gate 		offset++;
545*0Sstevel@tonic-gate 		inonum += 2;		/* use even numbers in daemon */
546*0Sstevel@tonic-gate 	}
547*0Sstevel@tonic-gate 	rdcp->full = 1;
548*0Sstevel@tonic-gate }
549*0Sstevel@tonic-gate 
550*0Sstevel@tonic-gate mutex_t cleanup_lock;
551*0Sstevel@tonic-gate cond_t cleanup_start_cv;
552*0Sstevel@tonic-gate cond_t cleanup_done_cv;
553*0Sstevel@tonic-gate 
554*0Sstevel@tonic-gate /*
555*0Sstevel@tonic-gate  * cache cleanup thread starting point
556*0Sstevel@tonic-gate  */
557*0Sstevel@tonic-gate void
558*0Sstevel@tonic-gate cache_cleanup(void)
559*0Sstevel@tonic-gate {
560*0Sstevel@tonic-gate 	timestruc_t reltime;
561*0Sstevel@tonic-gate 	struct rddir_cache *p, *next = NULL;
562*0Sstevel@tonic-gate 	int error;
563*0Sstevel@tonic-gate 
564*0Sstevel@tonic-gate 	mutex_init(&cleanup_lock, USYNC_THREAD, NULL);
565*0Sstevel@tonic-gate 	cond_init(&cleanup_start_cv, USYNC_THREAD, NULL);
566*0Sstevel@tonic-gate 	cond_init(&cleanup_done_cv, USYNC_THREAD, NULL);
567*0Sstevel@tonic-gate 
568*0Sstevel@tonic-gate 	mutex_lock(&cleanup_lock);
569*0Sstevel@tonic-gate 	for (;;) {
570*0Sstevel@tonic-gate 		reltime.tv_sec = RDDIR_CACHE_TIME/2;
571*0Sstevel@tonic-gate 		reltime.tv_nsec = 0;
572*0Sstevel@tonic-gate 
573*0Sstevel@tonic-gate 		/*
574*0Sstevel@tonic-gate 		 * delay RDDIR_CACHE_TIME seconds, or until some other thread
575*0Sstevel@tonic-gate 		 * requests that I cleanup the caches
576*0Sstevel@tonic-gate 		 */
577*0Sstevel@tonic-gate 		if (error = cond_reltimedwait(
578*0Sstevel@tonic-gate 		    &cleanup_start_cv, &cleanup_lock, &reltime)) {
579*0Sstevel@tonic-gate 			if (error != ETIME) {
580*0Sstevel@tonic-gate 				if (trace > 1)
581*0Sstevel@tonic-gate 					trace_prt(1,
582*0Sstevel@tonic-gate 					"cleanup thread wakeup (%d)\n", error);
583*0Sstevel@tonic-gate 				continue;
584*0Sstevel@tonic-gate 			}
585*0Sstevel@tonic-gate 		}
586*0Sstevel@tonic-gate 		mutex_unlock(&cleanup_lock);
587*0Sstevel@tonic-gate 
588*0Sstevel@tonic-gate 		/*
589*0Sstevel@tonic-gate 		 * Perform the cache cleanup
590*0Sstevel@tonic-gate 		 */
591*0Sstevel@tonic-gate 		rw_wrlock(&rddir_cache_lock);
592*0Sstevel@tonic-gate 		for (p = rddir_head; p != NULL; p = next) {
593*0Sstevel@tonic-gate 			next = p->next;
594*0Sstevel@tonic-gate 			if (p->in_use > 0) {
595*0Sstevel@tonic-gate 				/*
596*0Sstevel@tonic-gate 				 * cache entry busy, skip it
597*0Sstevel@tonic-gate 				 */
598*0Sstevel@tonic-gate 				if (trace > 1) {
599*0Sstevel@tonic-gate 					trace_prt(1,
600*0Sstevel@tonic-gate 					"%s cache in use\n", p->map);
601*0Sstevel@tonic-gate 				}
602*0Sstevel@tonic-gate 				continue;
603*0Sstevel@tonic-gate 			}
604*0Sstevel@tonic-gate 			/*
605*0Sstevel@tonic-gate 			 * Cache entry is not in use, and nobody can grab a
606*0Sstevel@tonic-gate 			 * new reference since I'm holding the rddir_cache_lock
607*0Sstevel@tonic-gate 			 */
608*0Sstevel@tonic-gate 
609*0Sstevel@tonic-gate 			/*
610*0Sstevel@tonic-gate 			 * error will be zero if some thread signaled us asking
611*0Sstevel@tonic-gate 			 * that the caches be freed. In such case, free caches
612*0Sstevel@tonic-gate 			 * even if they're still valid and nobody is referencing
613*0Sstevel@tonic-gate 			 * them at this time. Otherwise, free caches only
614*0Sstevel@tonic-gate 			 * if their time to live (ttl) has expired.
615*0Sstevel@tonic-gate 			 */
616*0Sstevel@tonic-gate 			if (error == ETIME && (p->ttl > time((time_t *)NULL))) {
617*0Sstevel@tonic-gate 				/*
618*0Sstevel@tonic-gate 				 * Scheduled cache cleanup, if cache is still
619*0Sstevel@tonic-gate 				 * valid don't free.
620*0Sstevel@tonic-gate 				 */
621*0Sstevel@tonic-gate 				if (trace > 1) {
622*0Sstevel@tonic-gate 					trace_prt(1,
623*0Sstevel@tonic-gate 					"%s cache still valid\n", p->map);
624*0Sstevel@tonic-gate 				}
625*0Sstevel@tonic-gate 				continue;
626*0Sstevel@tonic-gate 			}
627*0Sstevel@tonic-gate 			if (trace > 1)
628*0Sstevel@tonic-gate 				trace_prt(1, "%s freeing cache\n", p->map);
629*0Sstevel@tonic-gate 			assert(!p->in_use);
630*0Sstevel@tonic-gate 			error = rddir_cache_delete(p);
631*0Sstevel@tonic-gate 			assert(!error);
632*0Sstevel@tonic-gate 		}
633*0Sstevel@tonic-gate 		rw_unlock(&rddir_cache_lock);
634*0Sstevel@tonic-gate 
635*0Sstevel@tonic-gate 		/*
636*0Sstevel@tonic-gate 		 * wakeup the thread/threads waiting for the
637*0Sstevel@tonic-gate 		 * cleanup to finish
638*0Sstevel@tonic-gate 		 */
639*0Sstevel@tonic-gate 		mutex_lock(&cleanup_lock);
640*0Sstevel@tonic-gate 		cond_broadcast(&cleanup_done_cv);
641*0Sstevel@tonic-gate 	}
642*0Sstevel@tonic-gate 	/* NOTREACHED */
643*0Sstevel@tonic-gate }
644