summaryrefslogtreecommitdiff
path: root/archival/tar.c
diff options
context:
space:
mode:
authorGlenn L McGrath <bug1@ihug.co.nz>2001-10-05 02:58:48 +0000
committerGlenn L McGrath <bug1@ihug.co.nz>2001-10-05 02:58:48 +0000
commit2e772edacf70e9ff45a00a34af4c94e04d490fc2 (patch)
treec1cb95bdc501d6f090a02ff248638e8f09dd073c /archival/tar.c
parent4c557bf576e772bfd78429d5f9b2a436ccadf268 (diff)
downloadbusybox-w32-2e772edacf70e9ff45a00a34af4c94e04d490fc2.tar.gz
busybox-w32-2e772edacf70e9ff45a00a34af4c94e04d490fc2.tar.bz2
busybox-w32-2e772edacf70e9ff45a00a34af4c94e04d490fc2.zip
Change extraction/list code to use common unarchive code.
Diffstat (limited to 'archival/tar.c')
-rw-r--r--archival/tar.c1024
1 files changed, 313 insertions, 711 deletions
diff --git a/archival/tar.c b/archival/tar.c
index 389d7f02e..cd563393b 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -2,6 +2,9 @@
2/* 2/*
3 * Mini tar implementation for busybox 3 * Mini tar implementation for busybox
4 * 4 *
5 * Modifed to use common extraction code used by ar, cpio, dpkg-deb, dpkg
6 * Glenn McGrath <bug1@optushome.com.au>
7 *
5 * Note, that as of BusyBox-0.43, tar has been completely rewritten from the 8 * Note, that as of BusyBox-0.43, tar has been completely rewritten from the
6 * ground up. It still has remnents of the old code lying about, but it is 9 * ground up. It still has remnents of the old code lying about, but it is
7 * very different now (i.e., cleaner, less global variables, etc.) 10 * very different now (i.e., cleaner, less global variables, etc.)
@@ -35,35 +38,36 @@
35 * 38 *
36 */ 39 */
37 40
38
39#include <stdio.h>
40#include <dirent.h>
41#include <errno.h>
42#include <fcntl.h> 41#include <fcntl.h>
43#include <signal.h>
44#include <time.h>
45#include <utime.h>
46#include <sys/types.h>
47#include <sys/sysmacros.h>
48#include <getopt.h> 42#include <getopt.h>
49#include <fnmatch.h> 43#include <search.h>
50#include <string.h> 44#include <stdio.h>
51#include <stdlib.h> 45#include <stdlib.h>
52#include <unistd.h> 46#include <unistd.h>
47#include <fnmatch.h>
48#include <string.h>
49#include <errno.h>
53#include "busybox.h" 50#include "busybox.h"
54 51
52#ifdef BB_FEATURE_TAR_CREATE
53
55/* Tar file constants */ 54/* Tar file constants */
56#ifndef MAJOR 55# define TAR_MAGIC "ustar" /* ustar and a null */
57#define MAJOR(dev) (((dev)>>8)&0xff) 56# define TAR_VERSION " " /* Be compatable with GNU tar format */
58#define MINOR(dev) ((dev)&0xff)
59#endif
60 57
61enum { NAME_SIZE = 100 }; /* because gcc won't let me use 'static const int' */ 58# ifndef MAJOR
59# define MAJOR(dev) (((dev)>>8)&0xff)
60# define MINOR(dev) ((dev)&0xff)
61# endif
62
63static const int TAR_BLOCK_SIZE = 512;
64static const int TAR_MAGIC_LEN = 6;
65static const int TAR_VERSION_LEN = 2;
62 66
63/* POSIX tar Header Block, from POSIX 1003.1-1990 */ 67/* POSIX tar Header Block, from POSIX 1003.1-1990 */
68enum { NAME_SIZE = 100 }; /* because gcc won't let me use 'static const int' */
64struct TarHeader 69struct TarHeader
65{ 70{ /* byte offset */
66 /* byte offset */
67 char name[NAME_SIZE]; /* 0-99 */ 71 char name[NAME_SIZE]; /* 0-99 */
68 char mode[8]; /* 100-107 */ 72 char mode[8]; /* 100-107 */
69 char uid[8]; /* 108-115 */ 73 char uid[8]; /* 108-115 */
@@ -84,697 +88,6 @@ struct TarHeader
84}; 88};
85typedef struct TarHeader TarHeader; 89typedef struct TarHeader TarHeader;
86 90
87
88/* A few useful constants */
89#define TAR_MAGIC "ustar" /* ustar and a null */
90#define TAR_VERSION " " /* Be compatable with GNU tar format */
91static const int TAR_MAGIC_LEN = 6;
92static const int TAR_VERSION_LEN = 2;
93static const int TAR_BLOCK_SIZE = 512;
94
95/* A nice enum with all the possible tar file content types */
96enum TarFileType
97{
98 REGTYPE = '0', /* regular file */
99 REGTYPE0 = '\0', /* regular file (ancient bug compat)*/
100 LNKTYPE = '1', /* hard link */
101 SYMTYPE = '2', /* symbolic link */
102 CHRTYPE = '3', /* character special */
103 BLKTYPE = '4', /* block special */
104 DIRTYPE = '5', /* directory */
105 FIFOTYPE = '6', /* FIFO special */
106 CONTTYPE = '7', /* reserved */
107 GNULONGLINK = 'K', /* GNU long (>100 chars) link name */
108 GNULONGNAME = 'L', /* GNU long (>100 chars) file name */
109};
110typedef enum TarFileType TarFileType;
111
112/* This struct ignores magic, non-numeric user name,
113 * non-numeric group name, and the checksum, since
114 * these are all ignored by BusyBox tar. */
115struct TarInfo
116{
117 int tarFd; /* An open file descriptor for reading from the tarball */
118 char * name; /* File name */
119 mode_t mode; /* Unix mode, including device bits. */
120 uid_t uid; /* Numeric UID */
121 gid_t gid; /* Numeric GID */
122 size_t size; /* Size of file */
123 time_t mtime; /* Last-modified time */
124 enum TarFileType type; /* Regular, directory, link, etc. */
125 char * linkname; /* Name for symbolic and hard links */
126 long devmajor; /* Major number for special device */
127 long devminor; /* Minor number for special device */
128};
129typedef struct TarInfo TarInfo;
130
131/* Local procedures to restore files from a tar file. */
132static int readTarFile(int tarFd, int extractFlag, int listFlag,
133 int tostdoutFlag, int verboseFlag, char** extractList,
134 char** excludeList);
135
136#ifdef BB_FEATURE_TAR_CREATE
137/* Local procedures to save files into a tar file. */
138static int writeTarFile(const char* tarName, int verboseFlag, char **argv,
139 char** excludeList);
140#endif
141
142#if defined BB_FEATURE_TAR_EXCLUDE
143static struct option longopts[] = {
144 { "exclude", 1, NULL, 'e' },
145 { NULL, 0, NULL, 0 }
146};
147#endif
148
149extern int tar_main(int argc, char **argv)
150{
151 char** excludeList=NULL;
152 char** extractList=NULL;
153 const char *tarName="-";
154 const char *cwd=NULL;
155#if defined BB_FEATURE_TAR_EXCLUDE
156 int excludeListSize=0;
157 FILE *fileList;
158 char file[256];
159#endif
160#if defined BB_FEATURE_TAR_GZIP
161 FILE *comp_file = NULL;
162 int unzipFlag = FALSE;
163#endif
164 int listFlag = FALSE;
165 int extractFlag = FALSE;
166 int createFlag = FALSE;
167 int verboseFlag = FALSE;
168 int tostdoutFlag = FALSE;
169 int status = FALSE;
170 int opt;
171 pid_t pid;
172
173 if (argc <= 1)
174 show_usage();
175
176 if (argv[1][0] != '-') {
177 char *tmp = xmalloc(strlen(argv[1]) + 2);
178 tmp[0] = '-';
179 strcpy(tmp + 1, argv[1]);
180 argv[1] = tmp;
181 }
182
183 while (
184#ifndef BB_FEATURE_TAR_EXCLUDE
185 (opt = getopt(argc, argv, "cxtzvOf:pC:"))
186#else
187 (opt = getopt_long(argc, argv, "cxtzvOf:X:pC:", longopts, NULL))
188#endif
189 > 0) {
190 switch (opt) {
191 case 'c':
192 if (extractFlag == TRUE || listFlag == TRUE)
193 goto flagError;
194 createFlag = TRUE;
195 break;
196 case 'x':
197 if (listFlag == TRUE || createFlag == TRUE)
198 goto flagError;
199 extractFlag = TRUE;
200 break;
201 case 't':
202 if (extractFlag == TRUE || createFlag == TRUE)
203 goto flagError;
204 listFlag = TRUE;
205 break;
206#ifdef BB_FEATURE_TAR_GZIP
207 case 'z':
208 unzipFlag = TRUE;
209 break;
210#endif
211 case 'v':
212 verboseFlag = TRUE;
213 break;
214 case 'O':
215 tostdoutFlag = TRUE;
216 break;
217 case 'f':
218 if (*tarName != '-')
219 error_msg_and_die( "Only one 'f' option allowed");
220 tarName = optarg;
221 break;
222#if defined BB_FEATURE_TAR_EXCLUDE
223 case 'e':
224 excludeList=xrealloc( excludeList,
225 sizeof(char *) * (excludeListSize+2));
226 excludeList[excludeListSize] = optarg;
227 /* Tack a NULL onto the end of the list */
228 excludeList[++excludeListSize] = NULL;
229 case 'X':
230 fileList = xfopen(optarg, "r");
231 while (fgets(file, sizeof(file), fileList) != NULL) {
232 excludeList = xrealloc(excludeList,
233 sizeof(char *) * (excludeListSize+2));
234 chomp(file);
235 excludeList[excludeListSize] = xstrdup(file);
236 /* Tack a NULL onto the end of the list */
237 excludeList[++excludeListSize] = NULL;
238 }
239 fclose(fileList);
240 break;
241#endif
242 case 'p':
243 break;
244 case 'C':
245 cwd = xgetcwd((char *)cwd);
246 if (chdir(optarg)) {
247 printf("cd: %s: %s\n", optarg, strerror(errno));
248 return EXIT_FAILURE;
249 }
250 break;
251 default:
252 show_usage();
253 }
254 }
255
256 /*
257 * Do the correct type of action supplying the rest of the
258 * command line arguments as the list of files to process.
259 */
260 if (createFlag == TRUE) {
261#ifndef BB_FEATURE_TAR_CREATE
262 error_msg_and_die( "This version of tar was not compiled with tar creation support.");
263#else
264#ifdef BB_FEATURE_TAR_GZIP
265 if (unzipFlag==TRUE)
266 error_msg_and_die("Creation of compressed not internally support by tar, pipe to busybox gunzip");
267#endif
268 status = writeTarFile(tarName, verboseFlag, argv + optind, excludeList);
269#endif
270 }
271 if (listFlag == TRUE || extractFlag == TRUE) {
272 int tarFd;
273 if (argv[optind])
274 extractList = argv + optind;
275 /* Open the tar file for reading. */
276 if (!strcmp(tarName, "-"))
277 tarFd = fileno(stdin);
278 else
279 tarFd = open(tarName, O_RDONLY);
280 if (tarFd < 0)
281 perror_msg_and_die("Error opening '%s'", tarName);
282
283#ifdef BB_FEATURE_TAR_GZIP
284 /* unzip tarFd in a seperate process */
285 if (unzipFlag == TRUE) {
286 comp_file = fdopen(tarFd, "r");
287
288 /* set the buffer size */
289 setvbuf(comp_file, NULL, _IOFBF, 0x8000);
290
291 if ((tarFd = fileno(gz_open(comp_file, &pid))) == EXIT_FAILURE) {
292 error_msg_and_die("Couldnt unzip file");
293 }
294 }
295#endif
296 status = readTarFile(tarFd, extractFlag, listFlag, tostdoutFlag,
297 verboseFlag, extractList, excludeList);
298 close(tarFd);
299#ifdef BB_FEATURE_TAR_GZIP
300 if (unzipFlag == TRUE) {
301 gz_close(pid);
302 fclose(comp_file);
303 }
304#endif
305 }
306
307 if (cwd)
308 chdir(cwd);
309 if (status == TRUE)
310 return EXIT_SUCCESS;
311 else
312 return EXIT_FAILURE;
313
314 flagError:
315 error_msg_and_die( "Exactly one of 'c', 'x' or 't' must be specified");
316}
317
318static void
319fixUpPermissions(TarInfo *header)
320{
321 struct utimbuf t;
322 /* Now set permissions etc. for the new file */
323 chown(header->name, header->uid, header->gid);
324 chmod(header->name, header->mode);
325 /* Reset the time */
326 t.actime = time(0);
327 t.modtime = header->mtime;
328 utime(header->name, &t);
329}
330
331static int
332tarExtractRegularFile(TarInfo *header, int extractFlag, int tostdoutFlag)
333{
334 size_t writeSize;
335 size_t readSize;
336 size_t actualWriteSz;
337 char buffer[20 * TAR_BLOCK_SIZE];
338 size_t size = header->size;
339 int outFd=fileno(stdout);
340
341 /* Open the file to be written, if a file is supposed to be written */
342 if (extractFlag==TRUE && tostdoutFlag==FALSE) {
343 /* Create the path to the file, just in case it isn't there...
344 * This should not screw up path permissions or anything. */
345 char *buf, *dir;
346 buf = xstrdup (header->name);
347 dir = dirname (buf);
348 make_directory (dir, -1, FILEUTILS_RECUR);
349 free (buf);
350 if ((outFd=open(header->name, O_CREAT|O_TRUNC|O_WRONLY,
351 header->mode & ~S_IFMT)) < 0) {
352 error_msg(io_error, header->name, strerror(errno));
353 return( FALSE);
354 }
355 }
356
357 /* Write out the file, if we are supposed to be doing that */
358 while ( size > 0 ) {
359 actualWriteSz=0;
360 if ( size > sizeof(buffer) )
361 writeSize = readSize = sizeof(buffer);
362 else {
363 int mod = size % TAR_BLOCK_SIZE;
364 if ( mod != 0 )
365 readSize = size + (TAR_BLOCK_SIZE - mod);
366 else
367 readSize = size;
368 writeSize = size;
369 }
370 if ( (readSize = full_read(header->tarFd, buffer, readSize)) <= 0 ) {
371 /* Tarball seems to have a problem */
372 error_msg("Unexpected EOF in archive");
373 return( FALSE);
374 }
375 if ( readSize < writeSize )
376 writeSize = readSize;
377
378 /* Write out the file, if we are supposed to be doing that */
379 if (extractFlag==TRUE) {
380
381 if ((actualWriteSz=full_write(outFd, buffer, writeSize)) != writeSize ) {
382 /* Output file seems to have a problem */
383 error_msg(io_error, header->name, strerror(errno));
384 return( FALSE);
385 }
386 } else {
387 actualWriteSz=writeSize;
388 }
389
390 size -= actualWriteSz;
391 }
392
393 /* Now we are done writing the file out, so try
394 * and fix up the permissions and whatnot */
395 if (extractFlag==TRUE && tostdoutFlag==FALSE) {
396 close(outFd);
397 fixUpPermissions(header);
398 }
399 return( TRUE);
400}
401
402static int
403tarExtractDirectory(TarInfo *header, int extractFlag, int tostdoutFlag)
404{
405 if (extractFlag==FALSE || tostdoutFlag==TRUE)
406 return( TRUE);
407
408 if (make_directory(header->name, header->mode, FILEUTILS_RECUR) < 0)
409 return( FALSE);
410
411 fixUpPermissions(header);
412 return( TRUE);
413}
414
415static int
416tarExtractHardLink(TarInfo *header, int extractFlag, int tostdoutFlag)
417{
418 if (extractFlag==FALSE || tostdoutFlag==TRUE)
419 return( TRUE);
420
421 if (link(header->linkname, header->name) < 0) {
422 perror_msg("%s: Cannot create hard link to '%s'", header->name,
423 header->linkname);
424 return( FALSE);
425 }
426
427 /* Now set permissions etc. for the new directory */
428 fixUpPermissions(header);
429 return( TRUE);
430}
431
432static int
433tarExtractSymLink(TarInfo *header, int extractFlag, int tostdoutFlag)
434{
435 if (extractFlag==FALSE || tostdoutFlag==TRUE)
436 return( TRUE);
437
438#ifdef S_ISLNK
439 if (symlink(header->linkname, header->name) < 0) {
440 perror_msg("%s: Cannot create symlink to '%s'", header->name,
441 header->linkname);
442 return( FALSE);
443 }
444 /* Try to change ownership of the symlink.
445 * If libs doesn't support that, don't bother.
446 * Changing the pointed-to-file is the Wrong Thing(tm).
447 */
448#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1)
449 lchown(header->name, header->uid, header->gid);
450#endif
451
452 /* Do not change permissions or date on symlink,
453 * since it changes the pointed to file instead. duh. */
454#else
455 error_msg("%s: Cannot create symlink to '%s': %s",
456 header->name, header->linkname,
457 "symlinks not supported");
458#endif
459 return( TRUE);
460}
461
462static int
463tarExtractSpecial(TarInfo *header, int extractFlag, int tostdoutFlag)
464{
465 if (extractFlag==FALSE || tostdoutFlag==TRUE)
466 return( TRUE);
467
468 if (S_ISCHR(header->mode) || S_ISBLK(header->mode) || S_ISSOCK(header->mode)) {
469 if (mknod(header->name, header->mode, makedev(header->devmajor, header->devminor)) < 0) {
470 perror_msg("%s: Cannot mknod", header->name);
471 return( FALSE);
472 }
473 } else if (S_ISFIFO(header->mode)) {
474 if (mkfifo(header->name, header->mode) < 0) {
475 perror_msg("%s: Cannot mkfifo", header->name);
476 return( FALSE);
477 }
478 }
479
480 /* Now set permissions etc. for the new directory */
481 fixUpPermissions(header);
482 return( TRUE);
483}
484
485/* Parse the tar header and fill in the nice struct with the details */
486static int
487readTarHeader(struct TarHeader *rawHeader, struct TarInfo *header)
488{
489 int i;
490 long chksum, sum=0;
491 unsigned char *s = (unsigned char *)rawHeader;
492
493 header->name = rawHeader->name;
494 /* Check for and relativify any absolute paths */
495 if ( *(header->name) == '/' ) {
496 static int alreadyWarned=FALSE;
497
498 while (*(header->name) == '/')
499 header->name++;
500
501 if (alreadyWarned == FALSE) {
502 error_msg("Removing leading '/' from member names");
503 alreadyWarned = TRUE;
504 }
505 }
506
507 header->mode = strtol(rawHeader->mode, NULL, 8);
508 header->uid = strtol(rawHeader->uid, NULL, 8);
509 header->gid = strtol(rawHeader->gid, NULL, 8);
510 header->size = strtol(rawHeader->size, NULL, 8);
511 header->mtime = strtol(rawHeader->mtime, NULL, 8);
512 chksum = strtol(rawHeader->chksum, NULL, 8);
513 header->type = rawHeader->typeflag;
514 header->linkname = rawHeader->linkname;
515 header->devmajor = strtol(rawHeader->devmajor, NULL, 8);
516 header->devminor = strtol(rawHeader->devminor, NULL, 8);
517
518 /* Check the checksum */
519 for (i = sizeof(*rawHeader); i-- != 0;) {
520 sum += *s++;
521 }
522 /* Remove the effects of the checksum field (replace
523 * with blanks for the purposes of the checksum) */
524 s = rawHeader->chksum;
525 for (i = sizeof(rawHeader->chksum) ; i-- != 0;) {
526 sum -= *s++;
527 }
528 sum += ' ' * sizeof(rawHeader->chksum);
529 if (sum == chksum )
530 return ( TRUE);
531 return( FALSE);
532}
533
534static int exclude_file(char **excluded_files, const char *file)
535{
536 int i;
537
538 if (excluded_files == NULL)
539 return 0;
540
541 for (i = 0; excluded_files[i] != NULL; i++) {
542 if (excluded_files[i][0] == '/') {
543 if (fnmatch(excluded_files[i], file,
544 FNM_PATHNAME | FNM_LEADING_DIR) == 0)
545 return 1;
546 } else {
547 const char *p;
548
549 for (p = file; p[0] != '\0'; p++) {
550 if ((p == file || p[-1] == '/') && p[0] != '/' &&
551 fnmatch(excluded_files[i], p,
552 FNM_PATHNAME | FNM_LEADING_DIR) == 0)
553 return 1;
554 }
555 }
556 }
557
558 return 0;
559}
560
561static int extract_file(char **extract_files, const char *file)
562{
563 int i;
564
565 if (extract_files == NULL)
566 return 1;
567
568 for (i = 0; extract_files[i] != NULL; i++) {
569 if (fnmatch(extract_files[i], file, FNM_LEADING_DIR) == 0)
570 return 1;
571 }
572
573 return 0;
574}
575
576/*
577 * Read a tar file and extract or list the specified files within it.
578 * If the list is empty than all files are extracted or listed.
579 */
580static int readTarFile(int tarFd, int extractFlag, int listFlag,
581 int tostdoutFlag, int verboseFlag, char** extractList,
582 char** excludeList)
583{
584 int status;
585 int errorFlag=FALSE;
586 int skipNextHeaderFlag=FALSE;
587 TarHeader rawHeader;
588 TarInfo header;
589
590 /* Read the tar file, and iterate over it one file at a time */
591 while ( (status = full_read(tarFd, (char*)&rawHeader, TAR_BLOCK_SIZE)) == TAR_BLOCK_SIZE ) {
592
593 /* Try to read the header */
594 if ( readTarHeader(&rawHeader, &header) == FALSE ) {
595 if ( *(header.name) == '\0' ) {
596 goto endgame;
597 } else {
598 errorFlag=TRUE;
599 error_msg("Bad tar header, skipping");
600 continue;
601 }
602 }
603 if ( *(header.name) == '\0' )
604 continue;
605 header.tarFd = tarFd;
606
607 /* Skip funky extra GNU headers that precede long files */
608 if ( (header.type == GNULONGNAME) || (header.type == GNULONGLINK) ) {
609 skipNextHeaderFlag=TRUE;
610 if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE)
611 errorFlag = TRUE;
612 continue;
613 }
614 if ( skipNextHeaderFlag == TRUE ) {
615 skipNextHeaderFlag=FALSE;
616 error_msg(name_longer_than_foo, NAME_SIZE);
617 if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE)
618 errorFlag = TRUE;
619 continue;
620 }
621
622#if defined BB_FEATURE_TAR_EXCLUDE
623 if (exclude_file(excludeList, header.name)) {
624 /* There are not the droids you're looking for, move along */
625 /* If it is a regular file, pretend to extract it with
626 * the extractFlag set to FALSE, so the junk in the tarball
627 * is properly skipped over */
628 if ( header.type==REGTYPE || header.type==REGTYPE0 ) {
629 if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE)
630 errorFlag = TRUE;
631 }
632 continue;
633 }
634#endif
635
636 if (!extract_file(extractList, header.name)) {
637 /* There are not the droids you're looking for, move along */
638 /* If it is a regular file, pretend to extract it with
639 * the extractFlag set to FALSE, so the junk in the tarball
640 * is properly skipped over */
641 if ( header.type==REGTYPE || header.type==REGTYPE0 ) {
642 if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE)
643 errorFlag = TRUE;
644 }
645 continue;
646 }
647
648 if (listFlag == TRUE) {
649 /* Special treatment if the list (-t) flag is on */
650 if (verboseFlag == TRUE) {
651 int len, len1;
652 char buf[35];
653 struct tm *tm = localtime (&(header.mtime));
654
655 len=printf("%s ", mode_string(header.mode));
656 my_getpwuid(buf, header.uid);
657 if (! *buf)
658 len+=printf("%d", header.uid);
659 else
660 len+=printf("%s", buf);
661 my_getgrgid(buf, header.gid);
662 if (! *buf)
663 len+=printf("/%-d ", header.gid);
664 else
665 len+=printf("/%-s ", buf);
666
667 if (header.type==CHRTYPE || header.type==BLKTYPE) {
668 len1=snprintf(buf, sizeof(buf), "%ld,%-ld ",
669 header.devmajor, header.devminor);
670 } else {
671 len1=snprintf(buf, sizeof(buf), "%lu ", (long)header.size);
672 }
673 /* Jump through some hoops to make the columns match up */
674 for(;(len+len1)<31;len++)
675 printf(" ");
676 printf(buf);
677
678 /* Use ISO 8610 time format */
679 if (tm) {
680 printf ("%04d-%02d-%02d %02d:%02d:%02d ",
681 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
682 tm->tm_hour, tm->tm_min, tm->tm_sec);
683 }
684 }
685 printf("%s", header.name);
686 if (verboseFlag == TRUE) {
687 if (header.type==LNKTYPE) /* If this is a link, say so */
688 printf(" link to %s", header.linkname);
689 else if (header.type==SYMTYPE)
690 printf(" -> %s", header.linkname);
691 }
692 printf("\n");
693 }
694
695 /* List contents if we are supposed to do that */
696 if (verboseFlag == TRUE && extractFlag == TRUE) {
697 /* Now the normal listing */
698 FILE *vbFd = stdout;
699 if (tostdoutFlag == TRUE) // If the archive goes to stdout, verbose to stderr
700 vbFd = stderr;
701 fprintf(vbFd, "%s\n", header.name);
702 }
703
704 /* Remove files if we would overwrite them */
705 if (extractFlag == TRUE && tostdoutFlag == FALSE)
706 unlink(header.name);
707
708 /* If we got here, we can be certain we have a legitimate
709 * header to work with. So work with it. */
710 switch ( header.type ) {
711 case REGTYPE:
712 case REGTYPE0:
713 /* If the name ends in a '/' then assume it is
714 * supposed to be a directory, and fall through */
715 if (!last_char_is(header.name,'/')) {
716 if (tarExtractRegularFile(&header, extractFlag, tostdoutFlag)==FALSE)
717 errorFlag=TRUE;
718 break;
719 }
720 case DIRTYPE:
721 if (tarExtractDirectory( &header, extractFlag, tostdoutFlag)==FALSE)
722 errorFlag=TRUE;
723 break;
724 case LNKTYPE:
725 if (tarExtractHardLink( &header, extractFlag, tostdoutFlag)==FALSE)
726 errorFlag=TRUE;
727 break;
728 case SYMTYPE:
729 if (tarExtractSymLink( &header, extractFlag, tostdoutFlag)==FALSE)
730 errorFlag=TRUE;
731 break;
732 case CHRTYPE:
733 case BLKTYPE:
734 case FIFOTYPE:
735 if (tarExtractSpecial( &header, extractFlag, tostdoutFlag)==FALSE)
736 errorFlag=TRUE;
737 break;
738#if 0
739 /* Handled earlier */
740 case GNULONGNAME:
741 case GNULONGLINK:
742 skipNextHeaderFlag=TRUE;
743 break;
744#endif
745 default:
746 error_msg("Unknown file type '%c' in tar file", header.type);
747 close( tarFd);
748 return( FALSE);
749 }
750 }
751 close(tarFd);
752 if (status > 0) {
753 /* Bummer - we read a partial header */
754 perror_msg("Error reading tar file");
755 return ( FALSE);
756 }
757 else if (errorFlag==TRUE) {
758 error_msg( "Error exit delayed from previous errors");
759 return( FALSE);
760 } else
761 return( status);
762
763 /* Stuff to do when we are done */
764endgame:
765 close( tarFd);
766 if ( *(header.name) == '\0' ) {
767 if (errorFlag==TRUE)
768 error_msg( "Error exit delayed from previous errors");
769 else
770 return( TRUE);
771 }
772 return( FALSE);
773}
774
775
776#ifdef BB_FEATURE_TAR_CREATE
777
778/* 91/*
779** writeTarFile(), writeFileToTarball(), and writeTarHeader() are 92** writeTarFile(), writeFileToTarball(), and writeTarHeader() are
780** the only functions that deal with the HardLinkInfo structure. 93** the only functions that deal with the HardLinkInfo structure.
@@ -807,6 +120,22 @@ struct TarBallInfo
807}; 120};
808typedef struct TarBallInfo TarBallInfo; 121typedef struct TarBallInfo TarBallInfo;
809 122
123/* A nice enum with all the possible tar file content types */
124enum TarFileType
125{
126 REGTYPE = '0', /* regular file */
127 REGTYPE0 = '\0', /* regular file (ancient bug compat)*/
128 LNKTYPE = '1', /* hard link */
129 SYMTYPE = '2', /* symbolic link */
130 CHRTYPE = '3', /* character special */
131 BLKTYPE = '4', /* block special */
132 DIRTYPE = '5', /* directory */
133 FIFOTYPE = '6', /* FIFO special */
134 CONTTYPE = '7', /* reserved */
135 GNULONGLINK = 'K', /* GNU long (>100 chars) link name */
136 GNULONGNAME = 'L', /* GNU long (>100 chars) file name */
137};
138typedef enum TarFileType TarFileType;
810 139
811/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */ 140/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
812static void 141static void
@@ -985,6 +314,32 @@ writeTarHeader(struct TarBallInfo *tbInfo, const char *header_name,
985 return ( TRUE); 314 return ( TRUE);
986} 315}
987 316
317static int exclude_file(char **excluded_files, const char *file)
318{
319 int i;
320
321 if (excluded_files == NULL)
322 return 0;
323
324 for (i = 0; excluded_files[i] != NULL; i++) {
325 if (excluded_files[i][0] == '/') {
326 if (fnmatch(excluded_files[i], file,
327 FNM_PATHNAME | FNM_LEADING_DIR) == 0)
328 return 1;
329 } else {
330 const char *p;
331
332 for (p = file; p[0] != '\0'; p++) {
333 if ((p == file || p[-1] == '/') && p[0] != '/' &&
334 fnmatch(excluded_files[i], p,
335 FNM_PATHNAME | FNM_LEADING_DIR) == 0)
336 return 1;
337 }
338 }
339 }
340
341 return 0;
342}
988 343
989static int writeFileToTarball(const char *fileName, struct stat *statbuf, void* userData) 344static int writeFileToTarball(const char *fileName, struct stat *statbuf, void* userData)
990{ 345{
@@ -1040,11 +395,11 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf, void*
1040 if (header_name[0] == '\0') 395 if (header_name[0] == '\0')
1041 return TRUE; 396 return TRUE;
1042 397
1043#if defined BB_FEATURE_TAR_EXCLUDE 398# if defined BB_FEATURE_TAR_EXCLUDE
1044 if (exclude_file(tbInfo->excludeList, header_name)) { 399 if (exclude_file(tbInfo->excludeList, header_name)) {
1045 return SKIP; 400 return SKIP;
1046 } 401 }
1047#endif 402# endif //BB_FEATURE_TAR_EXCLUDE
1048 403
1049 if (writeTarHeader(tbInfo, header_name, fileName, statbuf)==FALSE) { 404 if (writeTarHeader(tbInfo, header_name, fileName, statbuf)==FALSE) {
1050 return( FALSE); 405 return( FALSE);
@@ -1144,7 +499,254 @@ static int writeTarFile(const char* tarName, int verboseFlag, char **argv,
1144 freeHardLinkInfo(&tbInfo.hlInfoHead); 499 freeHardLinkInfo(&tbInfo.hlInfoHead);
1145 return( TRUE); 500 return( TRUE);
1146} 501}
502#endif //tar_create
1147 503
504void append_file_to_list(char *filename, char ***name_list, int *num_of_entries)
505{
506 FILE *src_stream;
507 char *line;
508 char *line_ptr;
509
510 src_stream = xfopen(filename, "r");
511 while ((line = get_line_from_file(src_stream)) != NULL) {
512 line_ptr = last_char_is(line, '\n');
513 if (line_ptr) {
514 *line_ptr = '\0';
515 }
516 *name_list = realloc(*name_list, sizeof(char *) * (*num_of_entries + 1));
517 (*name_list)[*num_of_entries] = xstrdup(line);
518 (*num_of_entries)++;
519 free(line);
520 }
521 fclose(src_stream);
522}
1148 523
524#ifdef BB_FEATURE_TAR_EXCLUDE
525char **merge_list(char **include_list, char **exclude_list)
526{
527 char **new_include_list = NULL;
528 int new_include_count = 0;
529 int include_count = 0;
530 int exclude_count;
531
532 while (include_list[include_count] != NULL) {
533 int found = FALSE;
534 exclude_count = 0;
535 while (exclude_list[exclude_count] != NULL) {
536 if (strcmp(include_list[include_count], exclude_list[exclude_count]) == 0) {
537 found = TRUE;
538 break;
539 }
540 exclude_count++;
541 }
542
543 if (found == FALSE) {
544 new_include_list = realloc(new_include_list, sizeof(char *) * (include_count + 2));
545 new_include_list[new_include_count] = include_list[include_count];
546 new_include_count++;
547 new_include_list[new_include_count] = NULL;
548 } else {
549 free(include_list[include_count]);
550 }
551 include_count++;
552 }
553 return(new_include_list);
554}
1149#endif 555#endif
1150 556
557int tar_main(int argc, char **argv)
558{
559 enum untar_funct_e {
560 /* These are optional */
561 untar_from_file = 1,
562 untar_from_stdin = 2,
563 untar_unzip = 4,
564 /* Require one and only one of these */
565 untar_list = 8,
566 untar_create = 16,
567 untar_extract = 32
568 };
569
570#ifdef BB_FEATURE_TAR_EXCLUDE
571 char **exclude_list = NULL;
572 int exclude_count = 0;
573#endif
574
575 FILE *src_stream = NULL;
576 FILE *uncompressed_stream = NULL;
577 char **include_list = NULL;
578 char *src_filename = NULL;
579 char *dst_prefix = NULL;
580 char *file_list_name = NULL;
581 int opt;
582 unsigned short untar_funct = 0;
583 unsigned short untar_funct_required = 0;
584 unsigned short extract_function = 0;
585 int include_count = 0;
586 int gunzip_pid;
587 int gz_fd = 0;
588
589 if (argc < 2) {
590 show_usage();
591 }
592
593 /* Prepend '-' to the first argument if required */
594 if (argv[1][0] != '-') {
595 char *tmp = xmalloc(strlen(argv[1]) + 2);
596 tmp[0] = '-';
597 strcpy(tmp + 1, argv[1]);
598 argv[1] = tmp;
599 }
600
601 while ((opt = getopt(argc, argv, "ctxT:X:C:f:Opvz")) != -1) {
602 switch (opt) {
603
604 /* One and only one of these is required */
605 case 'c':
606 untar_funct_required |= untar_create;
607 break;
608 case 't':
609 untar_funct_required |= untar_list;
610 extract_function |= extract_list |extract_unconditional;
611 break;
612 case 'x':
613 untar_funct_required |= untar_extract;
614 extract_function |= (extract_all_to_fs | extract_unconditional | extract_create_leading_dirs);
615 break;
616
617 /* These are optional */
618 /* Exclude or Include files listed in <filename>*/
619#ifdef BB_FEATURE_TAR_EXCLUDE
620 case 'X':
621 append_file_to_list(optarg, &exclude_list, &exclude_count);
622 exclude_list[exclude_count] = NULL;
623 break;
624#endif
625 case 'T':
626 // by default a list is an include list
627 append_file_to_list(optarg, &include_list, &include_count);
628 break;
629
630 case 'C': // Change to dir <optarg>
631 /* Make sure dst_prefix ends in a '/' */
632 dst_prefix = concat_path_file(optarg, "/");
633 break;
634 case 'f': // archive filename
635 if (strcmp(optarg, "-") == 0) {
636 // Untar from stdin to stdout
637 untar_funct |= untar_from_stdin;
638 } else {
639 untar_funct |= untar_from_file;
640 src_filename = xstrdup(optarg);
641 }
642 break;
643 case 'O':
644 extract_function |= extract_to_stdout;
645 break;
646 case 'p':
647 break;
648 case 'v':
649 if (extract_function & extract_list) {
650 extract_function |= extract_verbose_list;
651 }
652 extract_function |= extract_list;
653 break;
654#ifdef BB_FEATURE_TAR_GZIP
655 case 'z':
656 untar_funct |= untar_unzip;
657 break;
658#endif
659 default:
660 show_usage();
661 }
662 }
663
664 /* Make sure the valid arguments were passed */
665 if (untar_funct_required == 0) {
666 error_msg_and_die("You must specify one of the `-ctx' options");
667 }
668 if ((untar_funct_required != untar_create) &&
669 (untar_funct_required != untar_extract) &&
670 (untar_funct_required != untar_list)) {
671 error_msg_and_die("You may not specify more than one `ctx' option.");
672 }
673 untar_funct |= untar_funct_required;
674
675 /* Setup an array of filenames to work with */
676 while (optind < argc) {
677 include_list = realloc(include_list, sizeof(char *) * (include_count + 2));
678 include_list[include_count] = xstrdup(argv[optind]);
679 include_count++;
680 optind++;
681 include_list[include_count] = NULL;
682 }
683
684#ifdef BB_FEATURE_TAR_EXCLUDE
685 /* Remove excluded files from the include list */
686 if (exclude_list != NULL) {
687 /* If both an exclude and include file list was present then
688 * its an exclude from include list only, if not its really an
689 * exclude list (and a poor choice of variable names) */
690 if (include_list == NULL) {
691 extract_function |= extract_exclude_list;
692 }
693 include_list = merge_list(include_list, exclude_list);
694 }
695#endif
696
697 if (extract_function & (extract_list | extract_all_to_fs)) {
698 if (dst_prefix == NULL) {
699 dst_prefix = xstrdup("./");
700 }
701
702 /* Setup the source of the tar data */
703 if (untar_funct & untar_from_file) {
704 src_stream = xfopen(src_filename, "r");
705 } else {
706 src_stream = stdin;
707 }
708#ifdef BB_FEATURE_TAR_GZIP
709 /* Get a binary tree of all the tar file headers */
710 if (untar_funct & untar_unzip) {
711 uncompressed_stream = gz_open(src_stream, &gunzip_pid);
712 } else
713#endif // BB_FEATURE_TAR_GZIP
714 uncompressed_stream = src_stream;
715
716 /* extract or list archive */
717 unarchive(uncompressed_stream, stdout, &get_header_tar, extract_function, dst_prefix, include_list);
718 fclose(uncompressed_stream);
719 }
720#ifdef BB_FEATURE_TAR_CREATE
721 /* create an archive */
722 else if (untar_funct & untar_create) {
723 int verboseFlag = FALSE;
724
725#ifdef BB_FEATURE_TAR_GZIP
726 if (untar_funct && untar_unzip) {
727 error_msg_and_die("Creation of compressed tarfile not internally support by tar, pipe to busybox gunzip");
728 }
729#endif // BB_FEATURE_TAR_GZIP
730 if (extract_function & extract_verbose_list) {
731 verboseFlag = TRUE;
732 }
733 writeTarFile(src_filename, verboseFlag, &argv[argc - 1], include_list);
734 }
735#endif // BB_FEATURE_TAR_CREATE
736
737 /* Cleanups */
738#ifdef BB_FEATURE_TAR_GZIP
739 if (untar_funct & untar_unzip) {
740 fclose(src_stream);
741 close(gz_fd);
742 gz_close(gunzip_pid);
743 }
744#endif // BB_FEATURE_TAR_GZIP
745 if (src_filename) {
746 free(src_filename);
747 }
748 if (file_list_name) {
749 free(file_list_name);
750 }
751 return(EXIT_SUCCESS);
752}