"I'll think about it" != "apply it now". It means I need to think about it.
[oweals/busybox.git] / archival / tar.c
index ab4d1f26626e29b3ed1377c1d50a443310839ff2..5d9e870fa3dab3e0c794bb65ffe4c0f66db606dd 100644 (file)
  *
  * Based in part on the tar implementation from busybox-0.28
  *  Copyright (C) 1995 Bruce Perens
- *  This is free software under the GNU General Public License.
  *
- * Licensed under GPL v2 (or later), see file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  */
 
-#include <fcntl.h>
-#include <getopt.h>
-#include <search.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fnmatch.h>
-#include <string.h>
-#include <errno.h>
-#include <signal.h>
-#include <sys/wait.h>
-#include <sys/socket.h>
-#include <sys/sysmacros.h>     /* major() and minor() */
-#include "unarchive.h"
 #include "busybox.h"
+#include "unarchive.h"
+#include <fnmatch.h>
+#include <getopt.h>
 
 #ifdef CONFIG_FEATURE_TAR_CREATE
 
 /* Tar file constants  */
-# define TAR_MAGIC          "ustar"    /* ustar and a null */
-# define TAR_VERSION        "  "       /* Be compatable with GNU tar format */
 
 #define TAR_BLOCK_SIZE         512
-#define TAR_MAGIC_LEN          6
-#define TAR_VERSION_LEN                2
 
 /* POSIX tar Header Block, from POSIX 1003.1-1990  */
 #define NAME_SIZE                      100
@@ -89,7 +73,6 @@ struct HardLinkInfo {
 
 /* Some info to be carried along when creating a new tarball */
 struct TarBallInfo {
-       char *fileName;                 /* File name of the tarball */
        int tarFd;                              /* Open-for-write file descriptor
                                                           for the tarball */
        struct stat statBuf;    /* Stat info for the tarball, letting
@@ -120,20 +103,20 @@ enum TarFileType {
 typedef enum TarFileType TarFileType;
 
 /* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
-static inline void addHardLinkInfo(HardLinkInfo ** hlInfoHeadPtr,
+static void addHardLinkInfo(HardLinkInfo ** hlInfoHeadPtr,
                                        struct stat *statbuf,
-                                       const char *name)
+                                       const char *fileName)
 {
        /* Note: hlInfoHeadPtr can never be NULL! */
        HardLinkInfo *hlInfo;
 
-       hlInfo = (HardLinkInfo *) xmalloc(sizeof(HardLinkInfo) + strlen(name));
+       hlInfo = xmalloc(sizeof(HardLinkInfo) + strlen(fileName));
        hlInfo->next = *hlInfoHeadPtr;
        *hlInfoHeadPtr = hlInfo;
        hlInfo->dev = statbuf->st_dev;
        hlInfo->ino = statbuf->st_ino;
        hlInfo->linkCount = statbuf->st_nlink;
-       strcpy(hlInfo->name, name);
+       strcpy(hlInfo->name, fileName);
 }
 
 static void freeHardLinkInfo(HardLinkInfo ** hlInfoHeadPtr)
@@ -154,14 +137,14 @@ static void freeHardLinkInfo(HardLinkInfo ** hlInfoHeadPtr)
 }
 
 /* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
-static inline HardLinkInfo *findHardLinkInfo(HardLinkInfo * hlInfo, struct stat *statbuf)
+static HardLinkInfo *findHardLinkInfo(HardLinkInfo * hlInfo, struct stat *statbuf)
 {
        while (hlInfo) {
                if ((statbuf->st_ino == hlInfo->ino) && (statbuf->st_dev == hlInfo->dev))
                        break;
                hlInfo = hlInfo->next;
        }
-       return (hlInfo);
+       return hlInfo;
 }
 
 /* Put an octal string into the specified buffer.
@@ -199,25 +182,24 @@ static int putOctal(char *cp, int len, long value)
 }
 
 /* Write out a tar header for the specified file/directory/whatever */
