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