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