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 (c) 2017 Peter Tribble. 24 */ 25 26 /* 27 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. 28 */ 29 30 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 31 /* All Rights Reserved */ 32 33 34 #include <stdio.h> 35 #include <string.h> 36 #include <locale.h> 37 #include <libintl.h> 38 #include <dirent.h> 39 #include <pkgstrct.h> 40 #include <pkgdev.h> 41 #include <pkglocs.h> 42 #include <archives.h> 43 #include <errno.h> 44 #include <fcntl.h> 45 #include <sys/stat.h> 46 #include <sys/param.h> 47 #include <stdlib.h> 48 #include <unistd.h> 49 #include <assert.h> 50 #include <wait.h> 51 52 /* 53 * libinstzones includes 54 */ 55 56 #include <instzones_api.h> 57 58 /* 59 * consolidation pkg command library includes 60 */ 61 62 #include <pkglib.h> 63 64 /* 65 * local pkg command library includes 66 */ 67 68 #include <install.h> 69 #include <libinst.h> 70 #include <libadm.h> 71 #include <dryrun.h> 72 #include <messages.h> 73 74 /* 75 * pkginstall local includes 76 */ 77 78 #include "pkginstall.h" 79 80 extern int pkgverbose; 81 extern fsblkcnt_t pkgmap_blks; /* main.c */ 82 83 extern struct pkgdev pkgdev; 84 85 extern char tmpdir[]; 86 extern char pkgbin[]; 87 extern char instdir[]; 88 extern char saveSpoolInstallDir[]; 89 extern char *pkginst; 90 91 extern int dbchg; 92 extern int nosetuid; 93 extern int nocnflct; 94 extern int warnflag; 95 96 #define DMRG_DONE -1 97 98 #define ck_efile(s, p) \ 99 ((p->cinfo.modtime >= 0) && \ 100 p->ainfo.local && \ 101 cverify(0, &p->ftype, s, &p->cinfo, 1)) 102 103 static int eocflag; 104 105 /* 106 * The variable below indicates that fix_attributes() will be inadequate 107 * because a replacement was permitted. 108 */ 109 static int repl_permitted = 0; 110 111 static int domerg(struct cfextra **extlist, int part, int nparts, 112 int myclass, char **srcp, char **dstp, 113 char **r_updated); 114 static void endofclass(struct cfextra **extlist, int myclass, 115 int ckflag, PKGserver server, VFP_T **a_cfTmpVfp); 116 static int fix_attributes(struct cfextra **, int); 117 static int dir_is_populated(char *dirpath); 118 static boolean_t absolutepath(char *path); 119 static boolean_t parametricpath(char *path, char **relocpath); 120 121 /* Used to keep track of the entries in extlist that are regular files. */ 122 struct reg_files { 123 struct reg_files *next; 124 int val; 125 }; 126 static struct reg_files *regfiles_head = NULL; 127 128 /* 129 * This is the function that actually installs one volume (usually that's 130 * all there is). Upon entry, the extlist is entirely correct: 131 * 132 * 1. It contains only those files which are to be installed 133 * from all volumes. 134 * 2. The mode bits in the ainfo structure for each file are set 135 * correctly in accordance with administrative defaults. 136 * 3. mstat.setuid/setgid reflect what the status *was* before 137 * pkgdbmerg() processed compliance. 138 */ 139 void 140 instvol(struct cfextra **extlist, char *srcinst, int part, 141 int nparts, PKGserver pkgserver, VFP_T **a_cfTmpVfp, 142 char **r_updated, char *a_zoneName) 143 { 144 FILE *listfp; 145 char *updated = (char *)NULL; 146 char *relocpath = (char *)NULL; 147 char *dstp; 148 char *listfile; 149 char *srcp; 150 char *pspool_loc; 151 char scrpt_dst[PATH_MAX]; 152 int count; 153 int entryidx; /* array of current package objects */ 154 int n; 155 int nc = 0; 156 int pass; /* pass count through the for loop. */ 157 int tcount; 158 struct cfent *ept; 159 struct cfextra *ext; 160 struct mergstat *mstat; 161 struct reg_files *rfp = NULL; 162 163 /* 164 * r_updated is an optional parameter that can be passed in 165 * by the caller if the caller wants to know if any objects are 166 * updated. Do not initialize r_updated; the call to instvol 167 * could be cumulative and any previous update indication must not 168 * be disturbed - this flag is only set, it must never be reset. 169 * This flag is a "char *" pointer so that the object that was 170 * updated can be displayed in debugging output. 171 */ 172 173 if (part == 1) { 174 pkgvolume(&pkgdev, srcinst, part, nparts); 175 } 176 177 tcount = 0; 178 nc = cl_getn(); 179 180 /* 181 * For each class in this volume, install those files. 182 * 183 * NOTE : This loop index may be decremented by code below forcing a 184 * second trip through for the same class. This happens only when a 185 * class is split between an archive and the tree. Examples would be 186 * old WOS packages and the occasional class containing dynamic 187 * libraries which require special treatment. 188 */ 189 190 if (is_depend_pkginfo_DB() == B_FALSE) { 191 int classidx; /* the current class */ 192 193 for (classidx = 0; classidx < nc; classidx++) { 194 int pass_relative = 0; 195 int rel_init = 0; 196 197 eocflag = count = pass = 0; 198 listfp = (FILE *)0; 199 listfile = NULL; 200 201 /* Now what do we pass to the class action script */ 202 203 if (cl_pthrel(classidx) == REL_2_CAS) { 204 pass_relative = 1; 205 } 206 207 for (;;) { 208 if (!tcount++) { 209 /* first file to install */ 210 if (a_zoneName == (char *)NULL) { 211 echo(MSG_INS_N_N, part, nparts); 212 } else { 213 echo(MSG_INS_N_N_LZ, part, nparts, 214 a_zoneName); 215 } 216 } 217 218 /* 219 * If there's an install class action script and no 220 * list file has been created yet, create that file 221 * and provide the pointer in listfp. 222 */ 223 if (cl_iscript(classidx) && !listfp) { 224 /* create list file */ 225 putparam("TMPDIR", tmpdir); 226 listfile = tempnam(tmpdir, "list"); 227 if ((listfp = fopen(listfile, "w")) == NULL) { 228 progerr(ERR_WTMPFILE, listfile); 229 quit(99); 230 } 231 } 232 233 /* 234 * The following function goes through the package 235 * object list returning the array index of the next 236 * regular file. If it encounters a directory, 237 * symlink, named pipe or device, it just creates it. 238 */ 239 240 entryidx = domerg(extlist, (pass++ ? 0 : part), nparts, 241 classidx, &srcp, &dstp, &updated); 242 243 /* Evaluate the return code */ 244 if (entryidx == DMRG_DONE) { 245 /* 246 * Set ept to the first entry in extlist 247 * which is guaranteed to exist so 248 * later checks against ept->ftype are 249 * not compared to NULL. 250 */ 251 ext = extlist[0]; 252 ept = &(ext->cf_ent); 253 break; /* no more entries to process */ 254 } 255 256 ext = extlist[entryidx]; 257 ept = &(ext->cf_ent); 258 mstat = &(ext->mstat); 259 260 /* 261 * If not installing from a partially spooled package 262 * (the "save/pspool" area), and the file contents can 263 * be changed (type is 'e' or 'v'), and the class is not 264 * "none": copy the file from the package (in pristine 265 * state with no actions performed) into the appropriate 266 * location in the packages destination "save/pspool" 267 * area. 268 */ 269 270 if ((!is_partial_inst()) && 271 ((ept->ftype == 'e') || (ept->ftype == 'v')) && 272 (strcmp(ept->pkg_class, "none") != 0)) { 273 274 if (absolutepath(ext->map_path) == B_TRUE && 275 parametricpath(ext->cf_ent.ainfo.local, 276 &relocpath) == B_FALSE) { 277 pspool_loc = ROOT; 278 } else { 279 pspool_loc = RELOC; 280 } 281 282 n = snprintf(scrpt_dst, PATH_MAX, "%s/%s/%s", 283 saveSpoolInstallDir, pspool_loc, 284 relocpath ? relocpath : ext->map_path); 285 286 if (n >= PATH_MAX) { 287 progerr(ERR_CREATE_PATH_2, 288 saveSpoolInstallDir, 289 ext->map_path); 290 quit(99); 291 } 292 293 /* copy, preserve source file mode */ 294 295 if (cppath(MODE_SRC, srcp, scrpt_dst, 0644)) { 296 warnflag++; 297 } 298 } 299 300 /* 301 * If this isn't writeable anyway, it's not going 302 * into the list file. Only count it if it's going 303 * into the list file. 304 */ 305 if (is_fs_writeable(ext->cf_ent.path, 306 &(ext->fsys_value))) 307 count++; 308 309 pkgvolume(&pkgdev, srcinst, part, nparts); 310 311 /* 312 * If source verification is OK for this class, make 313 * sure the source we're passing to the class action 314 * script is useable. 315 */ 316 if (cl_svfy(classidx) != NOVERIFY) { 317 if (cl_iscript(classidx) || 318 ((ept->ftype == 'e') || 319 (ept->ftype == 'n'))) { 320 if (ck_efile(srcp, ept)) { 321 progerr(ERR_CORRUPT, 322 srcp); 323 logerr(getErrbufAddr()); 324 warnflag++; 325 continue; 326 } 327 } 328 } 329 330 /* 331 * If there's a class action script for this class, 332 * just collect names in a temporary file 333 * that will be used as the stdin when the 334 * class action script is invoked. 335 */ 336 337 if ((cl_iscript(classidx)) && 338 ((is_fs_writeable(ept->path, 339 &(ext->fsys_value))))) { 340 if (pass_relative) { 341 if (!rel_init) { 342 (void) fputs(instdir, listfp); 343 (void) putc('\n', listfp); 344 rel_init++; 345 } 346 (void) fputs(ext->map_path, listfp); 347 (void) putc('\n', listfp); 348 } else { 349 (void) fputs(srcp ? 350 srcp : "/dev/null", listfp); 351 (void) putc(' ', listfp); 352 (void) fputs(dstp, listfp); 353 (void) putc('\n', listfp); 354 } 355 /* 356 * Note which entries in extlist are regular 357 * files to be installed via the class action 358 * script. 359 */ 360 if (regfiles_head == NULL) { 361 assert(rfp == NULL); 362 regfiles_head = 363 malloc(sizeof (struct reg_files)); 364 if (regfiles_head == NULL) { 365 progerr(ERR_MEMORY, errno); 366 quit(99); 367 } 368 regfiles_head->next = NULL; 369 regfiles_head->val = entryidx; 370 rfp = regfiles_head; 371 } else { 372 assert(rfp != NULL); 373 rfp->next = 374 malloc(sizeof (struct reg_files)); 375 if (rfp->next == NULL) { 376 progerr(ERR_MEMORY, errno); 377 quit(99); 378 } 379 rfp = rfp->next; 380 rfp->next = NULL; 381 rfp->val = entryidx; 382 } 383 384 /* 385 * A warning message about unwritable targets 386 * in a class may be appropriate here. 387 */ 388 continue; 389 } 390 391 /* 392 * If not installing from a partially spooled package 393 * (the "save/pspool" area), and the file contents can 394 * be changed (type is 'e' or 'v') and the class 395 * identifier is not "none": copy the file from the 396 * package (in pristine state with no actions performed) 397 * into the appropriate location in the packages 398 * destination "save/pspool" area. 399 */ 400 401 if ((!is_partial_inst()) && 402 ((ept->ftype == 'e') || (ept->ftype == 'v') && 403 (strcmp(ept->pkg_class, "none") != 0))) { 404 405 if (absolutepath(ext->map_path) == B_TRUE && 406 parametricpath(ext->cf_ent.ainfo.local, 407 &relocpath) == B_FALSE) { 408 pspool_loc = ROOT; 409 } else { 410 pspool_loc = RELOC; 411 } 412 413 n = snprintf(scrpt_dst, PATH_MAX, "%s/%s/%s", 414 saveSpoolInstallDir, pspool_loc, 415 relocpath ? relocpath : ext->map_path); 416 417 if (n >= PATH_MAX) { 418 progerr(ERR_CREATE_PATH_2, 419 saveSpoolInstallDir, 420 ext->map_path); 421 quit(99); 422 } 423 424 /* copy, preserve source file mode */ 425 426 if (cppath(MODE_SRC, srcp, scrpt_dst, 0644)) { 427 warnflag++; 428 } 429 } 430 431 /* 432 * There are several tests here to determine 433 * how we're going to deal with objects 434 * intended for remote read-only filesystems. 435 * We don't use is_served() because this may be 436 * a server. We're actually interested in if 437 * it's *really* remote and *really* not 438 * writeable. 439 */ 440 441 n = is_remote_fs(ept->path, &(ext->fsys_value)); 442 if ((n != 0) && 443 !is_fs_writeable(ept->path, 444 &(ext->fsys_value))) { 445 446 /* 447 * Don't change the file, we can't write 448 * to it anyway. 449 */ 450 451 mstat->attrchg = 0; 452 mstat->contchg = 0; 453 454 /* 455 * If it's currently mounted, we can 456 * at least test it for existence. 457 */ 458 459 if (is_mounted(ept->path, &(ext->fsys_value))) { 460 if (!isfile(NULL, dstp)) { 461 echo(MSG_IS_PRESENT, dstp); 462 } else { 463 echo(WRN_INSTVOL_NONE, dstp); 464 } 465 } else { 466 char *server_host; 467 468 server_host = get_server_host( 469 ext->fsys_value); 470 471 /* If not, we're just stuck. */ 472 echo(WRN_INSTVOL_NOVERIFY, 473 dstp, server_host); 474 } 475 476 continue; 477 } 478 479 /* echo output destination name */ 480 481 echo("%s", dstp); 482 483 /* 484 * if no source then no need to copy/verify 485 */ 486 487 if (srcp == (char *)NULL) { 488 continue; 489 } 490 491 /* 492 * If doing a partial installation (creating a 493 * non-global zone), extra steps need to be taken: 494 * 495 * If the file is not type 'e' and not type 'v' and 496 * the class is "none": then the file must already 497 * exist (as a result of the initial non-global zone 498 * installation which caused all non-e/v files to be 499 * copied from the global zone to the non-global 500 * zone). If this is the case, verify that the file 501 * exists and has the correct attributes. 502 */ 503 504 if (is_partial_inst() != 0) { 505 506 /* 507 * if not type 'e|v' and class 'none', then the 508 * file must already exist. 509 */ 510 511 if ((ept->ftype != 'e') && 512 (ept->ftype != 'v') && 513 (strcmp(cl_nam(ept->pkg_class_idx), 514 "none") == 0)) { 515 516 /* is file changed? */ 517 n = finalck(ept, 1, 1, B_TRUE); 518 519 /* not - ok - warn */ 520 if (n != 0) { 521 /* output warning message */ 522 logerr(NOTE_INSTVOL_FINALCKFAIL, 523 pkginst, ext->map_path); 524 } 525 continue; 526 } 527 } 528 529 /* 530 * Copy from source media to target path and fix file 531 * mode and permission now in case installation halted. 532 */ 533 534 /* 535 * If the filesystem is read-only don't attempt 536 * to copy a file. Just check that the content 537 * and attributes of the file are correct. 538 * 539 * Normally this doesn't happen, because files, 540 * which don't change, are not returned by 541 * domerg(). 542 */ 543 n = 0; 544 if (is_fs_writeable(ept->path, 545 &(ext->fsys_value))) 546 n = cppath(MODE_SET|DIR_DISPLAY, srcp, 547 dstp, ept->ainfo.mode); 548 549 if (n != 0) { 550 warnflag++; 551 } else if (!finalck(ept, 1, 1, B_FALSE)) { 552 /* 553 * everything checks here 554 */ 555 mstat->attrchg = 0; 556 mstat->contchg = 0; 557 } 558 559 /* NOTE: a package object was updated */ 560 561 if (updated == (char *)NULL) { 562 echoDebug(DBG_INSTVOL_OBJ_UPDATED, dstp); 563 updated = dstp; 564 } 565 } 566 567 /* 568 * We have now completed processing of all pathnames 569 * associated with this volume and class. 570 */ 571 if (cl_iscript(classidx)) { 572 /* 573 * Execute appropriate class action script 574 * with list of source/destination pathnames 575 * as the input to the script. 576 */ 577 578 if (chdir(pkgbin)) { 579 progerr(ERR_CHGDIR, pkgbin); 580 quit(99); 581 } 582 583 if (listfp) { 584 (void) fclose(listfp); 585 } 586 587 /* nothing updated */ 588 589 echoDebug(DBG_INSTVOL_CAS_INFO, is_partial_inst(), 590 updated ? updated : ""); 591 592 if ((is_partial_inst() != 0) && 593 (updated == (char *)NULL)) { 594 595 /* 596 * installing in non-global zone, and no object 597 * has been updated (installed/verified): 598 * do not run the class action script. 599 */ 600 601 echoDebug(DBG_INSTVOL_NOT_RUNNING_CAS, 602 a_zoneName ? a_zoneName : "?", 603 eocflag ? "ENDOFCLASS" : 604 cl_iscript(classidx), 605 cl_nam(classidx), 606 cl_iscript(classidx)); 607 608 } else { 609 /* run the class action script */ 610 611 echoDebug(DBG_INSTVOL_RUNNING_CAS, 612 a_zoneName ? a_zoneName : "?", 613 eocflag ? "ENDOFCLASS" : 614 cl_iscript(classidx), 615 cl_nam(classidx), 616 cl_iscript(classidx)); 617 618 /* Use ULIMIT if supplied. */ 619 set_ulimit(cl_iscript(classidx), ERR_CASFAIL); 620 621 if (eocflag) { 622 /* 623 * end of class detected. 624 * Since there are no more volumes which 625 * contain pathnames associated with 626 * this class, execute class action 627 * script with the ENDOFCLASS argument; 628 * we do this even if none of the path 629 * names associated with this class and 630 * volume needed installation to 631 * guarantee the class action script is 632 * executed at least once during package 633 * installation. 634 */ 635 if (pkgverbose) { 636 n = pkgexecl((listfp ? 637 listfile : CAS_STDIN), 638 CAS_STDOUT, 639 CAS_USER, CAS_GRP, 640 SHELL, "-x", 641 cl_iscript(classidx), 642 "ENDOFCLASS", NULL); 643 } else { 644 n = pkgexecl( 645 (listfp ? 646 listfile : CAS_STDIN), 647 CAS_STDOUT, CAS_USER, 648 CAS_GRP, SHELL, 649 cl_iscript(classidx), 650 "ENDOFCLASS", NULL); 651 } 652 ckreturn(n, ERR_CASFAIL); 653 } else if (count) { 654 /* execute class action script */ 655 if (pkgverbose) { 656 n = pkgexecl(listfile, 657 CAS_STDOUT, CAS_USER, 658 CAS_GRP, SHELL, "-x", 659 cl_iscript(classidx), 660 NULL); 661 } else { 662 n = pkgexecl(listfile, 663 CAS_STDOUT, CAS_USER, 664 CAS_GRP, SHELL, 665 cl_iscript(classidx), 666 NULL); 667 } 668 ckreturn(n, ERR_CASFAIL); 669 } 670 671 /* 672 * Ensure the mod times on disk match those 673 * in the pkgmap. In this case, call cverify 674 * with checksumming disabled, since the only 675 * action that needs to be done is to verify 676 * that the attributes are correct. 677 */ 678 679 if ((rfp = regfiles_head) != NULL) { 680 while (rfp != NULL) { 681 ept = &(extlist[rfp->val]->cf_ent); 682 cverify(1, &ept->ftype, ept->path, 683 &ept->cinfo, 0); 684 rfp = rfp->next; 685 } 686 regfiles_free(); 687 } 688 689 clr_ulimit(); 690 691 if ((r_updated != (char **)NULL) && 692 (*r_updated == (char *)NULL) && 693 (updated == (char *)NULL)) { 694 updated = "postinstall"; 695 echoDebug(DBG_INSTVOL_OBJ_UPDATED, 696 updated); 697 } 698 } 699 if (listfile) { 700 (void) remove(listfile); 701 } 702 } 703 704 if (eocflag && (!is_partial_inst() || (is_partial_inst() && 705 strcmp(cl_nam(classidx), "none") != 0))) { 706 if (cl_dvfy(classidx) == QKVERIFY && !repl_permitted) { 707 /* 708 * The quick verify just fixes everything. 709 * If it returns 0, all is well. If it 710 * returns 1, then the class installation 711 * was incomplete and we retry on the 712 * stuff that failed in the conventional 713 * way (without a CAS). this is primarily 714 * to accomodate old archives such as are 715 * found in pre-2.5 WOS; but, it is also 716 * used when a critical dynamic library 717 * is not archived with its class. 718 */ 719 if (!fix_attributes(extlist, classidx)) { 720 /* 721 * Reset the CAS pointer. If the 722 * function returns 0 then there 723 * was no script there in the first 724 * place and we'll just have to 725 * call this a miss. 726 */ 727 if (cl_deliscript(classidx)) 728 /* 729 * Decrement classidx for 730 * next pass. 731 */ 732 classidx--; 733 } 734 } else { 735 /* 736 * Finalize merge. This checks to make sure 737 * file attributes are correct and any links 738 * specified are created. 739 */ 740 (void) endofclass(extlist, classidx, 741 (cl_iscript(classidx) ? 0 : 1), 742 pkgserver, a_cfTmpVfp); 743 } 744 } 745 } 746 } 747 748 /* 749 * Instead of creating links back to the GZ files the logic is 750 * to let zdo recreate the files from the GZ then invoke pkgadd to 751 * install the editable files and skip over any 'f'type files. 752 * The commented out block is to create the links which should be 753 * removed once the current code is tested to be correct. 754 */ 755 756 /* 757 * Go through extlist creating links for 'f'type files 758 * if we're in a global zone. Note that this code lies 759 * here instead of in the main loop to support CAF packages. 760 * In a CAF package the files are installed by the i.none script 761 * and don't exist until all files are done being processed, thus 762 * the additional loop through extlist. 763 */ 764 765 /* 766 * output appropriate completion message 767 */ 768 769 if (is_depend_pkginfo_DB() == B_TRUE) { 770 /* updating database only (hollow package) */ 771 if (a_zoneName == (char *)NULL) { 772 echo(MSG_DBUPD_N_N, part, nparts); 773 } else { 774 echo(MSG_DBUPD_N_N_LZ, part, nparts, a_zoneName); 775 } 776 } else if (tcount == 0) { 777 /* updating package (non-hollow package) */ 778 if (a_zoneName == (char *)NULL) { 779 echo(MSG_INST_N_N, part, nparts); 780 } else { 781 echo(MSG_INST_N_N_LZ, part, nparts, a_zoneName); 782 } 783 } 784 785 /* 786 * if any package objects were updated (not otherwise already in 787 * existence), set the updated flag as appropriate 788 */ 789 790 if (updated != (char *)NULL) { 791 echoDebug(DBG_INSTVOL_OBJ_UPDATED, updated); 792 if (r_updated != (char **)NULL) { 793 *r_updated = updated; 794 } 795 } 796 797 } 798 799 /* 800 * Name: domerg 801 * Description: For the specified class, review each entry and return the array 802 * index number of the next regular file to process. Hard links are 803 * skipped (they are created in endofclass() and directories, 804 * symlinks, pipes and devices are created here, as well as any 805 * file that already exists and has the correct attributes. 806 * Arguments: struct cfextra **extlist - [RO, *RW] 807 * - Pointer to list of cfextra structures representing 808 * the pkgmap of the package to be installed 809 * int part - [RO, *RO] 810 * - the part of the package currently being processed; 811 * packages begin with part "1" and proceed for the 812 * number (nparts) that comprise the package (volume). 813 * int nparts - [RO, *RO] 814 * - the number of parts the package is divided into 815 * int myclass - [RO, *RO] 816 * - index into class array of the current class 817 * char **srcp - [RW, *RW] 818 * - pointer to pointer to string representing the source 819 * path for the next package to process - if this 820 * function returns != DMRG_DONE then this pointer is 821 * set to a pointer to a string representing the source 822 * path for the next object from the package to process 823 * char **dstp - [RW, *RW] 824 * - pointer to pointer to string representing the target 825 * path for the next package to process - if this 826 * function returns != DMRG_DONE then this pointer is 827 * set to a pointer to a string representing the target 828 * path for the next object from the package to process 829 * char **r_updated - [RO, *RW] 830 * - pointer to pointer to string - set if the last path 831 * returned exists or does not need updating. This is 832 * always set when a path to be installed exists and 833 * has the correct contents. 834 * Returns: int 835 * != DMRG_DONE - index into extlist of the next path to 836 * be processed - that needs to be installed/copied 837 * == DMRG_DONE - all entries processed 838 */ 839 840 static int 841 domerg(struct cfextra **extlist, int part, int nparts, 842 int myclass, char **srcp, char **dstp, 843 char **r_updated) 844 { 845 boolean_t stateFlag = B_FALSE; 846 int i; 847 int msg_ugid; 848 static int maxvol = 0; 849 static int svindx = 0; 850 static int svpart = 0; 851 struct cfent *ept = (struct cfent *)NULL; 852 struct mergstat *mstat = (struct mergstat *)NULL; 853 854 /* reset returned path pointers */ 855 856 *dstp = (char *)NULL; 857 *srcp = (char *)NULL; 858 859 /* set to start or continue based on which part being processed */ 860 861 if (part != 0) { 862 maxvol = 0; 863 svindx = 0; 864 svpart = part; 865 } else { 866 i = svindx; 867 part = svpart; 868 } 869 870 /* 871 * This goes through the pkgmap entries one by one testing them 872 * for inclusion in the package database as well as for validity 873 * against existing files. 874 */ 875 for (i = svindx; extlist[i]; i++) { 876 ept = &(extlist[i]->cf_ent); 877 mstat = &(extlist[i]->mstat); 878 879 /* if this isn't the class of current interest, skip it */ 880 881 if (myclass != ept->pkg_class_idx) { 882 continue; 883 } 884 885 /* if the class is invalid, announce it & exit */ 886 if (ept->pkg_class_idx == -1) { 887 progerr(ERR_CLIDX, ept->pkg_class_idx, 888 (ept->path && *ept->path) ? ept->path : "unknown"); 889 logerr(gettext("pathname=%s"), 890 (ept->path && *ept->path) ? ept->path : "unknown"); 891 logerr(gettext("class=<%s>"), 892 (ept->pkg_class && *ept->pkg_class) ? 893 ept->pkg_class : "Unknown"); 894 logerr(gettext("CLASSES=<%s>"), 895 getenv("CLASSES") ? getenv("CLASSES") : "Not Set"); 896 quit(99); 897 } 898 899 /* 900 * Next check to see if we are going to try to delete a 901 * populated directory in some distressing way. 902 */ 903 if (mstat->dir2nondir) 904 if (dir_is_populated(ept->path)) { 905 logerr(WRN_INSTVOL_NOTDIR, ept->path); 906 warnflag++; 907 mstat->denied = 1; /* install denied! */ 908 continue; 909 } else { /* Replace is OK. */ 910 /* 911 * Remove this directory, so it won't 912 * interfere with creation of the new object. 913 */ 914 if (rmdir(ept->path)) { 915 /* 916 * If it didn't work, there's nothing 917 * we can do. To continue would 918 * likely corrupt the filesystem 919 * which is unacceptable. 920 */ 921 progerr(ERR_RMDIR, ept->path); 922 quit(99); 923 } 924 925 repl_permitted = 1; /* flag it */ 926 } 927 928 /* adjust the max volume number appropriately */ 929 930 if (ept->volno > maxvol) { 931 maxvol = ept->volno; 932 } 933 934 /* if this part goes into another volume, skip it */ 935 936 if (part != ept->volno) { 937 continue; 938 } 939 940 /* 941 * If it's a conflicting file and it's not supposed to be 942 * installed, note it and skip. 943 */ 944 if (nocnflct && mstat->shared && ept->ftype != 'e') { 945 if (mstat->contchg || mstat->attrchg) { 946 echo(MSG_SHIGN, ept->path); 947 } 948 continue; 949 } 950 951 /* 952 * If we want to set uid or gid but user says no, note it. 953 * Remember that the actual mode bits in the structure have 954 * already been adjusted and the mstat flag is telling us 955 * about the original mode. 956 */ 957 if (nosetuid && (mstat->setuid || mstat->setgid)) { 958 msg_ugid = 1; /* don't repeat attribute message. */ 959 if (is_fs_writeable(ept->path, 960 &(extlist[i]->fsys_value))) { 961 if (!(mstat->contchg) && mstat->attrchg) { 962 echo(MSG_UGMOD, ept->path); 963 } else { 964 echo(MSG_UGID, ept->path); 965 } 966 } 967 } else { 968 msg_ugid = 0; 969 } 970 971 switch (ept->ftype) { 972 case 'l': /* hard link */ 973 /* links treated as object "update/skip" */ 974 stateFlag = B_TRUE; 975 continue; /* defer to final proc */ 976 977 case 's': /* for symlink, verify without fix first */ 978 /* links treated as object "update/skip" */ 979 stateFlag = B_TRUE; 980 981 /* Do this only for default verify */ 982 if (cl_dvfy(myclass) == DEFAULT) { 983 if (averify(0, &ept->ftype, 984 ept->path, &ept->ainfo)) 985 echo(MSG_SLINK, ept->path); 986 } 987 988 /*FALLTHRU*/ 989 990 case 'd': /* directory */ 991 case 'x': /* exclusive directory */ 992 case 'c': /* character special device */ 993 case 'b': /* block special device */ 994 case 'p': /* named pipe */ 995 /* these NOT treated as object "update/skip" */ 996 stateFlag = B_FALSE; 997 998 /* 999 * If we can't get to it for legitimate reasons, 1000 * don't try to verify it. 1001 */ 1002 if (is_remote_fs(ept->path, 1003 &(extlist[i]->fsys_value)) && 1004 !is_fs_writeable(ept->path, 1005 &(extlist[i]->fsys_value))) { 1006 mstat->attrchg = 0; 1007 mstat->contchg = 0; 1008 break; 1009 } 1010 1011 if (averify(1, &ept->ftype, ept->path, 1012 &ept->ainfo) == 0) { 1013 mstat->contchg = mstat->attrchg = 0; 1014 } else { 1015 progerr(ERR_CREATE_PKGOBJ, ept->path); 1016 logerr(getErrbufAddr()); 1017 warnflag++; 1018 } 1019 1020 break; 1021 1022 case 'i': /* information file */ 1023 /* not treated as object "update/skip" */ 1024 stateFlag = B_FALSE; 1025 break; 1026 1027 default: 1028 /* all files treated as object "update/skip" */ 1029 stateFlag = B_TRUE; 1030 break; 1031 } 1032 1033 /* 1034 * Both contchg and shared flags have to be taken into 1035 * account. contchg is set if the file is already present 1036 * in the package database, if it does not exist or if it 1037 * exists and is modified. 1038 * The shared flag is set when 'e' or 'v' file is not 1039 * present in the package database, exists and is not 1040 * modified. It also has to be checked here. 1041 * Shared flag is also set when file is present in package 1042 * database and owned by more than one package, but for 1043 * this case contchg has already been set. 1044 */ 1045 if (mstat->contchg || (mstat->shared && 1046 ((ept->ftype == 'e') || (ept->ftype == 'v')))) { 1047 *dstp = ept->path; 1048 if ((ept->ftype == 'f') || (ept->ftype == 'e') || 1049 (ept->ftype == 'v')) { 1050 *srcp = ept->ainfo.local; 1051 if (is_partial_inst() != 0) { 1052 if (*srcp[0] == '~') { 1053 /* Done only for C style */ 1054 char *tmp_ptr; 1055 tmp_ptr = extlist[i]->map_path; 1056 if (ept->ftype != 'f') { 1057 /* 1058 * translate source 1059 * pathname 1060 */ 1061 *srcp = 1062 srcpath(instdir, 1063 tmp_ptr, 1064 part, 1065 nparts); 1066 } else { 1067 /* 1068 * instdir has the absolute path 1069 * to saveSpoolInstallDir for 1070 * the package. This is only 1071 * useful for 'e','v' types. 1072 * 1073 * For 'f', we generate the 1074 * absolute src path with the 1075 * help of install root and the 1076 * basedir. 1077 */ 1078 *srcp = trans_srcp_pi( 1079 ept->ainfo.local); 1080 } 1081 } else { 1082 *srcp = extlist[i]->map_path; 1083 } 1084 } else { 1085 if (*srcp[0] == '~') { 1086 /* translate source pathname */ 1087 *srcp = srcpath(instdir, 1088 &(ept->ainfo.local[1]), 1089 part, nparts); 1090 } 1091 } 1092 1093 echoDebug(DBG_DOMERG_NO_SUCH_FILE, 1094 ept->ftype, cl_nam(ept->pkg_class_idx), 1095 ept->path); 1096 } else { 1097 /* 1098 * At this point, we're returning a non-file 1099 * that couldn't be created in the standard 1100 * way. If it refers to a filesystem that is 1101 * not writeable by us, don't waste the 1102 * calling process's time. 1103 */ 1104 if (!is_fs_writeable(ept->path, 1105 &(extlist[i]->fsys_value))) { 1106 echoDebug(DBG_DOMERG_NOT_WRITABLE, 1107 ept->ftype, 1108 cl_nam(ept->pkg_class_idx), 1109 ept->path); 1110 continue; 1111 } 1112 1113 *srcp = NULL; 1114 echoDebug(DBG_DOMERG_NOT_THERE, 1115 ept->ftype, cl_nam(ept->pkg_class_idx), 1116 ept->path); 1117 } 1118 1119 svindx = i+1; 1120 backup(*dstp, 1); 1121 return (i); 1122 } 1123 1124 if (mstat->attrchg) { 1125 backup(ept->path, 0); 1126 if (!msg_ugid) 1127 echo(MSG_ATTRIB, ept->path); 1128 1129 /* fix the attributes now for robustness sake */ 1130 if (averify(1, &ept->ftype, 1131 ept->path, 1132 &ept->ainfo) == 0) { 1133 mstat->attrchg = 0; 1134 } 1135 } 1136 1137 /* 1138 * package object exists, or does not need updating: 1139 * treat the object as if it were "updated" 1140 */ 1141 1142 /* LINTED warning: statement has no consequent: if */ 1143 if ((stateFlag == B_FALSE) || (ept == (struct cfent *)NULL)) { 1144 /* 1145 * the object in question is a directory or special 1146 * file - the fact that this type of object already 1147 * exists or does not need updating must not trigger 1148 * the object updated indication - that would cause 1149 * class action scripts to be run when installing a 1150 * new non-global zone 1151 */ 1152 } else { 1153 if (r_updated != (char **)NULL) { 1154 if (*r_updated == (char *)NULL) { 1155 echoDebug(DBG_INSTVOL_OBJ_UPDATED, 1156 ept->path); 1157 } 1158 *r_updated = ept->path; 1159 } 1160 } 1161 } 1162 1163 if (maxvol == part) { 1164 eocflag++; /* endofclass */ 1165 } 1166 1167 return (DMRG_DONE); /* no remaining entries on this volume */ 1168 } 1169 1170 /* 1171 * Determine if the provided directory is populated. Return 0 if so and 1 if 1172 * not. This also returns 0 if the dirpath is not a directory or if it does 1173 * not exist. 1174 */ 1175 static int 1176 dir_is_populated(char *dirpath) { 1177 DIR *dirfp; 1178 struct dirent *drp; 1179 int retcode = 0; 1180 1181 if ((dirfp = opendir(dirpath)) != NULL) { 1182 while ((drp = readdir(dirfp)) != NULL) { 1183 if (strcmp(drp->d_name, ".") == 0) { 1184 continue; 1185 } 1186 if (strcmp(drp->d_name, "..") == 0) { 1187 continue; 1188 } 1189 /* 1190 * If we get here, there's a real file in the 1191 * directory 1192 */ 1193 retcode = 1; 1194 break; 1195 } 1196 (void) closedir(dirfp); 1197 } 1198 1199 return (retcode); 1200 } 1201 1202 /* 1203 * This is the function that cleans up the installation of this class. 1204 * This is where hard links get put in since the stuff they're linking 1205 * probably exists by now. 1206 */ 1207 static void 1208 endofclass(struct cfextra **extlist, int myclass, int ckflag, 1209 PKGserver pkgserver, VFP_T **a_cfTmpVfp) 1210 { 1211 char *temppath; 1212 char *pspool_loc; 1213 char *relocpath = (char *)NULL; 1214 char scrpt_dst[PATH_MAX]; 1215 int flag; 1216 int idx; 1217 int n; 1218 struct cfent *ept; /* entry from the internal list */ 1219 struct cfextra entry; /* entry from the package database */ 1220 struct mergstat *mstat; /* merge status */ 1221 struct pinfo *pinfo; 1222 1223 /* open the package database (contents) file */ 1224 1225 if (!ocfile(&pkgserver, a_cfTmpVfp, pkgmap_blks)) { 1226 quit(99); 1227 } 1228 1229 echo(MSG_VERIFYING_CLASS, cl_nam(myclass)); 1230 1231 for (idx = 0; /* void */; idx++) { 1232 /* find next package object in this class */ 1233 while (extlist[idx]) { 1234 if ((extlist[idx]->cf_ent.ftype != 'i') && 1235 extlist[idx]->cf_ent.pkg_class_idx == myclass) { 1236 break; 1237 } 1238 idx++; 1239 } 1240 1241 if (extlist[idx] == NULL) 1242 break; 1243 1244 1245 ept = &(extlist[idx]->cf_ent); 1246 mstat = &(extlist[idx]->mstat); 1247 1248 temppath = extlist[idx]->client_path; 1249 1250 /* 1251 * At this point the only difference between the entry 1252 * in the contents file and the entry in extlist[] is 1253 * that the status indicator contains CONFIRM_CONT. 1254 * This function should return one or something is wrong. 1255 */ 1256 1257 n = srchcfile(&(entry.cf_ent), temppath, pkgserver); 1258 1259 if (n < 0) { 1260 char *errstr = getErrstr(); 1261 progerr(ERR_CFBAD); 1262 logerr(gettext("pathname=%s"), 1263 entry.cf_ent.path && *entry.cf_ent.path ? 1264 entry.cf_ent.path : "Unknown"); 1265 logerr(gettext("problem=%s"), 1266 (errstr && *errstr) ? errstr : "Unknown"); 1267 quit(99); 1268 } else if (n != 1) { 1269 /* 1270 * Check if path should be in the package 1271 * database. 1272 */ 1273 if ((mstat->shared && nocnflct)) { 1274 continue; 1275 } 1276 progerr(ERR_CFMISSING, ept->path); 1277 quit(99); 1278 } 1279 1280 /* 1281 * If merge was not appropriate for this object, now is the 1282 * time to choose one or the other. 1283 */ 1284 if (mstat->denied) { 1285 /* 1286 * If installation was denied AFTER the package 1287 * database was updated, skip this. We've already 1288 * announced the discrepancy and the verifications 1289 * that follow will make faulty decisions based on 1290 * the ftype, which may not be correct. 1291 */ 1292 progerr(ERR_COULD_NOT_INSTALL, ept->path); 1293 warnflag++; 1294 } else { 1295 if (mstat->replace) 1296 /* 1297 * This replaces the old entry with the new 1298 * one. This should never happen in the new 1299 * DB since the entries are already identical. 1300 */ 1301 repl_cfent(ept, &(entry.cf_ent)); 1302 1303 /* 1304 * Validate this entry and change the status flag in 1305 * the package database. 1306 */ 1307 if (ept->ftype == RM_RDY) { 1308 (void) eptstat(&(entry.cf_ent), pkginst, 1309 STAT_NEXT); 1310 } else { 1311 /* check the hard link now. */ 1312 if (ept->ftype == 'l') { 1313 if (averify(0, &ept->ftype, 1314 ept->path, &ept->ainfo)) { 1315 echo(MSG_HRDLINK, 1316 ept->path); 1317 mstat->attrchg++; 1318 } 1319 } 1320 1321 /* 1322 * Don't install or verify objects for 1323 * remote, read-only filesystems. We need 1324 * only flag them as shared from some server. 1325 * Otherwise, ok to do final check. 1326 */ 1327 if (is_remote_fs(ept->path, 1328 &(extlist[idx]->fsys_value)) && 1329 !is_fs_writeable(ept->path, 1330 &(extlist[idx]->fsys_value))) { 1331 flag = -1; 1332 } else { 1333 flag = finalck(ept, mstat->attrchg, 1334 (ckflag ? mstat->contchg : 1335 (-1)), B_FALSE); 1336 } 1337 1338 pinfo = entry.cf_ent.pinfo; 1339 1340 /* Find this package in the list. */ 1341 while (pinfo) { 1342 if (strcmp(pkginst, pinfo->pkg) == 0) { 1343 break; 1344 } 1345 pinfo = pinfo->next; 1346 } 1347 1348 /* 1349 * If this package owns this file, then store 1350 * it in the database with the appropriate 1351 * status. Need to check pinfo in case it 1352 * points to NULL which could happen if 1353 * pinfo->next = NULL above. 1354 */ 1355 if (pinfo) { 1356 if (flag < 0 || is_served(ept->path, 1357 &(extlist[idx]->fsys_value))) { 1358 /* 1359 * This is provided to 1360 * clients by a server. 1361 */ 1362 pinfo->status = SERVED_FILE; 1363 } else { 1364 /* 1365 * It's either there or it's 1366 * not. 1367 */ 1368 pinfo->status = (flag ? 1369 NOT_FND : ENTRY_OK); 1370 } 1371 } 1372 } 1373 } 1374 1375 /* 1376 * If not installing from a partially spooled package, the 1377 * "save/pspool" area, and the file contents can be 1378 * changed (type is 'e' or 'v'), and the class IS "none": 1379 * copy the installed volatile file into the appropriate 1380 * location in the packages destination "save/pspool" area. 1381 */ 1382 1383 if ((!is_partial_inst()) && 1384 ((ept->ftype == 'e') || (ept->ftype == 'v')) && 1385 (strcmp(ept->pkg_class, "none") == 0)) { 1386 1387 if (absolutepath(extlist[idx]->map_path) == B_TRUE && 1388 parametricpath(extlist[idx]->cf_ent.ainfo.local, 1389 &relocpath) == B_FALSE) { 1390 pspool_loc = ROOT; 1391 } else { 1392 pspool_loc = RELOC; 1393 } 1394 1395 n = snprintf(scrpt_dst, PATH_MAX, "%s/%s/%s", 1396 saveSpoolInstallDir, pspool_loc, 1397 relocpath ? relocpath : extlist[idx]->map_path); 1398 1399 if (n >= PATH_MAX) { 1400 progerr(ERR_CREATE_PATH_2, 1401 saveSpoolInstallDir, 1402 extlist[idx]->map_path); 1403 quit(99); 1404 } 1405 1406 /* copy, preserve source file mode */ 1407 1408 if (cppath(MODE_SRC, ept->path, scrpt_dst, 0644)) { 1409 warnflag++; 1410 } 1411 } 1412 1413 /* 1414 * Now insert this potentially changed package database 1415 * entry. 1416 */ 1417 if (entry.cf_ent.npkgs) { 1418 if (putcvfpfile(&(entry.cf_ent), *a_cfTmpVfp)) { 1419 quit(99); 1420 } 1421 } 1422 } 1423 1424 n = swapcfile(pkgserver, a_cfTmpVfp, pkginst, dbchg); 1425 if (n == RESULT_WRN) { 1426 warnflag++; 1427 } else if (n == RESULT_ERR) { 1428 quit(99); 1429 } 1430 } 1431 1432 /* 1433 * This function goes through and fixes all the attributes. This is called 1434 * out by using DST_QKVERIFY=this_class in the pkginfo file. The primary 1435 * use for this is to fix up files installed by a class action script 1436 * which is time-critical and reliable enough to assume likely success. 1437 * The first such format was for WOS compressed-cpio'd file sets. 1438 * The second format is the Class Archive Format. 1439 */ 1440 static int 1441 fix_attributes(struct cfextra **extlist, int idx) 1442 { 1443 struct cfextra *ext; 1444 int i, retval = 1; 1445 int nc = cl_getn(); 1446 int n; 1447 struct cfent *ept; 1448 struct mergstat *mstat; 1449 char scrpt_dst[PATH_MAX]; 1450 char *pspool_loc; 1451 char *relocpath = (char *)NULL; 1452 1453 for (i = 0; extlist[i]; i++) { 1454 ext = extlist[i]; 1455 ept = &(extlist[i]->cf_ent); 1456 mstat = &(extlist[i]->mstat); 1457 1458 /* 1459 * We don't care about 'i'nfo files because, they 1460 * aren't laid down, 'e'ditable files can change 1461 * anyway, so who cares and 's'ymlinks were already 1462 * fixed in domerg(); however, certain old WOS 1463 * package symlinks depend on a bug in the old 1464 * pkgadd which has recently been expunged. For 1465 * those packages in 2.2, we repeat the verification 1466 * of symlinks. 1467 * 1468 * By 2.6 or so, ftype == 's' should be added to this. 1469 */ 1470 if (ept->ftype == 'i' || ept->ftype == 'e' || 1471 (mstat->shared && nocnflct)) 1472 continue; 1473 1474 if (mstat->denied) { 1475 progerr(ERR_COULD_NOT_INSTALL, ept->path); 1476 warnflag++; 1477 continue; 1478 } 1479 1480 if (ept->pkg_class_idx < 0 || ept->pkg_class_idx > nc) { 1481 progerr(ERR_CLIDX, ept->pkg_class_idx, 1482 (ept->path && *ept->path) ? ept->path : "unknown"); 1483 continue; 1484 } 1485 1486 /* If this is the right class, do the fast verify. */ 1487 if (ept->pkg_class_idx == idx) { 1488 if (fverify(1, &ept->ftype, ept->path, 1489 &ept->ainfo, &ept->cinfo) == 0) { 1490 mstat->attrchg = 0; 1491 mstat->contchg = 0; 1492 } else /* We'll try full verify later */ 1493 retval = 0; 1494 } 1495 /* 1496 * Need to copy the installed volitale file back to the 1497 * partial spooled area if we are installing to a local zone 1498 * or similar installation method. 1499 */ 1500 1501 if ((!is_partial_inst()) && 1502 ((ept->ftype == 'e') || (ept->ftype == 'v')) && 1503 (strcmp(ept->pkg_class, "none") == 0)) { 1504 1505 if (absolutepath(ext->map_path) == B_TRUE && 1506 parametricpath(ext->cf_ent.ainfo.local, 1507 &relocpath) == B_FALSE) { 1508 pspool_loc = ROOT; 1509 } else { 1510 pspool_loc = RELOC; 1511 } 1512 1513 n = snprintf(scrpt_dst, PATH_MAX, "%s/%s/%s", 1514 saveSpoolInstallDir, pspool_loc, 1515 relocpath ? relocpath : ext->map_path); 1516 1517 if (n >= PATH_MAX) { 1518 progerr(ERR_CREATE_PATH_2, 1519 saveSpoolInstallDir, 1520 ext->map_path); 1521 quit(99); 1522 } 1523 1524 /* copy, preserve source file mode */ 1525 1526 if (cppath(MODE_SRC, ept->path, scrpt_dst, 0644)) { 1527 warnflag++; 1528 } 1529 } 1530 } 1531 1532 return (retval); 1533 } 1534 1535 /* 1536 * Check to see if first charcter in path is a '/'. 1537 * 1538 * Return: 1539 * B_TRUE - if path is prepended with '/' 1540 * B_FALSE - if not 1541 */ 1542 static boolean_t 1543 absolutepath(char *path) 1544 { 1545 assert(path != NULL); 1546 assert(path[0] != '\0'); 1547 1548 return (path[0] == '/' ? B_TRUE : B_FALSE); 1549 } 1550 1551 /* 1552 * Check to see if path contains a '$' which makes it 1553 * a parametric path and therefore relocatable. 1554 * 1555 * Parameters: 1556 * path - The path to determine if it is absolute 1557 * relocpath - The value of the unconditioned path 1558 * i.e. $OPTDIR/usr/ls 1559 * Return: 1560 * B_TRUE - if path is a parametric path 1561 * B_FALSE - if not 1562 */ 1563 static boolean_t 1564 parametricpath(char *path, char **relocpath) 1565 { 1566 assert(path != NULL); 1567 assert(path[0] != '\0'); 1568 1569 /* 1570 * If this is a valid parametric path then a '$' MUST occur at the 1571 * first or second character. 1572 */ 1573 1574 if (path[0] == '$' || path[1] == '$') { 1575 /* 1576 * If a parametric path exists then when copying the 1577 * path to the pspool directoy from the installing 1578 * pkgs reloc directory we want to use the uncononditional 1579 * varaiable path. 1580 */ 1581 *relocpath = (path + 1); 1582 return (B_TRUE); 1583 } 1584 return (B_FALSE); 1585 } 1586 1587 void 1588 regfiles_free() 1589 { 1590 if (regfiles_head != NULL) { 1591 struct reg_files *rfp = regfiles_head->next; 1592 1593 while (rfp != NULL) { 1594 free(regfiles_head); 1595 regfiles_head = rfp; 1596 rfp = regfiles_head->next; 1597 } 1598 free(regfiles_head); 1599 regfiles_head = NULL; 1600 } 1601 } 1602