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