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