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