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