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 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 22 /* All Rights Reserved */ 23 24 25 /* 26 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 27 * Use is subject to license terms. 28 */ 29 30 #pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.21 */ 31 32 /* 33 * Huffman decompressor 34 * Usage: pcat filename... 35 * or unpack filename... 36 */ 37 38 #include <stdio.h> 39 #include <fcntl.h> 40 #include <setjmp.h> 41 #include <signal.h> 42 #include <sys/types.h> 43 #include <sys/stat.h> 44 #include <unistd.h> 45 #include <locale.h> 46 #include <utime.h> 47 #include <stdlib.h> 48 #include <limits.h> 49 #include <sys/param.h> 50 #include <dirent.h> 51 #include <sys/acl.h> 52 #include <aclutils.h> 53 54 static struct utimbuf u_times; 55 56 static jmp_buf env; 57 static struct stat status; 58 static char *argv0, *argvk; 59 60 /* rmflg, when set it's ok to rm arvk file on caught signals */ 61 static int rmflg = 0; 62 63 #define SUF0 '.' 64 #define SUF1 'z' 65 #define US 037 66 #define RS 036 67 68 /* variables associated with i/o */ 69 static char filename[MAXPATHLEN]; 70 71 static short infile; 72 static short outfile; 73 static short inleft; 74 static short is_eof = 0; 75 static char *inp; 76 static char *outp; 77 static char inbuff[BUFSIZ]; 78 static char outbuff[BUFSIZ]; 79 80 /* the dictionary */ 81 static long origsize; 82 static short maxlev; 83 static short intnodes[25]; 84 static char *tree[25]; 85 static char characters[256]; 86 static char *eof; 87 88 static void putch(char c); 89 static int expand(); 90 static int decode(); 91 static int getwdsize(); 92 static int getch(); 93 static int getdict(); 94 static int mv_xattrs(); 95 96 /* read in the dictionary portion and build decoding structures */ 97 /* return 1 if successful, 0 otherwise */ 98 int 99 getdict() 100 { 101 register int c, i, nchildren; 102 103 /* 104 * check two-byte header 105 * get size of original file, 106 * get number of levels in maxlev, 107 * get number of leaves on level i in intnodes[i], 108 * set tree[i] to point to leaves for level i 109 */ 110 eof = &characters[0]; 111 112 inbuff[6] = 25; 113 inleft = read(infile, &inbuff[0], BUFSIZ); 114 if (inleft < 0) { 115 (void) fprintf(stderr, gettext( 116 "%s: %s: read error: "), argv0, filename); 117 perror(""); 118 return (0); 119 } 120 if (inbuff[0] != US) 121 goto goof; 122 123 if (inbuff[1] == US) { /* oldstyle packing */ 124 if (setjmp(env)) 125 return (0); 126 return (expand()); 127 } 128 if (inbuff[1] != RS) 129 goto goof; 130 131 inp = &inbuff[2]; 132 origsize = 0; 133 for (i = 0; i < 4; i++) 134 origsize = origsize*256 + ((*inp++) & 0377); 135 maxlev = *inp++ & 0377; 136 if (maxlev > 24) { 137 goof: (void) fprintf(stderr, gettext( 138 "%s: %s: not in packed format\n"), argv0, filename); 139 return (0); 140 } 141 for (i = 1; i <= maxlev; i++) 142 intnodes[i] = *inp++ & 0377; 143 for (i = 1; i <= maxlev; i++) { 144 tree[i] = eof; 145 for (c = intnodes[i]; c > 0; c--) { 146 if (eof >= &characters[255]) 147 goto goof; 148 *eof++ = *inp++; 149 } 150 } 151 *eof++ = *inp++; 152 intnodes[maxlev] += 2; 153 inleft -= inp - &inbuff[0]; 154 if (inleft < 0) 155 goto goof; 156 157 /* 158 * convert intnodes[i] to be number of 159 * internal nodes possessed by level i 160 */ 161 162 nchildren = 0; 163 for (i = maxlev; i >= 1; i--) { 164 c = intnodes[i]; 165 intnodes[i] = nchildren /= 2; 166 nchildren += c; 167 } 168 return (decode()); 169 } 170 171 /* unpack the file */ 172 /* return 1 if successful, 0 otherwise */ 173 int 174 decode() 175 { 176 register int bitsleft, c, i; 177 int j, lev, cont = 1; 178 char *p; 179 180 outp = &outbuff[0]; 181 lev = 1; 182 i = 0; 183 while (cont) { 184 if (inleft <= 0) { 185 inleft = read(infile, inp = &inbuff[0], BUFSIZ); 186 if (inleft < 0) { 187 (void) fprintf(stderr, gettext( 188 "%s: %s: read error: "), 189 argv0, filename); 190 perror(""); 191 return (0); 192 } 193 } 194 if (--inleft < 0) { 195 uggh: (void) fprintf(stderr, gettext( 196 "%s: %s: unpacking error\n"), 197 argv0, filename); 198 return (0); 199 } 200 c = *inp++; 201 bitsleft = 8; 202 while (--bitsleft >= 0) { 203 i *= 2; 204 if (c & 0200) 205 i++; 206 c <<= 1; 207 if ((j = i - intnodes[lev]) >= 0) { 208 p = &tree[lev][j]; 209 if (p == eof) { 210 c = outp - &outbuff[0]; 211 if (write(outfile, &outbuff[0], c) != c) { 212 wrerr: (void) fprintf(stderr, gettext( 213 "%s: %s: write error: "), 214 argv0, argvk); 215 perror(""); 216 return (0); 217 } 218 origsize -= c; 219 if (origsize != 0) 220 goto uggh; 221 return (1); 222 } 223 *outp++ = *p; 224 if (outp == &outbuff[BUFSIZ]) { 225 if (write(outfile, outp = &outbuff[0], 226 BUFSIZ) != BUFSIZ) 227 goto wrerr; 228 origsize -= BUFSIZ; 229 } 230 lev = 1; 231 i = 0; 232 } else 233 lev++; 234 } 235 } 236 return (1); /* we won't get here , but lint is pleased */ 237 } 238 239 int 240 main(int argc, char *argv[]) 241 { 242 extern int optind; 243 int i, k; 244 int error; 245 int sep, errflg = 0, pcat = 0; 246 register char *p1, *cp; 247 int fcount = 0; /* failure count */ 248 int max_name; 249 void onsig(int); 250 acl_t *aclp = NULL; 251 252 253 if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 254 #ifdef __STDC__ 255 signal((int)SIGHUP, onsig); 256 #else 257 signal((int)SIGHUP, onsig); 258 #endif 259 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 260 #ifdef __STDC__ 261 signal((int)SIGINT, onsig); 262 #else 263 signal((int)SIGINT, onsig); 264 #endif 265 if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 266 #ifdef __STDC__ 267 signal((int)SIGTERM, onsig); 268 #else 269 signal(SIGTERM, onsig); 270 #endif 271 272 (void) setlocale(LC_ALL, ""); 273 #if !defined(TEXT_DOMAIN) 274 #define TEXT_DOMAIN "SYS_TEST" 275 #endif 276 (void) textdomain(TEXT_DOMAIN); 277 278 p1 = *argv; 279 while (*p1++); /* Point p1 to end of argv[0] string */ 280 while (--p1 >= *argv) 281 if (*p1 == '/')break; 282 *argv = p1 + 1; 283 argv0 = argv[0]; 284 if (**argv == 'p')pcat++; /* User entered pcat (or /xx/xx/pcat) */ 285 286 while (getopt(argc, argv, "") != EOF) 287 ++errflg; 288 /* 289 * Check for invalid option. Also check for missing 290 * file operand, ie: "unpack" or "pcat". 291 */ 292 argc -= optind; 293 argv = &argv[optind]; 294 if (errflg || argc < 1) { 295 (void) fprintf(stderr, gettext("usage: %s file...\n"), argv0); 296 if (argc < 1) { 297 /* 298 * return 1 for usage error when no file was specified 299 */ 300 return (1); 301 } 302 } 303 /* loop through the file names */ 304 for (k = 0; k < argc; k++) { 305 fcount++; /* expect the worst */ 306 if (errflg) { 307 /* 308 * invalid option; just count the number of files not 309 * unpacked 310 */ 311 continue; 312 } 313 /* remove any .z suffix the user may have added */ 314 for (cp = argv[k]; *cp != '\0'; ++cp) 315 ; 316 if (cp[-1] == SUF1 && cp[-2] == SUF0) { 317 *cp-- = '\0'; *cp-- = '\0'; *cp = '\0'; 318 } 319 sep = -1; 320 cp = filename; 321 argvk = argv[k]; 322 /* copy argv[k] to filename and count chars in base name */ 323 for (i = 0; i < (MAXPATHLEN-3) && (*cp = argvk[i]); i++) 324 if (*cp++ == '/') 325 sep = i; 326 /* add .z suffix to filename */ 327 *cp++ = SUF0; 328 *cp++ = SUF1; 329 *cp = '\0'; 330 if ((infile = open(filename, O_RDONLY)) == -1) { 331 (void) fprintf(stderr, gettext( 332 "%s: %s: cannot open: "), 333 argv0, filename); 334 perror(""); 335 goto done; 336 } 337 if (pcat) 338 outfile = 1; /* standard output */ 339 else { 340 341 error = facl_get(infile, ACL_NO_TRIVIAL, &aclp); 342 if (error != 0) { 343 (void) printf(gettext( 344 "%s: %s: cannot retrieve ACL : %s\n"), 345 argv0, filename, acl_strerror(error)); 346 } 347 348 max_name = pathconf(filename, _PC_NAME_MAX); 349 if (max_name == -1) { 350 /* no limit on length of filename */ 351 max_name = _POSIX_NAME_MAX; 352 } 353 if (i >= (MAXPATHLEN-1) || (i - sep - 1) > max_name) { 354 (void) fprintf(stderr, gettext( 355 "%s: %s: file name too long\n"), 356 argv0, argvk); 357 goto done; 358 } 359 if (stat(argvk, &status) != -1) { 360 (void) fprintf(stderr, gettext( 361 "%s: %s: already exists\n"), 362 argv0, argvk); 363 goto done; 364 } 365 (void) fstat(infile, &status); 366 if (status.st_nlink != 1) { 367 (void) printf(gettext( 368 "%s: %s: Warning: file has links\n"), 369 argv0, filename); 370 } 371 if ((outfile = creat(argvk, status.st_mode)) == -1) { 372 (void) fprintf(stderr, gettext( 373 "%s: %s: cannot create: "), 374 argv0, argvk); 375 perror(""); 376 goto done; 377 } 378 rmflg = 1; 379 } 380 381 if (getdict() && /* unpack */ 382 (pcat || 383 (pathconf(filename, _PC_XATTR_EXISTS) != 1) || 384 (mv_xattrs(infile, outfile, 385 filename, 0) == 0))) { 386 if (!pcat) { 387 /* 388 * preserve acc & mod dates 389 */ 390 u_times.actime = status.st_atime; 391 u_times.modtime = status.st_mtime; 392 if (utime(argvk, &u_times) != 0) { 393 errflg++; 394 (void) fprintf(stderr, gettext( 395 "%s: cannot change times on %s: "), 396 argv0, argvk); 397 perror(""); 398 } 399 if (chmod(argvk, status.st_mode) != 0) { 400 errflg++; 401 (void) fprintf(stderr, gettext( 402 "%s: cannot change mode to %o on %s: "), 403 argv0, (uint_t)status.st_mode, 404 argvk); 405 perror(""); 406 } 407 (void) chown(argvk, 408 status.st_uid, status.st_gid); 409 if (aclp && (facl_set(outfile, aclp) < 0)) { 410 (void) printf(gettext("%s: cannot " 411 "set ACL on %s: "), argv0, argvk); 412 perror(""); 413 } 414 415 rmflg = 0; 416 (void) printf(gettext("%s: %s: unpacked\n"), 417 argv0, argvk); 418 (void) unlink(filename); 419 420 } 421 if (!errflg) 422 fcount--; /* success after all */ 423 } 424 else 425 if (!pcat) { 426 if (pathconf(argvk, _PC_XATTR_EXISTS) == 1) { 427 (void) mv_xattrs(outfile, infile, 428 argvk, 1); 429 } 430 (void) unlink(argvk); 431 } 432 done: (void) close(infile); 433 if (!pcat) 434 (void) close(outfile); 435 436 if (aclp) { 437 acl_free(aclp); 438 aclp = NULL; 439 } 440 } 441 return (fcount); 442 } 443 444 /* 445 * This code is for unpacking files that 446 * were packed using the previous algorithm. 447 */ 448 449 static int Tree[1024]; 450 451 /* return 1 if successful, 0 otherwise */ 452 453 int 454 expand() 455 { 456 int tp, bit; 457 short word; 458 int keysize, i, *t; 459 460 outp = outbuff; 461 inp = &inbuff[2]; 462 inleft -= 2; 463 464 origsize = ((long)(unsigned)getwdsize())*256*256; 465 origsize += (unsigned)getwdsize(); 466 if (origsize == 0 || is_eof) { 467 (void) fprintf(stderr, gettext( 468 "%s: %s: not in packed format\n"), 469 argv0, filename); 470 return (0); 471 } 472 t = Tree; 473 for (keysize = getwdsize(); keysize--; ) { 474 if ((i = getch()) == 0377) 475 *t++ = getwdsize(); 476 else { 477 /* 478 * reached EOF unexpectedly 479 */ 480 if (is_eof) { 481 (void) fprintf(stderr, gettext( 482 "%s: %s: not in packed format\n"), 483 argv0, filename); 484 return (0); 485 } 486 *t++ = i & 0377; 487 } 488 } 489 /* 490 * reached EOF unexpectedly 491 */ 492 if (is_eof) { 493 (void) fprintf(stderr, gettext( 494 "%s: %s: not in packed format\n"), 495 argv0, filename); 496 return (0); 497 } 498 499 500 bit = tp = 0; 501 for (;;) { 502 if (bit <= 0) { 503 word = getwdsize(); 504 /* 505 * reached EOF unexpectedly 506 */ 507 if (word == 0 && is_eof && origsize > 0) { 508 (void) fprintf(stderr, gettext( 509 "%s: %s: not in packed format\n"), 510 argv0, filename); 511 return (0); 512 } 513 bit = 16; 514 } 515 tp += Tree[tp + (word < 0)]; 516 word <<= 1; 517 bit--; 518 if (Tree[tp] == 0) { 519 putch(Tree[tp+1]); 520 tp = 0; 521 if ((origsize -= 1) == 0) { 522 (void) write(outfile, outbuff, outp - outbuff); 523 return (1); 524 } 525 } 526 } 527 } 528 529 int 530 getch() 531 { 532 if (inleft <= 0) { 533 inleft = read(infile, inp = inbuff, BUFSIZ); 534 if (inleft < 0) { 535 (void) fprintf(stderr, gettext( 536 "%s: %s: read error: "), 537 argv0, filename); 538 perror(""); 539 longjmp(env, 1); 540 } else { /* reached EOF, report it */ 541 if (inleft == 0) { 542 is_eof = 1; 543 return (EOF); 544 } 545 } 546 } 547 inleft--; 548 return (*inp++ & 0377); 549 } 550 551 int 552 getwdsize() 553 { 554 char c; 555 int d; 556 557 c = getch(); 558 d = getch(); 559 if (is_eof) 560 return (0); 561 d <<= 8; 562 d |= c & 0377; 563 return (d); 564 } 565 566 void 567 onsig(int sig) 568 { 569 /* could be running as unpack or pcat */ 570 /* but rmflg is set only when running */ 571 /* as unpack and only when file is */ 572 /* created by unpack and not yet done */ 573 if (rmflg == 1) 574 (void) unlink(argvk); 575 exit(1); 576 } 577 578 void 579 putch(char c) 580 { 581 int n; 582 583 *outp++ = c; 584 if (outp == &outbuff[BUFSIZ]) { 585 n = write(outfile, outp = outbuff, BUFSIZ); 586 if (n < BUFSIZ) { 587 (void) fprintf(stderr, gettext( 588 "%s: %s: write error: "), 589 argv0, argvk); 590 perror(""); 591 longjmp(env, 2); 592 } 593 } 594 } 595 596 /* 597 * mv_xattrs - move (via renameat) all of the extended attributes 598 * associated with the file referenced by infd to the file 599 * referenced by outfd. The infile and silent arguments are 600 * provided for error message processing. This function 601 * returns 0 on success and -1 on error. 602 */ 603 static int 604 mv_xattrs(int infd, int outfd, char *infile, int silent) 605 { 606 int indfd, outdfd, tmpfd; 607 DIR *dirp = NULL; 608 struct dirent *dp = NULL; 609 int error = 0; 610 char *etext; 611 612 indfd = outdfd = tmpfd = -1; 613 614 if ((indfd = openat(infd, ".", O_RDONLY|O_XATTR)) == -1) { 615 etext = gettext("cannot open source"); 616 error = -1; 617 goto out; 618 } 619 620 if ((outdfd = openat(outfd, ".", O_RDONLY|O_XATTR)) == -1) { 621 etext = gettext("cannot open target"); 622 error = -1; 623 goto out; 624 } 625 626 if ((tmpfd = dup(indfd)) == -1) { 627 etext = gettext("cannot dup descriptor"); 628 error = -1; 629 goto out; 630 631 } 632 if ((dirp = fdopendir(tmpfd)) == NULL) { 633 etext = gettext("cannot access source"); 634 error = -1; 635 goto out; 636 } 637 638 while (dp = readdir(dirp)) { 639 if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') || 640 (dp->d_name[0] == '.' && dp->d_name[1] == '.' && 641 dp->d_name[2] == '\0')) 642 continue; 643 if ((renameat(indfd, dp->d_name, outdfd, dp->d_name)) == -1) { 644 etext = dp->d_name; 645 error = -1; 646 goto out; 647 } 648 } 649 out: 650 if (error == -1 && silent == 0) { 651 fprintf(stderr, gettext( 652 "unpack: %s: cannot move extended attributes, "), 653 infile); 654 perror(etext); 655 } 656 if (dirp) 657 closedir(dirp); 658 if (indfd != -1) 659 close(indfd); 660 if (outdfd != -1) 661 close(outdfd); 662 return (error); 663 } 664