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