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