xref: /titanic_50/usr/src/cmd/modload/drvsubr.c (revision f899e5733f35e45012ad40c8325b2622dcc2b673)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <ctype.h>
29 #include <unistd.h>
30 #include <sys/sysmacros.h>
31 #include <libintl.h>
32 #include <wait.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <signal.h>
38 #include <sys/buf.h>
39 #include <sys/stat.h>
40 #include <grp.h>
41 #include "addrem.h"
42 #include "errmsg.h"
43 #include "plcysubr.h"
44 
45 /*
46  * Macros to produce a quoted string containing the value of a
47  * preprocessor macro. For example, if SIZE is defined to be 256,
48  * VAL2STR(SIZE) is "256". This is used to construct format
49  * strings for scanf-family functions below.
50  * Note: For format string use, the argument to VAL2STR() must
51  * be a numeric constant that is one less than the size of the
52  * corresponding data buffer.
53  */
54 #define	VAL2STR_QUOTE(x)	#x
55 #define	VAL2STR(x)		VAL2STR_QUOTE(x)
56 
57 /*
58  * Convenience macro to determine if a character is a quote
59  */
60 #define	isquote(c)	(((c) == '"') || ((c) == '\''))
61 
62 
63 static char *add_rem_lock;	/* lock file */
64 static char *tmphold;		/* temporary file for updating */
65 static int  add_rem_lock_fd = -1;
66 
67 static int get_cached_n_to_m_file(char *filename, char ***cache);
68 static int get_name_to_major_entry(int *major_no, char *driver_name,
69     char *file_name);
70 
71 static int is_blank(char *);
72 
73 /*ARGSUSED*/
74 void
75 log_minorperm_error(minorperm_err_t err, int key)
76 {
77 	switch (err) {
78 	case MP_FOPEN_ERR:
79 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
80 		    MINOR_PERM_FILE);
81 		break;
82 	case MP_FCLOSE_ERR:
83 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
84 		    MINOR_PERM_FILE);
85 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
86 		break;
87 	case MP_IGNORING_LINE_ERR:
88 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
89 		    MINOR_PERM_FILE);
90 		break;
91 	case MP_ALLOC_ERR:
92 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
93 		    MINOR_PERM_FILE);
94 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
95 		break;
96 	case MP_NVLIST_ERR:
97 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
98 		    MINOR_PERM_FILE);
99 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
100 		break;
101 	case MP_CANT_FIND_USER_ERR:
102 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
103 		    MINOR_PERM_FILE);
104 		break;
105 	case MP_CANT_FIND_GROUP_ERR:
106 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
107 		    MINOR_PERM_FILE);
108 		break;
109 	}
110 }
111 
112 /*
113  *  open file
114  * for each entry in list
115  *	where list entries are separated by <list_separator>
116  * 	append entry : driver_name <entry_separator> entry
117  * close file
118  * return error/noerr
119  */
120 int
121 append_to_file(
122 	char *driver_name,
123 	char *entry_list,
124 	char *filename,
125 	char list_separator,
126 	char *entry_separator,
127 	int quoted)
128 {
129 	int	len, line_len;
130 	int	fpint;
131 	char	*current_head, *previous_head;
132 	char	*line, *one_entry;
133 	FILE	*fp;
134 
135 	if ((fp = fopen(filename, "a")) == NULL) {
136 		perror(NULL);
137 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
138 		    filename);
139 		return (ERROR);
140 	}
141 
142 	len = strlen(entry_list);
143 
144 	one_entry = calloc(len + 1, 1);
145 	if (one_entry == NULL) {
146 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE), filename);
147 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
148 		(void) fclose(fp);
149 		return (ERROR);
150 	}
151 
152 	previous_head = entry_list;
153 
154 	line_len = strlen(driver_name) + len + 4;
155 	if (quoted)
156 		line_len += 2;
157 
158 	line = calloc(line_len, 1);
159 	if (line == NULL) {
160 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
161 		(void) fclose(fp);
162 		err_exit();
163 	}
164 
165 	/*
166 	 * get one entry at a time from list and append to <filename> file
167 	 */
168 
169 	do {
170 		bzero(one_entry, len + 1);
171 		bzero(line, line_len);
172 
173 		current_head = get_entry(previous_head, one_entry,
174 		    list_separator, quoted);
175 		previous_head = current_head;
176 
177 		(void) snprintf(line, line_len,
178 		    quoted ? "%s%s\"%s\"\n" : "%s%s%s\n",
179 		    driver_name, entry_separator, one_entry);
180 
181 		if ((fputs(line, fp)) == EOF) {
182 			perror(NULL);
183 			(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
184 			    filename);
185 		}
186 
187 	} while (*current_head != '\0');
188 
189 
190 	(void) fflush(fp);
191 
192 	fpint = fileno(fp);
193 	(void) fsync(fpint);
194 
195 	(void) fclose(fp);
196 
197 	free(one_entry);
198 	free(line);
199 
200 	return (NOERR);
201 }
202 
203 /*
204  *  open file
205  * for each entry in list
206  *	where list entries are separated by <list_separator>
207  * 	append entry : driver_name <entry_separator> entry
208  * close file
209  * return error/noerr
210  */
211 int
212 append_to_minor_perm(
213 	char *driver_name,
214 	char *entry_list,
215 	char *filename)
216 {
217 	int	len, line_len;
218 	int	fpint;
219 	char	*current_head, *previous_head;
220 	char	*line, *one_entry;
221 	FILE	*fp;
222 
223 	if ((fp = fopen(filename, "a")) == NULL) {
224 		perror(NULL);
225 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
226 		    filename);
227 		return (ERROR);
228 	}
229 
230 	len = strlen(entry_list);
231 
232 	one_entry = calloc(len + 1, 1);
233 	if (one_entry == NULL) {
234 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE), filename);
235 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
236 		(void) fclose(fp);
237 		return (ERROR);
238 	}
239 
240 	previous_head = entry_list;
241 
242 	line_len = strlen(driver_name) + len + 4;
243 	line = calloc(line_len, 1);
244 	if (line == NULL) {
245 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
246 		(void) fclose(fp);
247 		err_exit();
248 	}
249 
250 	/*
251 	 * get one entry at a time from list and append to <filename> file
252 	 */
253 	do {
254 		bzero(one_entry, len + 1);
255 		bzero(line, line_len);
256 
257 		current_head = get_perm_entry(previous_head, one_entry);
258 		previous_head = current_head;
259 
260 		(void) snprintf(line, line_len, "%s:%s\n",
261 		    driver_name, one_entry);
262 
263 		if ((fputs(line, fp)) == EOF) {
264 			perror(NULL);
265 			(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
266 			    filename);
267 		}
268 
269 	} while (*current_head != '\0');
270 
271 
272 	(void) fflush(fp);
273 
274 	fpint = fileno(fp);
275 	(void) fsync(fpint);
276 
277 	(void) fclose(fp);
278 
279 	free(one_entry);
280 	free(line);
281 
282 	return (NOERR);
283 }
284 
285 /*
286  * Require exact match to delete a driver alias/permission entry.
287  * Note line argument does not remain unchanged.  Return 1 if matched.
288  */
289 static int
290 match_entry(char *line, char *match)
291 {
292 	char	*token, *p;
293 	int	n;
294 
295 	/* skip any leading white space */
296 	while (*line && isspace(*line))
297 		line++;
298 	/*
299 	 * Find separator for driver name, either space or colon
300 	 *	minor_perm: <driver>:<perm>
301 	 *	driver_aliases: <driver> <alias>
302 	 *	extra_privs: <driver>:<priv>
303 	 */
304 	if ((token = strpbrk(line, " :\t")) == NULL)
305 		return (0);
306 	token++;
307 	/* skip leading white space and quotes */
308 	while (*token && (isspace(*token) || isquote(*token)))
309 		token++;
310 	/* strip trailing newline, white space and quotes */
311 	n = strlen(token);
312 	p = token + n-1;
313 	while (n > 0 && (*p == '\n' || isspace(*p) || isquote(*p))) {
314 		*p-- = 0;
315 		n--;
316 	}
317 	if (n == 0)
318 		return (0);
319 	return (strcmp(token, match) == 0);
320 }
321 
322 /*
323  *  open file
324  * read thru file, deleting all entries if first
325  *    entry = driver_name
326  * close
327  * if error, leave original file intact with message
328  * assumption : drvconfig has been modified to work with clone
329  *  entries in /etc/minor_perm as driver:mummble NOT
330  *  clone:driver mummble
331  * this implementation will NOT find clone entries
332  * clone:driver mummble
333  * match:
334  *	delete just the matching entry
335  *
336  */
337 int
338 delete_entry(
339 	char *oldfile,
340 	char *driver_name,
341 	char *marker,
342 	char *match)
343 {
344 	int		rv, i;
345 	int		status = NOERR;
346 	int		drvr_found = 0;
347 	boolean_t 	nomatch = B_TRUE;
348 	char		*newfile, *tptr, *cp;
349 	char		line[MAX_DBFILE_ENTRY];
350 	char		drv[FILENAME_MAX + 1];
351 	FILE		*fp, *newfp;
352 	struct group	*sysgrp;
353 	char		*copy;		/* same size as line */
354 	char		*match2 = NULL;	/* match with quotes cleaned up */
355 
356 	/*
357 	 * if match is specified, sanity check it and clean it
358 	 * up by removing surrounding quotes as we require
359 	 * an exact match.
360 	 */
361 	if (match) {
362 		cp = match;
363 		while (*cp && (isspace(*cp)))
364 			cp++;
365 		i = strlen(cp);
366 		if (i > 0) {
367 			if ((match2 = strdup(cp)) == NULL) {
368 				perror(NULL);
369 				(void) fprintf(stderr, gettext(ERR_NO_MEM));
370 				return (ERROR);
371 			}
372 			i = strlen(match2) - 1;
373 			while (i >= 0 && (isspace(match2[i]))) {
374 				match2[i] = 0;
375 				i--;
376 			}
377 		}
378 		if (match2 == NULL || (strlen(match2) == 0)) {
379 			(void) fprintf(stderr,
380 			    gettext(ERR_INT_UPDATE), oldfile);
381 			return (ERROR);
382 		}
383 	}
384 
385 	if ((fp = fopen(oldfile, "r")) == NULL) {
386 		perror(NULL);
387 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), oldfile);
388 		return (ERROR);
389 	}
390 
391 	/* Space for defensive copy of input line */
392 	copy = calloc(sizeof (line), 1);
393 
394 	/* Build filename for temporary file */
395 	tptr = calloc(strlen(oldfile) + strlen(XEND) + 1, 1);
396 	if (tptr == NULL || copy == NULL) {
397 		perror(NULL);
398 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
399 		return (ERROR);
400 	}
401 
402 	(void) strcpy(tptr, oldfile);
403 	(void) strcat(tptr, XEND);
404 
405 	/*
406 	 * Set gid so we preserve group attribute.  Ideally we wouldn't
407 	 * assume a gid of "sys" but we can't undo the damage on already
408 	 * installed systems unless we force the issue.
409 	 */
410 	if ((sysgrp = getgrnam("sys")) != NULL) {
411 		(void) setgid(sysgrp->gr_gid);
412 	}
413 
414 	newfile = mktemp(tptr);
415 
416 	if ((newfp = fopen(newfile, "w")) == NULL) {
417 		perror(NULL);
418 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
419 		    newfile);
420 		return (ERROR);
421 	}
422 
423 	while ((fgets(line, sizeof (line), fp) != NULL) && status == NOERR) {
424 		/* copy the whole line */
425 		if (strlcpy(copy, line, sizeof (line)) >= sizeof (line)) {
426 			(void) fprintf(stderr, gettext(ERR_UPDATE), oldfile);
427 			status = ERROR;
428 			break;
429 		}
430 		/* cut off comments starting with '#' */
431 		if ((cp = strchr(copy, '#')) != NULL)
432 			*cp = '\0';
433 		/* ignore comment or blank lines */
434 		if (is_blank(copy)) {
435 			if (fputs(line, newfp) == EOF) {
436 				(void) fprintf(stderr, gettext(ERR_UPDATE),
437 				    oldfile);
438 				status = ERROR;
439 			}
440 			continue;
441 		}
442 
443 		/* get the driver name */
444 		if (sscanf(copy, "%" VAL2STR(FILENAME_MAX) "s", drv) != 1) {
445 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
446 			    oldfile, line);
447 			status = ERROR;
448 			break;
449 		}
450 
451 		for (i = strcspn(drv, marker); i < FILENAME_MAX; i++) {
452 			drv[i] =  '\0';
453 		}
454 
455 		if (strcmp(driver_name, drv) != 0) {
456 			if ((fputs(line, newfp)) == EOF) {
457 				(void) fprintf(stderr, gettext(ERR_UPDATE),
458 				    oldfile);
459 				status = ERROR;
460 			}
461 		} else {
462 			drvr_found++;
463 			if (match2) {	/* Just delete one entry */
464 				/* for now delete just minor_perm and aliases */
465 				if ((strcmp(oldfile, minor_perm) == 0) ||
466 				    (strcmp(oldfile, extra_privs) == 0) ||
467 				    (strcmp(oldfile, driver_aliases) == 0)) {
468 
469 					/* make defensive copy */
470 					if (strlcpy(copy, line, sizeof (line))
471 					    >= sizeof (line)) {
472 						(void) fprintf(stderr,
473 						    gettext(ERR_UPDATE),
474 						    oldfile);
475 						status = ERROR;
476 						break;
477 					}
478 					if (match_entry(copy, match2)) {
479 						nomatch = B_FALSE;
480 					} else {
481 						if ((fputs(line, newfp)) ==
482 						    EOF) {
483 							(void) fprintf(stderr,
484 							    gettext(ERR_UPDATE),
485 							    oldfile);
486 							status = ERROR;
487 						}
488 						if (nomatch != B_FALSE)
489 							nomatch = B_TRUE;
490 					}
491 				}
492 			}
493 
494 		} /* end of else */
495 	} /* end of while */
496 
497 	(void) fclose(fp);
498 	free(tptr);
499 	free(copy);
500 	if (match2)
501 		free(match2);
502 
503 	/* Make sure that the file is on disk */
504 	if (fflush(newfp) != 0 || fsync(fileno(newfp)) != 0)
505 		status = ERROR;
506 	else
507 		rv = NOERR;
508 
509 	(void) fclose(newfp);
510 
511 	/* no matching driver found */
512 	rv = NOERR;
513 	if (!drvr_found ||
514 	    (nomatch == B_TRUE)) {
515 		rv = NONE_FOUND;
516 	}
517 
518 	/*
519 	 * if error, leave original file, delete new file
520 	 * if noerr, replace original file with new file
521 	 */
522 
523 	if (status == NOERR) {
524 		if (rename(oldfile, tmphold) == -1) {
525 			perror(NULL);
526 			(void) fprintf(stderr, gettext(ERR_UPDATE), oldfile);
527 			(void) unlink(newfile);
528 			return (ERROR);
529 		} else if (rename(newfile, oldfile) == -1) {
530 			perror(NULL);
531 			(void) fprintf(stderr, gettext(ERR_UPDATE), oldfile);
532 			(void) unlink(oldfile);
533 			(void) unlink(newfile);
534 			if (link(tmphold, oldfile) == -1) {
535 				perror(NULL);
536 				(void) fprintf(stderr, gettext(ERR_BAD_LINK),
537 				    oldfile, tmphold);
538 			}
539 			return (ERROR);
540 		}
541 		(void) unlink(tmphold);
542 	} else {
543 		/*
544 		 * since there's an error, leave file alone; remove
545 		 * new file
546 		 */
547 		if (unlink(newfile) == -1) {
548 			(void) fprintf(stderr, gettext(ERR_CANT_RM), newfile);
549 		}
550 		return (ERROR);
551 	}
552 
553 	return (rv);
554 }
555 
556 
557 /*
558  * wrapper for call to get_name_to_major_entry(): given driver name,
559  * retrieve major number.
560  */
561 int
562 get_major_no(char *driver_name, char *file_name)
563 {
564 	int major = UNIQUE;
565 
566 	if (get_name_to_major_entry(&major, driver_name, file_name) == ERROR)
567 		return (ERROR);
568 	else
569 		return (major);
570 }
571 
572 /*
573  * wrapper for call to get_name_to_major_entry(): given major number,
574  * retrieve driver name.
575  */
576 int
577 get_driver_name(int major, char *file_name, char *buf)
578 {
579 	if (major < 0)
580 		return (ERROR);
581 	return (get_name_to_major_entry(&major, buf, file_name));
582 }
583 
584 
585 /*
586  * return pointer to cached name_to_major file - reads file into
587  * cache if this has not already been done.  Since there may be
588  * requests for multiple name_to_major files (rem_name_to_major,
589  * name_to_major), this routine keeps a list of cached files.
590  */
591 static int
592 get_cached_n_to_m_file(char *filename, char ***cache)
593 {
594 	struct n_to_m_cache {
595 		char *file;
596 		char **cached_file;
597 		int size;
598 		struct n_to_m_cache *next;
599 	};
600 	static struct n_to_m_cache *head = NULL;
601 	struct n_to_m_cache *ptr;
602 	FILE *fp;
603 	char drv[FILENAME_MAX + 1];
604 	char entry[FILENAME_MAX + 1];
605 	char line[MAX_N2M_ALIAS_LINE], *cp;
606 	int maj;
607 	int size = 0;
608 
609 
610 	/*
611 	 * see if the file is already cached - either
612 	 * rem_name_to_major or name_to_major
613 	 */
614 	ptr = head;
615 	while (ptr != NULL) {
616 		if (strcmp(ptr->file, filename) == 0)
617 			break;
618 		ptr = ptr->next;
619 	}
620 
621 	if (ptr == NULL) {	/* we need to cache the contents */
622 		if ((fp = fopen(filename, "r")) == NULL) {
623 			perror(NULL);
624 			(void) fprintf(stderr, gettext(ERR_CANT_OPEN),
625 			    filename);
626 			return (ERROR);
627 		}
628 
629 		while (fgets(line, sizeof (line), fp) != NULL) {
630 			/* cut off comments starting with '#' */
631 			if ((cp = strchr(line, '#')) != NULL)
632 				*cp = '\0';
633 			/* ignore comment or blank lines */
634 			if (is_blank(line))
635 				continue;
636 			/* sanity-check */
637 			if (sscanf(line,
638 			    "%" VAL2STR(FILENAME_MAX) "s"	/* drv */
639 			    "%" VAL2STR(FILENAME_MAX) "s",	/* entry */
640 			    drv, entry) != 2) {
641 				(void) fprintf(stderr, gettext(ERR_BAD_LINE),
642 				    filename, line);
643 				continue;
644 			}
645 			maj = atoi(entry);
646 			if (maj > size)
647 				size = maj;
648 		}
649 
650 		/* allocate struct to cache the file */
651 		ptr = (struct n_to_m_cache *)calloc(1,
652 		    sizeof (struct n_to_m_cache));
653 		if (ptr == NULL) {
654 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
655 			return (ERROR);
656 		}
657 		ptr->size = size + 1;
658 		/* allocate space to cache contents of file */
659 		ptr->cached_file = (char **)calloc(ptr->size, sizeof (char *));
660 		if (ptr->cached_file == NULL) {
661 			free(ptr);
662 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
663 			return (ERROR);
664 		}
665 
666 		rewind(fp);
667 
668 		/*
669 		 * now fill the cache
670 		 * the cache is an array of char pointers indexed by major
671 		 * number
672 		 */
673 		while (fgets(line, sizeof (line), fp) != NULL) {
674 			/* cut off comments starting with '#' */
675 			if ((cp = strchr(line, '#')) != NULL)
676 				*cp = '\0';
677 			/* ignore comment or blank lines */
678 			if (is_blank(line))
679 				continue;
680 			/* sanity-check */
681 			if (sscanf(line,
682 			    "%" VAL2STR(FILENAME_MAX) "s"	/* drv */
683 			    "%" VAL2STR(FILENAME_MAX) "s",	/* entry */
684 			    drv, entry) != 2) {
685 				(void) fprintf(stderr, gettext(ERR_BAD_LINE),
686 				    filename, line);
687 				continue;
688 			}
689 			maj = atoi(entry);
690 			if ((ptr->cached_file[maj] = strdup(drv)) == NULL) {
691 				(void) fprintf(stderr, gettext(ERR_NO_MEM));
692 				free(ptr->cached_file);
693 				free(ptr);
694 				return (ERROR);
695 			}
696 			(void) strcpy(ptr->cached_file[maj], drv);
697 		}
698 		(void) fclose(fp);
699 		/* link the cache struct into the list of cached files */
700 		ptr->file = strdup(filename);
701 		if (ptr->file == NULL) {
702 			for (maj = 0; maj <= ptr->size; maj++)
703 				free(ptr->cached_file[maj]);
704 			free(ptr->cached_file);
705 			free(ptr);
706 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
707 			return (ERROR);
708 		}
709 		ptr->next = head;
710 		head = ptr;
711 	}
712 	/* return value pointer to contents of file */
713 	*cache = ptr->cached_file;
714 
715 	/* return size */
716 	return (ptr->size);
717 }
718 
719 
720 /*
721  * Using get_cached_n_to_m_file(), retrieve maximum major number
722  * found in the specificed file (name_to_major/rem_name_to_major).
723  *
724  * The return value is actually the size of the internal cache including 0.
725  */
726 int
727 get_max_major(char *file_name)
728 {
729 	char **n_to_m_cache = NULL;
730 
731 	return (get_cached_n_to_m_file(file_name, &n_to_m_cache));
732 }
733 
734 
735 /*
736  * searching name_to_major: if major_no == UNIQUE then the caller wants to
737  * use the driver name as the key.  Otherwise, the caller wants to use
738  * the major number as a key.
739  *
740  * This routine caches the contents of the name_to_major file on
741  * first call.  And it could be generalized to deal with other
742  * config files if necessary.
743  */
744 static int
745 get_name_to_major_entry(int *major_no, char *driver_name, char *file_name)
746 {
747 	int maj;
748 	char **n_to_m_cache = NULL;
749 	int size = 0;
750 
751 	int ret = NOT_UNIQUE;
752 
753 	/*
754 	 * read the file in - we cache it in case caller wants to
755 	 * do multiple lookups
756 	 */
757 	size = get_cached_n_to_m_file(file_name, &n_to_m_cache);
758 
759 	if (size == ERROR)
760 		return (ERROR);
761 
762 	/* search with driver name as key */
763 	if (*major_no == UNIQUE) {
764 		for (maj = 0; maj < size; maj++) {
765 			if ((n_to_m_cache[maj] != NULL) &&
766 			    (strcmp(driver_name, n_to_m_cache[maj]) == 0)) {
767 				*major_no = maj;
768 				break;
769 			}
770 		}
771 		if (maj >= size)
772 			ret = UNIQUE;
773 	/* search with major number as key */
774 	} else {
775 		/*
776 		 * Bugid 1254588, drvconfig dump core after loading driver
777 		 * with major number bigger than entries defined in
778 		 * /etc/name_to_major.
779 		 */
780 		if (*major_no >= size)
781 			return (UNIQUE);
782 
783 		if (n_to_m_cache[*major_no] != NULL) {
784 			(void) strcpy(driver_name, n_to_m_cache[*major_no]);
785 		} else
786 			ret = UNIQUE;
787 	}
788 	return (ret);
789 }
790 
791 /*
792  * Given pointer to begining of member 'n' in a space (or separator)
793  * separated list, return pointer to member 'n+1', and establish member 'n'
794  * in *current_entry.  If unquote, then we skip a leading quote and treat
795  * the trailing quote as a separator (and skip).
796  */
797 char *
798 get_entry(
799 	char *prev_member,
800 	char *current_entry,
801 	char separator,
802 	int  unquote)
803 {
804 	char	*ptr;
805 	int	quoted = 0;
806 
807 	ptr = prev_member;
808 
809 	/* skip white space */
810 	while (isspace(*ptr))
811 		ptr++;
812 
813 	/* if unquote skip leading quote */
814 	if (unquote && *ptr == '"') {
815 		quoted++;
816 		ptr++;
817 	}
818 
819 	/* read thru the current entry looking for end, separator, or unquote */
820 	while (*ptr &&
821 	    (*ptr != separator) && (!isspace(*ptr)) &&
822 	    (!quoted || (*ptr != '"'))) {
823 		*current_entry++ = *ptr++;
824 	}
825 	*current_entry = '\0';
826 
827 	if (separator && (*ptr == separator))
828 		ptr++;	/* skip over separator */
829 	if (quoted && (*ptr == '"'))
830 		ptr++;	/* skip over trailing quote */
831 
832 	/* skip white space */
833 	while (isspace(*ptr))
834 		ptr++;
835 
836 	return (ptr);
837 }
838 
839 /*
840  * A parser specific to the add_drv "-m permission" syntax:
841  *
842  *	-m '<minor-name> <permissions> <owner> <group>', ...
843  *
844  * One entry is parsed starting at prev_member and returned
845  * in the string pointed at by current_entry.  A pointer
846  * to the entry following is returned.
847  */
848 char *
849 get_perm_entry(
850 	char *prev_member,
851 	char *current_entry)
852 {
853 	char	*ptr;
854 	int	nfields = 0;
855 	int	maxfields = 4;		/* fields in a permissions format */
856 
857 	ptr = prev_member;
858 	while (isspace(*ptr))
859 		ptr++;
860 
861 	while (*ptr) {
862 		/* comma allowed in minor name token only */
863 		if (*ptr == ',' && nfields > 0) {
864 			break;
865 		} else if (isspace(*ptr)) {
866 			*current_entry++ = *ptr++;
867 			while (isspace(*ptr))
868 				ptr++;
869 			if (++nfields == maxfields)
870 				break;
871 		} else
872 			*current_entry++ = *ptr++;
873 	}
874 	*current_entry = '\0';
875 
876 	while (isspace(*ptr))
877 		ptr++;
878 	if (*ptr == ',') {
879 		ptr++;	/* skip over optional trailing comma */
880 	}
881 	while (isspace(*ptr))
882 		ptr++;
883 
884 	return (ptr);
885 }
886 
887 void
888 enter_lock(void)
889 {
890 	struct flock lock;
891 
892 	/*
893 	 * Attempt to create the lock file.  Open the file itself,
894 	 * and not a symlink to some other file.
895 	 */
896 	add_rem_lock_fd = open(add_rem_lock,
897 	    O_CREAT|O_RDWR|O_NOFOLLOW|O_NOLINKS, S_IRUSR|S_IWUSR);
898 	if (add_rem_lock_fd < 0) {
899 		(void) fprintf(stderr, gettext(ERR_CREAT_LOCK),
900 		    add_rem_lock, strerror(errno));
901 		exit(1);
902 	}
903 
904 	lock.l_type = F_WRLCK;
905 	lock.l_whence = SEEK_SET;
906 	lock.l_start = 0;
907 	lock.l_len = 0;
908 
909 	/* Try for the lock but don't wait. */
910 	if (fcntl(add_rem_lock_fd, F_SETLK, &lock) == -1) {
911 		if (errno == EACCES || errno == EAGAIN) {
912 			(void) fprintf(stderr, gettext(ERR_PROG_IN_USE));
913 		} else {
914 			(void) fprintf(stderr, gettext(ERR_LOCK),
915 			    add_rem_lock, strerror(errno));
916 		}
917 		exit(1);
918 	}
919 }
920 
921 void
922 err_exit(void)
923 {
924 	/* release memory allocated for moddir */
925 	cleanup_moddir();
926 	/* remove add_drv/rem_drv lock */
927 	exit_unlock();
928 	exit(1);
929 }
930 
931 void
932 cleanup_moddir(void)
933 {
934 	struct drvmod_dir *walk_ptr;
935 	struct drvmod_dir *free_ptr = moddir;
936 
937 	while (free_ptr != NULL) {
938 		walk_ptr = free_ptr->next;
939 		free(free_ptr);
940 		free_ptr = walk_ptr;
941 	}
942 }
943 
944 void
945 exit_unlock(void)
946 {
947 	struct flock unlock;
948 
949 	if (add_rem_lock_fd < 0)
950 		return;
951 
952 	unlock.l_type = F_UNLCK;
953 	unlock.l_whence = SEEK_SET;
954 	unlock.l_start = 0;
955 	unlock.l_len = 0;
956 
957 	if (fcntl(add_rem_lock_fd, F_SETLK, &unlock) == -1) {
958 		(void) fprintf(stderr, gettext(ERR_UNLOCK),
959 		    add_rem_lock, strerror(errno));
960 	} else {
961 		(void) close(add_rem_lock_fd);
962 		add_rem_lock_fd = -1;
963 	}
964 }
965 
966 /*
967  * error adding driver; need to back out any changes to files.
968  * check flag to see which files need entries removed
969  * entry removal based on driver name
970  */
971 void
972 remove_entry(
973 	int c_flag,
974 	char *driver_name)
975 {
976 
977 	if (c_flag & CLEAN_NAM_MAJ) {
978 		if (delete_entry(name_to_major, driver_name, " ",
979 		    NULL) == ERROR) {
980 			(void) fprintf(stderr, gettext(ERR_NO_CLEAN),
981 			    name_to_major, driver_name);
982 		}
983 	}
984 
985 	if (c_flag & CLEAN_DRV_ALIAS) {
986 		if (delete_entry(driver_aliases, driver_name, " ",
987 		    NULL) == ERROR) {
988 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
989 			    driver_name, driver_aliases);
990 		}
991 	}
992 
993 	if (c_flag & CLEAN_DRV_CLASSES) {
994 		if (delete_entry(driver_classes, driver_name, "\t", NULL) ==
995 		    ERROR) {
996 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
997 			    driver_name, driver_classes);
998 		}
999 	}
1000 
1001 	if (c_flag & CLEAN_MINOR_PERM) {
1002 		if (delete_entry(minor_perm, driver_name, ":", NULL) == ERROR) {
1003 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
1004 			    driver_name, minor_perm);
1005 		}
1006 	}
1007 	/*
1008 	 * There's no point in removing entries from files that don't
1009 	 * exist.  Prevent error messages by checking for file existence
1010 	 * first.
1011 	 */
1012 	if ((c_flag & CLEAN_DEV_POLICY) != 0 &&
1013 	    access(device_policy, F_OK) == 0) {
1014 		if (delete_plcy_entry(device_policy, driver_name) == ERROR) {
1015 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
1016 			    driver_name, device_policy);
1017 		}
1018 	}
1019 	if ((c_flag & CLEAN_DRV_PRIV) != 0 &&
1020 	    access(extra_privs, F_OK) == 0) {
1021 		if (delete_entry(extra_privs, driver_name, ":", NULL) ==
1022 		    ERROR) {
1023 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
1024 			    driver_name, extra_privs);
1025 		}
1026 	}
1027 }
1028 
1029 int
1030 check_perms_aliases(
1031 	int m_flag,
1032 	int i_flag)
1033 {
1034 	/*
1035 	 * If neither i_flag nor m_flag are specified no need to check the
1036 	 * files for access permissions
1037 	 */
1038 	if (!m_flag && !i_flag)
1039 		return (NOERR);
1040 
1041 	/* check minor_perm file : exits and is writable */
1042 	if (m_flag) {
1043 		if (access(minor_perm, R_OK | W_OK)) {
1044 			perror(NULL);
1045 			(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1046 			    minor_perm);
1047 			return (ERROR);
1048 		}
1049 	}
1050 
1051 	/* check driver_aliases file : exits and is writable */
1052 	if (i_flag) {
1053 		if (access(driver_aliases, R_OK | W_OK)) {
1054 			perror(NULL);
1055 			(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1056 			    driver_aliases);
1057 			return (ERROR);
1058 		}
1059 	}
1060 
1061 	return (NOERR);
1062 }
1063 
1064 
1065 int
1066 check_name_to_major(int mode)
1067 {
1068 	/* check name_to_major file : exists and is writable */
1069 	if (access(name_to_major, mode)) {
1070 		perror(NULL);
1071 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1072 		    name_to_major);
1073 		return (ERROR);
1074 	}
1075 
1076 	return (NOERR);
1077 }
1078 
1079 
1080 /*
1081  * All this stuff is to support a server installing
1082  * drivers on diskless clients.  When on the server
1083  * need to prepend the basedir
1084  */
1085 int
1086 build_filenames(char *basedir)
1087 {
1088 	int	len;
1089 	int	driver_aliases_len;
1090 	int	driver_classes_len;
1091 	int	minor_perm_len;
1092 	int	name_to_major_len;
1093 	int	rem_name_to_major_len;
1094 	int	add_rem_lock_len;
1095 	int	tmphold_len;
1096 	int	devfs_root_len;
1097 	int	device_policy_len;
1098 	int	extra_privs_len;
1099 
1100 	if (basedir == NULL) {
1101 		driver_aliases = DRIVER_ALIAS;
1102 		driver_classes = DRIVER_CLASSES;
1103 		minor_perm = MINOR_PERM;
1104 		name_to_major = NAM_TO_MAJ;
1105 		rem_name_to_major = REM_NAM_TO_MAJ;
1106 		add_rem_lock = ADD_REM_LOCK;
1107 		tmphold = TMPHOLD;
1108 		devfs_root = DEVFS_ROOT;
1109 		device_policy = DEV_POLICY;
1110 		extra_privs = EXTRA_PRIVS;
1111 
1112 	} else {
1113 		len = strlen(basedir) + 1;
1114 
1115 		driver_aliases_len = len + sizeof (DRIVER_ALIAS);
1116 		driver_classes_len = len + sizeof (DRIVER_CLASSES);
1117 		minor_perm_len = len + sizeof (MINOR_PERM);
1118 		name_to_major_len = len + sizeof (NAM_TO_MAJ);
1119 		rem_name_to_major_len = len + sizeof (REM_NAM_TO_MAJ);
1120 		add_rem_lock_len = len + sizeof (ADD_REM_LOCK);
1121 		tmphold_len = len + sizeof (TMPHOLD);
1122 		devfs_root_len = len + sizeof (DEVFS_ROOT);
1123 		device_policy_len = len + sizeof (DEV_POLICY);
1124 		extra_privs_len = len + sizeof (EXTRA_PRIVS);
1125 
1126 		driver_aliases = malloc(driver_aliases_len);
1127 		driver_classes = malloc(driver_classes_len);
1128 		minor_perm = malloc(minor_perm_len);
1129 		name_to_major = malloc(name_to_major_len);
1130 		rem_name_to_major = malloc(rem_name_to_major_len);
1131 		add_rem_lock = malloc(add_rem_lock_len);
1132 		tmphold = malloc(tmphold_len);
1133 		devfs_root = malloc(devfs_root_len);
1134 		device_policy = malloc(device_policy_len);
1135 		extra_privs = malloc(extra_privs_len);
1136 
1137 		if ((driver_aliases == NULL) ||
1138 		    (driver_classes == NULL) ||
1139 		    (minor_perm == NULL) ||
1140 		    (name_to_major == NULL) ||
1141 		    (rem_name_to_major == NULL) ||
1142 		    (add_rem_lock == NULL) ||
1143 		    (tmphold == NULL) ||
1144 		    (devfs_root == NULL) ||
1145 		    (device_policy == NULL) ||
1146 		    (extra_privs == NULL)) {
1147 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
1148 			return (ERROR);
1149 		}
1150 
1151 		(void) snprintf(driver_aliases, driver_aliases_len,
1152 		    "%s%s", basedir, DRIVER_ALIAS);
1153 		(void) snprintf(driver_classes, driver_classes_len,
1154 		    "%s%s", basedir, DRIVER_CLASSES);
1155 		(void) snprintf(minor_perm, minor_perm_len,
1156 		    "%s%s", basedir, MINOR_PERM);
1157 		(void) snprintf(name_to_major, name_to_major_len,
1158 		    "%s%s", basedir, NAM_TO_MAJ);
1159 		(void) snprintf(rem_name_to_major, rem_name_to_major_len,
1160 		    "%s%s", basedir, REM_NAM_TO_MAJ);
1161 		(void) snprintf(add_rem_lock, add_rem_lock_len,
1162 		    "%s%s", basedir, ADD_REM_LOCK);
1163 		(void) snprintf(tmphold, tmphold_len,
1164 		    "%s%s", basedir, TMPHOLD);
1165 		(void) snprintf(devfs_root, devfs_root_len,
1166 		    "%s%s", basedir, DEVFS_ROOT);
1167 		(void) snprintf(device_policy, device_policy_len,
1168 		    "%s%s", basedir, DEV_POLICY);
1169 		(void) snprintf(extra_privs, extra_privs_len,
1170 		    "%s%s", basedir, EXTRA_PRIVS);
1171 	}
1172 
1173 	return (NOERR);
1174 }
1175 
1176 static int
1177 exec_command(char *path, char *cmdline[MAX_CMD_LINE])
1178 {
1179 	pid_t pid;
1180 	uint_t stat_loc;
1181 	int waitstat;
1182 	int exit_status;
1183 
1184 	/* child */
1185 	if ((pid = fork()) == 0) {
1186 		(void) execv(path, cmdline);
1187 		perror(NULL);
1188 		return (ERROR);
1189 	} else if (pid == -1) {
1190 		/* fork failed */
1191 		perror(NULL);
1192 		(void) fprintf(stderr, gettext(ERR_FORK_FAIL), cmdline);
1193 		return (ERROR);
1194 	} else {
1195 		/* parent */
1196 		do {
1197 			waitstat = waitpid(pid, (int *)&stat_loc, 0);
1198 
1199 		} while ((!WIFEXITED(stat_loc) &&
1200 		    !WIFSIGNALED(stat_loc)) || (waitstat == 0));
1201 
1202 		exit_status = WEXITSTATUS(stat_loc);
1203 
1204 		return (exit_status);
1205 	}
1206 }
1207 
1208 /*
1209  * Exec devfsadm to perform driver config/unconfig operation,
1210  * adding or removing aliases.
1211  */
1212 static int
1213 exec_devfsadm(
1214 	boolean_t config,
1215 	char *driver_name,
1216 	major_t major_num,
1217 	char *aliases,
1218 	char *classes,
1219 	int verbose_flag,
1220 	int force_flag)
1221 {
1222 	int n = 0;
1223 	char *cmdline[MAX_CMD_LINE];
1224 	char maj_num[128];
1225 	char *previous;
1226 	char *current;
1227 	int len;
1228 	int rv;
1229 
1230 	/* build command line */
1231 	cmdline[n++] = DRVCONFIG;
1232 	if (config == B_FALSE) {
1233 		cmdline[n++] = "-u";		/* unconfigure */
1234 		if (force_flag)
1235 			cmdline[n++] = "-f";	/* force if currently in use */
1236 	}
1237 	if (verbose_flag) {
1238 		cmdline[n++] = "-v";
1239 	}
1240 	cmdline[n++] = "-b";
1241 	if (classes) {
1242 		cmdline[n++] = "-c";
1243 		cmdline[n++] = classes;
1244 	}
1245 	cmdline[n++] = "-i";
1246 	cmdline[n++] = driver_name;
1247 	cmdline[n++] = "-m";
1248 	(void) snprintf(maj_num, sizeof (maj_num), "%lu", major_num);
1249 	cmdline[n++] = maj_num;
1250 
1251 	if (aliases != NULL) {
1252 		len = strlen(aliases);
1253 		previous = aliases;
1254 		do {
1255 			cmdline[n++] = "-a";
1256 			cmdline[n] = calloc(len + 1, 1);
1257 			if (cmdline[n] == NULL) {
1258 				(void) fprintf(stderr,
1259 				    gettext(ERR_NO_MEM));
1260 				return (ERROR);
1261 			}
1262 			current = get_entry(previous,
1263 			    cmdline[n++], ' ', 0);
1264 			previous = current;
1265 
1266 		} while (*current != '\0');
1267 
1268 	}
1269 	cmdline[n] = (char *)0;
1270 
1271 	rv = exec_command(DRVCONFIG_PATH, cmdline);
1272 	if (rv == NOERR)
1273 		return (NOERR);
1274 	return (ERROR);
1275 }
1276 
1277 int
1278 unconfig_driver(
1279 	char *driver_name,
1280 	major_t major_num,
1281 	char *aliases,
1282 	int verbose_flag,
1283 	int force_flag)
1284 {
1285 	return (exec_devfsadm(B_FALSE, driver_name, major_num,
1286 	    aliases, NULL, verbose_flag, force_flag));
1287 }
1288 
1289 /*
1290  * check that major_num doesn't exceed maximum on this machine
1291  * do this here to support add_drv on server for diskless clients
1292  */
1293 int
1294 config_driver(
1295 	char *driver_name,
1296 	major_t major_num,
1297 	char *aliases,
1298 	char *classes,
1299 	int cleanup_flag,
1300 	int verbose_flag)
1301 {
1302 	int	max_dev;
1303 	int	rv;
1304 
1305 	if (modctl(MODRESERVED, NULL, &max_dev) < 0) {
1306 		perror(NULL);
1307 		(void) fprintf(stderr, gettext(ERR_MAX_MAJOR));
1308 		return (ERROR);
1309 	}
1310 
1311 	if (major_num >= max_dev) {
1312 		(void) fprintf(stderr, gettext(ERR_MAX_EXCEEDS),
1313 		    major_num, max_dev);
1314 		return (ERROR);
1315 	}
1316 
1317 	/* bind major number and driver name */
1318 	rv = exec_devfsadm(B_TRUE, driver_name, major_num,
1319 	    aliases, classes, verbose_flag, 0);
1320 
1321 	if (rv == NOERR)
1322 		return (NOERR);
1323 	perror(NULL);
1324 	remove_entry(cleanup_flag, driver_name);
1325 	return (ERROR);
1326 }
1327 
1328 void
1329 load_driver(char *driver_name, int verbose_flag)
1330 {
1331 	int n = 0;
1332 	char *cmdline[MAX_CMD_LINE];
1333 	int exec_status;
1334 
1335 	/* build command line */
1336 	cmdline[n++] = DEVFSADM;
1337 	if (verbose_flag) {
1338 		cmdline[n++] = "-v";
1339 	}
1340 	cmdline[n++] = "-i";
1341 	cmdline[n++] = driver_name;
1342 	cmdline[n] = (char *)0;
1343 
1344 	exec_status = exec_command(DEVFSADM_PATH, cmdline);
1345 
1346 	if (exec_status != NOERR) {
1347 		/* no clean : name and major number are bound */
1348 		(void) fprintf(stderr, gettext(ERR_CONFIG), driver_name);
1349 	}
1350 }
1351 
1352 void
1353 get_modid(char *driver_name, int *mod)
1354 {
1355 	struct modinfo	modinfo;
1356 
1357 	modinfo.mi_id = -1;
1358 	modinfo.mi_info = MI_INFO_ALL;
1359 	do {
1360 		/*
1361 		 * If we are at the end of the list of loaded modules
1362 		 * then set *mod = -1 and return
1363 		 */
1364 		if (modctl(MODINFO, 0, &modinfo) < 0) {
1365 			*mod = -1;
1366 			return;
1367 		}
1368 
1369 		*mod = modinfo.mi_id;
1370 	} while (strcmp(driver_name, modinfo.mi_name) != 0);
1371 }
1372 
1373 int
1374 create_reconfig(char *basedir)
1375 {
1376 	char reconfig_file[MAXPATHLEN + FILENAME_MAX + 1];
1377 	FILE *reconfig_fp;
1378 
1379 	if (basedir != NULL) {
1380 		(void) strcpy(reconfig_file, basedir);
1381 		(void) strcat(reconfig_file, RECONFIGURE);
1382 	} else {
1383 		(void) strcpy(reconfig_file, RECONFIGURE);
1384 	}
1385 	if ((reconfig_fp = fopen(reconfig_file, "a")) == NULL)
1386 		return (ERROR);
1387 
1388 	(void) fclose(reconfig_fp);
1389 	return (NOERR);
1390 }
1391 
1392 
1393 /*
1394  * update_minor_entry:
1395  *	open file
1396  *	for each entry in list
1397  *		where list entries are separated by <list_separator>
1398  * 		modify entry : driver_name <entry_separator> entry
1399  *	close file
1400  *
1401  *	return error/noerr
1402  */
1403 int
1404 update_minor_entry(char *driver_name, char *perm_list)
1405 {
1406 	FILE	*fp;
1407 	FILE	*newfp;
1408 	int	match = 0;
1409 	char	line[MAX_DBFILE_ENTRY];
1410 	char	drv[FILENAME_MAX + 1];
1411 	char	minor[FILENAME_MAX + 1];
1412 	char	perm[OPT_LEN + 1];
1413 	char	own[OPT_LEN + 1];
1414 	char	grp[OPT_LEN + 1];
1415 	int	status = NOERR, i;
1416 	char	*newfile, *tptr;
1417 	char	*cp, *dup, *drv_minor;
1418 	struct group *sysgrp;
1419 
1420 	if ((fp = fopen(minor_perm, "r")) == NULL) {
1421 		perror(NULL);
1422 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1423 		    minor_perm);
1424 
1425 		return (ERROR);
1426 	}
1427 
1428 	/*
1429 	 * Build filename for temporary file
1430 	 */
1431 	if ((tptr = calloc(strlen(minor_perm) + strlen(XEND) + 1, 1)) == NULL) {
1432 		perror(NULL);
1433 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
1434 	}
1435 	(void) strcpy(tptr, minor_perm);
1436 	(void) strcat(tptr, XEND);
1437 
1438 	/*
1439 	 * Set gid so we preserve group attribute.  Ideally we wouldn't
1440 	 * assume a gid of "sys" but we can't undo the damage on already
1441 	 * installed systems unless we force the issue.
1442 	 */
1443 	if ((sysgrp = getgrnam("sys")) != NULL) {
1444 		(void) setgid(sysgrp->gr_gid);
1445 	}
1446 
1447 	newfile = mktemp(tptr);
1448 	if ((newfp = fopen(newfile, "w")) == NULL) {
1449 		perror(NULL);
1450 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1451 		    newfile);
1452 		return (ERROR);
1453 	}
1454 
1455 	if (sscanf(perm_list,
1456 	    "%" VAL2STR(FILENAME_MAX) "s"	/* minor */
1457 	    "%" VAL2STR(OPT_LEN) "s"		/* perm */
1458 	    "%" VAL2STR(OPT_LEN) "s"		/* own */
1459 	    "%" VAL2STR(OPT_LEN) "s",		/* grp */
1460 	    minor, perm, own, grp) != 4) {
1461 		status = ERROR;
1462 	}
1463 
1464 	while ((fgets(line, sizeof (line), fp) != NULL) && status == NOERR) {
1465 		/* copy the whole line into dup */
1466 		if ((dup = strdup(line)) == NULL) {
1467 			perror(NULL);
1468 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
1469 			status = ERROR;
1470 			break;
1471 		}
1472 		/* cut off comments starting with '#' */
1473 		if ((cp = strchr(dup, '#')) != NULL)
1474 			*cp = '\0';
1475 		/* ignore comment or blank lines */
1476 		if (is_blank(dup)) {
1477 			if (fputs(line, newfp) == EOF) {
1478 				(void) fprintf(stderr, gettext(ERR_UPDATE),
1479 				    minor_perm);
1480 				status = ERROR;
1481 			}
1482 			free(dup);
1483 			continue;
1484 		}
1485 
1486 		/* get the driver name */
1487 		if (sscanf(dup, "%" VAL2STR(FILENAME_MAX) "s", drv) != 1) {
1488 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
1489 			    minor_perm, line);
1490 			status = ERROR;
1491 			free(dup);
1492 			break;
1493 		}
1494 
1495 		/*
1496 		 * get the minor name; place the NULL character at the
1497 		 * end of the driver name, then make the drv_minor
1498 		 * point to the first character of the minor name.
1499 		 * the line missing ':' must be treated as a broken one.
1500 		 */
1501 		i = strcspn(drv, ":");
1502 		if (i == strlen(drv)) {
1503 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
1504 			    minor_perm, line);
1505 			status = ERROR;
1506 			free(dup);
1507 			break;
1508 		}
1509 		drv[i] =  '\0';
1510 		drv_minor = &drv[strlen(drv) + 1];
1511 
1512 		/*
1513 		 * compare both of the driver name and the minor name.
1514 		 * then the new line should be written to the file if
1515 		 * both of them match
1516 		 */
1517 		if ((strcmp(drv, driver_name) == 0) &&
1518 		    (strcmp(minor, drv_minor) == 0)) {
1519 			/* if it has a comment, keep it */
1520 			if (cp != NULL) {
1521 				cp++; /* skip a terminator */
1522 				(void) snprintf(line, sizeof (line),
1523 				    "%s:%s %s %s %s #%s\n",
1524 				    drv, minor, perm, own, grp, cp);
1525 			} else {
1526 				(void) snprintf(line, sizeof (line),
1527 				    "%s:%s %s %s %s\n",
1528 				    drv, minor, perm, own, grp);
1529 			}
1530 			match = 1;
1531 		}
1532 		free(dup);
1533 
1534 		/* update the file */
1535 		if ((fputs(line, newfp)) == EOF) {
1536 			(void) fprintf(stderr, gettext(ERR_UPDATE),
1537 			    minor_perm);
1538 			status = ERROR;
1539 		}
1540 	}
1541 
1542 	if (!match) {
1543 		(void) bzero(line, sizeof (&line[0]));
1544 		(void) snprintf(line, sizeof (line),
1545 		    "%s:%s %s %s %s\n",
1546 		    driver_name, minor, perm, own, grp);
1547 
1548 		/* add the new entry */
1549 		if ((fputs(line, newfp)) == EOF) {
1550 			(void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
1551 			status = ERROR;
1552 		}
1553 	}
1554 
1555 	(void) fclose(fp);
1556 
1557 	if (fflush(newfp) != 0 || fsync(fileno(newfp)) != 0)
1558 		status = ERROR;
1559 
1560 	(void) fclose(newfp);
1561 
1562 	/*
1563 	 * if error, leave original file, delete new file
1564 	 * if noerr, replace original file with new file
1565 	 */
1566 	if (status == NOERR) {
1567 		if (rename(minor_perm, tmphold) == -1) {
1568 			perror(NULL);
1569 			(void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
1570 			(void) unlink(newfile);
1571 			return (ERROR);
1572 		} else if (rename(newfile, minor_perm) == -1) {
1573 			perror(NULL);
1574 			(void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
1575 			(void) unlink(minor_perm);
1576 			(void) unlink(newfile);
1577 			if (link(tmphold, minor_perm) == -1) {
1578 				perror(NULL);
1579 				(void) fprintf(stderr, gettext(ERR_BAD_LINK),
1580 				    minor_perm, tmphold);
1581 			}
1582 			return (ERROR);
1583 		}
1584 		(void) unlink(tmphold);
1585 	} else {
1586 		/*
1587 		 * since there's an error, leave file alone; remove
1588 		 * new file
1589 		 */
1590 		if (unlink(newfile) == -1) {
1591 			(void) fprintf(stderr, gettext(ERR_CANT_RM), newfile);
1592 		}
1593 		return (ERROR);
1594 	}
1595 
1596 	return (NOERR);
1597 
1598 }
1599 
1600 
1601 /*
1602  * list_entry:
1603  *	open file
1604  *	read thru file, listing all entries if first entry = driver_name
1605  *	close
1606  */
1607 void
1608 list_entry(
1609 	char *oldfile,
1610 	char *driver_name,
1611 	char *marker)
1612 {
1613 	FILE	*fp;
1614 	int	i;
1615 	char	line[MAX_DBFILE_ENTRY], *cp;
1616 	char	drv[FILENAME_MAX + 1];
1617 
1618 	if ((fp = fopen(oldfile, "r")) == NULL) {
1619 		perror(NULL);
1620 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), oldfile);
1621 
1622 		return;
1623 	}
1624 
1625 	while (fgets(line, sizeof (line), fp) != NULL) {
1626 		/* cut off comments starting with '#' */
1627 		if ((cp = strchr(line, '#')) != NULL)
1628 			*cp = '\0';
1629 		/* ignore comment or blank lines */
1630 		if (is_blank(line))
1631 			continue;
1632 		/* sanity-check */
1633 		if (sscanf(line, "%" VAL2STR(FILENAME_MAX) "s", drv) != 1) {
1634 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
1635 			    oldfile, line);
1636 		}
1637 
1638 		for (i = strcspn(drv, marker); i < FILENAME_MAX; i++) {
1639 			drv[i] =  '\0';
1640 		}
1641 
1642 		if (strcmp(driver_name, drv) == 0) {
1643 			(void) fprintf(stdout, "%s", line);
1644 		}
1645 	}
1646 
1647 	(void) fclose(fp);
1648 }
1649 
1650 static boolean_t
1651 is_token(char *tok)
1652 {
1653 	/*
1654 	 * Check the token here. According to IEEE1275 Open Firmware Boot
1655 	 * Standard, the name is composed of 1 to 31 letters,
1656 	 * digits and punctuation characters from the set ",._+-", and
1657 	 * uppercase and lowercase characters are considered distinct.
1658 	 * (ie. token := [a-zA-Z0-9,._+-]+, length(token) <= 31)
1659 	 * However, since either the definition of driver or aliase names is
1660 	 * not known well, only '#' is avoided explicitly. (the kernel lexical
1661 	 * analyzer treats it as a start of a comment)
1662 	 */
1663 	for (/* nothing */; *tok != '\0'; tok++)
1664 		if (*tok == '#' || iscntrl(*tok))
1665 			return (B_FALSE);
1666 
1667 	return (B_TRUE);
1668 }
1669 
1670 /*
1671  * check each entry in perm_list for:
1672  *	4 arguments
1673  *	permission arg is in valid range
1674  * permlist entries separated by comma
1675  * return ERROR/NOERR
1676  */
1677 int
1678 check_perm_opts(char *perm_list)
1679 {
1680 	char *current_head;
1681 	char *previous_head;
1682 	char *one_entry;
1683 	int len, scan_stat;
1684 	char minor[FILENAME_MAX + 1];
1685 	char perm[OPT_LEN + 1];
1686 	char own[OPT_LEN + 1];
1687 	char grp[OPT_LEN + 1];
1688 	char dumb[OPT_LEN + 1];
1689 	int status = NOERR;
1690 	int intperm;
1691 
1692 	if ((len = strlen(perm_list)) == 0)
1693 		return (ERROR);
1694 
1695 	if ((one_entry = calloc(len + 1, 1)) == NULL) {
1696 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
1697 		return (ERROR);
1698 	}
1699 
1700 	previous_head = perm_list;
1701 	current_head = perm_list;
1702 
1703 	while (*current_head != '\0') {
1704 		bzero(one_entry, len + 1);
1705 		current_head = get_perm_entry(previous_head, one_entry);
1706 
1707 		previous_head = current_head;
1708 		scan_stat = sscanf(one_entry,
1709 		    "%" VAL2STR(FILENAME_MAX) "s"	/* minor */
1710 		    "%" VAL2STR(OPT_LEN) "s"		/* perm */
1711 		    "%" VAL2STR(OPT_LEN) "s"		/* own */
1712 		    "%" VAL2STR(OPT_LEN) "s"		/* grp */
1713 		    "%" VAL2STR(OPT_LEN) "s",		/* dumb */
1714 		    minor, perm, own, grp, dumb);
1715 
1716 		if (scan_stat < 4) {
1717 			(void) fprintf(stderr, gettext(ERR_MIS_TOK),
1718 			    "-m", one_entry);
1719 			status = ERROR;
1720 		}
1721 		if (scan_stat > 4) {
1722 			(void) fprintf(stderr, gettext(ERR_TOO_MANY_ARGS),
1723 			    "-m", one_entry);
1724 			status = ERROR;
1725 		}
1726 
1727 		intperm = atoi(perm);
1728 		if (intperm < 0000 || intperm > 4777) {
1729 			(void) fprintf(stderr, gettext(ERR_BAD_MODE), perm);
1730 			status = ERROR;
1731 		}
1732 	}
1733 
1734 	free(one_entry);
1735 	return (status);
1736 }
1737 
1738 
1739 /*
1740  * check each alias :
1741  *	alias list members separated by white space
1742  *	cannot exist as driver name in /etc/name_to_major
1743  *	cannot exist as driver or alias name in /etc/driver_aliases
1744  */
1745 int
1746 aliases_unique(char *aliases)
1747 {
1748 	char *current_head;
1749 	char *previous_head;
1750 	char *one_entry;
1751 	int len;
1752 	int is_unique;
1753 	int err;
1754 
1755 	len = strlen(aliases);
1756 
1757 	one_entry = calloc(len + 1, 1);
1758 	if (one_entry == NULL) {
1759 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
1760 		return (ERROR);
1761 	}
1762 
1763 	previous_head = aliases;
1764 
1765 	do {
1766 		bzero(one_entry, len+1);
1767 		current_head = get_entry(previous_head, one_entry, ' ', 1);
1768 		previous_head = current_head;
1769 
1770 		if ((unique_driver_name(one_entry, name_to_major,
1771 		    &is_unique)) == ERROR)
1772 			goto err_out;
1773 
1774 		if (is_unique != UNIQUE) {
1775 			(void) fprintf(stderr, gettext(ERR_ALIAS_IN_NAM_MAJ),
1776 			    one_entry);
1777 			goto err_out;
1778 		}
1779 
1780 		if ((err = unique_drv_alias(one_entry)) != UNIQUE) {
1781 			if (err == NOT_UNIQUE) {
1782 				(void) fprintf(stderr,
1783 				    gettext(ERR_ALIAS_IN_USE), one_entry);
1784 			}
1785 			goto err_out;
1786 		}
1787 
1788 		if (!is_token(one_entry)) {
1789 			(void) fprintf(stderr, gettext(ERR_BAD_TOK),
1790 			    "-i", one_entry);
1791 			goto err_out;
1792 		}
1793 
1794 	} while (*current_head != '\0');
1795 
1796 	free(one_entry);
1797 	return (NOERR);
1798 
1799 err_out:
1800 	free(one_entry);
1801 	return (ERROR);
1802 }
1803 
1804 /*
1805  * verify each alias :
1806  *	alias list members separated by white space and quoted
1807  *	exist as alias name in /etc/driver_aliases
1808  */
1809 int
1810 aliases_exist(char *aliases)
1811 {
1812 	char *current_head;
1813 	char *previous_head;
1814 	char *one_entry;
1815 	int len;
1816 
1817 	len = strlen(aliases);
1818 
1819 	one_entry = calloc(len + 1, 1);
1820 	if (one_entry == NULL) {
1821 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
1822 		return (ERROR);
1823 	}
1824 
1825 	previous_head = aliases;
1826 
1827 	do {
1828 		bzero(one_entry, len+1);
1829 		current_head = get_entry(previous_head, one_entry, ' ', 1);
1830 		previous_head = current_head;
1831 
1832 		if (unique_drv_alias(one_entry) != NOT_UNIQUE)
1833 			goto err_out;
1834 
1835 		if (!is_token(one_entry)) {
1836 			(void) fprintf(stderr, gettext(ERR_BAD_TOK),
1837 			    "-i", one_entry);
1838 			goto err_out;
1839 		}
1840 
1841 	} while (*current_head != '\0');
1842 
1843 	free(one_entry);
1844 	return (NOERR);
1845 
1846 err_out:
1847 	free(one_entry);
1848 	return (ERROR);
1849 }
1850 
1851 
1852 /*
1853  * check each alias :
1854  *	if path-oriented alias, path exists
1855  */
1856 int
1857 aliases_paths_exist(char *aliases)
1858 {
1859 	char *current_head;
1860 	char *previous_head;
1861 	char *one_entry;
1862 	int i, len;
1863 	char path[MAXPATHLEN];
1864 	struct stat buf;
1865 
1866 	len = strlen(aliases);
1867 
1868 	one_entry = calloc(len + 1, 1);
1869 	if (one_entry == NULL) {
1870 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
1871 		return (ERROR);
1872 	}
1873 
1874 	previous_head = aliases;
1875 
1876 	do {
1877 		for (i = 0; i <= len; i++)
1878 			one_entry[i] = 0;
1879 
1880 		current_head = get_entry(previous_head, one_entry, ' ', 1);
1881 		previous_head = current_head;
1882 
1883 		/* if the alias is a path, ensure that the path exists */
1884 		if (*one_entry != '/')
1885 			continue;
1886 		(void) snprintf(path, sizeof (path), "/devices/%s", one_entry);
1887 		if (stat(path, &buf) == 0)
1888 			continue;
1889 
1890 		/* no device at specified path-oriented alias path */
1891 		(void) fprintf(stderr, gettext(ERR_PATH_ORIENTED_ALIAS),
1892 		    one_entry);
1893 		free(one_entry);
1894 		return (ERROR);
1895 
1896 	} while (*current_head != '\0');
1897 
1898 	free(one_entry);
1899 
1900 	return (NOERR);
1901 }
1902 
1903 
1904 int
1905 update_driver_aliases(
1906 	char *driver_name,
1907 	char *aliases)
1908 {
1909 	/* make call to update the aliases file */
1910 	return (append_to_file(driver_name, aliases, driver_aliases,
1911 	    ' ', " ", 1));
1912 }
1913 
1914 
1915 /*
1916  * Return:
1917  *	ERROR in case of memory or read error
1918  *	UNIQUE if there is no existing match to the supplied alias
1919  *	NOT_UNIQUE if there is a match
1920  * An error message is emitted in the case of ERROR,
1921  * up to the caller otherwise.
1922  */
1923 int
1924 unique_drv_alias(char *drv_alias)
1925 {
1926 	FILE *fp;
1927 	char drv[FILENAME_MAX + 1];
1928 	char line[MAX_N2M_ALIAS_LINE + 1], *cp;
1929 	char alias[FILENAME_MAX + 1];
1930 	char *a;
1931 	int status = UNIQUE;
1932 
1933 	fp = fopen(driver_aliases, "r");
1934 
1935 	if (fp != NULL) {
1936 		while ((fgets(line, sizeof (line), fp) != 0) &&
1937 		    status == UNIQUE) {
1938 			/* cut off comments starting with '#' */
1939 			if ((cp = strchr(line, '#')) != NULL)
1940 				*cp = '\0';
1941 			/* ignore comment or blank lines */
1942 			if (is_blank(line))
1943 				continue;
1944 			/* sanity-check */
1945 			if (sscanf(line,
1946 			    "%" VAL2STR(FILENAME_MAX) "s" 	/* drv */
1947 			    "%" VAL2STR(FILENAME_MAX) "s",	/* alias */
1948 			    drv, alias) != 2)
1949 				(void) fprintf(stderr, gettext(ERR_BAD_LINE),
1950 				    driver_aliases, line);
1951 
1952 			/* unquote for compare */
1953 			if ((*alias == '"') &&
1954 			    (*(alias + strlen(alias) - 1) == '"')) {
1955 				a = &alias[1];
1956 				alias[strlen(alias) - 1] = '\0';
1957 			} else
1958 				a = alias;
1959 
1960 			if ((strcmp(drv_alias, drv) == 0) ||
1961 			    (strcmp(drv_alias, a) == 0)) {
1962 				status = NOT_UNIQUE;
1963 				break;
1964 			}
1965 		}
1966 		(void) fclose(fp);
1967 		return (status);
1968 	} else {
1969 		perror(NULL);
1970 		(void) fprintf(stderr, gettext(ERR_CANT_OPEN), driver_aliases);
1971 		return (ERROR);
1972 	}
1973 }
1974 
1975 
1976 /*
1977  * search for driver_name in first field of file file_name
1978  * searching name_to_major and driver_aliases: name separated
1979  * from the remainder of the line by white space.
1980  */
1981 int
1982 unique_driver_name(char *driver_name, char *file_name,
1983 	int *is_unique)
1984 {
1985 	int ret, err;
1986 
1987 	if ((ret = get_major_no(driver_name, file_name)) == ERROR) {
1988 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1989 		    file_name);
1990 	} else {
1991 		/* check alias file for name collision */
1992 		if ((err = unique_drv_alias(driver_name)) != UNIQUE) {
1993 			if (err == NOT_UNIQUE) {
1994 				(void) fprintf(stderr,
1995 				    gettext(ERR_ALIAS_IN_USE),
1996 				    driver_name);
1997 			}
1998 			ret = ERROR;
1999 		} else {
2000 			if (ret != UNIQUE)
2001 				*is_unique = NOT_UNIQUE;
2002 			else
2003 				*is_unique = ret;
2004 			ret = NOERR;
2005 		}
2006 	}
2007 	return (ret);
2008 }
2009 
2010 /*
2011  * returns:
2012  *	SUCCESS - not an existing driver alias
2013  *	NOT_UNIQUE - matching driver alias exists
2014  *	ERROR - an error occurred
2015  */
2016 int
2017 check_duplicate_driver_alias(char *driver_name, char *drv_alias)
2018 {
2019 	FILE *fp;
2020 	char drv[FILENAME_MAX + 1];
2021 	char line[MAX_N2M_ALIAS_LINE + 1], *cp;
2022 	char alias[FILENAME_MAX + 1];
2023 	char *a;
2024 	int status = SUCCESS;
2025 
2026 	if ((fp = fopen(driver_aliases, "r")) == NULL) {
2027 		perror(NULL);
2028 		(void) fprintf(stderr, gettext(ERR_CANT_OPEN), driver_aliases);
2029 		return (ERROR);
2030 	}
2031 
2032 	while (fgets(line, sizeof (line), fp) != 0) {
2033 		/* cut off comments starting with '#' */
2034 		if ((cp = strchr(line, '#')) != NULL)
2035 			*cp = '\0';
2036 		/* ignore comment or blank lines */
2037 		if (is_blank(line))
2038 			continue;
2039 		/* sanity-check */
2040 		if (sscanf(line,
2041 		    "%" VAL2STR(FILENAME_MAX) "s"	/* drv */
2042 		    "%" VAL2STR(FILENAME_MAX) "s",	/* alias */
2043 		    drv, alias) != 2)
2044 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
2045 			    driver_aliases, line);
2046 
2047 		/* unquote for compare */
2048 		if ((*alias == '"') &&
2049 		    (*(alias + strlen(alias) - 1) == '"')) {
2050 			a = &alias[1];
2051 			alias[strlen(alias) - 1] = '\0';
2052 		} else
2053 			a = alias;
2054 
2055 		if ((strcmp(drv_alias, a) == 0) &&
2056 		    (strcmp(drv, driver_name) == 0)) {
2057 			status = NOT_UNIQUE;
2058 		}
2059 
2060 		if ((strcmp(drv_alias, drv) == 0) ||
2061 		    ((strcmp(drv_alias, a) == 0) &&
2062 		    (strcmp(drv, driver_name) != 0))) {
2063 			(void) fprintf(stderr,
2064 			    gettext(ERR_ALIAS_IN_USE),
2065 			    drv_alias);
2066 			status = ERROR;
2067 			goto done;
2068 		}
2069 	}
2070 
2071 done:
2072 	(void) fclose(fp);
2073 	return (status);
2074 }
2075 
2076 int
2077 trim_duplicate_aliases(char *driver_name, char *aliases, char **aliases2p)
2078 {
2079 	char *current_head;
2080 	char *previous_head;
2081 	char *one_entry;
2082 	char *aliases2;
2083 	int rv, len;
2084 	int n = 0;
2085 
2086 	*aliases2p = NULL;
2087 	len = strlen(aliases) + 1;
2088 
2089 	one_entry = calloc(len, 1);
2090 	aliases2 = calloc(len, 1);
2091 	if (one_entry == NULL || aliases2 == NULL) {
2092 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
2093 		return (ERROR);
2094 	}
2095 
2096 	previous_head = aliases;
2097 
2098 	do {
2099 		(void) bzero(one_entry, len);
2100 		current_head = get_entry(previous_head, one_entry, ' ', 1);
2101 		previous_head = current_head;
2102 
2103 		rv = check_duplicate_driver_alias(driver_name, one_entry);
2104 		switch (rv) {
2105 		case SUCCESS:
2106 			/* not an existing driver alias: add it */
2107 			if (n > 0) {
2108 				if (strlcat(aliases2, " ", len) >= len)
2109 					goto err;
2110 			}
2111 			if (strlcat(aliases2, one_entry, len) >= len)
2112 				goto err;
2113 			n++;
2114 			break;
2115 		case NOT_UNIQUE:
2116 			/* matching driver alias exists: do not add it */
2117 			break;
2118 		case ERROR:
2119 			/* error reading the alias file */
2120 			goto err;
2121 		default:
2122 			goto err;
2123 		}
2124 
2125 		if (!is_token(one_entry)) {
2126 			(void) fprintf(stderr, gettext(ERR_BAD_TOK),
2127 			    "-i", one_entry);
2128 			goto err;
2129 		}
2130 	} while (*current_head != '\0');
2131 
2132 	/*
2133 	 * If all the aliases listed are already
2134 	 * present we actually have none to do.
2135 	 */
2136 	if (n == 0) {
2137 		free(aliases2);
2138 	} else {
2139 		*aliases2p = aliases2;
2140 	}
2141 	free(one_entry);
2142 	return (NOERR);
2143 
2144 err:
2145 	free(aliases2);
2146 	free(one_entry);
2147 	return (ERROR);
2148 }
2149 
2150 int
2151 check_space_within_quote(char *str)
2152 {
2153 	register int i;
2154 	register int len;
2155 	int quoted = 0;
2156 
2157 	len = strlen(str);
2158 	for (i = 0; i < len; i++, str++) {
2159 		if (*str == '"') {
2160 			if (quoted == 0)
2161 				quoted++;
2162 			else
2163 				quoted--;
2164 		} else if (*str == ' ' && quoted)
2165 			return (ERROR);
2166 	}
2167 
2168 	return (0);
2169 }
2170 
2171 
2172 /*
2173  * get major number
2174  * write driver_name major_num to name_to_major file
2175  * major_num returned in major_num
2176  * return success/failure
2177  */
2178 int
2179 update_name_to_major(char *driver_name, major_t *major_num, int server)
2180 {
2181 	char major[MAX_STR_MAJOR + 1];
2182 	struct stat buf;
2183 	char *num_list;
2184 	char drv_majnum_str[MAX_STR_MAJOR + 1];
2185 	int new_maj = -1;
2186 	int i, tmp = 0, is_unique, have_rem_n2m = 0;
2187 	int max_dev = 0;
2188 
2189 	/*
2190 	 * if driver_name already in rem_name_to_major
2191 	 * 	delete entry from rem_nam_to_major
2192 	 *	put entry into name_to_major
2193 	 */
2194 
2195 	if (stat(rem_name_to_major, &buf) == 0) {
2196 		have_rem_n2m = 1;
2197 	}
2198 
2199 	if (have_rem_n2m) {
2200 		if ((is_unique = get_major_no(driver_name, rem_name_to_major))
2201 		    == ERROR)
2202 			return (ERROR);
2203 
2204 		/*
2205 		 * found a match in rem_name_to_major
2206 		 */
2207 		if (is_unique != UNIQUE) {
2208 			char scratch[FILENAME_MAX];
2209 
2210 			/*
2211 			 * If there is a match in /etc/rem_name_to_major then
2212 			 * be paranoid: is that major number already in
2213 			 * /etc/name_to_major (potentially under another name)?
2214 			 */
2215 			if (get_driver_name(is_unique, name_to_major,
2216 			    scratch) != UNIQUE) {
2217 				/*
2218 				 * nuke the rem_name_to_major entry-- it
2219 				 * isn't helpful.
2220 				 */
2221 				(void) delete_entry(rem_name_to_major,
2222 				    driver_name, " ", NULL);
2223 			} else {
2224 				(void) snprintf(major, sizeof (major),
2225 				    "%d", is_unique);
2226 
2227 				if (append_to_file(driver_name, major,
2228 				    name_to_major, ' ', " ", 0) == ERROR) {
2229 					(void) fprintf(stderr,
2230 					    gettext(ERR_NO_UPDATE),
2231 					    name_to_major);
2232 					return (ERROR);
2233 				}
2234 
2235 				if (delete_entry(rem_name_to_major,
2236 				    driver_name, " ", NULL) == ERROR) {
2237 					(void) fprintf(stderr,
2238 					    gettext(ERR_DEL_ENTRY), driver_name,
2239 					    rem_name_to_major);
2240 					return (ERROR);
2241 				}
2242 
2243 				/* found matching entry : no errors */
2244 				*major_num = is_unique;
2245 				return (NOERR);
2246 			}
2247 		}
2248 	}
2249 
2250 	/*
2251 	 * Bugid: 1264079
2252 	 * In a server case (with -b option), we can't use modctl() to find
2253 	 *    the maximum major number, we need to dig thru client's
2254 	 *    /etc/name_to_major and /etc/rem_name_to_major for the max_dev.
2255 	 *
2256 	 * if (server)
2257 	 *    get maximum major number thru (rem_)name_to_major file on client
2258 	 * else
2259 	 *    get maximum major number allowable on current system using modctl
2260 	 */
2261 	if (server) {
2262 		max_dev = 0;
2263 		tmp = 0;
2264 
2265 		max_dev = get_max_major(name_to_major);
2266 
2267 		/* If rem_name_to_major exists, we need to check it too */
2268 		if (have_rem_n2m) {
2269 			tmp = get_max_major(rem_name_to_major);
2270 
2271 			/*
2272 			 * If name_to_major is missing, we can get max_dev from
2273 			 * /etc/rem_name_to_major.  If both missing, bail out!
2274 			 */
2275 			if ((max_dev == ERROR) && (tmp == ERROR)) {
2276 				(void) fprintf(stderr,
2277 				    gettext(ERR_CANT_ACCESS_FILE),
2278 				    name_to_major);
2279 				return (ERROR);
2280 			}
2281 
2282 			/* guard against bigger maj_num in rem_name_to_major */
2283 			if (tmp > max_dev)
2284 				max_dev = tmp;
2285 		} else {
2286 			/*
2287 			 * If we can't get major from name_to_major file
2288 			 * and there is no /etc/rem_name_to_major file,
2289 			 * then we don't have a max_dev, bail out quick!
2290 			 */
2291 			if (max_dev == ERROR)
2292 				return (ERROR);
2293 		}
2294 
2295 		/*
2296 		 * In case there is no more slack in current name_to_major
2297 		 * table, provide at least 1 extra entry so the add_drv can
2298 		 * succeed.  Since only one add_drv process is allowed at one
2299 		 * time, and hence max_dev will be re-calculated each time
2300 		 * add_drv is ran, we don't need to worry about adding more
2301 		 * than 1 extra slot for max_dev.
2302 		 */
2303 		max_dev++;
2304 
2305 	} else {
2306 		if (modctl(MODRESERVED, NULL, &max_dev) < 0) {
2307 			perror(NULL);
2308 			(void) fprintf(stderr, gettext(ERR_MAX_MAJOR));
2309 			return (ERROR);
2310 		}
2311 	}
2312 
2313 	/*
2314 	 * max_dev is really how many slots the kernel has allocated for
2315 	 * devices... [0 , maxdev-1], not the largest available device num.
2316 	 */
2317 	if ((num_list = calloc(max_dev, 1)) == NULL) {
2318 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
2319 		return (ERROR);
2320 	}
2321 
2322 	/*
2323 	 * Populate the num_list array
2324 	 */
2325 	if (fill_n2m_array(name_to_major, &num_list, &max_dev) != 0) {
2326 		return (ERROR);
2327 	}
2328 	if (have_rem_n2m) {
2329 		if (fill_n2m_array(rem_name_to_major, &num_list, &max_dev) != 0)
2330 			return (ERROR);
2331 	}
2332 
2333 	/* find first free major number */
2334 	for (i = 0; i < max_dev; i++) {
2335 		if (num_list[i] != 1) {
2336 			new_maj = i;
2337 			break;
2338 		}
2339 	}
2340 
2341 	if (new_maj == -1) {
2342 		(void) fprintf(stderr, gettext(ERR_NO_FREE_MAJOR));
2343 		return (ERROR);
2344 	}
2345 
2346 	(void) snprintf(drv_majnum_str, sizeof (drv_majnum_str),
2347 	    "%d", new_maj);
2348 	if (do_the_update(driver_name, drv_majnum_str) == ERROR) {
2349 		return (ERROR);
2350 	}
2351 
2352 	*major_num = new_maj;
2353 	return (NOERR);
2354 }
2355 
2356 
2357 int
2358 fill_n2m_array(char *filename, char **array, int *nelems)
2359 {
2360 	FILE *fp;
2361 	char line[MAX_N2M_ALIAS_LINE + 1], *cp;
2362 	char drv[FILENAME_MAX + 1];
2363 	u_longlong_t dnum;
2364 	major_t drv_majnum;
2365 
2366 	/*
2367 	 * Read through the file, marking each major number found
2368 	 * order is not relevant
2369 	 */
2370 	if ((fp = fopen(filename, "r")) == NULL) {
2371 		perror(NULL);
2372 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), filename);
2373 		return (ERROR);
2374 	}
2375 
2376 	while (fgets(line, sizeof (line), fp) != 0) {
2377 		/* cut off comments starting with '#' */
2378 		if ((cp = strchr(line, '#')) != NULL)
2379 			*cp = '\0';
2380 		/* ignore comment or blank lines */
2381 		if (is_blank(line))
2382 			continue;
2383 		/* sanity-check */
2384 		if (sscanf(line,
2385 		    "%" VAL2STR(FILENAME_MAX) "s %llu", drv, &dnum) != 2) {
2386 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
2387 			    filename, line);
2388 			(void) fclose(fp);
2389 			return (ERROR);
2390 		}
2391 
2392 		if (dnum > L_MAXMAJ32) {
2393 			(void) fprintf(stderr, gettext(ERR_MAJ_TOOBIG), drv,
2394 			    dnum, filename, L_MAXMAJ32);
2395 			continue;
2396 		}
2397 		/*
2398 		 * cast down to a major_t; we can be sure this is safe because
2399 		 * of the above range-check.
2400 		 */
2401 		drv_majnum = (major_t)dnum;
2402 
2403 		if (drv_majnum >= *nelems) {
2404 			/*
2405 			 * Allocate some more space, up to drv_majnum + 1 so
2406 			 * we can accomodate 0 through drv_majnum.
2407 			 *
2408 			 * Note that in the failure case, we leak all of the
2409 			 * old contents of array.  It's ok, since we just
2410 			 * wind up exiting immediately anyway.
2411 			 */
2412 			*nelems = drv_majnum + 1;
2413 			*array = realloc(*array, *nelems);
2414 			if (*array == NULL) {
2415 				(void) fprintf(stderr, gettext(ERR_NO_MEM));
2416 				return (ERROR);
2417 			}
2418 		}
2419 		(*array)[drv_majnum] = 1;
2420 	}
2421 
2422 	(void) fclose(fp);
2423 	return (0);
2424 }
2425 
2426 
2427 int
2428 do_the_update(char *driver_name, char *major_number)
2429 {
2430 	return (append_to_file(driver_name, major_number, name_to_major,
2431 	    ' ', " ", 0));
2432 }
2433 
2434 /*
2435  * is_blank() returns 1 (true) if a line specified is composed of
2436  * whitespace characters only. otherwise, it returns 0 (false).
2437  *
2438  * Note. the argument (line) must be null-terminated.
2439  */
2440 static int
2441 is_blank(char *line)
2442 {
2443 	for (/* nothing */; *line != '\0'; line++)
2444 		if (!isspace(*line))
2445 			return (0);
2446 	return (1);
2447 }
2448