* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
-#include "busybox.h"
-#include "unarchive.h"
#include <fnmatch.h>
#include <getopt.h>
+#include "libbb.h"
+#include "unarchive.h"
+
+#define block_buf bb_common_bufsiz1
#if ENABLE_FEATURE_TAR_CREATE
/* POSIX tar Header Block, from POSIX 1003.1-1990 */
#define NAME_SIZE 100
#define NAME_SIZE_STR "100"
+typedef struct TarHeader TarHeader;
struct TarHeader { /* byte offset */
char name[NAME_SIZE]; /* 0-99 */
char mode[8]; /* 100-107 */
char prefix[155]; /* 345-499 */
char padding[12]; /* 500-512 (pad to exactly the TAR_BLOCK_SIZE) */
};
-typedef struct TarHeader TarHeader;
/*
** writeTarFile(), writeFileToTarball(), and writeTarHeader() are
};
/* Some info to be carried along when creating a new tarball */
+typedef struct TarBallInfo TarBallInfo;
struct TarBallInfo {
int tarFd; /* Open-for-write file descriptor
for the tarball */
HardLinkInfo *hlInfoHead; /* Hard Link Tracking Information */
HardLinkInfo *hlInfo; /* Hard Link Info for the current file */
};
-typedef struct TarBallInfo TarBallInfo;
/* A nice enum with all the possible tar file content types */
enum TarFileType {
}
*hlInfoHeadPtr = NULL;
}
- return;
}
/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
static void chksum_and_xwrite(int fd, struct TarHeader* hp)
{
+ /* POSIX says that checksum is done on unsigned bytes
+ * (Sun and HP-UX gets it wrong... more details in
+ * GNU tar source) */
const unsigned char *cp;
int chksum, size;
tbInfo->hlInfo->name, 0);
#endif
} else if (S_ISLNK(statbuf->st_mode)) {
- char *lpath = xreadlink(fileName);
- if (!lpath) /* Already printed err msg inside xreadlink() */
+ char *lpath = xmalloc_readlink_or_warn(fileName);
+ if (!lpath)
return FALSE;
header.typeflag = SYMTYPE;
strncpy(header.linkname, lpath, sizeof(header.linkname));
return TRUE;
}
-# if ENABLE_FEATURE_TAR_FROM
+#if ENABLE_FEATURE_TAR_FROM
static int exclude_file(const llist_t *excluded_files, const char *file)
{
while (excluded_files) {
return 0;
}
-# else
+#else
#define exclude_file(excluded_files, file) 0
-# endif
+#endif
static int writeFileToTarball(const char *fileName, struct stat *statbuf,
- void *userData, int depth)
+ void *userData, int depth ATTRIBUTE_UNUSED)
{
struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData;
const char *header_name;
header_name = fileName;
while (header_name[0] == '/') {
- static int alreadyWarned = FALSE;
+ static smallint warned;
- if (alreadyWarned == FALSE) {
+ if (!warned) {
bb_error_msg("removing leading '/' from member names");
- alreadyWarned = TRUE;
+ warned = 1;
}
header_name++;
}
/* Is this a regular file? */
if (tbInfo->hlInfo == NULL && S_ISREG(statbuf->st_mode)) {
/* open the file we want to archive, and make sure all is well */
- inputFileFd = open(fileName, O_RDONLY);
+ inputFileFd = open_or_warn(fileName, O_RDONLY);
if (inputFileFd < 0) {
- bb_perror_msg("%s: cannot open", fileName);
return FALSE;
}
}
/* If it was a regular file, write out the body */
if (inputFileFd >= 0) {
size_t readSize;
- /* Wwrite the file to the archive. */
+ /* Write the file to the archive. */
/* We record size into header first, */
/* and then write out file. If file shrinks in between, */
/* tar will be corrupted. So we don't allow for that. */
/* Pad the file up to the tar block size */
/* (a few tricks here in the name of code size) */
readSize = (-(int)statbuf->st_size) & (TAR_BLOCK_SIZE-1);
- memset(bb_common_bufsiz1, 0, readSize);
- xwrite(tbInfo->tarFd, bb_common_bufsiz1, readSize);
+ memset(block_buf, 0, readSize);
+ xwrite(tbInfo->tarFd, block_buf, readSize);
}
return TRUE;
int gzipDataPipe[2] = { -1, -1 };
int gzipStatusPipe[2] = { -1, -1 };
volatile int vfork_exec_errno = 0;
- char *zip_exec = (gzip == 1) ? "gzip" : "bzip2";
+ const char *zip_exec = (gzip == 1) ? "gzip" : "bzip2";
- if (pipe(gzipDataPipe) < 0 || pipe(gzipStatusPipe) < 0)
- bb_perror_msg_and_die("pipe");
+ xpipe(gzipDataPipe);
+ xpipe(gzipStatusPipe);
- signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */
+ signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */
-# if __GNUC__
+#if defined(__GNUC__) && __GNUC__
/* Avoid vfork clobbering */
(void) &include;
(void) &errorFlag;
(void) &zip_exec;
-# endif
+#endif
gzipPid = vfork();
close(gzipStatusPipe[0]);
fcntl(gzipStatusPipe[1], F_SETFD, FD_CLOEXEC); /* close on exec shows success */
- execlp(zip_exec, zip_exec, "-f", NULL);
+ BB_EXECLP(zip_exec, zip_exec, "-f", NULL);
vfork_exec_errno = errno;
close(gzipStatusPipe[1]);
/* Read the directory/files and iterate over them one at a time */
while (include) {
- if (!recursive_action(include->data, TRUE, dereferenceFlag,
- FALSE, writeFileToTarball, writeFileToTarball, &tbInfo, 0))
+ if (!recursive_action(include->data, ACTION_RECURSE |
+ (dereferenceFlag ? ACTION_FOLLOWLINKS : 0),
+ writeFileToTarball, writeFileToTarball, &tbInfo, 0))
{
errorFlag = TRUE;
}
include = include->link;
}
/* Write two empty blocks to the end of the archive */
- memset(bb_common_bufsiz1, 0, 2*TAR_BLOCK_SIZE);
- xwrite(tbInfo.tarFd, bb_common_bufsiz1, 2*TAR_BLOCK_SIZE);
+ memset(block_buf, 0, 2*TAR_BLOCK_SIZE);
+ xwrite(tbInfo.tarFd, block_buf, 2*TAR_BLOCK_SIZE);
/* To be pedantically correct, we would check if the tarball
* is smaller than 20 tar blocks, and pad it if it was smaller,
int writeTarFile(const int tar_fd, const int verboseFlag,
const unsigned long dereferenceFlag, const llist_t *include,
const llist_t *exclude, const int gzip);
-#endif /* tar_create */
+#endif /* FEATURE_TAR_CREATE */
#if ENABLE_FEATURE_TAR_FROM
static llist_t *append_file_list_to_list(llist_t *list)
return newlist;
}
#else
-#define append_file_list_to_list(x) 0
+#define append_file_list_to_list(x) 0
#endif
#if ENABLE_FEATURE_TAR_COMPRESS
return EXIT_FAILURE;
}
#else
-#define get_header_tar_Z 0
+#define get_header_tar_Z NULL
#endif
#ifdef CHECK_FOR_CHILD_EXITCODE
if (WIFEXITED(status) && WEXITSTATUS(status)==0)
/* child exited with 0 */
return;
- /* Cannot happen?
- if(!WIFSIGNALED(status) && !WIFEXITED(status)) return; */
+ /* Cannot happen?
+ if (!WIFSIGNALED(status) && !WIFEXITED(status)) return; */
child_error = 1;
}
#endif
};
#endif
+int tar_main(int argc, char **argv);
int tar_main(int argc, char **argv)
{
char (*get_header_ptr)(archive_handle_t *) = get_header_tar;
const char *tar_filename = "-";
unsigned opt;
int verboseFlag = 0;
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
llist_t *excludes = NULL;
+#endif
/* Initialise default values */
tar_handle = init_handle();
"tt:vv:" // count -t,-v
"?:" // bail out with usage instead of error return
"X::T::" // cumulative lists
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
"\xff::" // cumulative lists for --exclude
+#endif
USE_FEATURE_TAR_CREATE("c:") "t:x:" // at least one of these is reqd
USE_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive
SKIP_FEATURE_TAR_CREATE("t--x:x--t"); // mutually exclusive
USE_FEATURE_TAR_FROM( "T:X:")
USE_FEATURE_TAR_GZIP( "z" )
USE_FEATURE_TAR_COMPRESS("Z" )
- ,
- &base_dir, // -C dir
- &tar_filename, // -f filename
- USE_FEATURE_TAR_FROM(&(tar_handle->accept),) // T
- USE_FEATURE_TAR_FROM(&(tar_handle->reject),) // X
- USE_FEATURE_TAR_FROM(&excludes ,) // --exclude
- &verboseFlag, // combined count for -t and -v
- &verboseFlag // combined count for -t and -v
+ , &base_dir // -C dir
+ , &tar_filename // -f filename
+ USE_FEATURE_TAR_FROM(, &(tar_handle->accept)) // T
+ USE_FEATURE_TAR_FROM(, &(tar_handle->reject)) // X
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
+ , &excludes // --exclude
+#endif
+ , &verboseFlag // combined count for -t and -v
+ , &verboseFlag // combined count for -t and -v
);
if (verboseFlag) tar_handle->action_header = header_verbose_list;
if (opt & OPT_COMPRESS)
get_header_ptr = get_header_tar_Z;
- if (ENABLE_FEATURE_TAR_FROM) {
- tar_handle->reject = append_file_list_to_list(tar_handle->reject);
- /* Append excludes to reject */
- while (excludes) {
- llist_t *temp = excludes->link;
- excludes->link = tar_handle->reject;
- tar_handle->reject = excludes;
- excludes = temp;
- }
- tar_handle->accept = append_file_list_to_list(tar_handle->accept);
+#if ENABLE_FEATURE_TAR_FROM
+ tar_handle->reject = append_file_list_to_list(tar_handle->reject);
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS
+ /* Append excludes to reject */
+ while (excludes) {
+ llist_t *next = excludes->link;
+ excludes->link = tar_handle->reject;
+ tar_handle->reject = excludes;
+ excludes = next;
}
+#endif
+ tar_handle->accept = append_file_list_to_list(tar_handle->accept);
+#endif
/* Check if we are reading from stdin */
if (argv[optind] && *argv[optind] == '-') {
char *cp = last_char_is(argv[optind], '/');
if (cp > argv[optind])
*cp = '\0';
- llist_add_to(&tar_handle->accept, argv[optind]);
+ llist_add_to_end(&tar_handle->accept, argv[optind]);
optind++;
}
- tar_handle->accept = rev_llist(tar_handle->accept);
if (tar_handle->accept || tar_handle->reject)
tar_handle->filter = filter_accept_reject_list;