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