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