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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 31 32 #include <stdio.h> 33 #include <limits.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <strings.h> 37 #include <ctype.h> 38 #include <sys/types.h> 39 #include <libintl.h> 40 #include "pkglib.h" 41 #include "pkgstrct.h" 42 #include "pkglocale.h" 43 #include "pkglibmsgs.h" 44 45 /* 46 * Forward declarations 47 */ 48 49 static void findend(char **cp); 50 static int getend(char **cp); 51 static int getstr(char **cp, int n, char *str, int separator[]); 52 53 /* from gpkgmap.c */ 54 int getnumvfp(char **cp, int base, long *d, long bad); 55 int getlnumvfp(char **cp, int base, fsblkcnt_t *d, long bad); 56 57 /* 58 * Module globals 59 */ 60 61 static char lpath[PATH_MAX]; /* for ept->path */ 62 static char mylocal[PATH_MAX]; /* for ept->ainfo.local */ 63 static int decisionTableInit = 0; 64 65 /* 66 * These arrays must be indexable by an unsigned char. 67 */ 68 69 static int ISPKGPATHSEP[UCHAR_MAX+1]; 70 static int ISWORDSEP[UCHAR_MAX+1]; 71 static int ISPKGNAMESEP[UCHAR_MAX+1]; 72 73 /* 74 * Name: WRITEDATA 75 * Description: write out data to VFP_T given start and end pointers 76 * Arguments: VFP - (VFP_T *) - [RO, *RW] 77 * Contents file VFP to narrow search on 78 * FIRSTPOS - (char *) - [RO, *RO] 79 * Pointer to first byte to write out 80 * LASTPOS - (char *) - [RO, *RO] 81 * Pointer to last byte to write out 82 */ 83 84 #define WRITEDATA(VFP, FIRSTPOS, LASTPOS) \ 85 { \ 86 ssize_t XXlenXX; \ 87 /* compute number of bytes skipped */ \ 88 XXlenXX = (ptrdiff_t)(LASTPOS) - (ptrdiff_t)(FIRSTPOS); \ 89 /* write the bytes out */ \ 90 vfpPutBytes((VFP), (FIRSTPOS), XXlenXX); \ 91 } 92 93 /* 94 * Name: COPYPATH 95 * Description: copy path limiting size to destination capacity 96 * Arguments: DEST - (char []) - [RW] 97 * SRC - (char *) - [RO, *RO] 98 * Pointer to first byte of path to copy 99 * LEN - (int) - [RO] 100 * Number of bytes to copy 101 */ 102 103 #define COPYPATH(DEST, SRC, LEN) \ 104 { \ 105 /* assure return path does not overflow */ \ 106 if ((LEN) > sizeof ((DEST))) { \ 107 (LEN) = sizeof ((DEST))-1; \ 108 } \ 109 /* copy return path to local storage */ \ 110 (void) memcpy((DEST), (SRC), (LEN)); \ 111 (DEST)[(LEN)] = '\0'; \ 112 } 113 114 /* 115 * Name: narrowSearch 116 * Description: narrow the search location for a specified path 117 * The contents and package map files are always sorted by path. 118 * This function is given a target path to search for given the 119 * current location in a contents file. It is assured that the 120 * target path has not been searched for yet in the contents file 121 * so the current location in the contents file is guaranteed to 122 * be less than the location of the target path (if present). 123 * Given this employ a binary search to speed up the search for 124 * the path nearest to a specified target path. 125 * Arguments: a_vfp - (VFP_T *) - [RO, *RW] 126 * Contents file VFP to narrow search on 127 * a_path - (char *) - [RO, *RO] 128 * Pointer to path to search for 129 * a_pathLen - (size_t) - [RO] 130 * Length of string (a_path) 131 * Returns: char * - pointer to first byte of entry in contents file that 132 * is guaranteed to be the closest match to the specified 133 * a_path without being "greater than" the path. 134 * == (char *)NULL if no entry found 135 */ 136 137 static char * 138 narrowSearch(VFP_T *a_vfp, char *a_path, size_t a_pathLen) 139 { 140 char *phigh; 141 char *plow; 142 char *pmid; 143 int n; 144 size_t plen; 145 146 /* if no path to compare, start at beginning */ 147 148 if ((a_path == (char *)NULL) || (*a_path == '\0')) { 149 return ((char *)NULL); 150 } 151 152 /* if the contents file is empty, resort to sequential search */ 153 154 if (vfpGetBytesRemaining(a_vfp) <= 1) { 155 return ((char *)NULL); 156 } 157 158 /* 159 * test against first path - if the path specified is less than the 160 * first path in the contents file, then the path can be inserted 161 * before the first entry in the contents file. 162 */ 163 164 /* locate start of first line */ 165 166 plow = vfpGetCurrCharPtr(a_vfp); 167 pmid = plow; 168 169 /* if first path not absolute, resort to sequential search */ 170 171 if (*pmid != '/') { 172 return ((char *)NULL); 173 } 174 175 /* find end of path */ 176 177 while (ISPKGPATHSEP[(int)*pmid] == 0) { 178 pmid++; 179 } 180 181 /* determine length of path */ 182 183 plen = (ptrdiff_t)pmid - (ptrdiff_t)plow; 184 185 /* compare target path with current path */ 186 187 n = strncmp(a_path, plow, plen); 188 if (n == 0) { 189 /* if lengths same exact match return position found */ 190 if (a_pathLen == plen) { 191 return (plow); 192 } 193 /* not exact match - a_path > pm */ 194 n = a_pathLen; 195 } 196 197 /* return if target is less than or equal to first entry */ 198 199 if (n <= 0) { 200 return (plow); 201 } 202 203 /* 204 * test against last path - if the path specified is greater than the 205 * last path in the contents file, then the path can be appended after 206 * the last entry in the contents file. 207 */ 208 209 /* locate start of last line */ 210 211 plow = vfpGetCurrCharPtr(a_vfp); 212 pmid = vfpGetLastCharPtr(a_vfp); 213 214 while ((pmid > plow) && (!((pmid[0] == '/') && (pmid[-1] == '\n')))) { 215 pmid--; 216 } 217 218 /* if absolute path, do comparison */ 219 220 if ((pmid > plow) && (*pmid == '/')) { 221 plow = pmid; 222 223 /* find end of path */ 224 225 while (ISPKGPATHSEP[(int)*pmid] == 0) { 226 pmid++; 227 } 228 229 /* determine length of path */ 230 231 plen = (ptrdiff_t)pmid - (ptrdiff_t)plow; 232 233 /* compare target path with current path */ 234 235 n = strncmp(a_path, plow, plen); 236 if (n == 0) { 237 /* if lengths same exact match return position found */ 238 if (a_pathLen == plen) { 239 return (plow); 240 } 241 /* not exact match - a_path > pm */ 242 n = a_pathLen; 243 } 244 245 /* return if target is greater than or equal to entry */ 246 247 if (n >= 0) { 248 return (plow); 249 } 250 } 251 /* 252 * firstPath < targetpath < lastPath: 253 * binary search looking for closest "less than" match 254 */ 255 256 plow = vfpGetCurrCharPtr(a_vfp); 257 phigh = vfpGetLastCharPtr(a_vfp); 258 259 for (;;) { 260 char *pm; 261 262 /* determine number of bytes left in search area */ 263 264 plen = (ptrdiff_t)phigh - (ptrdiff_t)plow; 265 266 /* calculate mid point between current low and high points */ 267 268 pmid = plow + (plen >> 1); 269 270 /* backup and find first "\n/" -or- start of buffer */ 271 272 while ((pmid > plow) && 273 (!((pmid[0] == '/') && (pmid[-1] == '\n')))) { 274 pmid--; 275 } 276 277 /* return lowest line found if current line not past that */ 278 279 if (pmid <= plow) { 280 return (plow); 281 } 282 283 /* remember start of this line */ 284 285 pm = pmid; 286 287 /* find end of path */ 288 289 while (ISPKGPATHSEP[(int)*pmid] == 0) { 290 pmid++; 291 } 292 293 /* determine length of path */ 294 295 plen = (ptrdiff_t)pmid - (ptrdiff_t)pm; 296 297 /* compare target path with current path */ 298 299 n = strncmp(a_path, pm, plen); 300 301 if (n == 0) { 302 /* if lengths same exact match return position found */ 303 if (a_pathLen == plen) { 304 return (pm); 305 } 306 /* not exact match - a_path > pm */ 307 n = a_pathLen; 308 } 309 310 311 /* not exact match - determine which watermark to split */ 312 313 if (n > 0) { /* a_path > pm */ 314 plow = pm; 315 } else { /* a_path < pm */ 316 phigh = pm; 317 } 318 } 319 /*NOTREACHED*/ 320 } 321 322 /* 323 * Name: srchcfile 324 * Description: search contents file looking for closest match to entry, 325 * creating a new contents file if output contents file specified 326 * Arguments: ept - (struct cfent *) - [RO, *RW] 327 * - contents file entry, describing last item found 328 * path - (char *) - [RO, *RO] 329 * - path to search for in contents file 330 * - If path is "*", then the next entry is returned; 331 * the next entry always matches this path 332 * - If the path is (char *)NULL or "", then all remaining 333 * entries are processed and copied out to the 334 * file specified by cfTmpVFp 335 * cfVfp - (VFP_T *) - [RO, *RW] 336 * - VFP_T open on contents file to search 337 * cfTmpVfp - (VFP_T *) - [RO, *RW] 338 * - VFP_T open on temporary contents file to populate 339 * Returns: int 340 * < 0 - error occurred 341 * - Use getErrstr to retrieve character-string describing 342 * the reason for failure 343 * == 0 - no match found 344 * - specified path not in the contents file 345 * - all contents of cfVfp copied to cfTmpVfp 346 * - current character of cfVfp is at end of file 347 * == 1 - exact match found 348 * - specified path found in contents file 349 * - contents of cfVfp up to entry found copied to cfTmpVfp 350 * - current character of cfVfp is first character of 351 * entry found 352 * - this value is always returned if path is "*" and the 353 * next entry is returned - -1 is returned when no more 354 * entries are left to process 355 * == 2 - entry found which is GREATER than path specified 356 * - specified path would fit BEFORE entry found 357 * - contents of cfVfp up to entry found copied to cfTmpVfp 358 * - current character of cfVfp is first character of 359 * entry found 360 * Side Effects: 361 * - The ept structure supplied is filled in with a description of 362 * the item that caused the search to terminate, except in the 363 * case of '0' in which case the contents of 'ept' is undefined. 364 * - NOTE: the ept->path item points to a path that is statically 365 * allocated and will be overwritten on the next call. 366 * - NOTE: the ept->ainfo.local item points to a path that is 367 * statically allocated and will be overwritten on the next call. 368 */ 369 370 int 371 srchcfile(struct cfent *ept, char *path, VFP_T *cfVfp, VFP_T *cfTmpVfp) 372 { 373 char *cpath_start = (char *)NULL; 374 char *firstPos = vfpGetCurrCharPtr(cfVfp); 375 char *lastPos = NULL; 376 char *pos; 377 char classname[CLSSIZ+1]; 378 char pkgname[PKGSIZ+1]; 379 int anypath = 0; 380 int c; 381 int dataSkipped = 0; 382 int n; 383 int rdpath; 384 size_t cpath_len = 0; 385 size_t pathLength; 386 struct pinfo *lastpinfo; 387 struct pinfo *pinfo; 388 389 /* 390 * this code does not use nested subroutines because execution time 391 * of this routine is especially critical to installation and upgrade 392 */ 393 394 /* initialize local variables */ 395 396 setErrstr(NULL); /* no error message currently cached */ 397 pathLength = (path == (char *)NULL ? 0 : strlen(path)); 398 lpath[0] = '\0'; 399 lpath[sizeof (lpath)-1] = '\0'; 400 401 /* initialize ept structure values */ 402 403 (void) strlcpy(ept->ainfo.group, BADGROUP, sizeof (ept->ainfo.group)); 404 (void) strlcpy(ept->ainfo.owner, BADOWNER, sizeof (ept->ainfo.owner)); 405 (void) strlcpy(ept->pkg_class, BADCLASS, sizeof (ept->pkg_class)); 406 ept->ainfo.local = (char *)NULL; 407 ept->ainfo.mode = BADMODE; 408 ept->cinfo.cksum = BADCONT; 409 ept->cinfo.modtime = BADCONT; 410 ept->cinfo.size = (fsblkcnt_t)BADCONT; 411 ept->ftype = BADFTYPE; 412 ept->npkgs = 0; 413 ept->path = (char *)NULL; 414 ept->pinfo = (struct pinfo *)NULL; 415 ept->pkg_class_idx = -1; 416 ept->volno = 0; 417 418 /* 419 * populate decision tables that implement fast character checking; 420 * this is much faster than the equivalent strpbrk() call or a 421 * while() loop checking for the characters. It is only faster if 422 * there are at least 3 characters to scan for - when checking for 423 * one or two characters (such as '\n' or '\0') its faster to do 424 * a simple while() loop. 425 */ 426 427 if (decisionTableInit == 0) { 428 /* 429 * any chars listed stop scan; 430 * scan stops on first byte found that is set to '1' below 431 */ 432 433 /* 434 * Separators for path names, normal space and = 435 * for linked filenames 436 */ 437 bzero(ISPKGPATHSEP, sizeof (ISPKGPATHSEP)); 438 ISPKGPATHSEP['='] = 1; /* = */ 439 ISPKGPATHSEP[' '] = 1; /* space */ 440 ISPKGPATHSEP['\t'] = 1; /* horizontal-tab */ 441 ISPKGPATHSEP['\n'] = 1; /* new-line */ 442 ISPKGPATHSEP['\0'] = 1; /* NULL character */ 443 444 /* 445 * Separators for normal words 446 */ 447 bzero(ISWORDSEP, sizeof (ISWORDSEP)); 448 ISWORDSEP[' '] = 1; 449 ISWORDSEP['\t'] = 1; 450 ISWORDSEP['\n'] = 1; 451 ISWORDSEP['\0'] = 1; 452 453 /* 454 * Separators for list of packages, includes \\ for 455 * alternate ftype and : for classname 456 */ 457 bzero(ISPKGNAMESEP, sizeof (ISPKGNAMESEP)); 458 ISPKGNAMESEP[' '] = 1; 459 ISPKGNAMESEP['\t'] = 1; 460 ISPKGNAMESEP['\n'] = 1; 461 ISPKGNAMESEP[':'] = 1; 462 ISPKGNAMESEP['\\'] = 1; 463 ISPKGNAMESEP['\0'] = 1; 464 465 decisionTableInit = 1; 466 } 467 468 /* if no bytes in contents file, return 0 */ 469 470 if (vfpGetBytesRemaining(cfVfp) <= 1) { 471 return (0); 472 } 473 474 /* if the path to scan for is empty, act like no path was specified */ 475 476 if ((path != (char *)NULL) && (*path == '\0')) { 477 path = (char *)NULL; 478 } 479 480 /* 481 * if path to search for is "*", then we will return the first path 482 * we encounter as a match, otherwise we return an error 483 */ 484 485 if ((path != (char *)NULL) && (path[0] != '/')) { 486 if (strcmp(path, "*") != 0) { 487 setErrstr(pkg_gt(ERR_ILLEGAL_SEARCH_PATH)); 488 return (-1); 489 } 490 anypath = 1; 491 } 492 493 /* attempt to narrow down the search for the specified path */ 494 495 if (anypath == 0) { 496 char *np; 497 498 np = narrowSearch(cfVfp, path, pathLength); 499 if (np != (char *)NULL) { 500 dataSkipped = 1; 501 lastPos = np; 502 vfpSetCurrCharPtr(cfVfp, np); 503 } 504 } 505 506 /* 507 * If the path to search for in the source contents file is NULL, then 508 * this is a request to scan to the end of the source contents file. If 509 * there is a temporary contents file to copy entries to, all that needs 510 * to be done is to copy the data remaining from the current location in 511 * the source contents file to the end of the temporary contents file. 512 * if there is no temporary contents file to copy to, then all that 513 * needs to be done is to seek to the end of the source contents file. 514 */ 515 516 if ((anypath == 0) && (path == (char *)NULL)) { 517 if (cfTmpVfp != (VFP_T *)NULL) { 518 if (vfpGetBytesRemaining(cfVfp) > 0) { 519 WRITEDATA(cfTmpVfp, firstPos, 520 vfpGetLastCharPtr(cfVfp)+1); 521 } 522 *vfpGetLastCharPtr(cfTmpVfp) = '\0'; 523 } 524 vfpSeekToEnd(cfVfp); 525 return (0); 526 } 527 528 /* 529 * ********************************************************************* 530 * main loop processing entries from the contents file looking for 531 * the specified path 532 * ********************************************************************* 533 */ 534 535 for (;;) { 536 char *p; 537 538 /* not reading old style entry */ 539 540 rdpath = 0; 541 542 /* determine first character of the next entry */ 543 544 if (vfpGetBytesRemaining(cfVfp) <= 0) { 545 /* no bytes in contents file current char is NULL */ 546 547 c = '\0'; 548 } else { 549 /* grab path from first entry */ 550 551 c = vfpGetcNoInc(cfVfp); 552 } 553 554 /* save current position in file */ 555 556 pos = vfpGetCurrCharPtr(cfVfp); 557 558 /* 559 * ============================================================= 560 * at the first character of the next entry in the contents file 561 * if not absolute path check for exceptions and old style entry 562 * --> if end of contents file write out skipped data and return 563 * --> if comment character skip to end of line and restart loop 564 * --> else process "old style entry: ftype class path" 565 * ============================================================= 566 */ 567 568 if (c != '/') { 569 /* if NULL character then end of contents file found */ 570 571 if (c == '\0') { 572 /* write out skipped data before returning */ 573 if (dataSkipped && 574 (cfTmpVfp != (VFP_T *)NULL)) { 575 WRITEDATA(cfTmpVfp, firstPos, lastPos); 576 *vfpGetLastCharPtr(cfTmpVfp) = '\0'; 577 } 578 579 return (0); /* no more entries */ 580 } 581 582 /* ignore lines that begin with #, : or a "space" */ 583 584 if ((isspace(c) != 0) || (c == '#') || (c == ':')) { 585 /* line is a comment */ 586 findend(&vfpGetCurrCharPtr(cfVfp)); 587 continue; 588 } 589 590 /* 591 * old style entry - format is: 592 * ftype class path 593 * set ept->ftype to the type 594 * set ept->class to the class 595 * set ept->path to point to lpath 596 * set cpath_start/cpath_len to point to the file name 597 * set rdpath to '1' to indicate old style entry parsed 598 */ 599 600 while (isspace((c = vfpGetc(cfVfp)))) 601 ; 602 603 switch (c) { 604 case '?': case 'f': case 'v': case 'e': case 'l': 605 case 's': case 'p': case 'c': case 'b': case 'd': 606 case 'x': 607 /* save ftype */ 608 ept->ftype = (char)c; 609 610 /* save class */ 611 if (getstr(&vfpGetCurrCharPtr(cfVfp), CLSSIZ, 612 ept->pkg_class, ISWORDSEP)) { 613 setErrstr(ERR_CANNOT_READ_CLASS_TOKEN); 614 findend(&vfpGetCurrCharPtr(cfVfp)); 615 return (-1); 616 } 617 618 /* 619 * locate file name up to "=", set cpath_start 620 * and cpath_len to point to the file name 621 */ 622 cpath_start = vfpGetCurrCharPtr(cfVfp); 623 p = vfpGetCurrCharPtr(cfVfp); 624 625 /* 626 * skip past all bytes until first '= \t\n\0': 627 */ 628 while (ISPKGPATHSEP[(int)*p] == 0) { 629 p++; 630 } 631 632 cpath_len = vfpGetCurrPtrDelta(cfVfp, p); 633 634 /* 635 * if the path is zero bytes, line is corrupted 636 */ 637 638 if (cpath_len < 1) { 639 setErrstr(ERR_CANNOT_READ_PATHNAME_FLD); 640 findend(&vfpGetCurrCharPtr(cfVfp)); 641 return (-1); 642 } 643 644 vfpIncCurrPtrBy(cfVfp, cpath_len); 645 646 /* set path to point to local path cache */ 647 ept->path = lpath; 648 649 /* set flag indicating path already parsed */ 650 rdpath = 1; 651 break; 652 653 case '\0': 654 /* end of line before new-line seen */ 655 vfpDecCurrPtr(cfVfp); 656 setErrstr(ERR_INCOMPLETE_ENTRY); 657 return (-1); 658 659 case '0': case '1': case '2': case '3': case '4': 660 case '5': case '6': case '7': case '8': case '9': 661 /* volume number seen */ 662 setErrstr(ERR_VOLUMENO_UNEXPECTED); 663 findend(&vfpGetCurrCharPtr(cfVfp)); 664 return (-1); 665 666 case 'i': 667 /* type i files are not cataloged */ 668 setErrstr(ERR_FTYPE_I_UNEXPECTED); 669 findend(&vfpGetCurrCharPtr(cfVfp)); 670 return (-1); 671 672 default: 673 /* unknown ftype */ 674 setErrstr(ERR_UNKNOWN_FTYPE); 675 findend(&vfpGetCurrCharPtr(cfVfp)); 676 return (-1); 677 } 678 } else { 679 /* 680 * current entry DOES start with absolute path 681 * set ept->path to point to lpath 682 * set cpath_start/cpath_len to point to the file name 683 */ 684 /* copy first token into path element of passed structure */ 685 686 cpath_start = vfpGetCurrCharPtr(cfVfp); 687 688 p = cpath_start; 689 690 /* 691 * skip past all bytes until first from '= \t\n\0': 692 */ 693 694 while (ISPKGPATHSEP[(int)*p] == 0) { 695 p++; 696 } 697 698 cpath_len = vfpGetCurrPtrDelta(cfVfp, p); 699 700 vfpIncCurrPtrBy(cfVfp, cpath_len); 701 702 if (vfpGetcNoInc(cfVfp) == '\0') { 703 setErrstr(ERR_INCOMPLETE_ENTRY); 704 findend(&vfpGetCurrCharPtr(cfVfp)); 705 return (-1); 706 } 707 708 ept->path = lpath; 709 } 710 711 /* 712 * ============================================================= 713 * if absolute path then the path is collected and we are at the 714 * first byte following the absolute path name; 715 * if not an absolute path then an old style entry, ept has been 716 * filled with the type and class and path name. 717 * determine if we have read the pathname which identifies 718 * the entry we are searching for 719 * ============================================================= 720 */ 721 722 if (anypath != 0) { 723 n = 0; /* next entry is "equal to" */ 724 } else if (path == (char *)NULL) { 725 n = 1; /* next entry is "greater than" */ 726 } else { 727 n = strncmp(path, cpath_start, cpath_len); 728 if ((n == 0) && (cpath_len != pathLength)) { 729 n = cpath_len; 730 } 731 } 732 733 /* get first character following the end of the path */ 734 735 c = vfpGetc(cfVfp); 736 737 /* 738 * if an exact match, always parse out the local path 739 */ 740 741 if (n == 0) { 742 /* 743 * we want to return information about this path in 744 * the structure provided, so parse any local path 745 * and jump to code which parses rest of the input line 746 */ 747 if (c == '=') { 748 /* parse local path specification */ 749 if (getstr(&vfpGetCurrCharPtr(cfVfp), PATH_MAX, 750 mylocal, ISWORDSEP)) { 751 752 /* copy path found to 'lpath' */ 753 COPYPATH(lpath, cpath_start, cpath_len); 754 755 setErrstr(ERR_CANNOT_READ_LL_PATH); 756 findend(&vfpGetCurrCharPtr(cfVfp)); 757 return (-1); 758 } 759 ept->ainfo.local = mylocal; 760 } 761 } 762 763 /* 764 * if an exact match and processing a new style entry, read the 765 * remaining information from the new style entry - if this is 766 * an old style entry (rdpath != 0) then the existing info has 767 * already been processed as it exists before the pathname and 768 * not after like a new style entry 769 */ 770 771 if (n == 0 && rdpath == 0) { 772 while (isspace((c = vfpGetc(cfVfp)))) 773 ; 774 775 switch (c) { 776 case '?': case 'f': case 'v': case 'e': case 'l': 777 case 's': case 'p': case 'c': case 'b': case 'd': 778 case 'x': 779 /* save ftype */ 780 ept->ftype = (char)c; 781 782 /* save class */ 783 if (getstr(&vfpGetCurrCharPtr(cfVfp), CLSSIZ, 784 ept->pkg_class, ISWORDSEP)) { 785 786 /* copy path found to 'lpath' */ 787 COPYPATH(lpath, cpath_start, cpath_len); 788 789 setErrstr(ERR_CANNOT_READ_CLASS_TOKEN); 790 findend(&vfpGetCurrCharPtr(cfVfp)); 791 return (-1); 792 } 793 break; /* we already read the pathname */ 794 795 case '\0': 796 /* end of line before new-line seen */ 797 vfpDecCurrPtr(cfVfp); 798 799 /* copy path found to 'lpath' */ 800 COPYPATH(lpath, cpath_start, cpath_len); 801 802 setErrstr(ERR_INCOMPLETE_ENTRY); 803 return (-1); 804 805 case '0': case '1': case '2': case '3': case '4': 806 case '5': case '6': case '7': case '8': case '9': 807 808 /* copy path found to 'lpath' */ 809 COPYPATH(lpath, cpath_start, cpath_len); 810 811 setErrstr(ERR_VOLUMENO_UNEXPECTED); 812 findend(&vfpGetCurrCharPtr(cfVfp)); 813 return (-1); 814 815 case 'i': 816 817 /* copy path found to 'lpath' */ 818 COPYPATH(lpath, cpath_start, cpath_len); 819 820 setErrstr(ERR_FTYPE_I_UNEXPECTED); 821 findend(&vfpGetCurrCharPtr(cfVfp)); 822 return (-1); 823 824 default: 825 /* unknown ftype */ 826 827 /* copy path found to 'lpath' */ 828 COPYPATH(lpath, cpath_start, cpath_len); 829 830 setErrstr(ERR_UNKNOWN_FTYPE); 831 findend(&vfpGetCurrCharPtr(cfVfp)); 832 return (-1); 833 } 834 } 835 836 /* 837 * if an exact match all processing is completed; break out of 838 * the main processing loop and finish processing this entry 839 * prior to returning to the caller. 840 */ 841 842 if (n == 0) { 843 break; 844 } 845 846 /* 847 * this entry is not an exact match for the path being searched 848 * for - if this entry is GREATER THAN the path being searched 849 * for then finish processing and return GREATER THAN result 850 * to the caller so the entry for the path being searched for 851 * can be added to the contents file. 852 */ 853 854 if (n < 0) { 855 /* 856 * the entry we want would fit BEFORE the one we just 857 * read, so we need to unread what we've read by 858 * seeking back to the start of this entry 859 */ 860 861 vfpSetCurrCharPtr(cfVfp, pos); 862 863 /* copy path found to 'lpath' */ 864 COPYPATH(lpath, cpath_start, cpath_len); 865 866 /* write out any skipped data before returning */ 867 if (dataSkipped && (cfTmpVfp != (VFP_T *)NULL)) { 868 WRITEDATA(cfTmpVfp, firstPos, lastPos); 869 } 870 871 return (2); /* path would insert here */ 872 } 873 874 /* 875 * This entry is "LESS THAN" the specified path to search for 876 * need to process the next entry from the contents file. First, 877 * if writing to new contents file, update new contents file if 878 * processing old style entry; otherwise, update skipped data 879 * information to remember current last byte of skipped data. 880 */ 881 882 if (cfTmpVfp != (VFP_T *)NULL) { 883 char *px; 884 ssize_t len; 885 886 if (rdpath != 0) { 887 /* modify record: write out any skipped data */ 888 if (dataSkipped) { 889 WRITEDATA(cfTmpVfp, firstPos, lastPos); 890 } 891 892 /* 893 * copy what we've read and the rest of this 894 * line onto the specified output stream 895 */ 896 vfpPutBytes(cfTmpVfp, cpath_start, cpath_len); 897 vfpPutc(cfTmpVfp, c); 898 vfpPutc(cfTmpVfp, ept->ftype); 899 vfpPutc(cfTmpVfp, ' '); 900 vfpPuts(cfTmpVfp, ept->pkg_class); 901 902 px = strchr(vfpGetCurrCharPtr(cfVfp), '\n'); 903 904 if (px == (char *)NULL) { 905 len = vfpGetBytesRemaining(cfVfp); 906 vfpPutBytes(cfTmpVfp, 907 vfpGetCurrCharPtr(cfVfp), len); 908 vfpPutc(cfTmpVfp, '\n'); 909 vfpSeekToEnd(cfVfp); 910 } else { 911 len = vfpGetCurrPtrDelta(cfVfp, px); 912 vfpPutBytes(cfTmpVfp, 913 vfpGetCurrCharPtr(cfVfp), len); 914 vfpIncCurrPtrBy(cfVfp, len); 915 } 916 917 /* reset skiped bytes if any data skipped */ 918 if (dataSkipped) { 919 dataSkipped = 0; 920 lastPos = (char *)NULL; 921 firstPos = vfpGetCurrCharPtr(cfVfp); 922 } 923 } else { 924 /* skip data */ 925 dataSkipped = 1; 926 927 px = strchr(vfpGetCurrCharPtr(cfVfp), '\n'); 928 929 if (px == (char *)NULL) { 930 vfpSeekToEnd(cfVfp); 931 } else { 932 len = vfpGetCurrPtrDelta(cfVfp, px)+1; 933 vfpIncCurrPtrBy(cfVfp, len); 934 } 935 lastPos = vfpGetCurrCharPtr(cfVfp); 936 } 937 } else { 938 /* 939 * since this isn't the entry we want, just read the 940 * stream until we find the end of this entry and 941 * then start this search loop again 942 */ 943 char *px; 944 945 px = strchr(vfpGetCurrCharPtr(cfVfp), '\n'); 946 947 if (px == (char *)NULL) { 948 vfpSeekToEnd(cfVfp); 949 950 /* copy path found to 'lpath' */ 951 COPYPATH(lpath, cpath_start, cpath_len); 952 953 setErrstr(pkg_gt(ERR_MISSING_NEWLINE)); 954 findend(&vfpGetCurrCharPtr(cfVfp)); 955 return (-1); 956 } else { 957 ssize_t len; 958 959 len = vfpGetCurrPtrDelta(cfVfp, px)+1; 960 vfpIncCurrPtrBy(cfVfp, len); 961 } 962 } 963 } 964 965 /* 966 * ********************************************************************* 967 * end of main loop processing entries from contents file 968 * the loop is broken out of when an exact match for the 969 * path being searched for has been found and the type is one of: 970 * - ?fvelspcbdx 971 * at this point parsing is at the first character past the full path 972 * name on an exact match for the path being looked for - parse the 973 * remainder of the entries information into the ept structure. 974 * ********************************************************************* 975 */ 976 977 /* link/symbolic link must have link destination */ 978 979 if (((ept->ftype == 's') || (ept->ftype == 'l')) && 980 (ept->ainfo.local == NULL)) { 981 /* copy path found to 'lpath' */ 982 COPYPATH(lpath, cpath_start, cpath_len); 983 984 setErrstr(ERR_NO_LINK_SOURCE_SPECIFIED); 985 findend(&vfpGetCurrCharPtr(cfVfp)); 986 return (-1); 987 } 988 989 /* character/block devices have major/minor device numbers */ 990 991 if (((ept->ftype == 'c') || (ept->ftype == 'b'))) { 992 ept->ainfo.major = BADMAJOR; 993 ept->ainfo.minor = BADMINOR; 994 if (getnumvfp(&vfpGetCurrCharPtr(cfVfp), 10, 995 (long *)&ept->ainfo.major, BADMAJOR) || 996 getnumvfp(&vfpGetCurrCharPtr(cfVfp), 10, 997 (long *)&ept->ainfo.minor, BADMINOR)) { 998 /* copy path found to 'lpath' */ 999 COPYPATH(lpath, cpath_start, cpath_len); 1000 1001 setErrstr(pkg_gt(ERR_CANNOT_READ_MM_NUMS)); 1002 findend(&vfpGetCurrCharPtr(cfVfp)); 1003 return (-1); 1004 } 1005 } 1006 1007 /* most types have mode, owner, group identification components */ 1008 1009 if ((ept->ftype == 'd') || (ept->ftype == 'x') || (ept->ftype == 'c') || 1010 (ept->ftype == 'b') || (ept->ftype == 'p') || 1011 (ept->ftype == 'f') || (ept->ftype == 'v') || 1012 (ept->ftype == 'e')) { 1013 /* mode, owner, group should be here */ 1014 if (getnumvfp(&vfpGetCurrCharPtr(cfVfp), 8, 1015 (long *)&ept->ainfo.mode, BADMODE) || 1016 getstr(&vfpGetCurrCharPtr(cfVfp), sizeof (ept->ainfo.owner), 1017 ept->ainfo.owner, ISWORDSEP) || 1018 getstr(&vfpGetCurrCharPtr(cfVfp), sizeof (ept->ainfo.group), 1019 ept->ainfo.group, ISWORDSEP)) { 1020 /* copy path found to 'lpath' */ 1021 COPYPATH(lpath, cpath_start, cpath_len); 1022 1023 setErrstr(ERR_CANNOT_READ_MOG); 1024 findend(&vfpGetCurrCharPtr(cfVfp)); 1025 return (-1); 1026 } 1027 } 1028 1029 /* i/f/v/e have size, checksum, modification time components */ 1030 1031 if ((ept->ftype == 'i') || (ept->ftype == 'f') || 1032 (ept->ftype == 'v') || (ept->ftype == 'e')) { 1033 /* look for content description */ 1034 if (getlnumvfp(&vfpGetCurrCharPtr(cfVfp), 10, 1035 (fsblkcnt_t *)&ept->cinfo.size, BADCONT) || 1036 getnumvfp(&vfpGetCurrCharPtr(cfVfp), 10, 1037 (long *)&ept->cinfo.cksum, BADCONT) || 1038 getnumvfp(&vfpGetCurrCharPtr(cfVfp), 10, 1039 (long *)&ept->cinfo.modtime, BADCONT)) { 1040 /* copy path found to 'lpath' */ 1041 COPYPATH(lpath, cpath_start, cpath_len); 1042 1043 setErrstr(ERR_CANNOT_READ_CONTENT_INFO); 1044 findend(&vfpGetCurrCharPtr(cfVfp)); 1045 return (-1); 1046 } 1047 } 1048 1049 /* i files processing is completed - return 'exact match found' */ 1050 1051 if (ept->ftype == 'i') { 1052 /* copy path found to 'lpath' */ 1053 COPYPATH(lpath, cpath_start, cpath_len); 1054 1055 if (getend(&vfpGetCurrCharPtr(cfVfp))) { 1056 /* copy path found to 'lpath' */ 1057 COPYPATH(lpath, cpath_start, cpath_len); 1058 1059 setErrstr(ERR_EXTRA_TOKENS); 1060 return (-1); 1061 } 1062 1063 /* write out any skipped data before returning */ 1064 if (dataSkipped && (cfTmpVfp != (VFP_T *)NULL)) { 1065 WRITEDATA(cfTmpVfp, firstPos, lastPos); 1066 } 1067 1068 return (1); 1069 } 1070 1071 /* 1072 * determine list of packages which reference this entry 1073 */ 1074 1075 lastpinfo = (struct pinfo *)NULL; 1076 while ((c = getstr(&vfpGetCurrCharPtr(cfVfp), sizeof (pkgname), 1077 pkgname, ISPKGNAMESEP)) <= 0) { 1078 /* if c < 0 the string was too long to fix in the buffer */ 1079 1080 if (c < 0) { 1081 /* copy path found to 'lpath' */ 1082 COPYPATH(lpath, cpath_start, cpath_len); 1083 1084 setErrstr(ERR_PACKAGE_NAME_TOO_LONG); 1085 findend(&vfpGetCurrCharPtr(cfVfp)); 1086 return (-1); 1087 } 1088 1089 /* a package is present - create and populate pinfo structure */ 1090 1091 pinfo = (struct pinfo *)calloc(1, sizeof (struct pinfo)); 1092 if (!pinfo) { 1093 /* copy path found to 'lpath' */ 1094 COPYPATH(lpath, cpath_start, cpath_len); 1095 1096 setErrstr(ERR_NO_MEMORY); 1097 findend(&vfpGetCurrCharPtr(cfVfp)); 1098 return (-1); 1099 } 1100 if (!lastpinfo) { 1101 ept->pinfo = pinfo; /* first one */ 1102 } else { 1103 lastpinfo->next = pinfo; /* link list */ 1104 } 1105 lastpinfo = pinfo; 1106 1107 if ((pkgname[0] == '-') || (pkgname[0] == '+') || 1108 (pkgname[0] == '*') || (pkgname[0] == '~') || 1109 (pkgname[0] == '!') || (pkgname[0] == '%')) { 1110 pinfo->status = pkgname[0]; 1111 (void) strlcpy(pinfo->pkg, pkgname+1, 1112 sizeof (pinfo->pkg)); 1113 } else { 1114 (void) strlcpy(pinfo->pkg, pkgname, 1115 sizeof (pinfo->pkg)); 1116 } 1117 1118 /* pkg/[:[ftype][:class] */ 1119 c = (vfpGetc(cfVfp)); 1120 if (c == '\\') { 1121 /* get alternate ftype */ 1122 pinfo->editflag++; 1123 c = (vfpGetc(cfVfp)); 1124 } 1125 1126 if (c == ':') { 1127 /* get special classname */ 1128 (void) getstr(&vfpGetCurrCharPtr(cfVfp), 1129 sizeof (classname), classname, ISWORDSEP); 1130 (void) strlcpy(pinfo->aclass, classname, 1131 sizeof (pinfo->aclass)); 1132 c = (vfpGetc(cfVfp)); 1133 } 1134 ept->npkgs++; 1135 1136 /* break out of while if at end of entry */ 1137 1138 if ((c == '\n') || (c == '\0')) { 1139 break; 1140 } 1141 1142 /* if package not separated by a space return an error */ 1143 1144 if (!isspace(c)) { 1145 /* copy path found to 'lpath' */ 1146 COPYPATH(lpath, cpath_start, cpath_len); 1147 1148 setErrstr(ERR_BAD_ENTRY_END); 1149 findend(&vfpGetCurrCharPtr(cfVfp)); 1150 return (-1); 1151 } 1152 } 1153 1154 /* 1155 * parsing of the entry is complete 1156 */ 1157 1158 /* copy path found to 'lpath' */ 1159 COPYPATH(lpath, cpath_start, cpath_len); 1160 1161 /* write out any skipped data before returning */ 1162 if (dataSkipped && (cfTmpVfp != (VFP_T *)NULL)) { 1163 WRITEDATA(cfTmpVfp, firstPos, lastPos); 1164 } 1165 1166 /* if not at the end of the entry, make it so */ 1167 1168 if ((c != '\n') && (c != '\0')) { 1169 if (getend(&vfpGetCurrCharPtr(cfVfp)) && ept->pinfo) { 1170 setErrstr(ERR_EXTRA_TOKENS); 1171 return (-1); 1172 } 1173 } 1174 1175 return (1); 1176 } 1177 1178 static int 1179 getstr(char **cp, int n, char *str, int separator[]) 1180 { 1181 int c; 1182 char *p = *cp; 1183 char *p1; 1184 size_t len; 1185 1186 if (*p == '\0') { 1187 return (1); 1188 } 1189 1190 /* leading white space ignored */ 1191 1192 while (((c = *p) != '\0') && (isspace(*p++))) 1193 ; 1194 if ((c == '\0') || (c == '\n')) { 1195 p--; 1196 *cp = p; 1197 return (1); /* nothing there */ 1198 } 1199 1200 p--; 1201 1202 /* compute length based on delimiter found or not */ 1203 1204 p1 = p; 1205 while (separator[(int)*p1] == 0) { 1206 p1++; 1207 } 1208 1209 len = (ptrdiff_t)p1 - (ptrdiff_t)p; 1210 1211 /* if string will fit in result buffer copy string and return success */ 1212 1213 if (len < n) { 1214 (void) memcpy(str, p, len); 1215 str[len] = '\0'; 1216 p += len; 1217 *cp = p; 1218 return (0); 1219 } 1220 1221 /* result buffer too small; copy partial string, return error */ 1222 (void) memcpy(str, p, n-1); 1223 str[n-1] = '\0'; 1224 p += n; 1225 *cp = p; 1226 return (-1); 1227 } 1228 1229 static int 1230 getend(char **cp) 1231 { 1232 int n; 1233 char *p = *cp; 1234 1235 n = 0; 1236 1237 /* if at end of buffer return no more characters left */ 1238 1239 if (*p == '\0') { 1240 return (0); 1241 } 1242 1243 while ((*p != '\0') && (*p != '\n')) { 1244 if (n == 0) { 1245 if (!isspace(*p)) { 1246 n++; 1247 } 1248 } 1249 p++; 1250 } 1251 1252 *cp = ++p; 1253 return (n); 1254 } 1255 1256 static void 1257 findend(char **cp) 1258 { 1259 char *p1; 1260 char *p = *cp; 1261 1262 /* if at end of buffer return no more characters left */ 1263 1264 if (*p == '\0') { 1265 return; 1266 } 1267 1268 /* find the end of the line */ 1269 1270 p1 = strchr(p, '\n'); 1271 1272 if (p1 != (char *)NULL) { 1273 *cp = ++p1; 1274 return; 1275 } 1276 1277 *cp = strchr(p, '\0'); 1278 } 1279