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