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