xref: /illumos-gate/usr/src/cmd/unpack/unpack.c (revision d362b749)
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