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 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <stdio.h> 28 #include <sys/param.h> 29 #include <fcntl.h> 30 #include <stdlib.h> 31 #include <strings.h> 32 #include <errno.h> 33 #include <dirent.h> 34 #include <sys/stat.h> 35 36 #include "list.h" 37 #include "protodir.h" 38 #include "arch.h" 39 #include "exception_list.h" 40 41 #define FS " \t\n" 42 43 static char * 44 resolve_relative(const char *source, const char *link) 45 { 46 char *p; 47 char *l_next; 48 char *l_pos; 49 static char curpath[MAXPATHLEN]; 50 51 /* if absolute path - no relocation required */ 52 if (link[0] == '/') 53 return (strcpy(curpath, link)); 54 55 (void) strcpy(curpath, source); 56 p = rindex(curpath, '/'); 57 *p = '\0'; 58 l_pos = (char *)link; 59 do { 60 l_next = index(l_pos, '/'); 61 if (strncmp(l_pos, "../", 3) == 0) { 62 if ((p = rindex(curpath, '/')) != NULL) 63 *p = '\0'; 64 else 65 curpath[0] = '\0'; 66 } else if (strncmp(l_pos, "./", 2)) { 67 /* if not . then we process */ 68 if (curpath[0]) 69 (void) strcat(curpath, "/"); 70 if (l_next) { 71 (void) strncat(curpath, l_pos, 72 (l_next - l_pos)); 73 } else 74 (void) strcat(curpath, l_pos); 75 } 76 l_pos = l_next + 1; 77 } while (l_next); 78 79 return (curpath); 80 } 81 82 83 static int 84 parse_proto_line(const char *basedir, char *line, elem_list *list, short arch, 85 const char *pkgname) 86 { 87 char *type, *class, *file, *src, *maj, *min, *perm, *owner, *group; 88 char p_line[BUFSIZ]; 89 elem *dup; 90 static elem *e = NULL; 91 92 (void) strcpy(p_line, line); 93 if (!e) 94 e = (elem *)calloc(1, sizeof (elem)); 95 96 e->flag = 0; 97 98 if (!(type = strtok(p_line, FS))) { 99 (void) fprintf(stderr, "error: bad line(type) : %s\n", line); 100 return (-1); 101 } 102 103 e->file_type = type[0]; 104 105 if ((class = strtok(NULL, FS)) == NULL) { 106 (void) fprintf(stderr, "error: bad line(class) : %s\n", line); 107 return (-1); 108 } 109 110 /* 111 * Just ignore 'legacy' entries. These are not present in the proto 112 * area at all. They're phantoms. 113 */ 114 if (strcmp(class, "legacy") == 0) 115 return (0); 116 117 if (!(file = strtok(NULL, FS))) { 118 (void) fprintf(stderr, "error: bad line(file_name) : %s\n", 119 line); 120 return (-1); 121 } 122 123 e->symsrc = NULL; 124 if ((src = index(file, '=')) != NULL) { 125 /* 126 * The '=' operator is subtly different for link and non-link 127 * entries. For the hard or soft link case, the left hand side 128 * exists in the proto area and is created by the package. 129 * 130 * When the file is an editable file, it's very likely that the 131 * right hand side is only a fragment of that file, which is 132 * delivered by multiple packages in the consolidation. Thus it 133 * can't exist in the proto area, and because we can't really 134 * know where the file's root directory is, we should skip the 135 * file. 136 * 137 * For all other filetypes, assume the right hand side is in the 138 * proto area. 139 */ 140 if (e->file_type == SYM_LINK_T || e->file_type == LINK_T) { 141 *src++ = '\0'; 142 e->symsrc = strdup(src); 143 } else if (e->file_type == EDIT_T) { 144 return (0); 145 } else { 146 file = src + 1; 147 } 148 } 149 150 /* 151 * if a basedir has a value, prepend it to the filename 152 */ 153 if (basedir[0]) 154 (void) strcat(strcat(strcpy(e->name, basedir), "/"), file); 155 else 156 (void) strcpy(e->name, file); 157 158 if (e->file_type != SYM_LINK_T) { 159 if ((e->file_type == CHAR_DEV_T) || 160 (e->file_type == BLOCK_DEV_T)) { 161 if (!(maj = strtok(NULL, FS))) { 162 (void) fprintf(stderr, 163 "error: bad line(major number) : %s\n", 164 line); 165 return (-1); 166 } 167 e->major = atoi(maj); 168 169 if (!(min = strtok(NULL, FS))) { 170 (void) fprintf(stderr, 171 "error: bad line(minor number) : %s\n", 172 line); 173 return (-1); 174 } 175 e->minor = atoi(min); 176 } else { 177 e->major = -1; 178 e->minor = -1; 179 } 180 181 if (!(perm = strtok(NULL, FS))) { 182 (void) fprintf(stderr, 183 "error: bad line(permission) : %s\n", line); 184 return (-1); 185 } 186 e->perm = strtol(perm, NULL, 8); 187 188 if (!(owner = strtok(NULL, FS))) { 189 (void) fprintf(stderr, 190 "error: bad line(owner) : %s\n", line); 191 return (-1); 192 } 193 (void) strcpy(e->owner, owner); 194 195 if (!(group = strtok(NULL, FS))) { 196 (void) fprintf(stderr, 197 "error: bad line(group) : %s\n", line); 198 return (-1); 199 } 200 (void) strcpy(e->group, group); 201 } 202 203 e->inode = 0; 204 e->ref_cnt = 1; 205 e->arch = arch; 206 e->link_parent = NULL; 207 208 if (!(dup = find_elem(list, e, FOLLOW_LINK))) { 209 e->pkgs = add_pkg(NULL, pkgname); /* init pkgs list */ 210 add_elem(list, e); 211 e = NULL; 212 return (1); 213 } else if (dup->file_type == DIR_T) { 214 if (!(dup->pkgs = add_pkg(dup->pkgs, pkgname))) { 215 /* add entry to pkgs */ 216 (void) fprintf(stderr, 217 "warning: %s: Duplicate entry for %s\n", 218 pkgname, dup->name); 219 return (-1); 220 } 221 if (e->perm != dup->perm) { 222 (void) fprintf(stderr, 223 "warning: %s: permissions %#o of %s do not match " 224 "previous permissions %#o\n", 225 pkgname, e->perm, dup->name, dup->perm); 226 } 227 if (strcmp(e->owner, dup->owner) != 0) { 228 (void) fprintf(stderr, 229 "warning: %s: owner \"%s\" of %s does not match " 230 "previous owner \"%s\"\n", 231 pkgname, e->owner, dup->name, dup->owner); 232 } 233 if (strcmp(e->group, dup->group) != 0) { 234 (void) fprintf(stderr, 235 "warning: %s: group \"%s\" of %s does not match " 236 "previous group \"%s\"\n", 237 pkgname, e->group, dup->name, dup->group); 238 } 239 } else { 240 /* 241 * Signal an error only if this is something that's not on the 242 * exception list. 243 */ 244 (void) strcpy(e->name, file); 245 if (find_elem(&exception_list, e, FOLLOW_LINK) == NULL) { 246 (void) fprintf(stderr, 247 "warning: %s: duplicate entry for %s - ignored\n", 248 pkgname, e->name); 249 return (-1); 250 } 251 } 252 253 return (0); 254 } 255 256 static int 257 parse_proto_link(const char *basedir, char *line, elem_list *list, short arch, 258 const char *pkgname) 259 { 260 char *type, *file, *src; 261 char p_line[BUFSIZ]; 262 elem *p, *dup; 263 elem key; 264 static elem *e = NULL; 265 266 267 (void) strcpy(p_line, line); 268 if (!e) 269 e = (elem *)calloc(1, sizeof (elem)); 270 271 e->flag = 0; 272 type = strtok(p_line, FS); 273 e->arch = arch; 274 275 e->file_type = type[0]; 276 (void) strtok(NULL, FS); /* burn class */ 277 278 file = strtok(NULL, FS); 279 if ((src = index(file, '=')) != NULL) { 280 *src++ = '\0'; 281 e->symsrc = strdup(src); 282 } else { 283 (void) fprintf(stderr, 284 "error: %s: hard link does not have destination (%s)\n", 285 pkgname, file); 286 return (0); 287 } 288 289 /* 290 * if a basedir has a value, prepend it to the filename 291 */ 292 if (basedir[0]) 293 (void) strcat(strcat(strcpy(e->name, basedir), "/"), file); 294 else 295 (void) strcpy(e->name, file); 296 297 /* 298 * now we need to find the file that we link to - to do this 299 * we build a key. 300 */ 301 302 src = resolve_relative(e->name, e->symsrc); 303 (void) strcpy(key.name, src); 304 key.arch = e->arch; 305 if ((p = find_elem(list, &key, NO_FOLLOW_LINK)) == NULL) { 306 (void) fprintf(stderr, 307 "error: %s: hardlink to non-existent file: %s=%s\n", 308 pkgname, e->name, e->symsrc); 309 return (0); 310 } 311 if ((p->file_type == SYM_LINK_T) || (p->file_type == LINK_T)) { 312 (void) fprintf(stderr, 313 "error: %s: hardlink must link to a file or directory " 314 "(not other links): %s=%s\n", pkgname, e->name, p->name); 315 return (0); 316 } 317 e->link_parent = p; 318 e->link_sib = p->link_sib; 319 p->link_sib = e; 320 p->ref_cnt++; 321 e->inode = p->inode; 322 e->perm = p->perm; 323 e->ref_cnt = p->ref_cnt; 324 e->major = p->major; 325 e->minor = p->minor; 326 (void) strcpy(e->owner, p->owner); 327 (void) strcpy(e->group, p->owner); 328 329 if (!(dup = find_elem(list, e, NO_FOLLOW_LINK))) { 330 e->pkgs = add_pkg(NULL, pkgname); /* init pkgs list */ 331 e->link_sib = NULL; 332 add_elem(list, e); 333 e = NULL; 334 return (1); 335 } else { 336 /* 337 * Signal an error only if this is something that's not on the 338 * exception list. 339 */ 340 (void) strcpy(e->name, file); 341 if (find_elem(&exception_list, e, FOLLOW_LINK) == NULL) { 342 (void) fprintf(stderr, 343 "warning: %s: duplicate entry for %s - ignored\n", 344 pkgname, e->name); 345 return (-1); 346 } 347 } 348 349 return (0); 350 } 351 352 353 /* 354 * open up the pkginfo file and find the ARCH= and the BASEDIR= macros. 355 * I will set the arch and basedir variables based on these fields. 356 */ 357 static void 358 read_pkginfo(const char *protodir, short *arch, char *basedir) 359 { 360 char pkginfofile[MAXPATHLEN]; 361 char architecture[MAXPATHLEN]; 362 char buf[BUFSIZ]; 363 FILE *pkginfo_fp; 364 int hits = 0; 365 int i; 366 int index; 367 368 369 architecture[0] = '\0'; 370 basedir[0] = '\0'; 371 *arch = P_ISA; 372 373 /* 374 * determine whether the pkginfo file is a pkginfo.tmpl or 375 * a pkginfo file 376 */ 377 (void) strcat(strcat(strcpy(pkginfofile, protodir), "/"), 378 "pkginfo.tmpl"); 379 380 if ((pkginfo_fp = fopen(pkginfofile, "r")) == NULL) { 381 (void) strcat(strcat(strcpy(pkginfofile, protodir), "/"), 382 "pkginfo"); 383 if ((pkginfo_fp = fopen(pkginfofile, "r")) == NULL) { 384 perror(pkginfofile); 385 return; 386 } 387 } 388 389 390 while (fgets(buf, BUFSIZ, pkginfo_fp) && (hits != 3)) { 391 if (strncmp(buf, "ARCH=", 5) == 0) { 392 index = 0; 393 /* 394 * remove any '"' in the ARCH field. 395 */ 396 for (i = 5; buf[i]; i++) { 397 if (buf[i] != '"') 398 architecture[index++] = buf[i]; 399 } 400 /* -1 because above copy included '\n' */ 401 architecture[index-1] = '\0'; 402 hits += 1; 403 } else if (strncmp(buf, "BASEDIR=", 8) == 0) { 404 index = 0; 405 /* 406 * remove any '"' in the BASEDIR field, and 407 * strip off a leading '/' if present. 408 */ 409 for (i = 8; buf[i]; i++) { 410 if (buf[i] != '"' && 411 (buf[i] != '/' || index != 0)) { 412 buf[index++] = buf[i]; 413 } 414 } 415 /* -1 because above copy included '\n' */ 416 buf[index-1] = '\0'; 417 (void) strcpy(basedir, &buf[0]); 418 hits += 2; 419 } 420 } 421 (void) fclose(pkginfo_fp); 422 423 if (architecture[0]) 424 if ((*arch = assign_arch(architecture)) == 0) { 425 (void) fprintf(stderr, 426 "warning: Unknown architecture %s found in %s\n", 427 architecture, pkginfofile); 428 } 429 } 430 431 /* 432 * The first pass through the prototype file goes through and reads 433 * in all the entries except 'hard links'. Those must be processed 434 * in a second pass. 435 * 436 * If any !includes are found in the prototype file this routine 437 * will be called recursively. 438 * 439 * Args: 440 * protofile - full pathname to prototype file to be processed. 441 * protodir - directory in which prototype file resides. 442 * list - list to which elements will be added 443 * arch - architecture of current prototype 444 * basedir - basedir for package 445 * pkgname - name of package 446 * 447 * Returns: 448 * returns number of items added to list. 449 * 450 */ 451 static int 452 first_pass_prototype(const char *protofile, const char *protodir, 453 elem_list *list, short arch, const char *basedir, const char *pkgname) 454 { 455 int elem_count = 0; 456 FILE *proto_fp; 457 char include_file[MAXPATHLEN]; 458 char buf[BUFSIZ]; 459 460 if ((proto_fp = fopen(protofile, "r")) == NULL) { 461 perror(protofile); 462 return (0); 463 } 464 465 /* 466 * first pass through file - process everything but 467 * hard links. 468 */ 469 while (fgets(buf, BUFSIZ, proto_fp)) { 470 int rc; 471 472 switch (buf[0]) { 473 case FILE_T: 474 case EDIT_T: 475 case VOLATILE_T: 476 case DIR_T: 477 case SYM_LINK_T: 478 case CHAR_DEV_T: 479 case BLOCK_DEV_T: 480 if ((rc = parse_proto_line(basedir, buf, list, arch, 481 pkgname)) >= 0) { 482 elem_count += rc; 483 } else { 484 (void) fprintf(stderr, 485 "error: Errors found in %s\n", protofile); 486 } 487 break; 488 case LINK_T: 489 case 'i': 490 case '#': 491 case '\n': 492 break; 493 case '!': 494 /* Is this an include statement - if so process */ 495 if (strncmp(buf, "!include", 8) == 0) { 496 char *inc_file = (char *)(buf + 9); 497 498 /* burn white space */ 499 while ((*inc_file == ' ') || 500 (*inc_file == '\t')) 501 inc_file++; 502 if (*inc_file) { 503 /* remove trailing \n */ 504 inc_file[strlen(inc_file) - 1] = '\0'; 505 (void) strcat(strcat(strcpy( 506 include_file, protodir), "/"), 507 inc_file); 508 elem_count += 509 first_pass_prototype(include_file, 510 protodir, list, arch, basedir, 511 pkgname); 512 } else { 513 (void) fprintf(stderr, 514 "warning: bad !include statement " 515 "in prototype %s : %s\n", 516 protofile, buf); 517 } 518 } else { 519 (void) fprintf(stderr, 520 "warning: unexpected ! notation in " 521 "prototype %s : %s\n", protofile, buf); 522 523 } 524 break; 525 default: 526 (void) fprintf(stderr, 527 "warning: unexpected line in prototype %s : %s\n", 528 protofile, buf); 529 break; 530 } 531 } 532 533 (void) fclose(proto_fp); 534 535 return (elem_count); 536 } 537 538 /* 539 * The second pass through the prototype file goes through and reads 540 * and processes only the 'hard links' in the prototype file. These 541 * are resolved and added accordingly to the elements list(list). 542 * 543 * If any !includes are found in the prototype file this routine 544 * will be called recursively. 545 * 546 * Args: 547 * protofile - full pathname to prototype file to be processed. 548 * protodir - directory in which prototype file resides. 549 * list - list to which elements will be added 550 * arch - architecture of current prototype 551 * basedir - basedir for package 552 * pkgname - package name 553 * 554 * Returns: 555 * returns number of items added to list. 556 * 557 */ 558 static int 559 second_pass_prototype(const char *protofile, const char *protodir, 560 elem_list *list, short arch, const char *basedir, const char *pkgname) 561 { 562 FILE *proto_fp; 563 int elem_count = 0; 564 char include_file[MAXPATHLEN]; 565 char buf[BUFSIZ]; 566 567 if ((proto_fp = fopen(protofile, "r")) == NULL) { 568 perror(protofile); 569 return (0); 570 } 571 572 /* 573 * second pass through prototype file - process the hard links 574 * now. 575 */ 576 while (fgets(buf, BUFSIZ, proto_fp)) { 577 if (buf[0] == LINK_T) { 578 int rc; 579 580 if ((rc = parse_proto_link(basedir, buf, list, arch, 581 pkgname)) >= 0) { 582 elem_count += rc; 583 } else { 584 (void) fprintf(stderr, 585 "error: Errors found in %s\n", protofile); 586 } 587 } else if (strncmp(buf, "!include", 8) == 0) { 588 /* 589 * This is a file to include 590 */ 591 char *inc_file = (char *)(buf + 9); 592 593 /* burn white space */ 594 while ((*inc_file == ' ') || (*inc_file == '\t')) 595 inc_file++; 596 597 if (*inc_file) { 598 /* remove trailing \n */ 599 inc_file[strlen(inc_file) - 1] = '\0'; 600 /* build up include file name to be opened. */ 601 (void) strcat(strcat(strcpy(include_file, 602 protodir), "/"), inc_file); 603 /* 604 * recursively call this routine to process the 605 * !include file. 606 */ 607 elem_count += 608 second_pass_prototype(include_file, 609 protodir, list, arch, basedir, pkgname); 610 } else { 611 (void) fprintf(stderr, 612 "warning: Bad !include statement in " 613 "prototype %s : %s\n", protofile, buf); 614 } 615 } 616 } 617 (void) fclose(proto_fp); 618 619 return (elem_count); 620 } 621 622 /* 623 * Args: 624 * pkgname - name of package being processed 625 * protodir - pathname to package defs directory 626 * list - List of elements read in, elements are added to this 627 * as they are read in. 628 * verbose - verbose output 629 * 630 * Returns: 631 * number of elements added to list 632 */ 633 int 634 process_package_dir(const char *pkgname, const char *protodir, 635 elem_list *list, int verbose) 636 { 637 struct stat st_buf; 638 char protofile[MAXPATHLEN]; 639 char basedir[MAXPATHLEN]; 640 short arch; 641 int count = 0; 642 643 644 /* 645 * skip any packages we've already handled (because of 646 * dependencies) 647 */ 648 if (processed_package(pkgname)) { 649 return (0); 650 } 651 652 /* 653 * find the prototype file. Legal forms of the name are: 654 * prototype 655 * prototype_<mach> (where mach == (sparc || i386 || ppc) 656 */ 657 (void) strcat(strcat(strcpy(protofile, protodir), "/"), "prototype"); 658 if (stat(protofile, &st_buf) < 0) { 659 if (errno == ENOENT) { 660 (void) strcat(strcat(strcat(strcpy(protofile, 661 protodir), "/"), "prototype"), PROTO_EXT); 662 if (stat(protofile, &st_buf) < 0) { 663 if (errno == ENOENT) { 664 if (verbose) { 665 (void) fprintf(stderr, 666 "warning: no prototype " 667 "file found in %s, " 668 "skipping...\n", 669 protodir); 670 } 671 } else 672 perror(protofile); 673 return (0); 674 } 675 } else { 676 perror(protofile); 677 return (0); 678 } 679 } 680 681 mark_processed(pkgname); 682 683 read_pkginfo(protodir, &arch, basedir); 684 685 count += first_pass_prototype(protofile, protodir, list, arch, 686 basedir, pkgname); 687 count += second_pass_prototype(protofile, protodir, list, arch, 688 basedir, pkgname); 689 690 /* print_list(list); */ 691 return (count); 692 } 693 694 int 695 read_in_protodir(const char *dir_name, elem_list *list, int verbose) 696 { 697 DIR *p_dir; 698 struct dirent *dp; 699 char protodir[MAXPATHLEN]; 700 struct stat st_buf; 701 int count = 0; 702 703 if ((p_dir = opendir(dir_name)) == NULL) { 704 perror(dir_name); 705 exit(1); 706 } 707 708 list->type = PROTODIR_LIST; 709 710 while ((dp = readdir(p_dir)) != NULL) { 711 /* 712 * let's not check "." and ".." - I don't really like this 713 * but I wasn't really sure you could be sure that they 714 * are always the first two entries in the directory 715 * structure - so I put it in the loop. 716 * 717 * Also - we skip all directories that are names .del-*. 718 * and any SCCS directories too. 719 */ 720 if ((strcmp(dp->d_name, ".") == 0) || 721 (strcmp(dp->d_name, "..") == 0) || 722 (strncmp(dp->d_name, ".del-", 5) == 0) || 723 (strcmp(dp->d_name, "SCCS") == 0)) 724 continue; 725 726 (void) strcat(strcat(strcpy(protodir, dir_name), "/"), 727 dp->d_name); 728 if (stat(protodir, &st_buf) < 0) { 729 perror(protodir); 730 continue; 731 } 732 if (!S_ISDIR(st_buf.st_mode)) { 733 if (verbose) { 734 (void) fprintf(stderr, 735 "warning: %s not a directory\n", protodir); 736 } 737 continue; 738 } 739 740 count += process_dependencies(dp->d_name, dir_name, list, 741 verbose); 742 743 count += process_package_dir(dp->d_name, protodir, list, 744 verbose); 745 } 746 747 if (verbose) 748 (void) printf("read in %d lines\n", count); 749 750 (void) closedir(p_dir); 751 752 return (count); 753 } 754