X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=archival%2Ftar.c;h=375e838d279a54a4a3ae7fbd748c058cff7eb313;hb=0d0260fd1e55c39525660370094d090bc5412fe5;hp=7ec101d3111f5b9f75f6c5bc0e957aa6516c4694;hpb=425ad9c93b2736a0ebfbba6267bc1ad56c49d156;p=oweals%2Fbusybox.git diff --git a/archival/tar.c b/archival/tar.c index 7ec101d31..375e838d2 100644 --- a/archival/tar.c +++ b/archival/tar.c @@ -15,17 +15,36 @@ * Copyright (c) 1999 by David I. Bell * Permission is granted to use, distribute, or modify this source, * provided that this copyright notice remains intact. - * Permission to distribute sash derived code under the GPL has been granted. + * Permission to distribute sash derived code under GPL has been granted. * * Based in part on the tar implementation from busybox-0.28 * Copyright (C) 1995 Bruce Perens * - * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* TODO: security with -C DESTDIR option can be enhanced. + * Consider tar file created via: + * $ tar cvf bug.tar anything.txt + * $ ln -s /tmp symlink + * $ tar --append -f bug.tar symlink + * $ rm symlink + * $ mkdir symlink + * $ tar --append -f bug.tar symlink/evil.py + * + * This will result in an archive which contains: + * $ tar --list -f bug.tar + * anything.txt + * symlink + * symlink/evil.py + * + * Untarring it puts evil.py in '/tmp' even if the -C DESTDIR is given. + * This doesn't feel right, and IIRC GNU tar doesn't do that. */ #include #include "libbb.h" -#include "unarchive.h" +#include "archive.h" /* FIXME: Stop using this non-standard feature */ #ifndef FNM_LEADING_DIR # define FNM_LEADING_DIR 0 @@ -48,37 +67,6 @@ #if ENABLE_FEATURE_TAR_CREATE -/* Tar file constants */ - -#define TAR_BLOCK_SIZE 512 - -/* POSIX tar Header Block, from POSIX 1003.1-1990 */ -#define NAME_SIZE 100 -#define NAME_SIZE_STR "100" -typedef struct TarHeader { /* byte offset */ - char name[NAME_SIZE]; /* 0-99 */ - char mode[8]; /* 100-107 */ - char uid[8]; /* 108-115 */ - char gid[8]; /* 116-123 */ - char size[12]; /* 124-135 */ - char mtime[12]; /* 136-147 */ - char chksum[8]; /* 148-155 */ - char typeflag; /* 156-156 */ - char linkname[NAME_SIZE]; /* 157-256 */ - /* POSIX: "ustar" NUL "00" */ - /* GNU tar: "ustar " NUL */ - /* Normally it's defined as magic[6] followed by - * version[2], but we put them together to save code. - */ - char magic[8]; /* 257-264 */ - char uname[32]; /* 265-296 */ - char gname[32]; /* 297-328 */ - char devmajor[8]; /* 329-336 */ - char devminor[8]; /* 337-344 */ - char prefix[155]; /* 345-499 */ - char padding[12]; /* 500-512 (pad to exactly TAR_BLOCK_SIZE) */ -} TarHeader; - /* ** writeTarFile(), writeFileToTarball(), and writeTarHeader() are ** the only functions that deal with the HardLinkInfo structure. @@ -193,7 +181,7 @@ static void putOctal(char *cp, int len, off_t value) } #define PUT_OCTAL(a, b) putOctal((a), sizeof(a), (b)) -static void chksum_and_xwrite(int fd, struct TarHeader* hp) +static void chksum_and_xwrite(int fd, struct tar_header_t* hp) { /* POSIX says that checksum is done on unsigned bytes * (Sun and HP-UX gets it wrong... more details in @@ -235,7 +223,7 @@ static void writeLongname(int fd, int type, const char *name, int dir) "00000000000", "00000000000", }; - struct TarHeader header; + struct tar_header_t header; int size; dir = !!dir; /* normalize: 0/1 */ @@ -262,16 +250,12 @@ static void writeLongname(int fd, int type, const char *name, int dir) #endif /* Write out a tar header for the specified file/directory/whatever */ -void BUG_tar_header_size(void); static int writeTarHeader(struct TarBallInfo *tbInfo, const char *header_name, const char *fileName, struct stat *statbuf) { - struct TarHeader header; - - if (sizeof(header) != 512) - BUG_tar_header_size(); + struct tar_header_t header; - memset(&header, 0, sizeof(struct TarHeader)); + memset(&header, 0, sizeof(header)); strncpy(header.name, header_name, sizeof(header.name)); @@ -280,7 +264,8 @@ static int writeTarHeader(struct TarBallInfo *tbInfo, PUT_OCTAL(header.uid, statbuf->st_uid); PUT_OCTAL(header.gid, statbuf->st_gid); memset(header.size, '0', sizeof(header.size)-1); /* Regular file size is handled later */ - PUT_OCTAL(header.mtime, statbuf->st_mtime); + /* users report that files with negative st_mtime cause trouble, so: */ + PUT_OCTAL(header.mtime, statbuf->st_mtime >= 0 ? statbuf->st_mtime : 0); /* Enter the user and group names */ safe_strncpy(header.uname, get_cached_username(statbuf->st_uid), sizeof(header.uname)); @@ -332,15 +317,42 @@ static int writeTarHeader(struct TarBallInfo *tbInfo, } else if (S_ISFIFO(statbuf->st_mode)) { header.typeflag = FIFOTYPE; } else if (S_ISREG(statbuf->st_mode)) { - if (sizeof(statbuf->st_size) > 4 - && statbuf->st_size > (off_t)0777777777777LL + /* header.size field is 12 bytes long */ + /* Does octal-encoded size fit? */ + uoff_t filesize = statbuf->st_size; + if (sizeof(filesize) <= 4 + || filesize <= (uoff_t)0777777777777LL ) { + PUT_OCTAL(header.size, filesize); + } + /* Does base256-encoded size fit? + * It always does unless off_t is wider than 64 bits. + */ + else if (ENABLE_FEATURE_TAR_GNU_EXTENSIONS +#if ULLONG_MAX > 0xffffffffffffffffLL /* 2^64-1 */ + && (filesize <= 0x3fffffffffffffffffffffffLL) +#endif + ) { + /* GNU tar uses "base-256 encoding" for very large numbers. + * Encoding is binary, with highest bit always set as a marker + * and sign in next-highest bit: + * 80 00 .. 00 - zero + * bf ff .. ff - largest positive number + * ff ff .. ff - minus 1 + * c0 00 .. 00 - smallest negative number + */ + char *p8 = header.size + sizeof(header.size); + do { + *--p8 = (uint8_t)filesize; + filesize >>= 8; + } while (p8 != header.size); + *p8 |= 0x80; + } else { bb_error_msg_and_die("can't store file '%s' " "of size %"OFF_FMT"u, aborting", fileName, statbuf->st_size); } header.typeflag = REGTYPE; - PUT_OCTAL(header.size, statbuf->st_size); } else { bb_error_msg("%s: unknown file type", fileName); return FALSE; @@ -413,17 +425,8 @@ static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statb DBG("writeFileToTarball('%s')", fileName); - /* Strip leading '/' (must be before memorizing hardlink's name) */ - header_name = fileName; - while (header_name[0] == '/') { - static smallint warned; - - if (!warned) { - bb_error_msg("removing leading '/' from member names"); - warned = 1; - } - header_name++; - } + /* Strip leading '/' and such (must be before memorizing hardlink's name) */ + header_name = strip_unsafe_prefix(fileName); if (header_name[0] == '\0') return TRUE; @@ -549,9 +552,7 @@ static void NOINLINE vfork_compressor(int tar_fd, int gzip) (void) &zip_exec; # endif - gzipPid = vfork(); - if (gzipPid < 0) - bb_perror_msg_and_die("vfork"); + gzipPid = xvfork(); if (gzipPid == 0) { /* child */ @@ -609,8 +610,7 @@ static NOINLINE int writeTarFile(int tar_fd, int verboseFlag, /* Store the stat info for the tarball's file, so * can avoid including the tarball into itself.... */ - if (fstat(tbInfo.tarFd, &tbInfo.tarFileStatBuf) < 0) - bb_perror_msg_and_die("can't stat tar file"); + xfstat(tbInfo.tarFd, &tbInfo.tarFileStatBuf, "can't stat tar file"); #if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2 if (gzip) @@ -674,7 +674,7 @@ static llist_t *append_file_list_to_list(llist_t *list) llist_t *newlist = NULL; while (list) { - src_stream = xfopen_for_read(llist_pop(&list)); + src_stream = xfopen_stdin(llist_pop(&list)); while ((line = xmalloc_fgetline(src_stream)) != NULL) { /* kill trailing '/' unless the string is just "/" */ char *cp = last_char_is(line, '/'); @@ -738,6 +738,72 @@ static void handle_SIGCHLD(int status) } #endif +//usage:#define tar_trivial_usage +//usage: "-[" IF_FEATURE_TAR_CREATE("c") "xt" +//usage: IF_FEATURE_SEAMLESS_Z("Z") +//usage: IF_FEATURE_SEAMLESS_GZ("z") +//usage: IF_FEATURE_SEAMLESS_BZ2("j") +//usage: IF_FEATURE_SEAMLESS_LZMA("a") +//usage: IF_FEATURE_TAR_CREATE("h") +//usage: IF_FEATURE_TAR_NOPRESERVE_TIME("m") +//usage: "vO] " +//usage: IF_FEATURE_TAR_FROM("[-X FILE] [-T FILE] ") +//usage: "[-f TARFILE] [-C DIR] [FILE]..." +//usage:#define tar_full_usage "\n\n" +//usage: IF_FEATURE_TAR_CREATE("Create, extract, ") +//usage: IF_NOT_FEATURE_TAR_CREATE("Extract ") +//usage: "or list files from a tar file\n" +//usage: "\nOperation:" +//usage: IF_FEATURE_TAR_CREATE( +//usage: "\n c Create" +//usage: ) +//usage: "\n x Extract" +//usage: "\n t List" +//usage: "\n f Name of TARFILE ('-' for stdin/out)" +//usage: "\n C Change to DIR before operation" +//usage: "\n v Verbose" +//usage: IF_FEATURE_SEAMLESS_Z( +//usage: "\n Z (De)compress using compress" +//usage: ) +//usage: IF_FEATURE_SEAMLESS_GZ( +//usage: "\n z (De)compress using gzip" +//usage: ) +//usage: IF_FEATURE_SEAMLESS_BZ2( +//usage: "\n j (De)compress using bzip2" +//usage: ) +//usage: IF_FEATURE_SEAMLESS_LZMA( +//usage: "\n a (De)compress using lzma" +//usage: ) +//usage: "\n O Extract to stdout" +//usage: IF_FEATURE_TAR_CREATE( +//usage: "\n h Follow symlinks" +//usage: ) +//usage: IF_FEATURE_TAR_NOPRESERVE_TIME( +//usage: "\n m Don't restore mtime" +//usage: ) +//usage: IF_FEATURE_TAR_FROM( +//usage: IF_FEATURE_TAR_LONG_OPTIONS( +//usage: "\n exclude File to exclude" +//usage: ) +//usage: "\n X File with names to exclude" +//usage: "\n T File with names to include" +//usage: ) +//usage: +//usage:#define tar_example_usage +//usage: "$ zcat /tmp/tarball.tar.gz | tar -xf -\n" +//usage: "$ tar -cf /tmp/tarball.tar /usr/local\n" + +// Supported but aren't in --help: +// o no-same-owner +// p same-permissions +// k keep-old +// numeric-owner +// no-same-permissions +// overwrite +//IF_FEATURE_TAR_TO_COMMAND( +// to-command +//) + enum { OPTBIT_KEEP_OLD = 8, IF_FEATURE_TAR_CREATE( OPTBIT_CREATE ,) @@ -748,9 +814,12 @@ enum { IF_FEATURE_TAR_FROM( OPTBIT_EXCLUDE_FROM,) IF_FEATURE_SEAMLESS_GZ( OPTBIT_GZIP ,) IF_FEATURE_SEAMLESS_Z( OPTBIT_COMPRESS ,) // 16th bit + IF_FEATURE_TAR_NOPRESERVE_TIME(OPTBIT_NOPRESERVE_TIME,) #if ENABLE_FEATURE_TAR_LONG_OPTIONS + IF_FEATURE_TAR_TO_COMMAND(OPTBIT_2COMMAND ,) OPTBIT_NUMERIC_OWNER, OPTBIT_NOPRESERVE_PERM, + OPTBIT_OVERWRITE, #endif OPT_TEST = 1 << 0, // t OPT_EXTRACT = 1 << 1, // x @@ -769,8 +838,11 @@ enum { OPT_EXCLUDE_FROM = IF_FEATURE_TAR_FROM( (1 << OPTBIT_EXCLUDE_FROM)) + 0, // X OPT_GZIP = IF_FEATURE_SEAMLESS_GZ( (1 << OPTBIT_GZIP )) + 0, // z OPT_COMPRESS = IF_FEATURE_SEAMLESS_Z( (1 << OPTBIT_COMPRESS )) + 0, // Z + OPT_NOPRESERVE_TIME = IF_FEATURE_TAR_NOPRESERVE_TIME((1 << OPTBIT_NOPRESERVE_TIME)) + 0, // m + OPT_2COMMAND = IF_FEATURE_TAR_TO_COMMAND( (1 << OPTBIT_2COMMAND )) + 0, // to-command OPT_NUMERIC_OWNER = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NUMERIC_OWNER )) + 0, // numeric-owner OPT_NOPRESERVE_PERM = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NOPRESERVE_PERM)) + 0, // no-same-permissions + OPT_OVERWRITE = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_OVERWRITE )) + 0, // overwrite }; #if ENABLE_FEATURE_TAR_LONG_OPTIONS static const char tar_longopts[] ALIGN1 = @@ -805,11 +877,19 @@ static const char tar_longopts[] ALIGN1 = # endif # if ENABLE_FEATURE_SEAMLESS_Z "compress\0" No_argument "Z" +# endif +# if ENABLE_FEATURE_TAR_NOPRESERVE_TIME + "touch\0" No_argument "m" +# endif +# if ENABLE_FEATURE_TAR_TO_COMMAND + "to-command\0" Required_argument "\xfb" # endif /* use numeric uid/gid from tar header, not textual */ - "numeric-owner\0" No_argument "\xfd" + "numeric-owner\0" No_argument "\xfc" /* do not restore mode */ - "no-same-permissions\0" No_argument "\xfe" + "no-same-permissions\0" No_argument "\xfd" + /* on unpack, open with O_TRUNC and !O_EXCL */ + "overwrite\0" No_argument "\xfe" /* --exclude takes next bit position in option mask, */ /* therefore we have to put it _after_ --no-same-permissions */ # if ENABLE_FEATURE_TAR_FROM @@ -844,8 +924,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv) /* Prepend '-' to the first argument if required */ opt_complementary = "--:" // first arg is options "tt:vv:" // count -t,-v - "?:" // bail out with usage instead of error return - "X::T::" // cumulative lists + IF_FEATURE_TAR_FROM("X::T::") // cumulative lists #if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM "\xff::" // cumulative lists for --exclude #endif @@ -856,7 +935,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv) applet_long_options = tar_longopts; #endif #if ENABLE_DESKTOP - if (argv[1][0] != '-') { + if (argv[1] && argv[1][0] != '-') { /* Compat: * 1st argument without dash handles options with parameters * differently from dashed one: it takes *next argv[i]* @@ -890,10 +969,12 @@ int tar_main(int argc UNUSED_PARAM, char **argv) IF_FEATURE_TAR_FROM( "T:X:") IF_FEATURE_SEAMLESS_GZ( "z" ) IF_FEATURE_SEAMLESS_Z( "Z" ) + IF_FEATURE_TAR_NOPRESERVE_TIME("m") , &base_dir // -C dir , &tar_filename // -f filename IF_FEATURE_TAR_FROM(, &(tar_handle->accept)) // T IF_FEATURE_TAR_FROM(, &(tar_handle->reject)) // X + IF_FEATURE_TAR_TO_COMMAND(, &(tar_handle->tar__to_command)) // --to-command #if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM , &excludes // --exclude #endif @@ -912,6 +993,13 @@ int tar_main(int argc UNUSED_PARAM, char **argv) if (opt & OPT_2STDOUT) tar_handle->action_data = data_extract_to_stdout; + if (opt & OPT_2COMMAND) { + putenv((char*)"TAR_FILETYPE=f"); + signal(SIGPIPE, SIG_IGN); + tar_handle->action_data = data_extract_to_command; + IF_FEATURE_TAR_TO_COMMAND(tar_handle->tar__to_command_shell = xstrdup(get_shell_name());) + } + if (opt & OPT_KEEP_OLD) tar_handle->ah_flags &= ~ARCHIVE_UNLINK_OLD; @@ -924,6 +1012,11 @@ int tar_main(int argc UNUSED_PARAM, char **argv) if (opt & OPT_NOPRESERVE_PERM) tar_handle->ah_flags |= ARCHIVE_DONT_RESTORE_PERM; + if (opt & OPT_OVERWRITE) { + tar_handle->ah_flags &= ~ARCHIVE_UNLINK_OLD; + tar_handle->ah_flags |= ARCHIVE_O_TRUNC; + } + if (opt & OPT_GZIP) get_header_ptr = get_header_tar_gz; @@ -936,9 +1029,12 @@ int tar_main(int argc UNUSED_PARAM, char **argv) if (opt & OPT_COMPRESS) get_header_ptr = get_header_tar_Z; + if (opt & OPT_NOPRESERVE_TIME) + tar_handle->ah_flags &= ~ARCHIVE_RESTORE_DATE; + #if ENABLE_FEATURE_TAR_FROM tar_handle->reject = append_file_list_to_list(tar_handle->reject); -#if ENABLE_FEATURE_TAR_LONG_OPTIONS +# if ENABLE_FEATURE_TAR_LONG_OPTIONS /* Append excludes to reject */ while (excludes) { llist_t *next = excludes->link; @@ -946,12 +1042,12 @@ int tar_main(int argc UNUSED_PARAM, char **argv) tar_handle->reject = excludes; excludes = next; } -#endif +# endif tar_handle->accept = append_file_list_to_list(tar_handle->accept); #endif /* Setup an array of filenames to work with */ - /* TODO: This is the same as in ar, separate function ? */ + /* TODO: This is the same as in ar, make a separate function? */ while (*argv) { /* kill trailing '/' unless the string is just "/" */ char *cp = last_char_is(*argv, '/'); @@ -970,7 +1066,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv) int flags = O_RDONLY; if (opt & OPT_CREATE) { - /* Make sure there is at least one file to tar up. */ + /* Make sure there is at least one file to tar up */ if (tar_handle->accept == NULL) bb_error_msg_and_die("empty archive"); @@ -983,8 +1079,10 @@ int tar_main(int argc UNUSED_PARAM, char **argv) tar_handle->src_fd = tar_fd; tar_handle->seek = seek_by_read; } else { - if (ENABLE_FEATURE_TAR_AUTODETECT && flags == O_RDONLY) { - get_header_ptr = get_header_tar; + if (ENABLE_FEATURE_TAR_AUTODETECT + && flags == O_RDONLY + && get_header_ptr == get_header_tar + ) { tar_handle->src_fd = open_zipped(tar_filename); if (tar_handle->src_fd < 0) bb_perror_msg_and_die("can't open '%s'", tar_filename); @@ -1002,7 +1100,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv) signal(SIGCHLD, handle_SIGCHLD); #endif - /* create an archive */ + /* Create an archive */ if (opt & OPT_CREATE) { #if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2 int zipMode = 0;