10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51914Scasper  * Common Development and Distribution License (the "License").
61914Scasper  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*6279Sdjl  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  *
250Sstevel@tonic-gate  * Common code and structures used by name-service-switch "files" backends.
260Sstevel@tonic-gate  */
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
290Sstevel@tonic-gate 
300Sstevel@tonic-gate /*
310Sstevel@tonic-gate  * An implementation that used mmap() sensibly would be a wonderful thing,
320Sstevel@tonic-gate  *   but this here is just yer standard fgets() thang.
330Sstevel@tonic-gate  */
340Sstevel@tonic-gate 
350Sstevel@tonic-gate #include "files_common.h"
360Sstevel@tonic-gate #include <stdio.h>
370Sstevel@tonic-gate #include <stdlib.h>
380Sstevel@tonic-gate #include <string.h>
390Sstevel@tonic-gate #include <ctype.h>
400Sstevel@tonic-gate #include <fcntl.h>
410Sstevel@tonic-gate #include <poll.h>
420Sstevel@tonic-gate #include <unistd.h>
430Sstevel@tonic-gate #include <sys/stat.h>
442830Sdjl #include <sys/mman.h>
450Sstevel@tonic-gate 
460Sstevel@tonic-gate /*ARGSUSED*/
470Sstevel@tonic-gate nss_status_t
480Sstevel@tonic-gate _nss_files_setent(be, dummy)
490Sstevel@tonic-gate 	files_backend_ptr_t	be;
500Sstevel@tonic-gate 	void			*dummy;
510Sstevel@tonic-gate {
520Sstevel@tonic-gate 	if (be->f == 0) {
530Sstevel@tonic-gate 		if (be->filename == 0) {
540Sstevel@tonic-gate 			/* Backend isn't initialized properly? */
550Sstevel@tonic-gate 			return (NSS_UNAVAIL);
560Sstevel@tonic-gate 		}
571914Scasper 		if ((be->f = fopen(be->filename, "rF")) == 0) {
580Sstevel@tonic-gate 			return (NSS_UNAVAIL);
590Sstevel@tonic-gate 		}
600Sstevel@tonic-gate 	} else {
611914Scasper 		rewind(be->f);
620Sstevel@tonic-gate 	}
630Sstevel@tonic-gate 	return (NSS_SUCCESS);
640Sstevel@tonic-gate }
650Sstevel@tonic-gate 
660Sstevel@tonic-gate /*ARGSUSED*/
670Sstevel@tonic-gate nss_status_t
680Sstevel@tonic-gate _nss_files_endent(be, dummy)
690Sstevel@tonic-gate 	files_backend_ptr_t	be;
700Sstevel@tonic-gate 	void			*dummy;
710Sstevel@tonic-gate {
720Sstevel@tonic-gate 	if (be->f != 0) {
732830Sdjl 		(void) fclose(be->f);
740Sstevel@tonic-gate 		be->f = 0;
750Sstevel@tonic-gate 	}
760Sstevel@tonic-gate 	if (be->buf != 0) {
770Sstevel@tonic-gate 		free(be->buf);
780Sstevel@tonic-gate 		be->buf = 0;
790Sstevel@tonic-gate 	}
800Sstevel@tonic-gate 	return (NSS_SUCCESS);
810Sstevel@tonic-gate }
820Sstevel@tonic-gate 
830Sstevel@tonic-gate /*
840Sstevel@tonic-gate  * This routine reads a line, including the processing of continuation
850Sstevel@tonic-gate  * characters.  It always leaves (or inserts) \n\0 at the end of the line.
860Sstevel@tonic-gate  * It returns the length of the line read, excluding the \n\0.  Who's idea
870Sstevel@tonic-gate  * was this?
880Sstevel@tonic-gate  * Returns -1 on EOF.
890Sstevel@tonic-gate  *
900Sstevel@tonic-gate  * Note that since each concurrent call to _nss_files_read_line has
910Sstevel@tonic-gate  * it's own FILE pointer, we can use getc_unlocked w/o difficulties,
920Sstevel@tonic-gate  * a substantial performance win.
930Sstevel@tonic-gate  */
940Sstevel@tonic-gate int
950Sstevel@tonic-gate _nss_files_read_line(f, buffer, buflen)
961914Scasper 	FILE			*f;
970Sstevel@tonic-gate 	char			*buffer;
980Sstevel@tonic-gate 	int			buflen;
990Sstevel@tonic-gate {
1000Sstevel@tonic-gate 	int			linelen;	/* 1st unused slot in buffer */
1010Sstevel@tonic-gate 	int			c;
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate 	/*CONSTCOND*/
1040Sstevel@tonic-gate 	while (1) {
1050Sstevel@tonic-gate 		linelen = 0;
1060Sstevel@tonic-gate 		while (linelen < buflen - 1) {	/* "- 1" saves room for \n\0 */
1071914Scasper 			switch (c = getc_unlocked(f)) {
1080Sstevel@tonic-gate 			case EOF:
1090Sstevel@tonic-gate 				if (linelen == 0 ||
1100Sstevel@tonic-gate 				    buffer[linelen - 1] == '\\') {
1110Sstevel@tonic-gate 					return (-1);
1120Sstevel@tonic-gate 				} else {
1130Sstevel@tonic-gate 					buffer[linelen    ] = '\n';
1140Sstevel@tonic-gate 					buffer[linelen + 1] = '\0';
1150Sstevel@tonic-gate 					return (linelen);
1160Sstevel@tonic-gate 				}
1170Sstevel@tonic-gate 			case '\n':
1180Sstevel@tonic-gate 				if (linelen > 0 &&
1190Sstevel@tonic-gate 				    buffer[linelen - 1] == '\\') {
1200Sstevel@tonic-gate 					--linelen;  /* remove the '\\' */
1210Sstevel@tonic-gate 				} else {
1220Sstevel@tonic-gate 					buffer[linelen    ] = '\n';
1230Sstevel@tonic-gate 					buffer[linelen + 1] = '\0';
1240Sstevel@tonic-gate 					return (linelen);
1250Sstevel@tonic-gate 				}
1260Sstevel@tonic-gate 				break;
1270Sstevel@tonic-gate 			default:
1280Sstevel@tonic-gate 				buffer[linelen++] = c;
1290Sstevel@tonic-gate 			}
1300Sstevel@tonic-gate 		}
1310Sstevel@tonic-gate 		/* Buffer overflow -- eat rest of line and loop again */
1320Sstevel@tonic-gate 		/* ===> Should syslog() */
1330Sstevel@tonic-gate 		do {
1341914Scasper 			c = getc_unlocked(f);
1350Sstevel@tonic-gate 			if (c == EOF) {
1360Sstevel@tonic-gate 				return (-1);
1370Sstevel@tonic-gate 			}
1380Sstevel@tonic-gate 		} while (c != '\n');
1390Sstevel@tonic-gate 	}
1400Sstevel@tonic-gate 	/*NOTREACHED*/
1410Sstevel@tonic-gate }
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate /*
1440Sstevel@tonic-gate  * used only for getgroupbymem() now.
1450Sstevel@tonic-gate  */
1460Sstevel@tonic-gate nss_status_t
1470Sstevel@tonic-gate _nss_files_do_all(be, args, filter, func)
1480Sstevel@tonic-gate 	files_backend_ptr_t	be;
1490Sstevel@tonic-gate 	void			*args;
1500Sstevel@tonic-gate 	const char		*filter;
1510Sstevel@tonic-gate 	files_do_all_func_t	func;
1520Sstevel@tonic-gate {
153*6279Sdjl 	long			grlen;
1540Sstevel@tonic-gate 	char			*buffer;
1550Sstevel@tonic-gate 	int			buflen;
1560Sstevel@tonic-gate 	nss_status_t		res;
1570Sstevel@tonic-gate 
158*6279Sdjl 	if (be->buf == 0) {
159*6279Sdjl 		if ((grlen = sysconf(_SC_GETGR_R_SIZE_MAX)) > 0)
160*6279Sdjl 			be->minbuf = grlen;
161*6279Sdjl 		if ((be->buf = malloc(be->minbuf)) == 0)
162*6279Sdjl 			return (NSS_UNAVAIL);
1630Sstevel@tonic-gate 	}
1640Sstevel@tonic-gate 	buffer = be->buf;
1650Sstevel@tonic-gate 	buflen = be->minbuf;
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate 	if ((res = _nss_files_setent(be, 0)) != NSS_SUCCESS) {
1680Sstevel@tonic-gate 		return (res);
1690Sstevel@tonic-gate 	}
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate 	res = NSS_NOTFOUND;
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate 	do {
1740Sstevel@tonic-gate 		int		linelen;
1750Sstevel@tonic-gate 
1760Sstevel@tonic-gate 		if ((linelen = _nss_files_read_line(be->f, buffer,
1770Sstevel@tonic-gate 		    buflen)) < 0) {
1780Sstevel@tonic-gate 			/* End of file */
1790Sstevel@tonic-gate 			break;
1800Sstevel@tonic-gate 		}
1810Sstevel@tonic-gate 		if (filter != 0 && strstr(buffer, filter) == 0) {
1820Sstevel@tonic-gate 			/*
1830Sstevel@tonic-gate 			 * Optimization:  if the entry doesn't contain the
1840Sstevel@tonic-gate 			 *   filter string then it can't be the entry we want,
1850Sstevel@tonic-gate 			 *   so don't bother looking more closely at it.
1860Sstevel@tonic-gate 			 */
1870Sstevel@tonic-gate 			continue;
1880Sstevel@tonic-gate 		}
1890Sstevel@tonic-gate 		res = (*func)(buffer, linelen, args);
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate 	} while (res == NSS_NOTFOUND);
1920Sstevel@tonic-gate 
1932830Sdjl 	(void) _nss_files_endent(be, 0);
1940Sstevel@tonic-gate 	return (res);
1950Sstevel@tonic-gate }
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate /*
1980Sstevel@tonic-gate  * Could implement this as an iterator function on top of _nss_files_do_all(),
1990Sstevel@tonic-gate  *   but the shared code is small enough that it'd be pretty silly.
2000Sstevel@tonic-gate  */
2010Sstevel@tonic-gate nss_status_t
2020Sstevel@tonic-gate _nss_files_XY_all(be, args, netdb, filter, check)
2030Sstevel@tonic-gate 	files_backend_ptr_t	be;
2040Sstevel@tonic-gate 	nss_XbyY_args_t		*args;
2050Sstevel@tonic-gate 	int			netdb;		/* whether it uses netdb */
2060Sstevel@tonic-gate 						/* format or not */
2070Sstevel@tonic-gate 	const char		*filter;	/* advisory, to speed up */
2080Sstevel@tonic-gate 						/* string search */
2090Sstevel@tonic-gate 	files_XY_check_func	check;	/* NULL means one-shot, for getXXent */
2100Sstevel@tonic-gate {
211*6279Sdjl 	char			*r;
2120Sstevel@tonic-gate 	nss_status_t		res;
2130Sstevel@tonic-gate 	int	parsestat;
2140Sstevel@tonic-gate 	int (*func)();
2150Sstevel@tonic-gate 
2162830Sdjl 	if (filter != NULL && *filter == '\0')
2172830Sdjl 		return (NSS_NOTFOUND);
218*6279Sdjl 	if (be->buf == 0 || (be->minbuf < args->buf.buflen)) {
219*6279Sdjl 		if (be->minbuf < args->buf.buflen) {
220*6279Sdjl 			if (be->buf == 0) {
221*6279Sdjl 				be->minbuf = args->buf.buflen;
222*6279Sdjl 			} else if (
223*6279Sdjl 			    (r = realloc(be->buf, args->buf.buflen)) != NULL) {
224*6279Sdjl 				be->buf = r;
225*6279Sdjl 				be->minbuf = args->buf.buflen;
226*6279Sdjl 			}
227*6279Sdjl 		}
228*6279Sdjl 		if (be->buf == 0 &&
229*6279Sdjl 			(be->buf = malloc(be->minbuf)) == 0)
230*6279Sdjl 				return (NSS_UNAVAIL);
2310Sstevel@tonic-gate 	}
2320Sstevel@tonic-gate 
2330Sstevel@tonic-gate 	if (check != 0 || be->f == 0) {
2340Sstevel@tonic-gate 		if ((res = _nss_files_setent(be, 0)) != NSS_SUCCESS) {
2350Sstevel@tonic-gate 			return (res);
2360Sstevel@tonic-gate 		}
2370Sstevel@tonic-gate 	}
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate 	res = NSS_NOTFOUND;
2400Sstevel@tonic-gate 
2410Sstevel@tonic-gate 	/*CONSTCOND*/
2420Sstevel@tonic-gate 	while (1) {
2430Sstevel@tonic-gate 		char		*instr	= be->buf;
2440Sstevel@tonic-gate 		int		linelen;
2450Sstevel@tonic-gate 
2460Sstevel@tonic-gate 		if ((linelen = _nss_files_read_line(be->f, instr,
2470Sstevel@tonic-gate 		    be->minbuf)) < 0) {
2480Sstevel@tonic-gate 			/* End of file */
2490Sstevel@tonic-gate 			args->returnval = 0;
2502830Sdjl 			args->returnlen = 0;
2510Sstevel@tonic-gate 			break;
2520Sstevel@tonic-gate 		}
2530Sstevel@tonic-gate 		if (filter != 0 && strstr(instr, filter) == 0) {
2540Sstevel@tonic-gate 			/*
2550Sstevel@tonic-gate 			 * Optimization:  if the entry doesn't contain the
2560Sstevel@tonic-gate 			 *   filter string then it can't be the entry we want,
2570Sstevel@tonic-gate 			 *   so don't bother looking more closely at it.
2580Sstevel@tonic-gate 			 */
2590Sstevel@tonic-gate 			continue;
2600Sstevel@tonic-gate 		}
2610Sstevel@tonic-gate 		if (netdb) {
2620Sstevel@tonic-gate 			char		*first;
2630Sstevel@tonic-gate 			char		*last;
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 			if ((last = strchr(instr, '#')) == 0) {
2660Sstevel@tonic-gate 				last = instr + linelen;
2670Sstevel@tonic-gate 			}
2680Sstevel@tonic-gate 			*last-- = '\0';		/* Nuke '\n' or #comment */
2690Sstevel@tonic-gate 
2700Sstevel@tonic-gate 			/*
2710Sstevel@tonic-gate 			 * Skip leading whitespace.  Normally there isn't
2720Sstevel@tonic-gate 			 *   any, so it's not worth calling strspn().
2730Sstevel@tonic-gate 			 */
2740Sstevel@tonic-gate 			for (first = instr;  isspace(*first);  first++) {
2750Sstevel@tonic-gate 				;
2760Sstevel@tonic-gate 			}
2770Sstevel@tonic-gate 			if (*first == '\0') {
2780Sstevel@tonic-gate 				continue;
2790Sstevel@tonic-gate 			}
2800Sstevel@tonic-gate 			/*
2810Sstevel@tonic-gate 			 * Found something non-blank on the line.  Skip back
2820Sstevel@tonic-gate 			 * over any trailing whitespace;  since we know
2830Sstevel@tonic-gate 			 * there's non-whitespace earlier in the line,
2840Sstevel@tonic-gate 			 * checking for termination is easy.
2850Sstevel@tonic-gate 			 */
2860Sstevel@tonic-gate 			while (isspace(*last)) {
2870Sstevel@tonic-gate 				--last;
2880Sstevel@tonic-gate 			}
2890Sstevel@tonic-gate 
2900Sstevel@tonic-gate 			linelen = last - first + 1;
2910Sstevel@tonic-gate 			if (first != instr) {
2920Sstevel@tonic-gate 					instr = first;
2930Sstevel@tonic-gate 			}
2940Sstevel@tonic-gate 		}
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate 		args->returnval = 0;
2972830Sdjl 		args->returnlen = 0;
2982830Sdjl 
2992830Sdjl 		if (check != NULL && (*check)(args, instr, linelen) == 0)
3002830Sdjl 			continue;
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 		func = args->str2ent;
3030Sstevel@tonic-gate 		parsestat = (*func)(instr, linelen, args->buf.result,
3040Sstevel@tonic-gate 					args->buf.buffer, args->buf.buflen);
3050Sstevel@tonic-gate 
3060Sstevel@tonic-gate 		if (parsestat == NSS_STR_PARSE_SUCCESS) {
3072830Sdjl 			args->returnval = (args->buf.result != NULL)?
3082830Sdjl 					args->buf.result : args->buf.buffer;
3092830Sdjl 			args->returnlen = linelen;
3102830Sdjl 			res = NSS_SUCCESS;
3112830Sdjl 			break;
3120Sstevel@tonic-gate 		} else if (parsestat == NSS_STR_PARSE_ERANGE) {
3130Sstevel@tonic-gate 			args->erange = 1;
3140Sstevel@tonic-gate 			break;
3150Sstevel@tonic-gate 		} /* else if (parsestat == NSS_STR_PARSE_PARSE) don't care ! */
3160Sstevel@tonic-gate 	}
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 	/*
3190Sstevel@tonic-gate 	 * stayopen is set to 0 by default in order to close the opened
3200Sstevel@tonic-gate 	 * file.  Some applications may break if it is set to 1.
3210Sstevel@tonic-gate 	 */
3220Sstevel@tonic-gate 	if (check != 0 && !args->stayopen) {
3230Sstevel@tonic-gate 		(void) _nss_files_endent(be, 0);
3240Sstevel@tonic-gate 	}
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate 	return (res);
3270Sstevel@tonic-gate }
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate /*
3300Sstevel@tonic-gate  * File hashing support.  Critical for sites with large (e.g. 1000+ lines)
3310Sstevel@tonic-gate  * /etc/passwd or /etc/group files.  Currently only used by getpw*() and
3320Sstevel@tonic-gate  * getgr*() routines, but any files backend can use this stuff.
3330Sstevel@tonic-gate  */
3340Sstevel@tonic-gate static void
3350Sstevel@tonic-gate _nss_files_hash_destroy(files_hash_t *fhp)
3360Sstevel@tonic-gate {
3370Sstevel@tonic-gate 	free(fhp->fh_table);
3380Sstevel@tonic-gate 	fhp->fh_table = NULL;
3390Sstevel@tonic-gate 	free(fhp->fh_line);
3400Sstevel@tonic-gate 	fhp->fh_line = NULL;
3410Sstevel@tonic-gate 	free(fhp->fh_file_start);
3420Sstevel@tonic-gate 	fhp->fh_file_start = NULL;
3430Sstevel@tonic-gate }
3440Sstevel@tonic-gate #ifdef PIC
3450Sstevel@tonic-gate /*
3460Sstevel@tonic-gate  * It turns out the hashing stuff really needs to be disabled for processes
3470Sstevel@tonic-gate  * other than the nscd; the consumption of swap space and memory is otherwise
3480Sstevel@tonic-gate  * unacceptable when the nscd is killed w/ a large passwd file (4M) active.
3490Sstevel@tonic-gate  * See 4031930 for details.
3500Sstevel@tonic-gate  * So we just use this psuedo function to enable the hashing feature.  Since
3510Sstevel@tonic-gate  * this function name is private, we just create a function w/ the name
3520Sstevel@tonic-gate  *  __nss_use_files_hash in the nscd itself and everyone else uses the old
3530Sstevel@tonic-gate  * interface.
3540Sstevel@tonic-gate  * We also disable hashing for .a executables to avoid problems with large
3550Sstevel@tonic-gate  * files....
3560Sstevel@tonic-gate  */
3570Sstevel@tonic-gate 
3580Sstevel@tonic-gate #pragma weak __nss_use_files_hash
3590Sstevel@tonic-gate 
3600Sstevel@tonic-gate extern void  __nss_use_files_hash(void);
3610Sstevel@tonic-gate #endif /* pic */
3620Sstevel@tonic-gate 
3632830Sdjl /*ARGSUSED*/
3640Sstevel@tonic-gate nss_status_t
3650Sstevel@tonic-gate _nss_files_XY_hash(files_backend_ptr_t be, nss_XbyY_args_t *args,
3660Sstevel@tonic-gate 	int netdb, files_hash_t *fhp, int hashop, files_XY_check_func check)
3670Sstevel@tonic-gate {
3682830Sdjl 	/* LINTED E_FUNC_VAR_UNUSED */
3690Sstevel@tonic-gate 	int fd, retries, ht;
3702830Sdjl 	/* LINTED E_FUNC_VAR_UNUSED */
3710Sstevel@tonic-gate 	uint_t hash, line, f;
3722830Sdjl 	/* LINTED E_FUNC_VAR_UNUSED */
3730Sstevel@tonic-gate 	files_hashent_t *hp, *htab;
3742830Sdjl 	/* LINTED E_FUNC_VAR_UNUSED */
3750Sstevel@tonic-gate 	char *cp, *first, *last;
3762830Sdjl 	/* LINTED E_FUNC_VAR_UNUSED */
3770Sstevel@tonic-gate 	nss_XbyY_args_t xargs;
3782830Sdjl 	/* LINTED E_FUNC_VAR_UNUSED */
3790Sstevel@tonic-gate 	struct stat64 st;
3800Sstevel@tonic-gate 
3810Sstevel@tonic-gate #ifndef PIC
3820Sstevel@tonic-gate 	return (_nss_files_XY_all(be, args, netdb, 0, check));
3830Sstevel@tonic-gate }
3840Sstevel@tonic-gate #else
3850Sstevel@tonic-gate 	if (__nss_use_files_hash == 0)
3860Sstevel@tonic-gate 		return (_nss_files_XY_all(be, args, netdb, 0, check));
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 	mutex_lock(&fhp->fh_lock);
3890Sstevel@tonic-gate retry:
3900Sstevel@tonic-gate 	retries = 100;
3910Sstevel@tonic-gate 	while (stat64(be->filename, &st) < 0) {
3920Sstevel@tonic-gate 		/*
3930Sstevel@tonic-gate 		 * On a healthy system this can't happen except during brief
3940Sstevel@tonic-gate 		 * periods when the file is being modified/renamed.  Keep
3950Sstevel@tonic-gate 		 * trying until things settle down, but eventually give up.
3960Sstevel@tonic-gate 		 */
3970Sstevel@tonic-gate 		if (--retries == 0)
3980Sstevel@tonic-gate 			goto unavail;
3990Sstevel@tonic-gate 		poll(0, 0, 100);
4000Sstevel@tonic-gate 	}
4010Sstevel@tonic-gate 
4020Sstevel@tonic-gate 	if (st.st_mtim.tv_sec == fhp->fh_mtime.tv_sec &&
4030Sstevel@tonic-gate 	    st.st_mtim.tv_nsec == fhp->fh_mtime.tv_nsec &&
4040Sstevel@tonic-gate 	    fhp->fh_table != NULL) {
4050Sstevel@tonic-gate 		htab = &fhp->fh_table[hashop * fhp->fh_size];
4062830Sdjl 		hash = fhp->fh_hash_func[hashop](args, 1, NULL, 0);
4070Sstevel@tonic-gate 		for (hp = htab[hash % fhp->fh_size].h_first; hp != NULL;
4080Sstevel@tonic-gate 		    hp = hp->h_next) {
4090Sstevel@tonic-gate 			if (hp->h_hash != hash)
4100Sstevel@tonic-gate 				continue;
4110Sstevel@tonic-gate 			line = hp - htab;
4122830Sdjl 			if ((*check)(args, fhp->fh_line[line].l_start,
4132830Sdjl 					fhp->fh_line[line].l_len) == 0)
4142830Sdjl 				continue;
4150Sstevel@tonic-gate 			if ((*args->str2ent)(fhp->fh_line[line].l_start,
4160Sstevel@tonic-gate 			    fhp->fh_line[line].l_len, args->buf.result,
4170Sstevel@tonic-gate 			    args->buf.buffer, args->buf.buflen) ==
4180Sstevel@tonic-gate 			    NSS_STR_PARSE_SUCCESS) {
4192830Sdjl 				args->returnval = (args->buf.result)?
4202830Sdjl 					args->buf.result:args->buf.buffer;
4212830Sdjl 				args->returnlen = fhp->fh_line[line].l_len;
4222830Sdjl 				mutex_unlock(&fhp->fh_lock);
4232830Sdjl 				return (NSS_SUCCESS);
4240Sstevel@tonic-gate 			} else {
4250Sstevel@tonic-gate 				args->erange = 1;
4260Sstevel@tonic-gate 			}
4270Sstevel@tonic-gate 		}
4280Sstevel@tonic-gate 		args->returnval = 0;
4292830Sdjl 		args->returnlen = 0;
4300Sstevel@tonic-gate 		mutex_unlock(&fhp->fh_lock);
4310Sstevel@tonic-gate 		return (NSS_NOTFOUND);
4320Sstevel@tonic-gate 	}
4330Sstevel@tonic-gate 
4340Sstevel@tonic-gate 	_nss_files_hash_destroy(fhp);
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 	if (st.st_size > SSIZE_MAX)
4370Sstevel@tonic-gate 		goto unavail;
4380Sstevel@tonic-gate 
4390Sstevel@tonic-gate 	if ((fhp->fh_file_start = malloc((ssize_t)st.st_size + 1)) == NULL)
4400Sstevel@tonic-gate 		goto unavail;
4410Sstevel@tonic-gate 
4420Sstevel@tonic-gate 	if ((fd = open(be->filename, O_RDONLY)) < 0)
4430Sstevel@tonic-gate 		goto unavail;
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate 	if (read(fd, fhp->fh_file_start, (ssize_t)st.st_size) !=
4460Sstevel@tonic-gate 	    (ssize_t)st.st_size) {
4470Sstevel@tonic-gate 		close(fd);
4480Sstevel@tonic-gate 		goto retry;
4490Sstevel@tonic-gate 	}
4500Sstevel@tonic-gate 
4510Sstevel@tonic-gate 	close(fd);
4520Sstevel@tonic-gate 
4530Sstevel@tonic-gate 	fhp->fh_file_end = fhp->fh_file_start + (off_t)st.st_size;
4540Sstevel@tonic-gate 	*fhp->fh_file_end = '\n';
4550Sstevel@tonic-gate 	fhp->fh_mtime = st.st_mtim;
4560Sstevel@tonic-gate 
4570Sstevel@tonic-gate 	/*
4580Sstevel@tonic-gate 	 * If the file changed since we read it, or if it's less than
4590Sstevel@tonic-gate 	 * 1-2 seconds old, don't trust it; its modification may still
4600Sstevel@tonic-gate 	 * be in progress.  The latter is a heuristic hack to minimize
4610Sstevel@tonic-gate 	 * the likelihood of damage if someone modifies /etc/mumble
4620Sstevel@tonic-gate 	 * directly (as opposed to editing and renaming a temp file).
4630Sstevel@tonic-gate 	 *
4640Sstevel@tonic-gate 	 * Note: the cast to u_int is there in case (1) someone rdated
4650Sstevel@tonic-gate 	 * the system backwards since the last modification of /etc/mumble
4660Sstevel@tonic-gate 	 * or (2) this is a diskless client whose time is badly out of sync
4670Sstevel@tonic-gate 	 * with its server.  The 1-2 second age hack doesn't cover these
4680Sstevel@tonic-gate 	 * cases -- oh well.
4690Sstevel@tonic-gate 	 */
4700Sstevel@tonic-gate 	if (stat64(be->filename, &st) < 0 ||
4710Sstevel@tonic-gate 	    st.st_mtim.tv_sec != fhp->fh_mtime.tv_sec ||
4720Sstevel@tonic-gate 	    st.st_mtim.tv_nsec != fhp->fh_mtime.tv_nsec ||
4730Sstevel@tonic-gate 	    (uint_t)(time(0) - st.st_mtim.tv_sec + 2) < 4) {
4740Sstevel@tonic-gate 		poll(0, 0, 1000);
4750Sstevel@tonic-gate 		goto retry;
4760Sstevel@tonic-gate 	}
4770Sstevel@tonic-gate 
4780Sstevel@tonic-gate 	line = 1;
4790Sstevel@tonic-gate 	for (cp = fhp->fh_file_start; cp < fhp->fh_file_end; cp++)
4800Sstevel@tonic-gate 		if (*cp == '\n')
4810Sstevel@tonic-gate 			line++;
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate 	for (f = 2; f * f <= line; f++) {	/* find next largest prime */
4840Sstevel@tonic-gate 		if (line % f == 0) {
4850Sstevel@tonic-gate 			f = 1;
4860Sstevel@tonic-gate 			line++;
4870Sstevel@tonic-gate 		}
4880Sstevel@tonic-gate 	}
4890Sstevel@tonic-gate 
4900Sstevel@tonic-gate 	fhp->fh_size = line;
4910Sstevel@tonic-gate 	fhp->fh_line = malloc(line * sizeof (files_linetab_t));
4920Sstevel@tonic-gate 	fhp->fh_table = calloc(line * fhp->fh_nhtab, sizeof (files_hashent_t));
4930Sstevel@tonic-gate 	if (fhp->fh_line == NULL || fhp->fh_table == NULL)
4940Sstevel@tonic-gate 		goto unavail;
4950Sstevel@tonic-gate 
4960Sstevel@tonic-gate 	line = 0;
4970Sstevel@tonic-gate 	cp = fhp->fh_file_start;
4980Sstevel@tonic-gate 	while (cp < fhp->fh_file_end) {
4990Sstevel@tonic-gate 		first = cp;
5000Sstevel@tonic-gate 		while (*cp != '\n')
5010Sstevel@tonic-gate 			cp++;
5020Sstevel@tonic-gate 		if (cp > first && *(cp - 1) == '\\') {
5030Sstevel@tonic-gate 			memmove(first + 2, first, cp - first - 1);
5040Sstevel@tonic-gate 			cp = first + 2;
5050Sstevel@tonic-gate 			continue;
5060Sstevel@tonic-gate 		}
5070Sstevel@tonic-gate 		last = cp;
5080Sstevel@tonic-gate 		*cp++ = '\0';
5090Sstevel@tonic-gate 		if (netdb) {
5100Sstevel@tonic-gate 			if ((last = strchr(first, '#')) == 0)
5110Sstevel@tonic-gate 				last = cp - 1;
5120Sstevel@tonic-gate 			*last-- = '\0';		/* nuke '\n' or #comment */
5130Sstevel@tonic-gate 			while (isspace(*first))	/* nuke leading whitespace */
5140Sstevel@tonic-gate 				first++;
5150Sstevel@tonic-gate 			if (*first == '\0')	/* skip content-free lines */
5160Sstevel@tonic-gate 				continue;
5170Sstevel@tonic-gate 			while (isspace(*last))	/* nuke trailing whitespace */
5180Sstevel@tonic-gate 				--last;
5190Sstevel@tonic-gate 			*++last = '\0';
5200Sstevel@tonic-gate 		}
5210Sstevel@tonic-gate 		for (ht = 0; ht < fhp->fh_nhtab; ht++) {
5220Sstevel@tonic-gate 			hp = &fhp->fh_table[ht * fhp->fh_size + line];
5232830Sdjl 			hp->h_hash = fhp->fh_hash_func[ht](&xargs, 0, first,
5242830Sdjl 					last - first);
5250Sstevel@tonic-gate 		}
5260Sstevel@tonic-gate 		fhp->fh_line[line].l_start = first;
5270Sstevel@tonic-gate 		fhp->fh_line[line++].l_len = last - first;
5280Sstevel@tonic-gate 	}
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate 	/*
5310Sstevel@tonic-gate 	 * Populate the hash tables in reverse order so that the hash chains
5320Sstevel@tonic-gate 	 * end up in forward order.  This ensures that hashed lookups find
5330Sstevel@tonic-gate 	 * things in the same order that a linear search of the file would.
5340Sstevel@tonic-gate 	 * This is essential in cases where there could be multiple matches.
5350Sstevel@tonic-gate 	 * For example: until 2.7, root and smtp both had uid 0; but we
5360Sstevel@tonic-gate 	 * certainly wouldn't want getpwuid(0) to return smtp.
5370Sstevel@tonic-gate 	 */
5380Sstevel@tonic-gate 	for (ht = 0; ht < fhp->fh_nhtab; ht++) {
5390Sstevel@tonic-gate 		htab = &fhp->fh_table[ht * fhp->fh_size];
5400Sstevel@tonic-gate 		for (hp = &htab[line - 1]; hp >= htab; hp--) {
5410Sstevel@tonic-gate 			uint_t bucket = hp->h_hash % fhp->fh_size;
5420Sstevel@tonic-gate 			hp->h_next = htab[bucket].h_first;
5430Sstevel@tonic-gate 			htab[bucket].h_first = hp;
5440Sstevel@tonic-gate 		}
5450Sstevel@tonic-gate 	}
5460Sstevel@tonic-gate 
5470Sstevel@tonic-gate 	goto retry;
5480Sstevel@tonic-gate 
5490Sstevel@tonic-gate unavail:
5500Sstevel@tonic-gate 	_nss_files_hash_destroy(fhp);
5510Sstevel@tonic-gate 	mutex_unlock(&fhp->fh_lock);
5520Sstevel@tonic-gate 	return (NSS_UNAVAIL);
5530Sstevel@tonic-gate }
5540Sstevel@tonic-gate #endif /* PIC */
5550Sstevel@tonic-gate 
5560Sstevel@tonic-gate nss_status_t
5570Sstevel@tonic-gate _nss_files_getent_rigid(be, a)
5580Sstevel@tonic-gate 	files_backend_ptr_t	be;
5590Sstevel@tonic-gate 	void			*a;
5600Sstevel@tonic-gate {
5610Sstevel@tonic-gate 	nss_XbyY_args_t		*args = (nss_XbyY_args_t *)a;
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate 	return (_nss_files_XY_all(be, args, 0, 0, 0));
5640Sstevel@tonic-gate }
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate nss_status_t
5670Sstevel@tonic-gate _nss_files_getent_netdb(be, a)
5680Sstevel@tonic-gate 	files_backend_ptr_t	be;
5690Sstevel@tonic-gate 	void			*a;
5700Sstevel@tonic-gate {
5710Sstevel@tonic-gate 	nss_XbyY_args_t		*args = (nss_XbyY_args_t *)a;
5720Sstevel@tonic-gate 
5730Sstevel@tonic-gate 	return (_nss_files_XY_all(be, args, 1, 0, 0));
5740Sstevel@tonic-gate }
5750Sstevel@tonic-gate 
5760Sstevel@tonic-gate /*ARGSUSED*/
5770Sstevel@tonic-gate nss_status_t
5780Sstevel@tonic-gate _nss_files_destr(be, dummy)
5790Sstevel@tonic-gate 	files_backend_ptr_t	be;
5800Sstevel@tonic-gate 	void			*dummy;
5810Sstevel@tonic-gate {
5820Sstevel@tonic-gate 	if (be != 0) {
5830Sstevel@tonic-gate 		if (be->f != 0) {
5842830Sdjl 			(void) _nss_files_endent(be, 0);
5850Sstevel@tonic-gate 		}
5860Sstevel@tonic-gate 		if (be->hashinfo != NULL) {
5872830Sdjl 			(void) mutex_lock(&be->hashinfo->fh_lock);
5880Sstevel@tonic-gate 			if (--be->hashinfo->fh_refcnt == 0)
5890Sstevel@tonic-gate 				_nss_files_hash_destroy(be->hashinfo);
5902830Sdjl 			(void) mutex_unlock(&be->hashinfo->fh_lock);
5910Sstevel@tonic-gate 		}
5920Sstevel@tonic-gate 		free(be);
5930Sstevel@tonic-gate 	}
5940Sstevel@tonic-gate 	return (NSS_SUCCESS);	/* In case anyone is dumb enough to check */
5950Sstevel@tonic-gate }
5960Sstevel@tonic-gate 
5970Sstevel@tonic-gate nss_backend_t *
5980Sstevel@tonic-gate _nss_files_constr(ops, n_ops, filename, min_bufsize, fhp)
5990Sstevel@tonic-gate 	files_backend_op_t	ops[];
6000Sstevel@tonic-gate 	int			n_ops;
6010Sstevel@tonic-gate 	const char		*filename;
6020Sstevel@tonic-gate 	int			min_bufsize;
6030Sstevel@tonic-gate 	files_hash_t		*fhp;
6040Sstevel@tonic-gate {
6050Sstevel@tonic-gate 	files_backend_ptr_t	be;
6060Sstevel@tonic-gate 
6070Sstevel@tonic-gate 	if ((be = (files_backend_ptr_t)malloc(sizeof (*be))) == 0) {
6080Sstevel@tonic-gate 		return (0);
6090Sstevel@tonic-gate 	}
6100Sstevel@tonic-gate 	be->ops		= ops;
6110Sstevel@tonic-gate 	be->n_ops	= n_ops;
6120Sstevel@tonic-gate 	be->filename	= filename;
6130Sstevel@tonic-gate 	be->minbuf	= min_bufsize;
6140Sstevel@tonic-gate 	be->f		= 0;
6150Sstevel@tonic-gate 	be->buf		= 0;
6160Sstevel@tonic-gate 	be->hashinfo	= fhp;
6170Sstevel@tonic-gate 
6180Sstevel@tonic-gate 	if (fhp != NULL) {
6192830Sdjl 		(void) mutex_lock(&fhp->fh_lock);
6200Sstevel@tonic-gate 		fhp->fh_refcnt++;
6212830Sdjl 		(void) mutex_unlock(&fhp->fh_lock);
6220Sstevel@tonic-gate 	}
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate 	return ((nss_backend_t *)be);
6250Sstevel@tonic-gate }
6262830Sdjl 
6272830Sdjl int
6282830Sdjl _nss_files_check_name_colon(nss_XbyY_args_t *argp, const char *line,
6292830Sdjl 	int linelen)
6302830Sdjl {
6312830Sdjl 	const char	*linep, *limit;
6322830Sdjl 	const char	*keyp = argp->key.name;
6332830Sdjl 
6342830Sdjl 	linep = line;
6352830Sdjl 	limit = line + linelen;
6362830Sdjl 	while (*keyp && linep < limit && *keyp == *linep) {
6372830Sdjl 		keyp++;
6382830Sdjl 		linep++;
6392830Sdjl 	}
6402830Sdjl 	return (linep < limit && *keyp == '\0' && *linep == ':');
6412830Sdjl }
6422830Sdjl 
6432830Sdjl /*
6442830Sdjl  * This routine is used to parse lines of the form:
6452830Sdjl  * 	name number aliases
6462830Sdjl  * It returns 1 if the key in argp matches any one of the
6472830Sdjl  * names in the line, otherwise 0
6482830Sdjl  * Used by rpc, networks, protocols
6492830Sdjl  */
6502830Sdjl int
6512830Sdjl _nss_files_check_name_aliases(nss_XbyY_args_t *argp, const char *line,
6522830Sdjl 	int linelen)
6532830Sdjl {
6542830Sdjl 	const char	*limit, *linep, *keyp;
6552830Sdjl 
6562830Sdjl 	linep = line;
6572830Sdjl 	limit = line + linelen;
6582830Sdjl 	keyp = argp->key.name;
6592830Sdjl 
6602830Sdjl 	/* compare name */
6612830Sdjl 	while (*keyp && linep < limit && !isspace(*linep) && *keyp == *linep) {
6622830Sdjl 		keyp++;
6632830Sdjl 		linep++;
6642830Sdjl 	}
6652830Sdjl 	if (*keyp == '\0' && linep < limit && isspace(*linep))
6662830Sdjl 		return (1);
6672830Sdjl 	/* skip remainder of the name, if any */
6682830Sdjl 	while (linep < limit && !isspace(*linep))
6692830Sdjl 		linep++;
6702830Sdjl 	/* skip the delimiting spaces */
6712830Sdjl 	while (linep < limit && isspace(*linep))
6722830Sdjl 		linep++;
6732830Sdjl 	/* compare with the aliases */
6742830Sdjl 	while (linep < limit) {
6752830Sdjl 		/*
6762830Sdjl 		 * 1st pass: skip number
6772830Sdjl 		 * Other passes: skip remainder of the alias name, if any
6782830Sdjl 		 */
6792830Sdjl 		while (linep < limit && !isspace(*linep))
6802830Sdjl 			linep++;
6812830Sdjl 		/* skip the delimiting spaces */
6822830Sdjl 		while (linep < limit && isspace(*linep))
6832830Sdjl 			linep++;
6842830Sdjl 		/* compare with the alias name */
6852830Sdjl 		keyp = argp->key.name;
6862830Sdjl 		while (*keyp && linep < limit && !isspace(*linep) &&
687*6279Sdjl 		    *keyp == *linep) {
6882830Sdjl 			keyp++;
6892830Sdjl 			linep++;
6902830Sdjl 		}
6912830Sdjl 		if (*keyp == '\0' && (linep == limit || isspace(*linep)))
6922830Sdjl 			return (1);
6932830Sdjl 	}
6942830Sdjl 	return (0);
6952830Sdjl }
696