-static inline int writeTarHeader(struct TarBallInfo *tbInfo,
-               const char *header_name, const char *real_name, struct stat *statbuf)
+static int writeTarHeader(struct TarBallInfo *tbInfo,
+               const char *header_name, const char *fileName, struct stat *statbuf)
 {
        long chksum = 0;
        struct TarHeader header;
        const unsigned char *cp = (const unsigned char *) &header;
        ssize_t size = sizeof(struct TarHeader);
 
-       memset(&header, 0, size);
+       bzero(&header, size);
 
-       strncpy(header.name, header_name, sizeof(header.name));
+       safe_strncpy(header.name, header_name, sizeof(header.name));
 
        putOctal(header.mode, sizeof(header.mode), statbuf->st_mode);
        putOctal(header.uid, sizeof(header.uid), statbuf->st_uid);
        putOctal(header.gid, sizeof(header.gid), statbuf->st_gid);
        putOctal(header.size, sizeof(header.size), 0);  /* Regular file size is handled later */
        putOctal(header.mtime, sizeof(header.mtime), statbuf->st_mtime);
-       strncpy(header.magic, TAR_MAGIC TAR_VERSION,
-                       TAR_MAGIC_LEN + TAR_VERSION_LEN);
+       strcpy(header.magic, "ustar  ");
 
        /* Enter the user and group names (default to root if it fails) */
        if (bb_getpwuid(header.uname, statbuf->st_uid, sizeof(header.uname)) == NULL)
@@ -231,10 +213,10 @@ static inline int writeTarHeader(struct TarBallInfo *tbInfo,
                strncpy(header.linkname, tbInfo->hlInfo->name,
                                sizeof(header.linkname));
        } else if (S_ISLNK(statbuf->st_mode)) {
-               char *lpath = xreadlink(real_name);
+               char *lpath = xreadlink(fileName);
 
                if (!lpath)             /* Already printed err msg inside xreadlink() */
-                       return (FALSE);
+                       return FALSE;
                header.typeflag = SYMTYPE;
                strncpy(header.linkname, lpath, sizeof(header.linkname));
                free(lpath);
@@ -259,8 +241,8 @@ static inline int writeTarHeader(struct TarBallInfo *tbInfo,
                header.typeflag = REGTYPE;
                putOctal(header.size, sizeof(header.size), statbuf->st_size);
        } else {
-               bb_error_msg("%s: Unknown file type", real_name);
-               return (FALSE);
+               bb_error_msg("%s: unknown file type", fileName);
+               return FALSE;
        }
 
        /* Calculate and store the checksum (i.e., the sum of all of the bytes of
@@ -275,16 +257,8 @@ static inline int writeTarHeader(struct TarBallInfo *tbInfo,
        putOctal(header.chksum, 7, chksum);
 
        /* Now write the header out to disk */
-       if ((size =
-                bb_full_write(tbInfo->tarFd, (char *) &header,
-                                       sizeof(struct TarHeader))) < 0) {
-               bb_error_msg(bb_msg_io_error, real_name);
-               return (FALSE);
-       }
-       /* Pad the header up to the tar block size */
-       for (; size < TAR_BLOCK_SIZE; size++) {
-               write(tbInfo->tarFd, "\0", 1);
-       }
+       xwrite(tbInfo->tarFd, &header, sizeof(struct TarHeader));
+
        /* Now do the verbose thing (or not) */
 
        if (tbInfo->verboseFlag) {
@@ -296,11 +270,11 @@ static inline int writeTarHeader(struct TarBallInfo *tbInfo,
                fprintf(vbFd, "%s\n", header.name);
        }
 
-       return (TRUE);
+       return TRUE;
 }
 
 # ifdef CONFIG_FEATURE_TAR_FROM
-static inline 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) {
                if (excluded_files->data[0] == '/') {
@@ -334,11 +308,11 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf,
        int inputFileFd = -1;
 
        /*
-          ** Check to see if we are dealing with a hard link.
-          ** If so -
-          ** Treat the first occurance of a given dev/inode as a file while
-          ** treating any additional occurances as hard links.  This is done
-          ** by adding the file information to the HardLinkInfo linked list.
+        * Check to see if we are dealing with a hard link.
+        * If so -
+        * Treat the first occurance of a given dev/inode as a file while
+        * treating any additional occurances as hard links.  This is done
+        * by adding the file information to the HardLinkInfo linked list.
         */
        tbInfo->hlInfo = NULL;
        if (statbuf->st_nlink > 1) {
@@ -350,7 +324,7 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf,
        /* It is against the rules to archive a socket */
        if (S_ISSOCK(statbuf->st_mode)) {
                bb_error_msg("%s: socket ignored", fileName);
-               return (TRUE);
+               return TRUE;
        }
 
        /* It is a bad idea to store the archive we are in the process of creating,
@@ -359,7 +333,7 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf,
        if (tbInfo->statBuf.st_dev == statbuf->st_dev &&
                tbInfo->statBuf.st_ino == statbuf->st_ino) {
                bb_error_msg("%s: file is the archive; skipping", fileName);
-               return (TRUE);
+               return TRUE;
        }
 
        header_name = fileName;
@@ -367,7 +341,7 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf,
                static int alreadyWarned = FALSE;
 
                if (alreadyWarned == FALSE) {
-                       bb_error_msg("Removing leading '/' from member names");
+                       bb_error_msg("removing leading '/' from member names");
                        alreadyWarned = TRUE;
                }
                header_name++;
@@ -375,7 +349,7 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf,
 
        if (strlen(fileName) >= NAME_SIZE) {
                bb_error_msg(bb_msg_name_longer_than_foo, NAME_SIZE);
-               return (TRUE);
+               return TRUE;
        }
 
        if (header_name[0] == '\0')
@@ -388,21 +362,20 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf,
 
        /* 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 */
                if ((inputFileFd = open(fileName, O_RDONLY)) < 0) {
-                       bb_perror_msg("%s: Cannot open", fileName);
-                       return (FALSE);
+                       bb_perror_msg("%s: cannot open", fileName);
+                       return FALSE;
                }
        }
 
        /* Add an entry to the tarball */
        if (writeTarHeader(tbInfo, header_name, fileName, statbuf) == FALSE) {
-               return (FALSE);
+               return FALSE;
        }
 
        /* If it was a regular file, write out the body */
-       if (inputFileFd >= 0 ) {
+       if (inputFileFd >= 0) {
                ssize_t readSize = 0;
 
                /* write the file to the archive */
@@ -410,21 +383,20 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf,
                close(inputFileFd);
 
                /* Pad the file up to the tar block size */
-               for (; (readSize % TAR_BLOCK_SIZE) != 0; readSize++)
-                       write(tbInfo->tarFd, "\0", 1);
+               readSize = (TAR_BLOCK_SIZE - readSize) & (TAR_BLOCK_SIZE-1);
+               bzero(bb_common_bufsiz1, readSize);
+               xwrite(tbInfo->tarFd, bb_common_bufsiz1, readSize);
        }
 
-       return (TRUE);
+       return TRUE;
 }
 
-static inline int writeTarFile(const int tar_fd, const int verboseFlag,
+static int writeTarFile(const int tar_fd, const int verboseFlag,
        const unsigned long dereferenceFlag, const llist_t *include,
        const llist_t *exclude, const int gzip)
 {
        pid_t gzipPid = 0;
-
        int errorFlag = FALSE;
-       ssize_t size;
        struct TarBallInfo tbInfo;
 
        tbInfo.hlInfoHead = NULL;
@@ -436,7 +408,7 @@ static inline int writeTarFile(const int tar_fd, const int verboseFlag,
        /* Store the stat info for the tarball's file, so
         * can avoid including the tarball into itself....  */
        if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0)
-               bb_perror_msg_and_die("Couldnt stat tar file");
+               bb_perror_msg_and_die("cannot stat tar file");
 
        if ((ENABLE_FEATURE_TAR_GZIP || ENABLE_FEATURE_TAR_BZIP2) && gzip) {
                int gzipDataPipe[2] = { -1, -1 };
@@ -446,7 +418,7 @@ static inline int writeTarFile(const int tar_fd, const int verboseFlag,
 
 
                if (pipe(gzipDataPipe) < 0 || pipe(gzipStatusPipe) < 0)
-                       bb_perror_msg_and_die("create pipe");
+                       bb_perror_msg_and_die("pipe");
 
                signal(SIGPIPE, SIG_IGN);       /* we only want EPIPE on errors */
 
@@ -481,7 +453,7 @@ static inline int writeTarFile(const int tar_fd, const int verboseFlag,
                        while (1) {
                                char buf;
 
-                               int n = bb_full_read(gzipStatusPipe[0], &buf, 1);
+                               int n = full_read(gzipStatusPipe[0], &buf, 1);
 
                                if (n == 0 && vfork_exec_errno != 0) {
                                        errno = vfork_exec_errno;
@@ -508,8 +480,8 @@ static inline int writeTarFile(const int tar_fd, const int verboseFlag,
                include = include->link;
        }
        /* Write two empty blocks to the end of the archive */
-       for (size = 0; size < (2 * TAR_BLOCK_SIZE); size++)
-               write(tbInfo.tarFd, "\0", 1);
+       bzero(bb_common_bufsiz1, 2*TAR_BLOCK_SIZE);
+       xwrite(tbInfo.tarFd, bb_common_bufsiz1, 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,
@@ -547,12 +519,16 @@ static llist_t *append_file_list_to_list(llist_t *list)
        llist_t *newlist = NULL;
 
        while (cur) {
-               src_stream = bb_xfopen(cur->data, "r");
+               src_stream = xfopen(cur->data, "r");
                tmp = cur;
                cur = cur->link;
                free(tmp);
-               while ((line = bb_get_chomped_line_from_file(src_stream)) != NULL)
-                               newlist = llist_add_to(newlist, line);
+               while ((line = bb_get_chomped_line_from_file(src_stream)) != NULL) {
+                       char *filename_ptr = last_char_is(line, '/');
+                       if (filename_ptr > line)
+                               *filename_ptr = '\0';
+                       llist_add_to(&newlist, line);
+               }
                fclose(src_stream);
        }
        return newlist;
@@ -568,15 +544,16 @@ static char get_header_tar_Z(archive_handle_t *archive_handle)
        archive_handle->seek = seek_by_char;
 
        /* do the decompression, and cleanup */
-       if (bb_xread_char(archive_handle->src_fd) != 0x1f ||
-               bb_xread_char(archive_handle->src_fd) != 0x9d)
+       if (xread_char(archive_handle->src_fd) != 0x1f ||
+               xread_char(archive_handle->src_fd) != 0x9d)
        {
-               bb_error_msg_and_die("Invalid magic");
+               bb_error_msg_and_die("invalid magic");
        }
 
        archive_handle->src_fd = open_transformer(archive_handle->src_fd, uncompress);
        archive_handle->offset = 0;
-       while (get_header_tar(archive_handle) == EXIT_SUCCESS);
+       while (get_header_tar(archive_handle) == EXIT_SUCCESS)
+               /* nothing */;
 
        /* Can only do one file at a time */
        return(EXIT_FAILURE);
@@ -597,7 +574,7 @@ static char get_header_tar_Z(archive_handle_t *archive_handle)
 #define TAR_OPT_AFTER_START               8
 
 #define CTX_CREATE                        (1 << (TAR_OPT_AFTER_START))
-#define TAR_OPT_DEREFERNCE                (1 << (TAR_OPT_AFTER_START + 1))
+#define TAR_OPT_DEREFERENCE               (1 << (TAR_OPT_AFTER_START + 1))
 #ifdef CONFIG_FEATURE_TAR_CREATE
 # define TAR_OPT_STR_CREATE               "ch"
 # define TAR_OPT_AFTER_CREATE             TAR_OPT_AFTER_START + 2
@@ -801,7 +778,7 @@ int tar_main(int argc, char **argv)
                if (filename_ptr > argv[optind])
                        *filename_ptr = '\0';
 
-               tar_handle->accept = llist_add_to(tar_handle->accept, argv[optind]);
+               llist_add_to(&(tar_handle->accept), argv[optind]);
                optind++;
        }
 
@@ -830,12 +807,12 @@ int tar_main(int argc, char **argv)
                        tar_handle->src_fd = fileno(tar_stream);
                        tar_handle->seek = seek_by_char;
                } else {
-                       tar_handle->src_fd = bb_xopen(tar_filename, flags);
+                       tar_handle->src_fd = xopen3(tar_filename, flags, 0666);
                }
        }
 
-       if ((base_dir) && (chdir(base_dir)))
-               bb_perror_msg_and_die("Couldnt chdir to %s", base_dir);
+       if (base_dir)
+               xchdir(base_dir);
 
        /* create an archive */
        if (ENABLE_FEATURE_TAR_CREATE && (opt & CTX_CREATE)) {
@@ -852,17 +829,19 @@ int tar_main(int argc, char **argv)
                {
                        verboseFlag = TRUE;
                }
-               writeTarFile(tar_handle->src_fd, verboseFlag, opt & TAR_OPT_DEREFERNCE, tar_handle->accept,
+               writeTarFile(tar_handle->src_fd, verboseFlag, opt & TAR_OPT_DEREFERENCE,
+                               tar_handle->accept,
                        tar_handle->reject, zipMode);
        } else {
-               while (get_header_ptr(tar_handle) == EXIT_SUCCESS);
+               while (get_header_ptr(tar_handle) == EXIT_SUCCESS)
+                       /* nothing */;
 
                /* Check that every file that should have been extracted was */
                while (tar_handle->accept) {
                        if (!find_list_entry(tar_handle->reject, tar_handle->accept->data)
                                && !find_list_entry(tar_handle->passed, tar_handle->accept->data))
                        {
-                               bb_error_msg_and_die("%s: Not found in archive", tar_handle->accept->data);
+                               bb_error_msg_and_die("%s: not found in archive", tar_handle->accept->data);
                        }
                        tar_handle->accept = tar_handle->accept->link;
                }