3 |
* Mini tar implementation for busybox |
* Mini tar implementation for busybox |
4 |
* |
* |
5 |
* Modified to use common extraction code used by ar, cpio, dpkg-deb, dpkg |
* Modified to use common extraction code used by ar, cpio, dpkg-deb, dpkg |
6 |
* Glenn McGrath <bug1@iinet.net.au> |
* by Glenn McGrath |
7 |
* |
* |
8 |
* Note, that as of BusyBox-0.43, tar has been completely rewritten from the |
* Note, that as of BusyBox-0.43, tar has been completely rewritten from the |
9 |
* ground up. It still has remnants of the old code lying about, but it is |
* ground up. It still has remnants of the old code lying about, but it is |
24 |
*/ |
*/ |
25 |
|
|
26 |
#include <fnmatch.h> |
#include <fnmatch.h> |
27 |
#include <getopt.h> |
#include "libbb.h" |
|
#include "busybox.h" |
|
28 |
#include "unarchive.h" |
#include "unarchive.h" |
29 |
|
|
30 |
|
/* FIXME: Stop using this non-standard feature */ |
31 |
|
#ifndef FNM_LEADING_DIR |
32 |
|
#define FNM_LEADING_DIR 0 |
33 |
|
#endif |
34 |
|
|
35 |
|
|
36 |
|
#define block_buf bb_common_bufsiz1 |
37 |
|
|
38 |
|
|
39 |
|
#if !ENABLE_FEATURE_SEAMLESS_GZ && !ENABLE_FEATURE_SEAMLESS_BZ2 |
40 |
|
/* Do not pass gzip flag to writeTarFile() */ |
41 |
|
#define writeTarFile(tar_fd, verboseFlag, dereferenceFlag, include, exclude, gzip) \ |
42 |
|
writeTarFile(tar_fd, verboseFlag, dereferenceFlag, include, exclude) |
43 |
|
#endif |
44 |
|
|
45 |
|
|
46 |
#if ENABLE_FEATURE_TAR_CREATE |
#if ENABLE_FEATURE_TAR_CREATE |
47 |
|
|
48 |
/* Tar file constants */ |
/* Tar file constants */ |
63 |
char chksum[8]; /* 148-155 */ |
char chksum[8]; /* 148-155 */ |
64 |
char typeflag; /* 156-156 */ |
char typeflag; /* 156-156 */ |
65 |
char linkname[NAME_SIZE]; /* 157-256 */ |
char linkname[NAME_SIZE]; /* 157-256 */ |
66 |
char magic[6]; /* 257-262 */ |
/* POSIX: "ustar" NUL "00" */ |
67 |
char version[2]; /* 263-264 */ |
/* GNU tar: "ustar " NUL */ |
68 |
|
/* Normally it's defined as magic[6] followed by |
69 |
|
* version[2], but we put them together to save code. |
70 |
|
*/ |
71 |
|
char magic[8]; /* 257-264 */ |
72 |
char uname[32]; /* 265-296 */ |
char uname[32]; /* 265-296 */ |
73 |
char gname[32]; /* 297-328 */ |
char gname[32]; /* 297-328 */ |
74 |
char devmajor[8]; /* 329-336 */ |
char devmajor[8]; /* 329-336 */ |
75 |
char devminor[8]; /* 337-344 */ |
char devminor[8]; /* 337-344 */ |
76 |
char prefix[155]; /* 345-499 */ |
char prefix[155]; /* 345-499 */ |
77 |
char padding[12]; /* 500-512 (pad to exactly the TAR_BLOCK_SIZE) */ |
char padding[12]; /* 500-512 (pad to exactly TAR_BLOCK_SIZE) */ |
78 |
}; |
}; |
79 |
|
|
80 |
/* |
/* |
84 |
*/ |
*/ |
85 |
typedef struct HardLinkInfo HardLinkInfo; |
typedef struct HardLinkInfo HardLinkInfo; |
86 |
struct HardLinkInfo { |
struct HardLinkInfo { |
87 |
HardLinkInfo *next; /* Next entry in list */ |
HardLinkInfo *next; /* Next entry in list */ |
88 |
dev_t dev; /* Device number */ |
dev_t dev; /* Device number */ |
89 |
ino_t ino; /* Inode number */ |
ino_t ino; /* Inode number */ |
90 |
short linkCount; /* (Hard) Link Count */ |
short linkCount; /* (Hard) Link Count */ |
91 |
char name[1]; /* Start of filename (must be last) */ |
char name[1]; /* Start of filename (must be last) */ |
92 |
}; |
}; |
93 |
|
|
94 |
/* Some info to be carried along when creating a new tarball */ |
/* Some info to be carried along when creating a new tarball */ |
95 |
typedef struct TarBallInfo TarBallInfo; |
typedef struct TarBallInfo TarBallInfo; |
96 |
struct TarBallInfo { |
struct TarBallInfo { |
97 |
int tarFd; /* Open-for-write file descriptor |
int tarFd; /* Open-for-write file descriptor |
98 |
for the tarball */ |
* for the tarball */ |
99 |
struct stat statBuf; /* Stat info for the tarball, letting |
struct stat statBuf; /* Stat info for the tarball, letting |
100 |
us know the inode and device that the |
* us know the inode and device that the |
101 |
tarball lives, so we can avoid trying |
* tarball lives, so we can avoid trying |
102 |
to include the tarball into itself */ |
* to include the tarball into itself */ |
103 |
int verboseFlag; /* Whether to print extra stuff or not */ |
int verboseFlag; /* Whether to print extra stuff or not */ |
104 |
const llist_t *excludeList; /* List of files to not include */ |
const llist_t *excludeList; /* List of files to not include */ |
105 |
HardLinkInfo *hlInfoHead; /* Hard Link Tracking Information */ |
HardLinkInfo *hlInfoHead; /* Hard Link Tracking Information */ |
106 |
HardLinkInfo *hlInfo; /* Hard Link Info for the current file */ |
HardLinkInfo *hlInfo; /* Hard Link Info for the current file */ |
107 |
}; |
}; |
108 |
|
|
109 |
/* A nice enum with all the possible tar file content types */ |
/* A nice enum with all the possible tar file content types */ |
123 |
typedef enum TarFileType TarFileType; |
typedef enum TarFileType TarFileType; |
124 |
|
|
125 |
/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */ |
/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */ |
126 |
static void addHardLinkInfo(HardLinkInfo ** hlInfoHeadPtr, |
static void addHardLinkInfo(HardLinkInfo **hlInfoHeadPtr, |
127 |
struct stat *statbuf, |
struct stat *statbuf, |
128 |
const char *fileName) |
const char *fileName) |
129 |
{ |
{ |
139 |
strcpy(hlInfo->name, fileName); |
strcpy(hlInfo->name, fileName); |
140 |
} |
} |
141 |
|
|
142 |
static void freeHardLinkInfo(HardLinkInfo ** hlInfoHeadPtr) |
static void freeHardLinkInfo(HardLinkInfo **hlInfoHeadPtr) |
143 |
{ |
{ |
144 |
HardLinkInfo *hlInfo; |
HardLinkInfo *hlInfo; |
145 |
HardLinkInfo *hlInfoNext; |
HardLinkInfo *hlInfoNext; |
153 |
} |
} |
154 |
*hlInfoHeadPtr = NULL; |
*hlInfoHeadPtr = NULL; |
155 |
} |
} |
|
return; |
|
156 |
} |
} |
157 |
|
|
158 |
/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */ |
/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */ |
159 |
static HardLinkInfo *findHardLinkInfo(HardLinkInfo * hlInfo, struct stat *statbuf) |
static HardLinkInfo *findHardLinkInfo(HardLinkInfo *hlInfo, struct stat *statbuf) |
160 |
{ |
{ |
161 |
while (hlInfo) { |
while (hlInfo) { |
162 |
if ((statbuf->st_ino == hlInfo->ino) && (statbuf->st_dev == hlInfo->dev)) |
if ((statbuf->st_ino == hlInfo->ino) && (statbuf->st_dev == hlInfo->dev)) |
191 |
|
|
192 |
static void chksum_and_xwrite(int fd, struct TarHeader* hp) |
static void chksum_and_xwrite(int fd, struct TarHeader* hp) |
193 |
{ |
{ |
194 |
|
/* POSIX says that checksum is done on unsigned bytes |
195 |
|
* (Sun and HP-UX gets it wrong... more details in |
196 |
|
* GNU tar source) */ |
197 |
const unsigned char *cp; |
const unsigned char *cp; |
198 |
int chksum, size; |
int chksum, size; |
199 |
|
|
294 |
tbInfo->hlInfo->name, 0); |
tbInfo->hlInfo->name, 0); |
295 |
#endif |
#endif |
296 |
} else if (S_ISLNK(statbuf->st_mode)) { |
} else if (S_ISLNK(statbuf->st_mode)) { |
297 |
char *lpath = xreadlink(fileName); |
char *lpath = xmalloc_readlink_or_warn(fileName); |
298 |
if (!lpath) /* Already printed err msg inside xreadlink() */ |
if (!lpath) |
299 |
return FALSE; |
return FALSE; |
300 |
header.typeflag = SYMTYPE; |
header.typeflag = SYMTYPE; |
301 |
strncpy(header.linkname, lpath, sizeof(header.linkname)); |
strncpy(header.linkname, lpath, sizeof(header.linkname)); |
396 |
#define exclude_file(excluded_files, file) 0 |
#define exclude_file(excluded_files, file) 0 |
397 |
#endif |
#endif |
398 |
|
|
399 |
static int writeFileToTarball(const char *fileName, struct stat *statbuf, |
static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statbuf, |
400 |
void *userData, int depth ATTRIBUTE_UNUSED) |
void *userData, int depth UNUSED_PARAM) |
401 |
{ |
{ |
402 |
struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData; |
struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData; |
403 |
const char *header_name; |
const char *header_name; |
404 |
int inputFileFd = -1; |
int inputFileFd = -1; |
405 |
|
|
406 |
|
/* Strip leading '/' (must be before memorizing hardlink's name) */ |
407 |
|
header_name = fileName; |
408 |
|
while (header_name[0] == '/') { |
409 |
|
static smallint warned; |
410 |
|
|
411 |
|
if (!warned) { |
412 |
|
bb_error_msg("removing leading '/' from member names"); |
413 |
|
warned = 1; |
414 |
|
} |
415 |
|
header_name++; |
416 |
|
} |
417 |
|
|
418 |
|
if (header_name[0] == '\0') |
419 |
|
return TRUE; |
420 |
|
|
421 |
|
/* It is against the rules to archive a socket */ |
422 |
|
if (S_ISSOCK(statbuf->st_mode)) { |
423 |
|
bb_error_msg("%s: socket ignored", fileName); |
424 |
|
return TRUE; |
425 |
|
} |
426 |
|
|
427 |
/* |
/* |
428 |
* Check to see if we are dealing with a hard link. |
* Check to see if we are dealing with a hard link. |
429 |
* If so - |
* If so - |
435 |
if (statbuf->st_nlink > 1) { |
if (statbuf->st_nlink > 1) { |
436 |
tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf); |
tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf); |
437 |
if (tbInfo->hlInfo == NULL) |
if (tbInfo->hlInfo == NULL) |
438 |
addHardLinkInfo(&tbInfo->hlInfoHead, statbuf, fileName); |
addHardLinkInfo(&tbInfo->hlInfoHead, statbuf, header_name); |
|
} |
|
|
|
|
|
/* It is against the rules to archive a socket */ |
|
|
if (S_ISSOCK(statbuf->st_mode)) { |
|
|
bb_error_msg("%s: socket ignored", fileName); |
|
|
return TRUE; |
|
439 |
} |
} |
440 |
|
|
441 |
/* It is a bad idea to store the archive we are in the process of creating, |
/* It is a bad idea to store the archive we are in the process of creating, |
442 |
* so check the device and inode to be sure that this particular file isn't |
* so check the device and inode to be sure that this particular file isn't |
443 |
* the new tarball */ |
* the new tarball */ |
444 |
if (tbInfo->statBuf.st_dev == statbuf->st_dev && |
if (tbInfo->statBuf.st_dev == statbuf->st_dev |
445 |
tbInfo->statBuf.st_ino == statbuf->st_ino) { |
&& tbInfo->statBuf.st_ino == statbuf->st_ino |
446 |
|
) { |
447 |
bb_error_msg("%s: file is the archive; skipping", fileName); |
bb_error_msg("%s: file is the archive; skipping", fileName); |
448 |
return TRUE; |
return TRUE; |
449 |
} |
} |
450 |
|
|
451 |
header_name = fileName; |
if (exclude_file(tbInfo->excludeList, header_name)) |
452 |
while (header_name[0] == '/') { |
return SKIP; |
|
static int alreadyWarned = FALSE; |
|
|
|
|
|
if (alreadyWarned == FALSE) { |
|
|
bb_error_msg("removing leading '/' from member names"); |
|
|
alreadyWarned = TRUE; |
|
|
} |
|
|
header_name++; |
|
|
} |
|
453 |
|
|
454 |
#if !ENABLE_FEATURE_TAR_GNU_EXTENSIONS |
#if !ENABLE_FEATURE_TAR_GNU_EXTENSIONS |
455 |
if (strlen(fileName) >= NAME_SIZE) { |
if (strlen(header_name) >= NAME_SIZE) { |
456 |
bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported"); |
bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported"); |
457 |
return TRUE; |
return TRUE; |
458 |
} |
} |
459 |
#endif |
#endif |
460 |
|
|
|
if (header_name[0] == '\0') |
|
|
return TRUE; |
|
|
|
|
|
if (exclude_file(tbInfo->excludeList, header_name)) |
|
|
return SKIP; |
|
|
|
|
461 |
/* Is this a regular file? */ |
/* Is this a regular file? */ |
462 |
if (tbInfo->hlInfo == NULL && S_ISREG(statbuf->st_mode)) { |
if (tbInfo->hlInfo == NULL && S_ISREG(statbuf->st_mode)) { |
463 |
/* open the file we want to archive, and make sure all is well */ |
/* open the file we want to archive, and make sure all is well */ |
464 |
inputFileFd = open(fileName, O_RDONLY); |
inputFileFd = open_or_warn(fileName, O_RDONLY); |
465 |
if (inputFileFd < 0) { |
if (inputFileFd < 0) { |
|
bb_perror_msg("%s: cannot open", fileName); |
|
466 |
return FALSE; |
return FALSE; |
467 |
} |
} |
468 |
} |
} |
475 |
/* If it was a regular file, write out the body */ |
/* If it was a regular file, write out the body */ |
476 |
if (inputFileFd >= 0) { |
if (inputFileFd >= 0) { |
477 |
size_t readSize; |
size_t readSize; |
478 |
/* Wwrite the file to the archive. */ |
/* Write the file to the archive. */ |
479 |
/* We record size into header first, */ |
/* We record size into header first, */ |
480 |
/* and then write out file. If file shrinks in between, */ |
/* and then write out file. If file shrinks in between, */ |
481 |
/* tar will be corrupted. So we don't allow for that. */ |
/* tar will be corrupted. So we don't allow for that. */ |
496 |
/* Pad the file up to the tar block size */ |
/* Pad the file up to the tar block size */ |
497 |
/* (a few tricks here in the name of code size) */ |
/* (a few tricks here in the name of code size) */ |
498 |
readSize = (-(int)statbuf->st_size) & (TAR_BLOCK_SIZE-1); |
readSize = (-(int)statbuf->st_size) & (TAR_BLOCK_SIZE-1); |
499 |
memset(bb_common_bufsiz1, 0, readSize); |
memset(block_buf, 0, readSize); |
500 |
xwrite(tbInfo->tarFd, bb_common_bufsiz1, readSize); |
xwrite(tbInfo->tarFd, block_buf, readSize); |
501 |
} |
} |
502 |
|
|
503 |
return TRUE; |
return TRUE; |
504 |
} |
} |
505 |
|
|
506 |
static int writeTarFile(const int tar_fd, const int verboseFlag, |
#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2 |
507 |
const unsigned long dereferenceFlag, const llist_t *include, |
#if !(ENABLE_FEATURE_SEAMLESS_GZ && ENABLE_FEATURE_SEAMLESS_BZ2) |
508 |
const llist_t *exclude, const int gzip) |
#define vfork_compressor(tar_fd, gzip) vfork_compressor(tar_fd) |
509 |
|
#endif |
510 |
|
/* Don't inline: vfork scares gcc and pessimizes code */ |
511 |
|
static void NOINLINE vfork_compressor(int tar_fd, int gzip) |
512 |
|
{ |
513 |
|
pid_t gzipPid; |
514 |
|
#if ENABLE_FEATURE_SEAMLESS_GZ && ENABLE_FEATURE_SEAMLESS_BZ2 |
515 |
|
const char *zip_exec = (gzip == 1) ? "gzip" : "bzip2"; |
516 |
|
#elif ENABLE_FEATURE_SEAMLESS_GZ |
517 |
|
const char *zip_exec = "gzip"; |
518 |
|
#else /* only ENABLE_FEATURE_SEAMLESS_BZ2 */ |
519 |
|
const char *zip_exec = "bzip2"; |
520 |
|
#endif |
521 |
|
// On Linux, vfork never unpauses parent early, although standard |
522 |
|
// allows for that. Do we want to waste bytes checking for it? |
523 |
|
#define WAIT_FOR_CHILD 0 |
524 |
|
volatile int vfork_exec_errno = 0; |
525 |
|
struct fd_pair gzipDataPipe; |
526 |
|
#if WAIT_FOR_CHILD |
527 |
|
struct fd_pair gzipStatusPipe; |
528 |
|
xpiped_pair(gzipStatusPipe); |
529 |
|
#endif |
530 |
|
xpiped_pair(gzipDataPipe); |
531 |
|
|
532 |
|
signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */ |
533 |
|
|
534 |
|
#if defined(__GNUC__) && __GNUC__ |
535 |
|
/* Avoid vfork clobbering */ |
536 |
|
(void) &zip_exec; |
537 |
|
#endif |
538 |
|
|
539 |
|
gzipPid = vfork(); |
540 |
|
if (gzipPid < 0) |
541 |
|
bb_perror_msg_and_die("vfork"); |
542 |
|
|
543 |
|
if (gzipPid == 0) { |
544 |
|
/* child */ |
545 |
|
/* NB: close _first_, then move fds! */ |
546 |
|
close(gzipDataPipe.wr); |
547 |
|
#if WAIT_FOR_CHILD |
548 |
|
close(gzipStatusPipe.rd); |
549 |
|
/* gzipStatusPipe.wr will close only on exec - |
550 |
|
* parent waits for this close to happen */ |
551 |
|
fcntl(gzipStatusPipe.wr, F_SETFD, FD_CLOEXEC); |
552 |
|
#endif |
553 |
|
xmove_fd(gzipDataPipe.rd, 0); |
554 |
|
xmove_fd(tar_fd, 1); |
555 |
|
/* exec gzip/bzip2 program/applet */ |
556 |
|
BB_EXECLP(zip_exec, zip_exec, "-f", NULL); |
557 |
|
vfork_exec_errno = errno; |
558 |
|
_exit(EXIT_FAILURE); |
559 |
|
} |
560 |
|
|
561 |
|
/* parent */ |
562 |
|
xmove_fd(gzipDataPipe.wr, tar_fd); |
563 |
|
close(gzipDataPipe.rd); |
564 |
|
#if WAIT_FOR_CHILD |
565 |
|
close(gzipStatusPipe.wr); |
566 |
|
while (1) { |
567 |
|
char buf; |
568 |
|
int n; |
569 |
|
|
570 |
|
/* Wait until child execs (or fails to) */ |
571 |
|
n = full_read(gzipStatusPipe.rd, &buf, 1); |
572 |
|
if (n < 0 /* && errno == EAGAIN */) |
573 |
|
continue; /* try it again */ |
574 |
|
} |
575 |
|
close(gzipStatusPipe.rd); |
576 |
|
#endif |
577 |
|
if (vfork_exec_errno) { |
578 |
|
errno = vfork_exec_errno; |
579 |
|
bb_perror_msg_and_die("cannot exec %s", zip_exec); |
580 |
|
} |
581 |
|
} |
582 |
|
#endif /* ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2 */ |
583 |
|
|
584 |
|
|
585 |
|
/* gcc 4.2.1 inlines it, making code bigger */ |
586 |
|
static NOINLINE int writeTarFile(int tar_fd, int verboseFlag, |
587 |
|
int dereferenceFlag, const llist_t *include, |
588 |
|
const llist_t *exclude, int gzip) |
589 |
{ |
{ |
|
pid_t gzipPid = 0; |
|
590 |
int errorFlag = FALSE; |
int errorFlag = FALSE; |
591 |
struct TarBallInfo tbInfo; |
struct TarBallInfo tbInfo; |
592 |
|
|
593 |
tbInfo.hlInfoHead = NULL; |
tbInfo.hlInfoHead = NULL; |
|
|
|
|
fchmod(tar_fd, 0644); |
|
594 |
tbInfo.tarFd = tar_fd; |
tbInfo.tarFd = tar_fd; |
595 |
tbInfo.verboseFlag = verboseFlag; |
tbInfo.verboseFlag = verboseFlag; |
596 |
|
|
599 |
if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0) |
if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0) |
600 |
bb_perror_msg_and_die("cannot stat tar file"); |
bb_perror_msg_and_die("cannot stat tar file"); |
601 |
|
|
602 |
if ((ENABLE_FEATURE_TAR_GZIP || ENABLE_FEATURE_TAR_BZIP2) && gzip) { |
#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2 |
603 |
int gzipDataPipe[2] = { -1, -1 }; |
if (gzip) |
604 |
int gzipStatusPipe[2] = { -1, -1 }; |
vfork_compressor(tbInfo.tarFd, gzip); |
|
volatile int vfork_exec_errno = 0; |
|
|
char *zip_exec = (gzip == 1) ? "gzip" : "bzip2"; |
|
|
|
|
|
if (pipe(gzipDataPipe) < 0 || pipe(gzipStatusPipe) < 0) |
|
|
bb_perror_msg_and_die("pipe"); |
|
|
|
|
|
signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */ |
|
|
|
|
|
#if defined(__GNUC__) && __GNUC__ |
|
|
/* Avoid vfork clobbering */ |
|
|
(void) &include; |
|
|
(void) &errorFlag; |
|
|
(void) &zip_exec; |
|
605 |
#endif |
#endif |
606 |
|
|
|
gzipPid = vfork(); |
|
|
|
|
|
if (gzipPid == 0) { |
|
|
dup2(gzipDataPipe[0], 0); |
|
|
close(gzipDataPipe[1]); |
|
|
|
|
|
dup2(tbInfo.tarFd, 1); |
|
|
|
|
|
close(gzipStatusPipe[0]); |
|
|
fcntl(gzipStatusPipe[1], F_SETFD, FD_CLOEXEC); /* close on exec shows success */ |
|
|
|
|
|
execlp(zip_exec, zip_exec, "-f", NULL); |
|
|
vfork_exec_errno = errno; |
|
|
|
|
|
close(gzipStatusPipe[1]); |
|
|
exit(-1); |
|
|
} else if (gzipPid > 0) { |
|
|
close(gzipDataPipe[0]); |
|
|
close(gzipStatusPipe[1]); |
|
|
|
|
|
while (1) { |
|
|
char buf; |
|
|
|
|
|
int n = full_read(gzipStatusPipe[0], &buf, 1); |
|
|
|
|
|
if (n == 0 && vfork_exec_errno != 0) { |
|
|
errno = vfork_exec_errno; |
|
|
bb_perror_msg_and_die("cannot exec %s", zip_exec); |
|
|
} else if ((n < 0) && (errno == EAGAIN || errno == EINTR)) |
|
|
continue; /* try it again */ |
|
|
break; |
|
|
} |
|
|
close(gzipStatusPipe[0]); |
|
|
|
|
|
tbInfo.tarFd = gzipDataPipe[1]; |
|
|
} else bb_perror_msg_and_die("vfork gzip"); |
|
|
} |
|
|
|
|
607 |
tbInfo.excludeList = exclude; |
tbInfo.excludeList = exclude; |
608 |
|
|
609 |
/* Read the directory/files and iterate over them one at a time */ |
/* Read the directory/files and iterate over them one at a time */ |
610 |
while (include) { |
while (include) { |
611 |
if (!recursive_action(include->data, TRUE, dereferenceFlag, |
if (!recursive_action(include->data, ACTION_RECURSE | |
612 |
FALSE, writeFileToTarball, writeFileToTarball, &tbInfo, 0)) |
(dereferenceFlag ? ACTION_FOLLOWLINKS : 0), |
613 |
|
writeFileToTarball, writeFileToTarball, &tbInfo, 0)) |
614 |
{ |
{ |
615 |
errorFlag = TRUE; |
errorFlag = TRUE; |
616 |
} |
} |
617 |
include = include->link; |
include = include->link; |
618 |
} |
} |
619 |
/* Write two empty blocks to the end of the archive */ |
/* Write two empty blocks to the end of the archive */ |
620 |
memset(bb_common_bufsiz1, 0, 2*TAR_BLOCK_SIZE); |
memset(block_buf, 0, 2*TAR_BLOCK_SIZE); |
621 |
xwrite(tbInfo.tarFd, bb_common_bufsiz1, 2*TAR_BLOCK_SIZE); |
xwrite(tbInfo.tarFd, block_buf, 2*TAR_BLOCK_SIZE); |
622 |
|
|
623 |
/* To be pedantically correct, we would check if the tarball |
/* To be pedantically correct, we would check if the tarball |
624 |
* is smaller than 20 tar blocks, and pad it if it was smaller, |
* is smaller than 20 tar blocks, and pad it if it was smaller, |
635 |
if (errorFlag) |
if (errorFlag) |
636 |
bb_error_msg("error exit delayed from previous errors"); |
bb_error_msg("error exit delayed from previous errors"); |
637 |
|
|
638 |
if (gzipPid) { |
#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2 |
639 |
|
if (gzip) { |
640 |
int status; |
int status; |
641 |
if (waitpid(gzipPid, &status, 0) == -1) |
if (safe_waitpid(-1, &status, 0) == -1) |
642 |
bb_perror_msg("waitpid"); |
bb_perror_msg("waitpid"); |
643 |
else if (!WIFEXITED(status) || WEXITSTATUS(status)) |
else if (!WIFEXITED(status) || WEXITSTATUS(status)) |
644 |
/* gzip was killed or has exited with nonzero! */ |
/* gzip was killed or has exited with nonzero! */ |
645 |
errorFlag = TRUE; |
errorFlag = TRUE; |
646 |
} |
} |
647 |
|
#endif |
648 |
return errorFlag; |
return errorFlag; |
649 |
} |
} |
650 |
#else |
#else |
651 |
int writeTarFile(const int tar_fd, const int verboseFlag, |
int writeTarFile(int tar_fd, int verboseFlag, |
652 |
const unsigned long dereferenceFlag, const llist_t *include, |
int dereferenceFlag, const llist_t *include, |
653 |
const llist_t *exclude, const int gzip); |
const llist_t *exclude, int gzip); |
654 |
#endif /* FEATURE_TAR_CREATE */ |
#endif /* FEATURE_TAR_CREATE */ |
655 |
|
|
656 |
#if ENABLE_FEATURE_TAR_FROM |
#if ENABLE_FEATURE_TAR_FROM |
657 |
static llist_t *append_file_list_to_list(llist_t *list) |
static llist_t *append_file_list_to_list(llist_t *list) |
658 |
{ |
{ |
659 |
FILE *src_stream; |
FILE *src_stream; |
|
llist_t *cur = list; |
|
|
llist_t *tmp; |
|
660 |
char *line; |
char *line; |
661 |
llist_t *newlist = NULL; |
llist_t *newlist = NULL; |
662 |
|
|
663 |
while (cur) { |
while (list) { |
664 |
src_stream = xfopen(cur->data, "r"); |
src_stream = xfopen_for_read(llist_pop(&list)); |
665 |
tmp = cur; |
while ((line = xmalloc_fgetline(src_stream)) != NULL) { |
|
cur = cur->link; |
|
|
free(tmp); |
|
|
while ((line = xmalloc_getline(src_stream)) != NULL) { |
|
666 |
/* kill trailing '/' unless the string is just "/" */ |
/* kill trailing '/' unless the string is just "/" */ |
667 |
char *cp = last_char_is(line, '/'); |
char *cp = last_char_is(line, '/'); |
668 |
if (cp > line) |
if (cp > line) |
677 |
#define append_file_list_to_list(x) 0 |
#define append_file_list_to_list(x) 0 |
678 |
#endif |
#endif |
679 |
|
|
680 |
#if ENABLE_FEATURE_TAR_COMPRESS |
#if ENABLE_FEATURE_SEAMLESS_Z |
681 |
static char get_header_tar_Z(archive_handle_t *archive_handle) |
static char FAST_FUNC get_header_tar_Z(archive_handle_t *archive_handle) |
682 |
{ |
{ |
683 |
/* Can't lseek over pipes */ |
/* Can't lseek over pipes */ |
684 |
archive_handle->seek = seek_by_read; |
archive_handle->seek = seek_by_read; |
690 |
bb_error_msg_and_die("invalid magic"); |
bb_error_msg_and_die("invalid magic"); |
691 |
} |
} |
692 |
|
|
693 |
archive_handle->src_fd = open_transformer(archive_handle->src_fd, uncompress); |
open_transformer(archive_handle->src_fd, unpack_Z_stream, "uncompress"); |
694 |
archive_handle->offset = 0; |
archive_handle->offset = 0; |
695 |
while (get_header_tar(archive_handle) == EXIT_SUCCESS) |
while (get_header_tar(archive_handle) == EXIT_SUCCESS) |
696 |
/* nothing */; |
continue; |
697 |
|
|
698 |
/* Can only do one file at a time */ |
/* Can only do one file at a time */ |
699 |
return EXIT_FAILURE; |
return EXIT_FAILURE; |
712 |
/* Actually, 'status' is a signo. We reuse it for other needs */ |
/* Actually, 'status' is a signo. We reuse it for other needs */ |
713 |
|
|
714 |
/* Wait for any child without blocking */ |
/* Wait for any child without blocking */ |
715 |
if (waitpid(-1, &status, WNOHANG) < 0) |
if (wait_any_nohang(&status) < 0) |
716 |
/* wait failed?! I'm confused... */ |
/* wait failed?! I'm confused... */ |
717 |
return; |
return; |
718 |
|
|
720 |
/* child exited with 0 */ |
/* child exited with 0 */ |
721 |
return; |
return; |
722 |
/* Cannot happen? |
/* Cannot happen? |
723 |
if(!WIFSIGNALED(status) && !WIFEXITED(status)) return; */ |
if (!WIFSIGNALED(status) && !WIFEXITED(status)) return; */ |
724 |
child_error = 1; |
child_error = 1; |
725 |
} |
} |
726 |
#endif |
#endif |
727 |
|
|
728 |
enum { |
enum { |
729 |
OPTBIT_KEEP_OLD = 7, |
OPTBIT_KEEP_OLD = 7, |
730 |
USE_FEATURE_TAR_CREATE( OPTBIT_CREATE ,) |
USE_FEATURE_TAR_CREATE( OPTBIT_CREATE ,) |
731 |
USE_FEATURE_TAR_CREATE( OPTBIT_DEREFERENCE ,) |
USE_FEATURE_TAR_CREATE( OPTBIT_DEREFERENCE ,) |
732 |
USE_FEATURE_TAR_BZIP2( OPTBIT_BZIP2 ,) |
USE_FEATURE_SEAMLESS_BZ2( OPTBIT_BZIP2 ,) |
733 |
USE_FEATURE_TAR_LZMA( OPTBIT_LZMA ,) |
USE_FEATURE_SEAMLESS_LZMA(OPTBIT_LZMA ,) |
734 |
USE_FEATURE_TAR_FROM( OPTBIT_INCLUDE_FROM,) |
USE_FEATURE_TAR_FROM( OPTBIT_INCLUDE_FROM,) |
735 |
USE_FEATURE_TAR_FROM( OPTBIT_EXCLUDE_FROM,) |
USE_FEATURE_TAR_FROM( OPTBIT_EXCLUDE_FROM,) |
736 |
USE_FEATURE_TAR_GZIP( OPTBIT_GZIP ,) |
USE_FEATURE_SEAMLESS_GZ( OPTBIT_GZIP ,) |
737 |
USE_FEATURE_TAR_COMPRESS(OPTBIT_COMPRESS ,) |
USE_FEATURE_SEAMLESS_Z( OPTBIT_COMPRESS ,) |
738 |
OPTBIT_NOPRESERVE_OWN, |
OPTBIT_NOPRESERVE_OWN, |
739 |
OPTBIT_NOPRESERVE_PERM, |
OPTBIT_NOPRESERVE_PERM, |
740 |
OPT_TEST = 1 << 0, // t |
OPT_TEST = 1 << 0, // t |
745 |
OPT_P = 1 << 5, // p |
OPT_P = 1 << 5, // p |
746 |
OPT_VERBOSE = 1 << 6, // v |
OPT_VERBOSE = 1 << 6, // v |
747 |
OPT_KEEP_OLD = 1 << 7, // k |
OPT_KEEP_OLD = 1 << 7, // k |
748 |
OPT_CREATE = USE_FEATURE_TAR_CREATE( (1<<OPTBIT_CREATE )) + 0, // c |
OPT_CREATE = USE_FEATURE_TAR_CREATE( (1 << OPTBIT_CREATE )) + 0, // c |
749 |
OPT_DEREFERENCE = USE_FEATURE_TAR_CREATE( (1<<OPTBIT_DEREFERENCE )) + 0, // h |
OPT_DEREFERENCE = USE_FEATURE_TAR_CREATE( (1 << OPTBIT_DEREFERENCE )) + 0, // h |
750 |
OPT_BZIP2 = USE_FEATURE_TAR_BZIP2( (1<<OPTBIT_BZIP2 )) + 0, // j |
OPT_BZIP2 = USE_FEATURE_SEAMLESS_BZ2( (1 << OPTBIT_BZIP2 )) + 0, // j |
751 |
OPT_LZMA = USE_FEATURE_TAR_LZMA( (1<<OPTBIT_LZMA )) + 0, // a |
OPT_LZMA = USE_FEATURE_SEAMLESS_LZMA((1 << OPTBIT_LZMA )) + 0, // a |
752 |
OPT_INCLUDE_FROM = USE_FEATURE_TAR_FROM( (1<<OPTBIT_INCLUDE_FROM)) + 0, // T |
OPT_INCLUDE_FROM = USE_FEATURE_TAR_FROM( (1 << OPTBIT_INCLUDE_FROM)) + 0, // T |
753 |
OPT_EXCLUDE_FROM = USE_FEATURE_TAR_FROM( (1<<OPTBIT_EXCLUDE_FROM)) + 0, // X |
OPT_EXCLUDE_FROM = USE_FEATURE_TAR_FROM( (1 << OPTBIT_EXCLUDE_FROM)) + 0, // X |
754 |
OPT_GZIP = USE_FEATURE_TAR_GZIP( (1<<OPTBIT_GZIP )) + 0, // z |
OPT_GZIP = USE_FEATURE_SEAMLESS_GZ( (1 << OPTBIT_GZIP )) + 0, // z |
755 |
OPT_COMPRESS = USE_FEATURE_TAR_COMPRESS((1<<OPTBIT_COMPRESS )) + 0, // Z |
OPT_COMPRESS = USE_FEATURE_SEAMLESS_Z( (1 << OPTBIT_COMPRESS )) + 0, // Z |
756 |
OPT_NOPRESERVE_OWN = 1 << OPTBIT_NOPRESERVE_OWN , // no-same-owner |
OPT_NOPRESERVE_OWN = 1 << OPTBIT_NOPRESERVE_OWN , // no-same-owner |
757 |
OPT_NOPRESERVE_PERM = 1 << OPTBIT_NOPRESERVE_PERM, // no-same-permissions |
OPT_NOPRESERVE_PERM = 1 << OPTBIT_NOPRESERVE_PERM, // no-same-permissions |
758 |
}; |
}; |
759 |
#if ENABLE_FEATURE_TAR_LONG_OPTIONS |
#if ENABLE_FEATURE_TAR_LONG_OPTIONS |
760 |
static const struct option tar_long_options[] = { |
static const char tar_longopts[] ALIGN1 = |
761 |
{ "list", 0, NULL, 't' }, |
"list\0" No_argument "t" |
762 |
{ "extract", 0, NULL, 'x' }, |
"extract\0" No_argument "x" |
763 |
{ "directory", 1, NULL, 'C' }, |
"directory\0" Required_argument "C" |
764 |
{ "file", 1, NULL, 'f' }, |
"file\0" Required_argument "f" |
765 |
{ "to-stdout", 0, NULL, 'O' }, |
"to-stdout\0" No_argument "O" |
766 |
{ "same-permissions", 0, NULL, 'p' }, |
"same-permissions\0" No_argument "p" |
767 |
{ "verbose", 0, NULL, 'v' }, |
"verbose\0" No_argument "v" |
768 |
{ "keep-old", 0, NULL, 'k' }, |
"keep-old\0" No_argument "k" |
769 |
# if ENABLE_FEATURE_TAR_CREATE |
# if ENABLE_FEATURE_TAR_CREATE |
770 |
{ "create", 0, NULL, 'c' }, |
"create\0" No_argument "c" |
771 |
{ "dereference", 0, NULL, 'h' }, |
"dereference\0" No_argument "h" |
772 |
# endif |
# endif |
773 |
# if ENABLE_FEATURE_TAR_BZIP2 |
# if ENABLE_FEATURE_SEAMLESS_BZ2 |
774 |
{ "bzip2", 0, NULL, 'j' }, |
"bzip2\0" No_argument "j" |
775 |
# endif |
# endif |
776 |
# if ENABLE_FEATURE_TAR_LZMA |
# if ENABLE_FEATURE_SEAMLESS_LZMA |
777 |
{ "lzma", 0, NULL, 'a' }, |
"lzma\0" No_argument "a" |
778 |
# endif |
# endif |
779 |
# if ENABLE_FEATURE_TAR_FROM |
# if ENABLE_FEATURE_TAR_FROM |
780 |
{ "files-from", 1, NULL, 'T' }, |
"files-from\0" Required_argument "T" |
781 |
{ "exclude-from", 1, NULL, 'X' }, |
"exclude-from\0" Required_argument "X" |
782 |
# endif |
# endif |
783 |
# if ENABLE_FEATURE_TAR_GZIP |
# if ENABLE_FEATURE_SEAMLESS_GZ |
784 |
{ "gzip", 0, NULL, 'z' }, |
"gzip\0" No_argument "z" |
785 |
# endif |
# endif |
786 |
# if ENABLE_FEATURE_TAR_COMPRESS |
# if ENABLE_FEATURE_SEAMLESS_Z |
787 |
{ "compress", 0, NULL, 'Z' }, |
"compress\0" No_argument "Z" |
788 |
# endif |
# endif |
789 |
{ "no-same-owner", 0, NULL, 0xfd }, |
"no-same-owner\0" No_argument "\xfd" |
790 |
{ "no-same-permissions",0, NULL, 0xfe }, |
"no-same-permissions\0" No_argument "\xfe" |
791 |
/* --exclude takes next bit position in option mask, */ |
/* --exclude takes next bit position in option mask, */ |
792 |
/* therefore we have to either put it _after_ --no-same-perm */ |
/* therefore we have to either put it _after_ --no-same-perm */ |
793 |
/* or add OPT[BIT]_EXCLUDE before OPT[BIT]_NOPRESERVE_OWN */ |
/* or add OPT[BIT]_EXCLUDE before OPT[BIT]_NOPRESERVE_OWN */ |
794 |
# if ENABLE_FEATURE_TAR_FROM |
# if ENABLE_FEATURE_TAR_FROM |
795 |
{ "exclude", 1, NULL, 0xff }, |
"exclude\0" Required_argument "\xff" |
796 |
# endif |
# endif |
797 |
{ 0, 0, 0, 0 } |
; |
|
}; |
|
798 |
#endif |
#endif |
799 |
|
|
800 |
int tar_main(int argc, char **argv) |
int tar_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
801 |
|
int tar_main(int argc UNUSED_PARAM, char **argv) |
802 |
{ |
{ |
803 |
char (*get_header_ptr)(archive_handle_t *) = get_header_tar; |
char FAST_FUNC (*get_header_ptr)(archive_handle_t *) = get_header_tar; |
804 |
archive_handle_t *tar_handle; |
archive_handle_t *tar_handle; |
805 |
char *base_dir = NULL; |
char *base_dir = NULL; |
806 |
const char *tar_filename = "-"; |
const char *tar_filename = "-"; |
812 |
|
|
813 |
/* Initialise default values */ |
/* Initialise default values */ |
814 |
tar_handle = init_handle(); |
tar_handle = init_handle(); |
815 |
tar_handle->flags = ARCHIVE_CREATE_LEADING_DIRS |
tar_handle->ah_flags = ARCHIVE_CREATE_LEADING_DIRS |
816 |
| ARCHIVE_PRESERVE_DATE |
| ARCHIVE_PRESERVE_DATE |
817 |
| ARCHIVE_EXTRACT_UNCONDITIONAL; |
| ARCHIVE_EXTRACT_UNCONDITIONAL; |
818 |
|
|
819 |
|
/* Apparently only root's tar preserves perms (see bug 3844) */ |
820 |
|
if (getuid() != 0) |
821 |
|
tar_handle->ah_flags |= ARCHIVE_NOPRESERVE_PERM; |
822 |
|
|
823 |
/* Prepend '-' to the first argument if required */ |
/* Prepend '-' to the first argument if required */ |
824 |
opt_complementary = "--:" // first arg is options |
opt_complementary = "--:" // first arg is options |
832 |
USE_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive |
USE_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive |
833 |
SKIP_FEATURE_TAR_CREATE("t--x:x--t"); // mutually exclusive |
SKIP_FEATURE_TAR_CREATE("t--x:x--t"); // mutually exclusive |
834 |
#if ENABLE_FEATURE_TAR_LONG_OPTIONS |
#if ENABLE_FEATURE_TAR_LONG_OPTIONS |
835 |
applet_long_options = tar_long_options; |
applet_long_options = tar_longopts; |
836 |
#endif |
#endif |
837 |
opt = getopt32(argc, argv, |
opt = getopt32(argv, |
838 |
"txC:f:Opvk" |
"txC:f:Opvk" |
839 |
USE_FEATURE_TAR_CREATE( "ch" ) |
USE_FEATURE_TAR_CREATE( "ch" ) |
840 |
USE_FEATURE_TAR_BZIP2( "j" ) |
USE_FEATURE_SEAMLESS_BZ2( "j" ) |
841 |
USE_FEATURE_TAR_LZMA( "a" ) |
USE_FEATURE_SEAMLESS_LZMA("a" ) |
842 |
USE_FEATURE_TAR_FROM( "T:X:") |
USE_FEATURE_TAR_FROM( "T:X:") |
843 |
USE_FEATURE_TAR_GZIP( "z" ) |
USE_FEATURE_SEAMLESS_GZ( "z" ) |
844 |
USE_FEATURE_TAR_COMPRESS("Z" ) |
USE_FEATURE_SEAMLESS_Z( "Z" ) |
845 |
, &base_dir // -C dir |
, &base_dir // -C dir |
846 |
, &tar_filename // -f filename |
, &tar_filename // -f filename |
847 |
USE_FEATURE_TAR_FROM(, &(tar_handle->accept)) // T |
USE_FEATURE_TAR_FROM(, &(tar_handle->accept)) // T |
852 |
, &verboseFlag // combined count for -t and -v |
, &verboseFlag // combined count for -t and -v |
853 |
, &verboseFlag // combined count for -t and -v |
, &verboseFlag // combined count for -t and -v |
854 |
); |
); |
855 |
|
argv += optind; |
856 |
|
|
857 |
if (verboseFlag) tar_handle->action_header = header_verbose_list; |
if (verboseFlag) tar_handle->action_header = header_verbose_list; |
858 |
if (verboseFlag == 1) tar_handle->action_header = header_list; |
if (verboseFlag == 1) tar_handle->action_header = header_list; |
864 |
tar_handle->action_data = data_extract_to_stdout; |
tar_handle->action_data = data_extract_to_stdout; |
865 |
|
|
866 |
if (opt & OPT_KEEP_OLD) |
if (opt & OPT_KEEP_OLD) |
867 |
tar_handle->flags &= ~ARCHIVE_EXTRACT_UNCONDITIONAL; |
tar_handle->ah_flags &= ~ARCHIVE_EXTRACT_UNCONDITIONAL; |
868 |
|
|
869 |
if (opt & OPT_NOPRESERVE_OWN) |
if (opt & OPT_NOPRESERVE_OWN) |
870 |
tar_handle->flags |= ARCHIVE_NOPRESERVE_OWN; |
tar_handle->ah_flags |= ARCHIVE_NOPRESERVE_OWN; |
871 |
|
|
872 |
if (opt & OPT_NOPRESERVE_PERM) |
if (opt & OPT_NOPRESERVE_PERM) |
873 |
tar_handle->flags |= ARCHIVE_NOPRESERVE_PERM; |
tar_handle->ah_flags |= ARCHIVE_NOPRESERVE_PERM; |
874 |
|
|
875 |
if (opt & OPT_GZIP) |
if (opt & OPT_GZIP) |
876 |
get_header_ptr = get_header_tar_gz; |
get_header_ptr = get_header_tar_gz; |
898 |
tar_handle->accept = append_file_list_to_list(tar_handle->accept); |
tar_handle->accept = append_file_list_to_list(tar_handle->accept); |
899 |
#endif |
#endif |
900 |
|
|
|
/* Check if we are reading from stdin */ |
|
|
if (argv[optind] && *argv[optind] == '-') { |
|
|
/* Default is to read from stdin, so just skip to next arg */ |
|
|
optind++; |
|
|
} |
|
|
|
|
901 |
/* Setup an array of filenames to work with */ |
/* Setup an array of filenames to work with */ |
902 |
/* TODO: This is the same as in ar, separate function ? */ |
/* TODO: This is the same as in ar, separate function ? */ |
903 |
while (optind < argc) { |
while (*argv) { |
904 |
/* kill trailing '/' unless the string is just "/" */ |
/* kill trailing '/' unless the string is just "/" */ |
905 |
char *cp = last_char_is(argv[optind], '/'); |
char *cp = last_char_is(*argv, '/'); |
906 |
if (cp > argv[optind]) |
if (cp > *argv) |
907 |
*cp = '\0'; |
*cp = '\0'; |
908 |
llist_add_to(&tar_handle->accept, argv[optind]); |
llist_add_to_end(&tar_handle->accept, *argv); |
909 |
optind++; |
argv++; |
910 |
} |
} |
|
tar_handle->accept = rev_llist(tar_handle->accept); |
|
911 |
|
|
912 |
if (tar_handle->accept || tar_handle->reject) |
if (tar_handle->accept || tar_handle->reject) |
913 |
tar_handle->filter = filter_accept_reject_list; |
tar_handle->filter = filter_accept_reject_list; |
924 |
|
|
925 |
tar_stream = stdout; |
tar_stream = stdout; |
926 |
/* Mimicking GNU tar 1.15.1: */ |
/* Mimicking GNU tar 1.15.1: */ |
927 |
flags = O_WRONLY|O_CREAT|O_TRUNC; |
flags = O_WRONLY | O_CREAT | O_TRUNC; |
|
/* was doing unlink; open(O_WRONLY|O_CREAT|O_EXCL); why? */ |
|
928 |
} else { |
} else { |
929 |
tar_stream = stdin; |
tar_stream = stdin; |
930 |
flags = O_RDONLY; |
flags = O_RDONLY; |
934 |
tar_handle->src_fd = fileno(tar_stream); |
tar_handle->src_fd = fileno(tar_stream); |
935 |
tar_handle->seek = seek_by_read; |
tar_handle->seek = seek_by_read; |
936 |
} else { |
} else { |
937 |
tar_handle->src_fd = xopen(tar_filename, flags); |
if (ENABLE_FEATURE_TAR_AUTODETECT |
938 |
|
&& get_header_ptr == get_header_tar |
939 |
|
&& flags == O_RDONLY |
940 |
|
) { |
941 |
|
tar_handle->src_fd = open_zipped(tar_filename); |
942 |
|
if (tar_handle->src_fd < 0) |
943 |
|
bb_perror_msg_and_die("can't open '%s'", tar_filename); |
944 |
|
} else { |
945 |
|
tar_handle->src_fd = xopen(tar_filename, flags); |
946 |
|
} |
947 |
} |
} |
948 |
} |
} |
949 |
|
|
957 |
|
|
958 |
/* create an archive */ |
/* create an archive */ |
959 |
if (opt & OPT_CREATE) { |
if (opt & OPT_CREATE) { |
960 |
|
#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2 |
961 |
int zipMode = 0; |
int zipMode = 0; |
962 |
if (ENABLE_FEATURE_TAR_GZIP && get_header_ptr == get_header_tar_gz) |
if (ENABLE_FEATURE_SEAMLESS_GZ && (opt & OPT_GZIP)) |
963 |
zipMode = 1; |
zipMode = 1; |
964 |
if (ENABLE_FEATURE_TAR_BZIP2 && get_header_ptr == get_header_tar_bz2) |
if (ENABLE_FEATURE_SEAMLESS_BZ2 && (opt & OPT_BZIP2)) |
965 |
zipMode = 2; |
zipMode = 2; |
966 |
|
#endif |
967 |
/* NB: writeTarFile() closes tar_handle->src_fd */ |
/* NB: writeTarFile() closes tar_handle->src_fd */ |
968 |
return writeTarFile(tar_handle->src_fd, verboseFlag, opt & OPT_DEREFERENCE, |
return writeTarFile(tar_handle->src_fd, verboseFlag, opt & OPT_DEREFERENCE, |
969 |
tar_handle->accept, |
tar_handle->accept, |
971 |
} |
} |
972 |
|
|
973 |
while (get_header_ptr(tar_handle) == EXIT_SUCCESS) |
while (get_header_ptr(tar_handle) == EXIT_SUCCESS) |
974 |
/* nothing */; |
continue; |
975 |
|
|
976 |
/* Check that every file that should have been extracted was */ |
/* Check that every file that should have been extracted was */ |
977 |
while (tar_handle->accept) { |
while (tar_handle->accept) { |