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 1995-2003 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  *
26*0Sstevel@tonic-gate  * Common code and structures used by name-service-switch "files" backends.
27*0Sstevel@tonic-gate  */
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
30*0Sstevel@tonic-gate 
31*0Sstevel@tonic-gate /*
32*0Sstevel@tonic-gate  * An implementation that used mmap() sensibly would be a wonderful thing,
33*0Sstevel@tonic-gate  *   but this here is just yer standard fgets() thang.
34*0Sstevel@tonic-gate  */
35*0Sstevel@tonic-gate 
36*0Sstevel@tonic-gate #include "files_common.h"
37*0Sstevel@tonic-gate #include <stdio.h>
38*0Sstevel@tonic-gate #include <stdlib.h>
39*0Sstevel@tonic-gate #include <string.h>
40*0Sstevel@tonic-gate #include <ctype.h>
41*0Sstevel@tonic-gate #include <fcntl.h>
42*0Sstevel@tonic-gate #include <poll.h>
43*0Sstevel@tonic-gate #include <unistd.h>
44*0Sstevel@tonic-gate #include <sys/stat.h>
45*0Sstevel@tonic-gate 
46*0Sstevel@tonic-gate /*ARGSUSED*/
47*0Sstevel@tonic-gate nss_status_t
48*0Sstevel@tonic-gate _nss_files_setent(be, dummy)
49*0Sstevel@tonic-gate 	files_backend_ptr_t	be;
50*0Sstevel@tonic-gate 	void			*dummy;
51*0Sstevel@tonic-gate {
52*0Sstevel@tonic-gate 	if (be->f == 0) {
53*0Sstevel@tonic-gate 		if (be->filename == 0) {
54*0Sstevel@tonic-gate 			/* Backend isn't initialized properly? */
55*0Sstevel@tonic-gate 			return (NSS_UNAVAIL);
56*0Sstevel@tonic-gate 		}
57*0Sstevel@tonic-gate 		if ((be->f = __nsl_fopen(be->filename, "r")) == 0) {
58*0Sstevel@tonic-gate 			return (NSS_UNAVAIL);
59*0Sstevel@tonic-gate 		}
60*0Sstevel@tonic-gate 	} else {
61*0Sstevel@tonic-gate 		__nsl_rewind(be->f);
62*0Sstevel@tonic-gate 	}
63*0Sstevel@tonic-gate 	return (NSS_SUCCESS);
64*0Sstevel@tonic-gate }
65*0Sstevel@tonic-gate 
66*0Sstevel@tonic-gate /*ARGSUSED*/
67*0Sstevel@tonic-gate nss_status_t
68*0Sstevel@tonic-gate _nss_files_endent(be, dummy)
69*0Sstevel@tonic-gate 	files_backend_ptr_t	be;
70*0Sstevel@tonic-gate 	void			*dummy;
71*0Sstevel@tonic-gate {
72*0Sstevel@tonic-gate 	if (be->f != 0) {
73*0Sstevel@tonic-gate 		__nsl_fclose(be->f);
74*0Sstevel@tonic-gate 		be->f = 0;
75*0Sstevel@tonic-gate 	}
76*0Sstevel@tonic-gate 	if (be->buf != 0) {
77*0Sstevel@tonic-gate 		free(be->buf);
78*0Sstevel@tonic-gate 		be->buf = 0;
79*0Sstevel@tonic-gate 	}
80*0Sstevel@tonic-gate 	return (NSS_SUCCESS);
81*0Sstevel@tonic-gate }
82*0Sstevel@tonic-gate 
83*0Sstevel@tonic-gate /*
84*0Sstevel@tonic-gate  * This routine reads a line, including the processing of continuation
85*0Sstevel@tonic-gate  * characters.  It always leaves (or inserts) \n\0 at the end of the line.
86*0Sstevel@tonic-gate  * It returns the length of the line read, excluding the \n\0.  Who's idea
87*0Sstevel@tonic-gate  * was this?
88*0Sstevel@tonic-gate  * Returns -1 on EOF.
89*0Sstevel@tonic-gate  *
90*0Sstevel@tonic-gate  * Note that since each concurrent call to _nss_files_read_line has
91*0Sstevel@tonic-gate  * it's own FILE pointer, we can use getc_unlocked w/o difficulties,
92*0Sstevel@tonic-gate  * a substantial performance win.
93*0Sstevel@tonic-gate  */
94*0Sstevel@tonic-gate int
95*0Sstevel@tonic-gate _nss_files_read_line(f, buffer, buflen)
96*0Sstevel@tonic-gate 	__NSL_FILE		*f;
97*0Sstevel@tonic-gate 	char			*buffer;
98*0Sstevel@tonic-gate 	int			buflen;
99*0Sstevel@tonic-gate {
100*0Sstevel@tonic-gate 	int			linelen;	/* 1st unused slot in buffer */
101*0Sstevel@tonic-gate 	int			c;
102*0Sstevel@tonic-gate 
103*0Sstevel@tonic-gate 	/*CONSTCOND*/
104*0Sstevel@tonic-gate 	while (1) {
105*0Sstevel@tonic-gate 		linelen = 0;
106*0Sstevel@tonic-gate 		while (linelen < buflen - 1) {	/* "- 1" saves room for \n\0 */
107*0Sstevel@tonic-gate 			switch (c = __nsl_getc_unlocked(f)) {
108*0Sstevel@tonic-gate 			case EOF:
109*0Sstevel@tonic-gate 				if (linelen == 0 ||
110*0Sstevel@tonic-gate 				    buffer[linelen - 1] == '\\') {
111*0Sstevel@tonic-gate 					return (-1);
112*0Sstevel@tonic-gate 				} else {
113*0Sstevel@tonic-gate 					buffer[linelen    ] = '\n';
114*0Sstevel@tonic-gate 					buffer[linelen + 1] = '\0';
115*0Sstevel@tonic-gate 					return (linelen);
116*0Sstevel@tonic-gate 				}
117*0Sstevel@tonic-gate 			case '\n':
118*0Sstevel@tonic-gate 				if (linelen > 0 &&
119*0Sstevel@tonic-gate 				    buffer[linelen - 1] == '\\') {
120*0Sstevel@tonic-gate 					--linelen;  /* remove the '\\' */
121*0Sstevel@tonic-gate 				} else {
122*0Sstevel@tonic-gate 					buffer[linelen    ] = '\n';
123*0Sstevel@tonic-gate 					buffer[linelen + 1] = '\0';
124*0Sstevel@tonic-gate 					return (linelen);
125*0Sstevel@tonic-gate 				}
126*0Sstevel@tonic-gate 				break;
127*0Sstevel@tonic-gate 			default:
128*0Sstevel@tonic-gate 				buffer[linelen++] = c;
129*0Sstevel@tonic-gate 			}
130*0Sstevel@tonic-gate 		}
131*0Sstevel@tonic-gate 		/* Buffer overflow -- eat rest of line and loop again */
132*0Sstevel@tonic-gate 		/* ===> Should syslog() */
133*0Sstevel@tonic-gate 		do {
134*0Sstevel@tonic-gate 			c = __nsl_getc_unlocked(f);
135*0Sstevel@tonic-gate 			if (c == EOF) {
136*0Sstevel@tonic-gate 				return (-1);
137*0Sstevel@tonic-gate 			}
138*0Sstevel@tonic-gate 		} while (c != '\n');
139*0Sstevel@tonic-gate 	}
140*0Sstevel@tonic-gate 	/*NOTREACHED*/
141*0Sstevel@tonic-gate }
142*0Sstevel@tonic-gate 
143*0Sstevel@tonic-gate /*
144*0Sstevel@tonic-gate  * used only for getgroupbymem() now.
145*0Sstevel@tonic-gate  */
146*0Sstevel@tonic-gate nss_status_t
147*0Sstevel@tonic-gate _nss_files_do_all(be, args, filter, func)
148*0Sstevel@tonic-gate 	files_backend_ptr_t	be;
149*0Sstevel@tonic-gate 	void			*args;
150*0Sstevel@tonic-gate 	const char		*filter;
151*0Sstevel@tonic-gate 	files_do_all_func_t	func;
152*0Sstevel@tonic-gate {
153*0Sstevel@tonic-gate 	char			*buffer;
154*0Sstevel@tonic-gate 	int			buflen;
155*0Sstevel@tonic-gate 	nss_status_t		res;
156*0Sstevel@tonic-gate 
157*0Sstevel@tonic-gate 	if (be->buf == 0 &&
158*0Sstevel@tonic-gate 		(be->buf = malloc(be->minbuf)) == 0) {
159*0Sstevel@tonic-gate 		return (NSS_UNAVAIL);
160*0Sstevel@tonic-gate 	}
161*0Sstevel@tonic-gate 	buffer = be->buf;
162*0Sstevel@tonic-gate 	buflen = be->minbuf;
163*0Sstevel@tonic-gate 
164*0Sstevel@tonic-gate 	if ((res = _nss_files_setent(be, 0)) != NSS_SUCCESS) {
165*0Sstevel@tonic-gate 		return (res);
166*0Sstevel@tonic-gate 	}
167*0Sstevel@tonic-gate 
168*0Sstevel@tonic-gate 	res = NSS_NOTFOUND;
169*0Sstevel@tonic-gate 
170*0Sstevel@tonic-gate 	do {
171*0Sstevel@tonic-gate 		int		linelen;
172*0Sstevel@tonic-gate 
173*0Sstevel@tonic-gate 		if ((linelen = _nss_files_read_line(be->f, buffer,
174*0Sstevel@tonic-gate 		    buflen)) < 0) {
175*0Sstevel@tonic-gate 			/* End of file */
176*0Sstevel@tonic-gate 			break;
177*0Sstevel@tonic-gate 		}
178*0Sstevel@tonic-gate 		if (filter != 0 && strstr(buffer, filter) == 0) {
179*0Sstevel@tonic-gate 			/*
180*0Sstevel@tonic-gate 			 * Optimization:  if the entry doesn't contain the
181*0Sstevel@tonic-gate 			 *   filter string then it can't be the entry we want,
182*0Sstevel@tonic-gate 			 *   so don't bother looking more closely at it.
183*0Sstevel@tonic-gate 			 */
184*0Sstevel@tonic-gate 			continue;
185*0Sstevel@tonic-gate 		}
186*0Sstevel@tonic-gate 		res = (*func)(buffer, linelen, args);
187*0Sstevel@tonic-gate 
188*0Sstevel@tonic-gate 	} while (res == NSS_NOTFOUND);
189*0Sstevel@tonic-gate 
190*0Sstevel@tonic-gate 	_nss_files_endent(be, 0);
191*0Sstevel@tonic-gate 	return (res);
192*0Sstevel@tonic-gate }
193*0Sstevel@tonic-gate 
194*0Sstevel@tonic-gate /*
195*0Sstevel@tonic-gate  * Could implement this as an iterator function on top of _nss_files_do_all(),
196*0Sstevel@tonic-gate  *   but the shared code is small enough that it'd be pretty silly.
197*0Sstevel@tonic-gate  */
198*0Sstevel@tonic-gate nss_status_t
199*0Sstevel@tonic-gate _nss_files_XY_all(be, args, netdb, filter, check)
200*0Sstevel@tonic-gate 	files_backend_ptr_t	be;
201*0Sstevel@tonic-gate 	nss_XbyY_args_t		*args;
202*0Sstevel@tonic-gate 	int			netdb;		/* whether it uses netdb */
203*0Sstevel@tonic-gate 						/* format or not */
204*0Sstevel@tonic-gate 	const char		*filter;	/* advisory, to speed up */
205*0Sstevel@tonic-gate 						/* string search */
206*0Sstevel@tonic-gate 	files_XY_check_func	check;	/* NULL means one-shot, for getXXent */
207*0Sstevel@tonic-gate {
208*0Sstevel@tonic-gate 	nss_status_t		res;
209*0Sstevel@tonic-gate 	int	parsestat;
210*0Sstevel@tonic-gate 	int (*func)();
211*0Sstevel@tonic-gate 
212*0Sstevel@tonic-gate 	if (be->buf == 0 &&
213*0Sstevel@tonic-gate 		(be->buf = malloc(be->minbuf)) == 0) {
214*0Sstevel@tonic-gate 		return (NSS_UNAVAIL); /* really panic, malloc failed */
215*0Sstevel@tonic-gate 	}
216*0Sstevel@tonic-gate 
217*0Sstevel@tonic-gate 	if (check != 0 || be->f == 0) {
218*0Sstevel@tonic-gate 		if ((res = _nss_files_setent(be, 0)) != NSS_SUCCESS) {
219*0Sstevel@tonic-gate 			return (res);
220*0Sstevel@tonic-gate 		}
221*0Sstevel@tonic-gate 	}
222*0Sstevel@tonic-gate 
223*0Sstevel@tonic-gate 	res = NSS_NOTFOUND;
224*0Sstevel@tonic-gate 
225*0Sstevel@tonic-gate 	/*CONSTCOND*/
226*0Sstevel@tonic-gate 	while (1) {
227*0Sstevel@tonic-gate 		char		*instr	= be->buf;
228*0Sstevel@tonic-gate 		int		linelen;
229*0Sstevel@tonic-gate 
230*0Sstevel@tonic-gate 		if ((linelen = _nss_files_read_line(be->f, instr,
231*0Sstevel@tonic-gate 		    be->minbuf)) < 0) {
232*0Sstevel@tonic-gate 			/* End of file */
233*0Sstevel@tonic-gate 			args->returnval = 0;
234*0Sstevel@tonic-gate 			args->erange    = 0;
235*0Sstevel@tonic-gate 			break;
236*0Sstevel@tonic-gate 		}
237*0Sstevel@tonic-gate 		if (filter != 0 && strstr(instr, filter) == 0) {
238*0Sstevel@tonic-gate 			/*
239*0Sstevel@tonic-gate 			 * Optimization:  if the entry doesn't contain the
240*0Sstevel@tonic-gate 			 *   filter string then it can't be the entry we want,
241*0Sstevel@tonic-gate 			 *   so don't bother looking more closely at it.
242*0Sstevel@tonic-gate 			 */
243*0Sstevel@tonic-gate 			continue;
244*0Sstevel@tonic-gate 		}
245*0Sstevel@tonic-gate 		if (netdb) {
246*0Sstevel@tonic-gate 			char		*first;
247*0Sstevel@tonic-gate 			char		*last;
248*0Sstevel@tonic-gate 
249*0Sstevel@tonic-gate 			if ((last = strchr(instr, '#')) == 0) {
250*0Sstevel@tonic-gate 				last = instr + linelen;
251*0Sstevel@tonic-gate 			}
252*0Sstevel@tonic-gate 			*last-- = '\0';		/* Nuke '\n' or #comment */
253*0Sstevel@tonic-gate 
254*0Sstevel@tonic-gate 			/*
255*0Sstevel@tonic-gate 			 * Skip leading whitespace.  Normally there isn't
256*0Sstevel@tonic-gate 			 *   any, so it's not worth calling strspn().
257*0Sstevel@tonic-gate 			 */
258*0Sstevel@tonic-gate 			for (first = instr;  isspace(*first);  first++) {
259*0Sstevel@tonic-gate 				;
260*0Sstevel@tonic-gate 			}
261*0Sstevel@tonic-gate 			if (*first == '\0') {
262*0Sstevel@tonic-gate 				continue;
263*0Sstevel@tonic-gate 			}
264*0Sstevel@tonic-gate 			/*
265*0Sstevel@tonic-gate 			 * Found something non-blank on the line.  Skip back
266*0Sstevel@tonic-gate 			 * over any trailing whitespace;  since we know
267*0Sstevel@tonic-gate 			 * there's non-whitespace earlier in the line,
268*0Sstevel@tonic-gate 			 * checking for termination is easy.
269*0Sstevel@tonic-gate 			 */
270*0Sstevel@tonic-gate 			while (isspace(*last)) {
271*0Sstevel@tonic-gate 				--last;
272*0Sstevel@tonic-gate 			}
273*0Sstevel@tonic-gate 
274*0Sstevel@tonic-gate 			linelen = last - first + 1;
275*0Sstevel@tonic-gate 			if (first != instr) {
276*0Sstevel@tonic-gate 					instr = first;
277*0Sstevel@tonic-gate 			}
278*0Sstevel@tonic-gate 		}
279*0Sstevel@tonic-gate 
280*0Sstevel@tonic-gate 		args->returnval = 0;
281*0Sstevel@tonic-gate 
282*0Sstevel@tonic-gate 		func = args->str2ent;
283*0Sstevel@tonic-gate 		parsestat = (*func)(instr, linelen, args->buf.result,
284*0Sstevel@tonic-gate 					args->buf.buffer, args->buf.buflen);
285*0Sstevel@tonic-gate 
286*0Sstevel@tonic-gate 		if (parsestat == NSS_STR_PARSE_SUCCESS) {
287*0Sstevel@tonic-gate 			args->returnval = args->buf.result;
288*0Sstevel@tonic-gate 			if (check == 0 || (*check)(args)) {
289*0Sstevel@tonic-gate 				res = NSS_SUCCESS;
290*0Sstevel@tonic-gate 				break;
291*0Sstevel@tonic-gate 			}
292*0Sstevel@tonic-gate 		} else if (parsestat == NSS_STR_PARSE_ERANGE) {
293*0Sstevel@tonic-gate 			args->erange = 1;
294*0Sstevel@tonic-gate 			break;
295*0Sstevel@tonic-gate 		} /* else if (parsestat == NSS_STR_PARSE_PARSE) don't care ! */
296*0Sstevel@tonic-gate 	}
297*0Sstevel@tonic-gate 
298*0Sstevel@tonic-gate 	/*
299*0Sstevel@tonic-gate 	 * stayopen is set to 0 by default in order to close the opened
300*0Sstevel@tonic-gate 	 * file.  Some applications may break if it is set to 1.
301*0Sstevel@tonic-gate 	 */
302*0Sstevel@tonic-gate 	if (check != 0 && !args->stayopen) {
303*0Sstevel@tonic-gate 		(void) _nss_files_endent(be, 0);
304*0Sstevel@tonic-gate 	}
305*0Sstevel@tonic-gate 
306*0Sstevel@tonic-gate 	return (res);
307*0Sstevel@tonic-gate }
308*0Sstevel@tonic-gate 
309*0Sstevel@tonic-gate /*
310*0Sstevel@tonic-gate  * File hashing support.  Critical for sites with large (e.g. 1000+ lines)
311*0Sstevel@tonic-gate  * /etc/passwd or /etc/group files.  Currently only used by getpw*() and
312*0Sstevel@tonic-gate  * getgr*() routines, but any files backend can use this stuff.
313*0Sstevel@tonic-gate  */
314*0Sstevel@tonic-gate static void
315*0Sstevel@tonic-gate _nss_files_hash_destroy(files_hash_t *fhp)
316*0Sstevel@tonic-gate {
317*0Sstevel@tonic-gate 	free(fhp->fh_table);
318*0Sstevel@tonic-gate 	fhp->fh_table = NULL;
319*0Sstevel@tonic-gate 	free(fhp->fh_line);
320*0Sstevel@tonic-gate 	fhp->fh_line = NULL;
321*0Sstevel@tonic-gate 	free(fhp->fh_file_start);
322*0Sstevel@tonic-gate 	fhp->fh_file_start = NULL;
323*0Sstevel@tonic-gate }
324*0Sstevel@tonic-gate #ifdef PIC
325*0Sstevel@tonic-gate /*
326*0Sstevel@tonic-gate  * It turns out the hashing stuff really needs to be disabled for processes
327*0Sstevel@tonic-gate  * other than the nscd; the consumption of swap space and memory is otherwise
328*0Sstevel@tonic-gate  * unacceptable when the nscd is killed w/ a large passwd file (4M) active.
329*0Sstevel@tonic-gate  * See 4031930 for details.
330*0Sstevel@tonic-gate  * So we just use this psuedo function to enable the hashing feature.  Since
331*0Sstevel@tonic-gate  * this function name is private, we just create a function w/ the name
332*0Sstevel@tonic-gate  *  __nss_use_files_hash in the nscd itself and everyone else uses the old
333*0Sstevel@tonic-gate  * interface.
334*0Sstevel@tonic-gate  * We also disable hashing for .a executables to avoid problems with large
335*0Sstevel@tonic-gate  * files....
336*0Sstevel@tonic-gate  */
337*0Sstevel@tonic-gate 
338*0Sstevel@tonic-gate #pragma weak __nss_use_files_hash
339*0Sstevel@tonic-gate 
340*0Sstevel@tonic-gate extern void  __nss_use_files_hash(void);
341*0Sstevel@tonic-gate #endif /* pic */
342*0Sstevel@tonic-gate 
343*0Sstevel@tonic-gate nss_status_t
344*0Sstevel@tonic-gate _nss_files_XY_hash(files_backend_ptr_t be, nss_XbyY_args_t *args,
345*0Sstevel@tonic-gate 	int netdb, files_hash_t *fhp, int hashop, files_XY_check_func check)
346*0Sstevel@tonic-gate {
347*0Sstevel@tonic-gate 	int fd, retries, ht;
348*0Sstevel@tonic-gate 	uint_t hash, line, f;
349*0Sstevel@tonic-gate 	files_hashent_t *hp, *htab;
350*0Sstevel@tonic-gate 	char *cp, *first, *last;
351*0Sstevel@tonic-gate 	nss_XbyY_args_t xargs;
352*0Sstevel@tonic-gate 	struct stat64 st;
353*0Sstevel@tonic-gate 
354*0Sstevel@tonic-gate #ifndef PIC
355*0Sstevel@tonic-gate 	return (_nss_files_XY_all(be, args, netdb, 0, check));
356*0Sstevel@tonic-gate }
357*0Sstevel@tonic-gate #else
358*0Sstevel@tonic-gate 	if (__nss_use_files_hash == 0)
359*0Sstevel@tonic-gate 		return (_nss_files_XY_all(be, args, netdb, 0, check));
360*0Sstevel@tonic-gate 
361*0Sstevel@tonic-gate 	mutex_lock(&fhp->fh_lock);
362*0Sstevel@tonic-gate retry:
363*0Sstevel@tonic-gate 	retries = 100;
364*0Sstevel@tonic-gate 	while (stat64(be->filename, &st) < 0) {
365*0Sstevel@tonic-gate 		/*
366*0Sstevel@tonic-gate 		 * On a healthy system this can't happen except during brief
367*0Sstevel@tonic-gate 		 * periods when the file is being modified/renamed.  Keep
368*0Sstevel@tonic-gate 		 * trying until things settle down, but eventually give up.
369*0Sstevel@tonic-gate 		 */
370*0Sstevel@tonic-gate 		if (--retries == 0)
371*0Sstevel@tonic-gate 			goto unavail;
372*0Sstevel@tonic-gate 		poll(0, 0, 100);
373*0Sstevel@tonic-gate 	}
374*0Sstevel@tonic-gate 
375*0Sstevel@tonic-gate 	if (st.st_mtim.tv_sec == fhp->fh_mtime.tv_sec &&
376*0Sstevel@tonic-gate 	    st.st_mtim.tv_nsec == fhp->fh_mtime.tv_nsec &&
377*0Sstevel@tonic-gate 	    fhp->fh_table != NULL) {
378*0Sstevel@tonic-gate 		htab = &fhp->fh_table[hashop * fhp->fh_size];
379*0Sstevel@tonic-gate 		hash = fhp->fh_hash_func[hashop](args, 1);
380*0Sstevel@tonic-gate 		for (hp = htab[hash % fhp->fh_size].h_first; hp != NULL;
381*0Sstevel@tonic-gate 		    hp = hp->h_next) {
382*0Sstevel@tonic-gate 			if (hp->h_hash != hash)
383*0Sstevel@tonic-gate 				continue;
384*0Sstevel@tonic-gate 			line = hp - htab;
385*0Sstevel@tonic-gate 			if ((*args->str2ent)(fhp->fh_line[line].l_start,
386*0Sstevel@tonic-gate 			    fhp->fh_line[line].l_len, args->buf.result,
387*0Sstevel@tonic-gate 			    args->buf.buffer, args->buf.buflen) ==
388*0Sstevel@tonic-gate 			    NSS_STR_PARSE_SUCCESS) {
389*0Sstevel@tonic-gate 				args->returnval = args->buf.result;
390*0Sstevel@tonic-gate 				if ((*check)(args)) {
391*0Sstevel@tonic-gate 					mutex_unlock(&fhp->fh_lock);
392*0Sstevel@tonic-gate 					return (NSS_SUCCESS);
393*0Sstevel@tonic-gate 				}
394*0Sstevel@tonic-gate 			} else {
395*0Sstevel@tonic-gate 				args->erange = 1;
396*0Sstevel@tonic-gate 			}
397*0Sstevel@tonic-gate 		}
398*0Sstevel@tonic-gate 		args->returnval = 0;
399*0Sstevel@tonic-gate 		mutex_unlock(&fhp->fh_lock);
400*0Sstevel@tonic-gate 		return (NSS_NOTFOUND);
401*0Sstevel@tonic-gate 	}
402*0Sstevel@tonic-gate 
403*0Sstevel@tonic-gate 	_nss_files_hash_destroy(fhp);
404*0Sstevel@tonic-gate 
405*0Sstevel@tonic-gate 	if (st.st_size > SSIZE_MAX)
406*0Sstevel@tonic-gate 		goto unavail;
407*0Sstevel@tonic-gate 
408*0Sstevel@tonic-gate 	if ((fhp->fh_file_start = malloc((ssize_t)st.st_size + 1)) == NULL)
409*0Sstevel@tonic-gate 		goto unavail;
410*0Sstevel@tonic-gate 
411*0Sstevel@tonic-gate 	if ((fd = open(be->filename, O_RDONLY)) < 0)
412*0Sstevel@tonic-gate 		goto unavail;
413*0Sstevel@tonic-gate 
414*0Sstevel@tonic-gate 	if (read(fd, fhp->fh_file_start, (ssize_t)st.st_size) !=
415*0Sstevel@tonic-gate 	    (ssize_t)st.st_size) {
416*0Sstevel@tonic-gate 		close(fd);
417*0Sstevel@tonic-gate 		goto retry;
418*0Sstevel@tonic-gate 	}
419*0Sstevel@tonic-gate 
420*0Sstevel@tonic-gate 	close(fd);
421*0Sstevel@tonic-gate 
422*0Sstevel@tonic-gate 	fhp->fh_file_end = fhp->fh_file_start + (off_t)st.st_size;
423*0Sstevel@tonic-gate 	*fhp->fh_file_end = '\n';
424*0Sstevel@tonic-gate 	fhp->fh_mtime = st.st_mtim;
425*0Sstevel@tonic-gate 
426*0Sstevel@tonic-gate 	/*
427*0Sstevel@tonic-gate 	 * If the file changed since we read it, or if it's less than
428*0Sstevel@tonic-gate 	 * 1-2 seconds old, don't trust it; its modification may still
429*0Sstevel@tonic-gate 	 * be in progress.  The latter is a heuristic hack to minimize
430*0Sstevel@tonic-gate 	 * the likelihood of damage if someone modifies /etc/mumble
431*0Sstevel@tonic-gate 	 * directly (as opposed to editing and renaming a temp file).
432*0Sstevel@tonic-gate 	 *
433*0Sstevel@tonic-gate 	 * Note: the cast to u_int is there in case (1) someone rdated
434*0Sstevel@tonic-gate 	 * the system backwards since the last modification of /etc/mumble
435*0Sstevel@tonic-gate 	 * or (2) this is a diskless client whose time is badly out of sync
436*0Sstevel@tonic-gate 	 * with its server.  The 1-2 second age hack doesn't cover these
437*0Sstevel@tonic-gate 	 * cases -- oh well.
438*0Sstevel@tonic-gate 	 */
439*0Sstevel@tonic-gate 	if (stat64(be->filename, &st) < 0 ||
440*0Sstevel@tonic-gate 	    st.st_mtim.tv_sec != fhp->fh_mtime.tv_sec ||
441*0Sstevel@tonic-gate 	    st.st_mtim.tv_nsec != fhp->fh_mtime.tv_nsec ||
442*0Sstevel@tonic-gate 	    (uint_t)(time(0) - st.st_mtim.tv_sec + 2) < 4) {
443*0Sstevel@tonic-gate 		poll(0, 0, 1000);
444*0Sstevel@tonic-gate 		goto retry;
445*0Sstevel@tonic-gate 	}
446*0Sstevel@tonic-gate 
447*0Sstevel@tonic-gate 	line = 1;
448*0Sstevel@tonic-gate 	for (cp = fhp->fh_file_start; cp < fhp->fh_file_end; cp++)
449*0Sstevel@tonic-gate 		if (*cp == '\n')
450*0Sstevel@tonic-gate 			line++;
451*0Sstevel@tonic-gate 
452*0Sstevel@tonic-gate 	for (f = 2; f * f <= line; f++) {	/* find next largest prime */
453*0Sstevel@tonic-gate 		if (line % f == 0) {
454*0Sstevel@tonic-gate 			f = 1;
455*0Sstevel@tonic-gate 			line++;
456*0Sstevel@tonic-gate 		}
457*0Sstevel@tonic-gate 	}
458*0Sstevel@tonic-gate 
459*0Sstevel@tonic-gate 	fhp->fh_size = line;
460*0Sstevel@tonic-gate 	fhp->fh_line = malloc(line * sizeof (files_linetab_t));
461*0Sstevel@tonic-gate 	fhp->fh_table = calloc(line * fhp->fh_nhtab, sizeof (files_hashent_t));
462*0Sstevel@tonic-gate 	if (fhp->fh_line == NULL || fhp->fh_table == NULL)
463*0Sstevel@tonic-gate 		goto unavail;
464*0Sstevel@tonic-gate 
465*0Sstevel@tonic-gate 	xargs = *args;
466*0Sstevel@tonic-gate 	xargs.buf.result = malloc(fhp->fh_resultsize + fhp->fh_bufsize);
467*0Sstevel@tonic-gate 	if (xargs.buf.result == NULL)
468*0Sstevel@tonic-gate 		goto unavail;
469*0Sstevel@tonic-gate 	xargs.buf.buffer = (char *)xargs.buf.result + fhp->fh_resultsize;
470*0Sstevel@tonic-gate 	xargs.buf.buflen = fhp->fh_bufsize;
471*0Sstevel@tonic-gate 	xargs.returnval = xargs.buf.result;
472*0Sstevel@tonic-gate 
473*0Sstevel@tonic-gate 	line = 0;
474*0Sstevel@tonic-gate 	cp = fhp->fh_file_start;
475*0Sstevel@tonic-gate 	while (cp < fhp->fh_file_end) {
476*0Sstevel@tonic-gate 		first = cp;
477*0Sstevel@tonic-gate 		while (*cp != '\n')
478*0Sstevel@tonic-gate 			cp++;
479*0Sstevel@tonic-gate 		if (cp > first && *(cp - 1) == '\\') {
480*0Sstevel@tonic-gate 			memmove(first + 2, first, cp - first - 1);
481*0Sstevel@tonic-gate 			cp = first + 2;
482*0Sstevel@tonic-gate 			continue;
483*0Sstevel@tonic-gate 		}
484*0Sstevel@tonic-gate 		last = cp;
485*0Sstevel@tonic-gate 		*cp++ = '\0';
486*0Sstevel@tonic-gate 		if (netdb) {
487*0Sstevel@tonic-gate 			if ((last = strchr(first, '#')) == 0)
488*0Sstevel@tonic-gate 				last = cp - 1;
489*0Sstevel@tonic-gate 			*last-- = '\0';		/* nuke '\n' or #comment */
490*0Sstevel@tonic-gate 			while (isspace(*first))	/* nuke leading whitespace */
491*0Sstevel@tonic-gate 				first++;
492*0Sstevel@tonic-gate 			if (*first == '\0')	/* skip content-free lines */
493*0Sstevel@tonic-gate 				continue;
494*0Sstevel@tonic-gate 			while (isspace(*last))	/* nuke trailing whitespace */
495*0Sstevel@tonic-gate 				--last;
496*0Sstevel@tonic-gate 			*++last = '\0';
497*0Sstevel@tonic-gate 		}
498*0Sstevel@tonic-gate 		if ((*xargs.str2ent)(first, last - first,
499*0Sstevel@tonic-gate 		    xargs.buf.result, xargs.buf.buffer, xargs.buf.buflen) !=
500*0Sstevel@tonic-gate 		    NSS_STR_PARSE_SUCCESS)
501*0Sstevel@tonic-gate 			continue;
502*0Sstevel@tonic-gate 		for (ht = 0; ht < fhp->fh_nhtab; ht++) {
503*0Sstevel@tonic-gate 			hp = &fhp->fh_table[ht * fhp->fh_size + line];
504*0Sstevel@tonic-gate 			hp->h_hash = fhp->fh_hash_func[ht](&xargs, 0);
505*0Sstevel@tonic-gate 		}
506*0Sstevel@tonic-gate 		fhp->fh_line[line].l_start = first;
507*0Sstevel@tonic-gate 		fhp->fh_line[line++].l_len = last - first;
508*0Sstevel@tonic-gate 	}
509*0Sstevel@tonic-gate 	free(xargs.buf.result);
510*0Sstevel@tonic-gate 
511*0Sstevel@tonic-gate 	/*
512*0Sstevel@tonic-gate 	 * Populate the hash tables in reverse order so that the hash chains
513*0Sstevel@tonic-gate 	 * end up in forward order.  This ensures that hashed lookups find
514*0Sstevel@tonic-gate 	 * things in the same order that a linear search of the file would.
515*0Sstevel@tonic-gate 	 * This is essential in cases where there could be multiple matches.
516*0Sstevel@tonic-gate 	 * For example: until 2.7, root and smtp both had uid 0; but we
517*0Sstevel@tonic-gate 	 * certainly wouldn't want getpwuid(0) to return smtp.
518*0Sstevel@tonic-gate 	 */
519*0Sstevel@tonic-gate 	for (ht = 0; ht < fhp->fh_nhtab; ht++) {
520*0Sstevel@tonic-gate 		htab = &fhp->fh_table[ht * fhp->fh_size];
521*0Sstevel@tonic-gate 		for (hp = &htab[line - 1]; hp >= htab; hp--) {
522*0Sstevel@tonic-gate 			uint_t bucket = hp->h_hash % fhp->fh_size;
523*0Sstevel@tonic-gate 			hp->h_next = htab[bucket].h_first;
524*0Sstevel@tonic-gate 			htab[bucket].h_first = hp;
525*0Sstevel@tonic-gate 		}
526*0Sstevel@tonic-gate 	}
527*0Sstevel@tonic-gate 
528*0Sstevel@tonic-gate 	goto retry;
529*0Sstevel@tonic-gate 
530*0Sstevel@tonic-gate unavail:
531*0Sstevel@tonic-gate 	_nss_files_hash_destroy(fhp);
532*0Sstevel@tonic-gate 	mutex_unlock(&fhp->fh_lock);
533*0Sstevel@tonic-gate 	return (NSS_UNAVAIL);
534*0Sstevel@tonic-gate }
535*0Sstevel@tonic-gate #endif /* PIC */
536*0Sstevel@tonic-gate 
537*0Sstevel@tonic-gate nss_status_t
538*0Sstevel@tonic-gate _nss_files_getent_rigid(be, a)
539*0Sstevel@tonic-gate 	files_backend_ptr_t	be;
540*0Sstevel@tonic-gate 	void			*a;
541*0Sstevel@tonic-gate {
542*0Sstevel@tonic-gate 	nss_XbyY_args_t		*args = (nss_XbyY_args_t *)a;
543*0Sstevel@tonic-gate 
544*0Sstevel@tonic-gate 	return (_nss_files_XY_all(be, args, 0, 0, 0));
545*0Sstevel@tonic-gate }
546*0Sstevel@tonic-gate 
547*0Sstevel@tonic-gate nss_status_t
548*0Sstevel@tonic-gate _nss_files_getent_netdb(be, a)
549*0Sstevel@tonic-gate 	files_backend_ptr_t	be;
550*0Sstevel@tonic-gate 	void			*a;
551*0Sstevel@tonic-gate {
552*0Sstevel@tonic-gate 	nss_XbyY_args_t		*args = (nss_XbyY_args_t *)a;
553*0Sstevel@tonic-gate 
554*0Sstevel@tonic-gate 	return (_nss_files_XY_all(be, args, 1, 0, 0));
555*0Sstevel@tonic-gate }
556*0Sstevel@tonic-gate 
557*0Sstevel@tonic-gate /*ARGSUSED*/
558*0Sstevel@tonic-gate nss_status_t
559*0Sstevel@tonic-gate _nss_files_destr(be, dummy)
560*0Sstevel@tonic-gate 	files_backend_ptr_t	be;
561*0Sstevel@tonic-gate 	void			*dummy;
562*0Sstevel@tonic-gate {
563*0Sstevel@tonic-gate 	if (be != 0) {
564*0Sstevel@tonic-gate 		if (be->f != 0) {
565*0Sstevel@tonic-gate 			_nss_files_endent(be, 0);
566*0Sstevel@tonic-gate 		}
567*0Sstevel@tonic-gate 		if (be->hashinfo != NULL) {
568*0Sstevel@tonic-gate 			mutex_lock(&be->hashinfo->fh_lock);
569*0Sstevel@tonic-gate 			if (--be->hashinfo->fh_refcnt == 0)
570*0Sstevel@tonic-gate 				_nss_files_hash_destroy(be->hashinfo);
571*0Sstevel@tonic-gate 			mutex_unlock(&be->hashinfo->fh_lock);
572*0Sstevel@tonic-gate 		}
573*0Sstevel@tonic-gate 		free(be);
574*0Sstevel@tonic-gate 	}
575*0Sstevel@tonic-gate 	return (NSS_SUCCESS);	/* In case anyone is dumb enough to check */
576*0Sstevel@tonic-gate }
577*0Sstevel@tonic-gate 
578*0Sstevel@tonic-gate nss_backend_t *
579*0Sstevel@tonic-gate _nss_files_constr(ops, n_ops, filename, min_bufsize, fhp)
580*0Sstevel@tonic-gate 	files_backend_op_t	ops[];
581*0Sstevel@tonic-gate 	int			n_ops;
582*0Sstevel@tonic-gate 	const char		*filename;
583*0Sstevel@tonic-gate 	int			min_bufsize;
584*0Sstevel@tonic-gate 	files_hash_t		*fhp;
585*0Sstevel@tonic-gate {
586*0Sstevel@tonic-gate 	files_backend_ptr_t	be;
587*0Sstevel@tonic-gate 
588*0Sstevel@tonic-gate 	if ((be = (files_backend_ptr_t)malloc(sizeof (*be))) == 0) {
589*0Sstevel@tonic-gate 		return (0);
590*0Sstevel@tonic-gate 	}
591*0Sstevel@tonic-gate 	be->ops		= ops;
592*0Sstevel@tonic-gate 	be->n_ops	= n_ops;
593*0Sstevel@tonic-gate 	be->filename	= filename;
594*0Sstevel@tonic-gate 	be->minbuf	= min_bufsize;
595*0Sstevel@tonic-gate 	be->f		= 0;
596*0Sstevel@tonic-gate 	be->buf		= 0;
597*0Sstevel@tonic-gate 	be->hashinfo	= fhp;
598*0Sstevel@tonic-gate 
599*0Sstevel@tonic-gate 	if (fhp != NULL) {
600*0Sstevel@tonic-gate 		mutex_lock(&fhp->fh_lock);
601*0Sstevel@tonic-gate 		fhp->fh_refcnt++;
602*0Sstevel@tonic-gate 		mutex_unlock(&fhp->fh_lock);
603*0Sstevel@tonic-gate 	}
604*0Sstevel@tonic-gate 
605*0Sstevel@tonic-gate 	return ((nss_backend_t *)be);
606*0Sstevel@tonic-gate }
607