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