tar: code shrink, better help text

function                                             old     new   delta
tar_main                                             994    1013     +19
packed_usage                                       31893   31863     -30
writeTarFile                                         250     207     -43
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/2 up/down: 19/-73)            Total: -54 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2017-11-13 01:40:28 +01:00
parent 02e93b3a28
commit 931cf64ae7

View File

@ -49,7 +49,7 @@
//config: tarballs. Currently it works only on files (not pipes etc). //config: tarballs. Currently it works only on files (not pipes etc).
//config: //config:
//config:config FEATURE_TAR_FROM //config:config FEATURE_TAR_FROM
//config: bool "Enable -X (exclude from) and -T (include from) options)" //config: bool "Enable -X (exclude from) and -T (include from) options"
//config: default y //config: default y
//config: depends on TAR //config: depends on TAR
//config: help //config: help
@ -156,7 +156,9 @@ typedef struct TarBallInfo {
int tarFd; /* Open-for-write file descriptor int tarFd; /* Open-for-write file descriptor
* for the tarball */ * for the tarball */
int verboseFlag; /* Whether to print extra stuff or not */ int verboseFlag; /* Whether to print extra stuff or not */
# if ENABLE_FEATURE_TAR_FROM
const llist_t *excludeList; /* List of files to not include */ const llist_t *excludeList; /* List of files to not include */
# endif
HardLinkInfo *hlInfoHead; /* Hard Link Tracking Information */ HardLinkInfo *hlInfoHead; /* Hard Link Tracking Information */
HardLinkInfo *hlInfo; /* Hard Link Info for the current file */ HardLinkInfo *hlInfo; /* Hard Link Info for the current file */
//TODO: save only st_dev + st_ino //TODO: save only st_dev + st_ino
@ -278,7 +280,7 @@ static void chksum_and_xwrite(int fd, struct tar_header_t* hp)
xwrite(fd, hp, sizeof(*hp)); xwrite(fd, hp, sizeof(*hp));
} }
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS # if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
static void writeLongname(int fd, int type, const char *name, int dir) static void writeLongname(int fd, int type, const char *name, int dir)
{ {
static const struct { static const struct {
@ -318,7 +320,7 @@ static void writeLongname(int fd, int type, const char *name, int dir)
memset(&header, 0, size); memset(&header, 0, size);
xwrite(fd, &header, size); xwrite(fd, &header, size);
} }
#endif # endif
/* Write out a tar header for the specified file/directory/whatever */ /* Write out a tar header for the specified file/directory/whatever */
static int writeTarHeader(struct TarBallInfo *tbInfo, static int writeTarHeader(struct TarBallInfo *tbInfo,
@ -347,30 +349,30 @@ static int writeTarHeader(struct TarBallInfo *tbInfo,
header.typeflag = LNKTYPE; header.typeflag = LNKTYPE;
strncpy(header.linkname, tbInfo->hlInfo->name, strncpy(header.linkname, tbInfo->hlInfo->name,
sizeof(header.linkname)); sizeof(header.linkname));
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS # if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
/* Write out long linkname if needed */ /* Write out long linkname if needed */
if (header.linkname[sizeof(header.linkname)-1]) if (header.linkname[sizeof(header.linkname)-1])
writeLongname(tbInfo->tarFd, GNULONGLINK, writeLongname(tbInfo->tarFd, GNULONGLINK,
tbInfo->hlInfo->name, 0); tbInfo->hlInfo->name, 0);
#endif # endif
} else if (S_ISLNK(statbuf->st_mode)) { } else if (S_ISLNK(statbuf->st_mode)) {
char *lpath = xmalloc_readlink_or_warn(fileName); char *lpath = xmalloc_readlink_or_warn(fileName);
if (!lpath) if (!lpath)
return FALSE; return FALSE;
header.typeflag = SYMTYPE; header.typeflag = SYMTYPE;
strncpy(header.linkname, lpath, sizeof(header.linkname)); strncpy(header.linkname, lpath, sizeof(header.linkname));
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS # if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
/* Write out long linkname if needed */ /* Write out long linkname if needed */
if (header.linkname[sizeof(header.linkname)-1]) if (header.linkname[sizeof(header.linkname)-1])
writeLongname(tbInfo->tarFd, GNULONGLINK, lpath, 0); writeLongname(tbInfo->tarFd, GNULONGLINK, lpath, 0);
#else # else
/* If it is larger than 100 bytes, bail out */ /* If it is larger than 100 bytes, bail out */
if (header.linkname[sizeof(header.linkname)-1]) { if (header.linkname[sizeof(header.linkname)-1]) {
free(lpath); free(lpath);
bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported"); bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported");
return FALSE; return FALSE;
} }
#endif # endif
free(lpath); free(lpath);
} else if (S_ISDIR(statbuf->st_mode)) { } else if (S_ISDIR(statbuf->st_mode)) {
header.typeflag = DIRTYPE; header.typeflag = DIRTYPE;
@ -400,9 +402,9 @@ static int writeTarHeader(struct TarBallInfo *tbInfo,
* It always does unless off_t is wider than 64 bits. * It always does unless off_t is wider than 64 bits.
*/ */
else if (ENABLE_FEATURE_TAR_GNU_EXTENSIONS else if (ENABLE_FEATURE_TAR_GNU_EXTENSIONS
#if ULLONG_MAX > 0xffffffffffffffffLL /* 2^64-1 */ # if ULLONG_MAX > 0xffffffffffffffffLL /* 2^64-1 */
&& (filesize <= 0x3fffffffffffffffffffffffLL) && (filesize <= 0x3fffffffffffffffffffffffLL)
#endif # endif
) { ) {
/* GNU tar uses "base-256 encoding" for very large numbers. /* GNU tar uses "base-256 encoding" for very large numbers.
* Encoding is binary, with highest bit always set as a marker * Encoding is binary, with highest bit always set as a marker
@ -429,13 +431,13 @@ static int writeTarHeader(struct TarBallInfo *tbInfo,
return FALSE; return FALSE;
} }
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS # if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
/* Write out long name if needed */ /* Write out long name if needed */
/* (we, like GNU tar, output long linkname *before* long name) */ /* (we, like GNU tar, output long linkname *before* long name) */
if (header.name[sizeof(header.name)-1]) if (header.name[sizeof(header.name)-1])
writeLongname(tbInfo->tarFd, GNULONGNAME, writeLongname(tbInfo->tarFd, GNULONGNAME,
header_name, S_ISDIR(statbuf->st_mode)); header_name, S_ISDIR(statbuf->st_mode));
#endif # endif
/* Now write the header out to disk */ /* Now write the header out to disk */
chksum_and_xwrite(tbInfo->tarFd, &header); chksum_and_xwrite(tbInfo->tarFd, &header);
@ -457,7 +459,7 @@ static int writeTarHeader(struct TarBallInfo *tbInfo,
return TRUE; return TRUE;
} }
#if ENABLE_FEATURE_TAR_FROM # if ENABLE_FEATURE_TAR_FROM
static int exclude_file(const llist_t *excluded_files, const char *file) static int exclude_file(const llist_t *excluded_files, const char *file)
{ {
while (excluded_files) { while (excluded_files) {
@ -483,9 +485,9 @@ static int exclude_file(const llist_t *excluded_files, const char *file)
return 0; return 0;
} }
#else # else
# define exclude_file(excluded_files, file) 0 # define exclude_file(excluded_files, file) 0
#endif # endif
static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statbuf, static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statbuf,
void *userData, int depth UNUSED_PARAM) void *userData, int depth UNUSED_PARAM)
@ -538,12 +540,12 @@ static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statb
if (exclude_file(tbInfo->excludeList, header_name)) if (exclude_file(tbInfo->excludeList, header_name))
return SKIP; return SKIP;
#if !ENABLE_FEATURE_TAR_GNU_EXTENSIONS # if !ENABLE_FEATURE_TAR_GNU_EXTENSIONS
if (strlen(header_name) >= NAME_SIZE) { if (strlen(header_name) >= NAME_SIZE) {
bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported"); bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported");
return TRUE; return TRUE;
} }
#endif # endif
/* Is this a regular file? */ /* Is this a regular file? */
if (tbInfo->hlInfo == NULL && S_ISREG(statbuf->st_mode)) { if (tbInfo->hlInfo == NULL && S_ISREG(statbuf->st_mode)) {
@ -590,7 +592,7 @@ static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statb
return TRUE; return TRUE;
} }
#if SEAMLESS_COMPRESSION # if SEAMLESS_COMPRESSION
/* Don't inline: vfork scares gcc and pessimizes code */ /* Don't inline: vfork scares gcc and pessimizes code */
static void NOINLINE vfork_compressor(int tar_fd, const char *gzip) static void NOINLINE vfork_compressor(int tar_fd, const char *gzip)
{ {
@ -598,13 +600,13 @@ static void NOINLINE vfork_compressor(int tar_fd, const char *gzip)
// On Linux, vfork never unpauses parent early, although standard // On Linux, vfork never unpauses parent early, although standard
// allows for that. Do we want to waste bytes checking for it? // allows for that. Do we want to waste bytes checking for it?
# define WAIT_FOR_CHILD 0 # define WAIT_FOR_CHILD 0
volatile int vfork_exec_errno = 0; volatile int vfork_exec_errno = 0;
struct fd_pair gzipDataPipe; struct fd_pair gzipDataPipe;
# if WAIT_FOR_CHILD # if WAIT_FOR_CHILD
struct fd_pair gzipStatusPipe; struct fd_pair gzipStatusPipe;
xpiped_pair(gzipStatusPipe); xpiped_pair(gzipStatusPipe);
# endif # endif
xpiped_pair(gzipDataPipe); xpiped_pair(gzipDataPipe);
signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */ signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */
@ -615,12 +617,12 @@ static void NOINLINE vfork_compressor(int tar_fd, const char *gzip)
/* child */ /* child */
/* NB: close _first_, then move fds! */ /* NB: close _first_, then move fds! */
close(gzipDataPipe.wr); close(gzipDataPipe.wr);
# if WAIT_FOR_CHILD # if WAIT_FOR_CHILD
close(gzipStatusPipe.rd); close(gzipStatusPipe.rd);
/* gzipStatusPipe.wr will close only on exec - /* gzipStatusPipe.wr will close only on exec -
* parent waits for this close to happen */ * parent waits for this close to happen */
fcntl(gzipStatusPipe.wr, F_SETFD, FD_CLOEXEC); fcntl(gzipStatusPipe.wr, F_SETFD, FD_CLOEXEC);
# endif # endif
xmove_fd(gzipDataPipe.rd, 0); xmove_fd(gzipDataPipe.rd, 0);
xmove_fd(tar_fd, 1); xmove_fd(tar_fd, 1);
/* exec gzip/bzip2 program/applet */ /* exec gzip/bzip2 program/applet */
@ -632,7 +634,7 @@ static void NOINLINE vfork_compressor(int tar_fd, const char *gzip)
/* parent */ /* parent */
xmove_fd(gzipDataPipe.wr, tar_fd); xmove_fd(gzipDataPipe.wr, tar_fd);
close(gzipDataPipe.rd); close(gzipDataPipe.rd);
# if WAIT_FOR_CHILD # if WAIT_FOR_CHILD
close(gzipStatusPipe.wr); close(gzipStatusPipe.wr);
while (1) { while (1) {
char buf; char buf;
@ -644,55 +646,52 @@ static void NOINLINE vfork_compressor(int tar_fd, const char *gzip)
continue; /* try it again */ continue; /* try it again */
} }
close(gzipStatusPipe.rd); close(gzipStatusPipe.rd);
# endif # endif
if (vfork_exec_errno) { if (vfork_exec_errno) {
errno = vfork_exec_errno; errno = vfork_exec_errno;
bb_perror_msg_and_die("can't execute '%s'", gzip); bb_perror_msg_and_die("can't execute '%s'", gzip);
} }
} }
#endif /* SEAMLESS_COMPRESSION */ # endif /* SEAMLESS_COMPRESSION */
#if !SEAMLESS_COMPRESSION # if !SEAMLESS_COMPRESSION
/* Do not pass gzip flag to writeTarFile() */ /* Do not pass gzip flag to writeTarFile() */
#define writeTarFile(tar_fd, verboseFlag, recurseFlags, include, exclude, gzip) \ #define writeTarFile(tbInfo, recurseFlags, filelist, gzip) \
writeTarFile(tar_fd, verboseFlag, recurseFlags, include, exclude) writeTarFile(tbInfo, recurseFlags, filelist)
#endif # endif
/* gcc 4.2.1 inlines it, making code bigger */ /* gcc 4.2.1 inlines it, making code bigger */
static NOINLINE int writeTarFile(int tar_fd, int verboseFlag, static NOINLINE int writeTarFile(
int recurseFlags, const llist_t *include, struct TarBallInfo *tbInfo,
const llist_t *exclude, const char *gzip) int recurseFlags,
const llist_t *filelist,
const char *gzip)
{ {
int errorFlag = FALSE; int errorFlag = FALSE;
struct TarBallInfo tbInfo;
tbInfo.hlInfoHead = NULL; /*tbInfo->hlInfoHead = NULL; - already is */
tbInfo.tarFd = tar_fd;
tbInfo.verboseFlag = verboseFlag;
/* Store the stat info for the tarball's file, so /* Store the stat info for the tarball's file, so
* can avoid including the tarball into itself.... */ * can avoid including the tarball into itself.... */
xfstat(tbInfo.tarFd, &tbInfo.tarFileStatBuf, "can't stat tar file"); xfstat(tbInfo->tarFd, &tbInfo->tarFileStatBuf, "can't stat tar file");
#if SEAMLESS_COMPRESSION # if SEAMLESS_COMPRESSION
if (gzip) if (gzip)
vfork_compressor(tbInfo.tarFd, gzip); vfork_compressor(tbInfo->tarFd, gzip);
#endif # endif
tbInfo.excludeList = exclude;
/* Read the directory/files and iterate over them one at a time */ /* Read the directory/files and iterate over them one at a time */
while (include) { while (filelist) {
if (!recursive_action(include->data, recurseFlags, if (!recursive_action(filelist->data, recurseFlags,
writeFileToTarball, writeFileToTarball, &tbInfo, 0) writeFileToTarball, writeFileToTarball, tbInfo, 0)
) { ) {
errorFlag = TRUE; errorFlag = TRUE;
} }
include = include->link; filelist = filelist->link;
} }
/* Write two empty blocks to the end of the archive */ /* Write two empty blocks to the end of the archive */
memset(block_buf, 0, 2*TAR_BLOCK_SIZE); memset(block_buf, 0, 2*TAR_BLOCK_SIZE);
xwrite(tbInfo.tarFd, block_buf, 2*TAR_BLOCK_SIZE); xwrite(tbInfo->tarFd, block_buf, 2*TAR_BLOCK_SIZE);
/* To be pedantically correct, we would check if the tarball /* To be pedantically correct, we would check if the tarball
* 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,
@ -700,16 +699,16 @@ static NOINLINE int writeTarFile(int tar_fd, int verboseFlag,
* so is considered a waste of space */ * so is considered a waste of space */
/* Close so the child process (if any) will exit */ /* Close so the child process (if any) will exit */
close(tbInfo.tarFd); close(tbInfo->tarFd);
/* Hang up the tools, close up shop, head home */ /* Hang up the tools, close up shop, head home */
if (ENABLE_FEATURE_CLEAN_UP) if (ENABLE_FEATURE_CLEAN_UP)
freeHardLinkInfo(&tbInfo.hlInfoHead); freeHardLinkInfo(&tbInfo->hlInfoHead);
if (errorFlag) if (errorFlag)
bb_error_msg("error exit delayed from previous errors"); bb_error_msg("error exit delayed from previous errors");
#if SEAMLESS_COMPRESSION # if SEAMLESS_COMPRESSION
if (gzip) { if (gzip) {
int status; int status;
if (safe_waitpid(-1, &status, 0) == -1) if (safe_waitpid(-1, &status, 0) == -1)
@ -718,21 +717,25 @@ static NOINLINE int writeTarFile(int tar_fd, int verboseFlag,
/* gzip was killed or has exited with nonzero! */ /* gzip was killed or has exited with nonzero! */
errorFlag = TRUE; errorFlag = TRUE;
} }
#endif # endif
return errorFlag; return errorFlag;
} }
#else /* !FEATURE_TAR_CREATE */ #else /* !FEATURE_TAR_CREATE */
# define writeTarFile(...) 0 # define writeTarFile(...) 0
#endif #endif
#if ENABLE_FEATURE_TAR_FROM #if ENABLE_FEATURE_TAR_FROM
static llist_t *append_file_list_to_list(llist_t *list) static llist_t *append_file_list_to_list(llist_t *list)
{ {
FILE *src_stream;
char *line;
llist_t *newlist = NULL; llist_t *newlist = NULL;
while (list) { while (list) {
FILE *src_stream;
char *line;
src_stream = xfopen_stdin(llist_pop(&list)); src_stream = xfopen_stdin(llist_pop(&list));
while ((line = xmalloc_fgetline(src_stream)) != NULL) { while ((line = xmalloc_fgetline(src_stream)) != NULL) {
/* kill trailing '/' unless the string is just "/" */ /* kill trailing '/' unless the string is just "/" */
@ -758,7 +761,7 @@ static llist_t *append_file_list_to_list(llist_t *list)
//usage: IF_FEATURE_TAR_NOPRESERVE_TIME("m") //usage: IF_FEATURE_TAR_NOPRESERVE_TIME("m")
//usage: "vO] " //usage: "vO] "
//usage: "[-f TARFILE] [-C DIR] " //usage: "[-f TARFILE] [-C DIR] "
//usage: IF_FEATURE_TAR_FROM("[-T FILE] [-X FILE] "IF_FEATURE_TAR_LONG_OPTIONS("[--exclude PATTERN] ")) //usage: IF_FEATURE_TAR_FROM("[-T FILE] [-X FILE] "IF_FEATURE_TAR_LONG_OPTIONS("[--exclude PATTERN]... "))
//usage: "[FILE]..." //usage: "[FILE]..."
//usage:#define tar_full_usage "\n\n" //usage:#define tar_full_usage "\n\n"
//usage: IF_FEATURE_TAR_CREATE("Create, extract, ") //usage: IF_FEATURE_TAR_CREATE("Create, extract, ")
@ -1170,13 +1173,10 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
if (base_dir) if (base_dir)
xchdir(base_dir); xchdir(base_dir);
//if (SEAMLESS_COMPRESSION)
// /* We need to know whether child (gzip/bzip/etc) exits abnormally */
// signal(SIGCHLD, check_errors_in_children);
#if ENABLE_FEATURE_TAR_CREATE #if ENABLE_FEATURE_TAR_CREATE
/* Create an archive */ /* Create an archive */
if (opt & OPT_CREATE) { if (opt & OPT_CREATE) {
struct TarBallInfo *tbInfo;
# if SEAMLESS_COMPRESSION # if SEAMLESS_COMPRESSION
const char *zipMode = NULL; const char *zipMode = NULL;
if (opt & OPT_COMPRESS) if (opt & OPT_COMPRESS)
@ -1189,13 +1189,19 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
zipMode = "lzma"; zipMode = "lzma";
if (opt & OPT_XZ) if (opt & OPT_XZ)
zipMode = "xz"; zipMode = "xz";
# endif
tbInfo = xzalloc(sizeof(*tbInfo));
tbInfo->tarFd = tar_handle->src_fd;
tbInfo->verboseFlag = verboseFlag;
# if ENABLE_FEATURE_TAR_FROM
tbInfo->excludeList = tar_handle->reject;
# endif # endif
/* NB: writeTarFile() closes tar_handle->src_fd */ /* NB: writeTarFile() closes tar_handle->src_fd */
return writeTarFile(tar_handle->src_fd, verboseFlag, return writeTarFile(tbInfo,
(opt & OPT_DEREFERENCE ? ACTION_FOLLOWLINKS : 0) (opt & OPT_DEREFERENCE ? ACTION_FOLLOWLINKS : 0)
| (opt & OPT_NORECURSION ? 0 : ACTION_RECURSE), | (opt & OPT_NORECURSION ? 0 : ACTION_RECURSE),
tar_handle->accept, tar_handle->accept,
tar_handle->reject, zipMode); zipMode);
} }
#endif #endif