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