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