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