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