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 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 28 #include <stdio.h> 29 #include <limits.h> 30 #include <stdlib.h> 31 #include <unistd.h> 32 #include <errno.h> 33 #include <string.h> 34 #include <sys/types.h> 35 #include <pkgstrct.h> 36 #include <locale.h> 37 #include <libintl.h> 38 #include <pkglib.h> 39 #include <install.h> 40 #include <libinst.h> 41 42 #define WRN_NOPKGOBJ "WARNING: no package objects found" 43 44 #define ERR_MEMORY "memory allocation failure" 45 #define ERR_DUPPATH "duplicate pathname <%s>" 46 47 /* libpkg/gpkgmap */ 48 extern int getmapmode(void); 49 50 #define EPTMALLOC 512 51 52 static struct cfextra **extlist; 53 54 int eptnum; 55 static int array_preloaded = 0; 56 static int errflg; 57 static int nparts; 58 static int xspace = -1; 59 60 void pkgobjinit(void); 61 static int pkgobjassign(struct cfent *ept, char **server_local, 62 char **client_local, char **server_path, 63 char **client_path, char **map_path, int mapflag, 64 int nc); 65 66 static int ckdup(struct cfent *ept1, struct cfent *ept2); 67 static int sortentry(int index); 68 static int dup_merg(struct cfextra *ext1, struct cfextra *ext2); 69 70 void 71 pkgobjinit(void) 72 { 73 if (array_preloaded) /* Already done. */ 74 return; 75 76 errflg = nparts = eptnum = 0; 77 78 if (xspace != -1) { 79 ar_free(xspace); 80 xspace = -1; 81 } 82 83 /* 84 * initialize dynamic memory used to store 85 * path information which is read in 86 */ 87 (void) pathdup((char *)0); 88 } 89 90 /* 91 * This function assigns appropriate values based upon the pkgmap entry 92 * in the cfent structure. 93 */ 94 static int 95 pkgobjassign(struct cfent *ept, char **server_local, char **client_local, 96 char **server_path, char **client_path, char **map_path, int mapflag, 97 int nc) 98 { 99 int path_duped = 0; 100 int local_duped = 0; 101 char source[PATH_MAX+1]; 102 103 if (nc >= 0 && ept->ftype != 'i') 104 if ((ept->pkg_class_idx = cl_idx(ept->pkg_class)) == -1) 105 return (1); 106 107 if (ept->volno > nparts) 108 nparts++; 109 110 /* 111 * Generate local (delivered source) paths for files 112 * which need them so that the install routine will know 113 * where to get the file from the package. Note that we 114 * do not resolve path environment variables here since 115 * they won't be resolved in the reloc directory. 116 */ 117 if ((mapflag > 1) && strchr("fve", ept->ftype)) { 118 if (ept->ainfo.local == NULL) { 119 source[0] = '~'; 120 (void) strlcpy(&source[1], ept->path, 121 sizeof (source)-1); 122 ept->ainfo.local = pathdup(source); 123 *server_local = ept->ainfo.local; 124 *client_local = ept->ainfo.local; 125 126 local_duped = 1; 127 } 128 } 129 130 /* 131 * Evaluate the destination path based upon available 132 * environment, then produce a client-relative and 133 * server-relative canonized path. 134 */ 135 if (mapflag && (ept->ftype != 'i')) { 136 mappath(getmapmode(), ept->path); /* evaluate variables */ 137 canonize(ept->path); /* Fix path as necessary. */ 138 139 (void) eval_path(server_path, 140 client_path, 141 map_path, 142 ept->path); 143 path_duped = 1; /* eval_path dup's it */ 144 ept->path = *server_path; /* default */ 145 } 146 147 /* 148 * Deal with source for hard and soft links. 149 */ 150 if (strchr("sl", ept->ftype)) { 151 if (mapflag) { 152 mappath(getmapmode(), ept->ainfo.local); 153 if (!RELATIVE(ept->ainfo.local)) { 154 canonize(ept->ainfo.local); 155 156 /* check for hard link */ 157 if (ept->ftype == 'l') { 158 (void) eval_path( 159 server_local, 160 client_local, 161 NULL, 162 ept->ainfo.local); 163 local_duped = 1; 164 165 /* Default to server. */ 166 ept->ainfo.local = *server_local; 167 } 168 } 169 } 170 } 171 172 /* 173 * For the paths (both source and target) that were too mundane to 174 * have been copied into dup space yet, do that. 175 */ 176 if (!path_duped) { 177 *server_path = pathdup(ept->path); 178 *client_path = *server_path; 179 ept->path = *server_path; 180 181 path_duped = 1; 182 } 183 if (ept->ainfo.local != NULL) 184 if (!local_duped) { 185 *server_local = pathdup(ept->ainfo.local); 186 ept->ainfo.local = *server_local; 187 *client_local = ept->ainfo.local; 188 189 local_duped = 1; 190 } 191 192 return (0); 193 } 194 195 /* This initializes the package object array. */ 196 int 197 init_pkgobjspace(void) 198 { 199 if (array_preloaded) /* Already done. */ 200 return (1); 201 202 if (xspace == -1) { 203 xspace = ar_create(EPTMALLOC, sizeof (struct cfextra), 204 "package object"); 205 if (xspace == -1) { 206 progerr(gettext(ERR_MEMORY)); 207 return (0); 208 } 209 } 210 211 return (1); 212 } 213 214 int 215 seed_pkgobjmap(struct cfextra *ext_entry, char *path, char *local) 216 { 217 struct cfextra *ext, **ext_ptr; 218 219 /* offsets for the various path images. */ 220 int client_path_os; 221 int server_path_os; 222 int map_path_os; 223 int client_local_os; 224 int server_local_os; 225 226 ext_ptr = (struct cfextra **)ar_next_avail(xspace); 227 228 if (ext_ptr == NULL || *ext_ptr == NULL) { 229 progerr(gettext(ERR_MEMORY)); 230 return (0); 231 } 232 233 ext = *ext_ptr; 234 235 (void) memcpy(ext, ext_entry, sizeof (struct cfextra)); 236 237 /* Figure out all of the offsets. */ 238 client_path_os = ((ptrdiff_t)ext->client_path - 239 (ptrdiff_t)ext->cf_ent.path); 240 server_path_os = ((ptrdiff_t)ext->server_path - 241 (ptrdiff_t)ext->cf_ent.path); 242 map_path_os = ((ptrdiff_t)ext->map_path - 243 (ptrdiff_t)ext->cf_ent.path); 244 client_local_os = ((ptrdiff_t)ext->client_local - 245 (ptrdiff_t)ext->cf_ent.ainfo.local); 246 server_local_os = ((ptrdiff_t)ext->server_local - 247 (ptrdiff_t)ext->cf_ent.ainfo.local); 248 249 /* Allocate and store the path name. */ 250 ext->cf_ent.path = pathdup(path); 251 252 /* Assign the path substring pointers. */ 253 ext->client_path = (ext->cf_ent.path + client_path_os); 254 ext->server_path = (ext->cf_ent.path + server_path_os); 255 ext->map_path = (ext->cf_ent.path + map_path_os); 256 257 /* If there's a local entry, allocate and store it as well. */ 258 if (local) { 259 ext->cf_ent.ainfo.local = pathdup(local); 260 261 ext->client_local = (ext->cf_ent.ainfo.local + client_local_os); 262 ext->server_local = (ext->cf_ent.ainfo.local + server_local_os); 263 } else { 264 ext->cf_ent.ainfo.local = NULL; 265 ext->client_local = NULL; 266 ext->server_local = NULL; 267 } 268 269 eptnum++; 270 array_preloaded = 1; 271 272 return (0); 273 } 274 275 /* 276 * This function reads the pkgmap (or any file similarly formatted) and 277 * returns a pointer to a list of struct cfextra (each of which 278 * contains a struct cfent) representing the contents of that file. 279 */ 280 281 /* ARGSUSED ir in pkgobjmap */ 282 struct cfextra ** 283 pkgobjmap(VFP_T *vfp, int mapflag, char *ir) 284 { 285 struct cfextra *ext, **ext_ptr; 286 struct cfent *ept, map_entry; 287 int i; 288 int n; 289 int nc; 290 291 pkgobjinit(); 292 if (!init_pkgobjspace()) 293 quit(99); 294 295 nc = cl_getn(); 296 for (;;) { 297 /* Clear the buffer. */ 298 (void) memset(&map_entry, '\000', sizeof (struct cfent)); 299 300 /* 301 * Fill in a cfent structure in a very preliminary fashion. 302 * ept->path and ept->ainfo.local point to static memory 303 * areas of size PATH_MAX. These are manipulated and 304 * then provided their own allocations later in this function. 305 */ 306 n = gpkgmapvfp(&map_entry, vfp); 307 308 if (n == 0) 309 break; /* no more entries in pkgmap */ 310 else if (n < 0) { 311 char *errstr = getErrstr(); 312 progerr(gettext("bad entry read in pkgmap")); 313 logerr(gettext("pathname=%s"), 314 (map_entry.path && *map_entry.path) ? 315 map_entry.path : "Unknown"); 316 logerr(gettext("problem=%s"), 317 (errstr && *errstr) ? errstr : "Unknown"); 318 return (NULL); 319 } 320 321 /* 322 * A valid entry was found in the map, so allocate an 323 * official record. 324 */ 325 ext_ptr = (struct cfextra **)ar_next_avail(xspace); 326 if (ext_ptr == NULL || *ext_ptr == NULL) { 327 progerr(gettext(ERR_MEMORY)); 328 return (NULL); 329 } 330 331 ext = *ext_ptr; 332 ept = &(ext->cf_ent); 333 334 /* Transfer what we just read in. */ 335 (void) memcpy(ept, &map_entry, sizeof (struct cfent)); 336 337 /* And process it into the cfextra structure. */ 338 if (pkgobjassign(ept, 339 &(ext->server_local), 340 &(ext->client_local), 341 &(ext->server_path), 342 &(ext->client_path), 343 &(ext->map_path), 344 mapflag, nc)) { 345 /* It didn't take. */ 346 (void) ar_delete(xspace, eptnum); 347 continue; 348 } 349 350 eptnum++; 351 ext->fsys_value = BADFSYS; /* No file system data yet */ 352 ext->fsys_base = BADFSYS; 353 } 354 355 if (eptnum == 0) { 356 logerr(gettext(WRN_NOPKGOBJ)); 357 return (NULL); 358 } 359 360 /* setup a pointer array to point to malloc'd entries space */ 361 extlist = (struct cfextra **)ar_get_head(xspace); 362 if (extlist == NULL) { 363 progerr(gettext(ERR_MEMORY)); 364 return (NULL); 365 } 366 367 (void) sortentry(-1); 368 for (i = 0; i < eptnum; /* void */) { 369 if (!sortentry(i)) 370 i++; 371 } 372 373 return (errflg ? NULL : extlist); 374 } 375 376 /* 377 * This function sorts the final list of cfextra entries. If index = -1, the 378 * function is initialized. index = 0 doesn't get us anywhere because this 379 * sorts against index-1. Positive natural index values are compared and 380 * sorted into the array appropriately. Yes, it does seem we should use a 381 * quicksort on the whole array or something. The apparent reason for taking 382 * this approach is that there are enough special considerations to be 383 * applied to each package object that inserting them one-by-one doesn't cost 384 * that much. 385 */ 386 static int 387 sortentry(int index) 388 { 389 struct cfextra *ext; 390 struct cfent *ept, *ept_i; 391 static int last = 0; 392 int i, n, j; 393 int upper, lower; 394 395 if (index == 0) 396 return (0); 397 else if (index < 0) { 398 last = 0; 399 return (0); 400 } 401 402 /* 403 * Based on the index, this is the package object we're going to 404 * review. It may stay where it is or it may be repositioned in the 405 * array. 406 */ 407 ext = extlist[index]; 408 ept = &(ext->cf_ent); 409 410 /* quick comparison optimization for pre-sorted arrays */ 411 if (strcmp(ept->path, extlist[index-1]->cf_ent.path) > 0) { 412 /* do nothing */ 413 last = index-1; 414 return (0); 415 } 416 417 lower = 0; /* lower bound of the unsorted elements */ 418 upper = index; /* upper bound */ 419 i = last; 420 do { 421 /* 422 * NOTE: This does a binary sort on path. There are lots of 423 * other worthy items in the array, but path is the key into 424 * the package database. 425 */ 426 ept_i = &(extlist[i]->cf_ent); 427 428 n = strcmp(ept->path, ept_i->path); 429 if (n == 0) { 430 if (!ckdup(ept, ept_i)) { 431 /* 432 * If the array was seeded then there are 433 * bound to be occasional duplicates. 434 * Otherwise, duplicates are definitely a 435 * sign of major damage. 436 */ 437 if (array_preloaded) { 438 if (!dup_merg(ext, extlist[i])) { 439 progerr(gettext(ERR_DUPPATH), 440 ept->path); 441 errflg++; 442 } 443 } else { 444 progerr(gettext(ERR_DUPPATH), 445 ept->path); 446 errflg++; 447 } 448 } 449 /* remove the entry at index */ 450 (void) ar_delete(xspace, index); 451 452 eptnum--; 453 return (1); /* Use this index again. */ 454 } else if (n < 0) { 455 /* 456 * The path of interest is smaller than the path 457 * under test. Move down array using the method of 458 * division 459 */ 460 upper = i; 461 i = lower + (upper-lower)/2; 462 } else { 463 /* Move up array */ 464 lower = i+1; 465 i = upper - (upper-lower)/2 - 1; 466 } 467 } while (upper != lower); 468 last = i = upper; 469 470 /* expand to insert at i */ 471 for (j = index; j > i; j--) 472 extlist[j] = extlist[j-1]; 473 474 extlist[i] = ext; 475 476 return (0); 477 } 478 479 /* Return the number of blocks required by the package object provided. */ 480 static fsblkcnt_t 481 nblks(short fsys_entry, struct cfextra *ext) 482 { 483 fsblkcnt_t blk; 484 ulong_t block_size; 485 ulong_t frag_size; 486 487 block_size = (ulong_t)get_blk_size_n(fsys_entry); 488 frag_size = (ulong_t)get_frag_size_n(fsys_entry); 489 490 if (strchr("dxs", ext->cf_ent.ftype)) 491 blk = 492 nblk(block_size, block_size, frag_size); 493 else if (ext->cf_ent.cinfo.size != BADCONT) 494 blk = nblk(ext->cf_ent.cinfo.size, block_size, 495 frag_size); 496 else 497 blk = 0; 498 499 return (blk); 500 } 501 502 /* Remove ext1 from the filesystem size calculations and add ext2. */ 503 static void 504 size_xchng(struct cfextra *ext1, struct cfextra *ext2) 505 { 506 fsblkcnt_t bused; 507 ulong_t block_size; 508 ulong_t frag_size; 509 fsblkcnt_t blks1, blks2; 510 short fsys_entry; 511 512 /* 513 * Since these are on the same filesystem, either one will yield the 514 * correct block and fragment size. 515 */ 516 fsys_entry = ext1->fsys_base; 517 block_size = (ulong_t)get_blk_size_n(fsys_entry); 518 frag_size = (ulong_t)get_frag_size_n(fsys_entry); 519 520 blks1 = nblk(ext1->cf_ent.cinfo.size, block_size, frag_size); 521 blks2 = nblk(ext2->cf_ent.cinfo.size, block_size, frag_size); 522 523 if (blks1 != blks2) { 524 /* First, lose the old size, then add the new size. */ 525 bused = get_blk_used_n(fsys_entry); 526 bused -= nblks(fsys_entry, ext1); 527 bused += nblks(fsys_entry, ext2); 528 529 set_blk_used_n(fsys_entry, bused); 530 } 531 } 532 533 /* 534 * This function merges duplicate non-directory entries resulting from a 535 * dryrun or other procedure which preloads the extlist. It uses an odd 536 * heuristic to determine which package object is newest: only package 537 * objects from the dryrun file will have pinfo pointers. Therefore, the 538 * object with a pinfo pointer is from the dryrun file and it will be 539 * overwritten by the object being installed by this package. 540 * 541 * Assumptions: 542 * 1. The newer object will be overwriting the older object. 543 * 2. The two objects are close enough to the same size that 544 * the sizing is still OK. 545 * 546 * The calling routine will overwrite ept1, so this must return ept2 with 547 * the correct data to keep. There being only one logical outcome of a 548 * failure, this returns 1 for OK and 0 for FAIL. 549 */ 550 static int 551 dup_merg(struct cfextra *ext1, struct cfextra *ext2) 552 { 553 struct cfent *ept1, *ept2; 554 555 ept1 = &(ext1->cf_ent); 556 ept2 = &(ext2->cf_ent); 557 558 if (strchr("?dx", ept1->ftype)) 559 return (0); 560 561 if (strchr("?dx", ept2->ftype)) 562 return (0); 563 564 /* First, which is the eldest? */ 565 if (ext2->mstat.preloaded) { 566 /* 567 * While ept2 has the correct pinfo list (it was preloaded into 568 * the array before the pkgmap was read), ept1 has everything 569 * else. Here we copy the guts of ept1 into ept2. 570 * 571 * Start by grabbing the pointers to the ext2 items that we 572 * need to either restore or free. 573 */ 574 /* to free() */ 575 char *path = ept2->path; 576 char *local = ept2->ainfo.local; 577 578 /* to preserve */ 579 short npkgs = ept2->npkgs; 580 struct pinfo *pinfo = ept2->pinfo; 581 582 /* Copy everything from the new entry to the old */ 583 (void) memcpy(ept2, ept1, sizeof (struct cfent)); 584 585 /* Now restore the original stuff.. */ 586 ept2->path = path; 587 ept2->ainfo.local = local; 588 ept2->npkgs = npkgs; 589 ept2->pinfo = pinfo; 590 591 size_xchng(ext2, ext1); 592 } else if (ext1->mstat.preloaded) { 593 /* 594 * ept2 is already the one we will keep. All we have to do is 595 * copy over the pinfo pointer. 596 */ 597 ept2->pinfo = ept1->pinfo; 598 size_xchng(ext1, ext2); 599 } else 600 return (0); 601 602 return (1); 603 } 604 605 /* 606 * Check duplicate entries in the package object list. If it's a directory, 607 * this just merges them, if not, it returns a 0 to force further processing. 608 */ 609 static int 610 ckdup(struct cfent *ept1, struct cfent *ept2) 611 { 612 /* ept2 will be modified to contain "merged" entries */ 613 614 if (!strchr("?dx", ept1->ftype)) 615 return (0); 616 617 if (!strchr("?dx", ept2->ftype)) 618 return (0); 619 620 if (ept2->ainfo.mode == BADMODE) 621 ept2->ainfo.mode = ept1->ainfo.mode; 622 if ((ept1->ainfo.mode != ept2->ainfo.mode) && 623 (ept1->ainfo.mode != BADMODE)) 624 return (0); 625 626 if (strcmp(ept2->ainfo.owner, "?") == 0) 627 (void) strlcpy(ept2->ainfo.owner, ept1->ainfo.owner, 628 sizeof (ept2->ainfo.owner)); 629 if (strcmp(ept1->ainfo.owner, ept2->ainfo.owner) && 630 strcmp(ept1->ainfo.owner, "?")) 631 return (0); 632 633 if (strcmp(ept2->ainfo.group, "?") == 0) 634 (void) strlcpy(ept2->ainfo.group, ept1->ainfo.group, 635 sizeof (ept2->ainfo.group)); 636 if (strcmp(ept1->ainfo.group, ept2->ainfo.group) && 637 strcmp(ept1->ainfo.group, "?")) 638 return (0); 639 640 if (ept1->pinfo) { 641 ept2->npkgs = ept1->npkgs; 642 ept2->pinfo = ept1->pinfo; 643 } 644 645 return (1); 646 } 647 648 /* 649 * Replace the old package database entry with the new one preserving the 650 * data which remains constant across the replacement. 651 * copied directly: 652 * ftype, pkg_class 653 * 654 * preserved from old: 655 * path, npkgs, pinfo 656 */ 657 void 658 repl_cfent(struct cfent *new, struct cfent *old) 659 { 660 char *path = old->path; 661 short npkgs = old->npkgs; 662 struct pinfo *pinfo = old->pinfo; 663 664 /* Copy everything from the new entry over */ 665 (void) memcpy(old, new, sizeof (struct cfent)); 666 667 if (strchr("sl", new->ftype) == NULL) 668 old->ainfo.local = NULL; 669 670 old->path = path; 671 old->npkgs = npkgs; 672 old->pinfo = pinfo; 673 674 old->volno = 0; 675 } 676 677 /* 678 * Copy critical portions of cf_ent (from the package database) and el_ent 679 * (constructed from the pkgmap) into a merged cfent structure, tp. Then copy 680 * that to the el_ent structure. The approach we take here is to copy over 681 * everything from the package database entry, condition the paths based upon 682 * the currently installed path and then insert the following entries from 683 * the new structure : 684 * cfent.volno 685 * pkg_class 686 * pkg_class_idx 687 * 688 * The pinfo list is then copied from the cfent list. While 689 * fsys_value is also copied over, it hasn't been set yet. This function 690 * copies over whatever the default value is from the new structure. 691 * 692 * The copied entry is returned in the el_ent argument and the function 693 * value is 1 on success, 0 on failure. There is no recovery plan for 694 * failure. 695 */ 696 int 697 cp_cfent(struct cfent *cf_ent, struct cfextra *el_ent) 698 { 699 struct cfextra *tp; 700 701 /* Allocate space for cfent copy */ 702 if ((tp = (struct cfextra *)calloc(1, 703 sizeof (struct cfextra))) == NULL) { 704 progerr(gettext("cp_cfent: memory allocation error")); 705 return (0); 706 } 707 708 /* Copy everything from the package database over */ 709 (void) memcpy(&(tp->cf_ent), cf_ent, sizeof (struct cfent)); 710 711 /* Now overlay new items from the pkgmap */ 712 tp->fsys_value = el_ent->fsys_value; 713 tp->cf_ent.volno = el_ent->cf_ent.volno; 714 (void) strlcpy(tp->cf_ent.pkg_class, el_ent->cf_ent.pkg_class, 715 sizeof (tp->cf_ent.pkg_class)); 716 tp->cf_ent.pkg_class_idx = el_ent->cf_ent.pkg_class_idx; 717 tp->cf_ent.pinfo = cf_ent->pinfo; 718 719 /* 720 * The paths are identical, so we get them from the new entry. These 721 * are pointing to a malloc'd section of memory containing a string 722 * that we aren't moving in this operation, so everybody points to 723 * the same thing during these transfers. 724 */ 725 tp->cf_ent.path = el_ent->client_path; 726 tp->server_path = el_ent->server_path; 727 tp->client_path = el_ent->client_path; 728 tp->map_path = el_ent->map_path; 729 730 /* 731 * Since instvol() expects to work with the *original* mstat data, 732 * mstat is just copied here. NOTE: mstat looks like a structure, but 733 * it's really a short bit array. 734 */ 735 tp->mstat = el_ent->mstat; 736 737 /* Copy everything from the temporary structure to the new entry */ 738 (void) memcpy(el_ent, tp, sizeof (struct cfextra)); 739 free(tp); 740 741 return (1); 742 } 743