Fixes with help from Stefan, Typo in prerm script, and when reinstall a package the...
[oweals/busybox.git] / archival / tar.c
index 4bf8004eafad45f56f9fd5b79e951646ac32ac4f..389d7f02e26ae1298f540ef45c20b6be5e8caefa 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Note, that as of BusyBox-0.43, tar has been completely rewritten from the
  * ground up.  It still has remnents of the old code lying about, but it is
- * very different now (i.e. cleaner, less global variables, etc)
+ * very different now (i.e., cleaner, less global variables, etc.)
  *
  * Copyright (C) 1999,2000,2001 by Lineo, inc.
  * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
@@ -121,7 +121,7 @@ struct TarInfo
        gid_t            gid;            /* Numeric GID */
        size_t           size;           /* Size of file */
        time_t           mtime;          /* Last-modified time */
-       enum TarFileType type;           /* Regular, directory, link, etc */
+       enum TarFileType type;           /* Regular, directory, link, etc. */
        char *           linkname;       /* Name for symbolic and hard links */
        long             devmajor;       /* Major number for special device */
        long             devminor;       /* Minor number for special device */
@@ -151,6 +151,7 @@ extern int tar_main(int argc, char **argv)
        char** excludeList=NULL;
        char** extractList=NULL;
        const char *tarName="-";
+       const char *cwd=NULL;
 #if defined BB_FEATURE_TAR_EXCLUDE
        int excludeListSize=0;
        FILE *fileList;
@@ -181,9 +182,9 @@ extern int tar_main(int argc, char **argv)
 
        while (
 #ifndef BB_FEATURE_TAR_EXCLUDE
-                       (opt = getopt(argc, argv, "cxtzvOf:"))
+                       (opt = getopt(argc, argv, "cxtzvOf:pC:"))
 #else
-                       (opt = getopt_long(argc, argv, "cxtzvOf:X:", longopts, NULL))
+                       (opt = getopt_long(argc, argv, "cxtzvOf:X:pC:", longopts, NULL))
 #endif
                        > 0) {
                switch (opt) {
@@ -238,7 +239,16 @@ extern int tar_main(int argc, char **argv)
                                fclose(fileList);
                                break;
 #endif
-                               default:
+                       case 'p':
+                               break;
+                       case 'C':
+                               cwd = xgetcwd((char *)cwd);
+                               if (chdir(optarg)) {
+                                       printf("cd: %s: %s\n", optarg, strerror(errno));
+                                       return EXIT_FAILURE;
+                               }
+                               break;
+                       default:
                                        show_usage();
                }
        }
@@ -274,7 +284,11 @@ extern int tar_main(int argc, char **argv)
                /* unzip tarFd in a seperate process */
                if (unzipFlag == TRUE) {
                        comp_file = fdopen(tarFd, "r");
-                       if ((tarFd = gz_open(comp_file, &pid)) == EXIT_FAILURE) {
+
+                       /* set the buffer size */
+                       setvbuf(comp_file, NULL, _IOFBF, 0x8000);
+
+                       if ((tarFd = fileno(gz_open(comp_file, &pid))) == EXIT_FAILURE) {
                                error_msg_and_die("Couldnt unzip file");
                        }
                }
@@ -290,6 +304,8 @@ extern int tar_main(int argc, char **argv)
 #endif                 
        }
 
+       if (cwd)
+               chdir(cwd);
        if (status == TRUE)
                return EXIT_SUCCESS;
        else
@@ -303,7 +319,7 @@ static void
 fixUpPermissions(TarInfo *header)
 {
        struct utimbuf t;
-       /* Now set permissions etc for the new file */
+       /* Now set permissions etc. for the new file */
        chown(header->name, header->uid, header->gid);
        chmod(header->name, header->mode);
        /* Reset the time */
@@ -318,7 +334,7 @@ tarExtractRegularFile(TarInfo *header, int extractFlag, int tostdoutFlag)
        size_t  writeSize;
        size_t  readSize;
        size_t  actualWriteSz;
-       char    buffer[BUFSIZ];
+       char    buffer[20 * TAR_BLOCK_SIZE];
        size_t  size = header->size;
        int outFd=fileno(stdout);
 
@@ -326,7 +342,11 @@ tarExtractRegularFile(TarInfo *header, int extractFlag, int tostdoutFlag)
        if (extractFlag==TRUE && tostdoutFlag==FALSE) {
                /* Create the path to the file, just in case it isn't there...
                 * This should not screw up path permissions or anything. */
-               create_path(header->name, 0777);
+               char *buf, *dir;
+               buf = xstrdup (header->name);
+               dir = dirname (buf);
+               make_directory (dir, -1, FILEUTILS_RECUR);
+               free (buf);
                if ((outFd=open(header->name, O_CREAT|O_TRUNC|O_WRONLY, 
                                                header->mode & ~S_IFMT)) < 0) {
                        error_msg(io_error, header->name, strerror(errno)); 
@@ -340,9 +360,9 @@ tarExtractRegularFile(TarInfo *header, int extractFlag, int tostdoutFlag)
                if ( size > sizeof(buffer) )
                        writeSize = readSize = sizeof(buffer);
                else {
-                       int mod = size % 512;
+                       int mod = size % TAR_BLOCK_SIZE;
                        if ( mod != 0 )
-                               readSize = size + (512 - mod);
+                               readSize = size + (TAR_BLOCK_SIZE - mod);
                        else
                                readSize = size;
                        writeSize = size;
@@ -382,21 +402,11 @@ tarExtractRegularFile(TarInfo *header, int extractFlag, int tostdoutFlag)
 static int
 tarExtractDirectory(TarInfo *header, int extractFlag, int tostdoutFlag)
 {
-
        if (extractFlag==FALSE || tostdoutFlag==TRUE)
                return( TRUE);
 
-       if (create_path(header->name, header->mode) != TRUE) {
-               perror_msg("%s: Cannot mkdir", header->name); 
+       if (make_directory(header->name, header->mode, FILEUTILS_RECUR) < 0)
                return( FALSE);
-       }
-       /* make the final component, just in case it was
-        * omitted by create_path() (which will skip the
-        * directory if it doesn't have a terminating '/') */
-       if (mkdir(header->name, header->mode) < 0 && errno != EEXIST) {
-               perror_msg("%s", header->name);
-               return FALSE;
-       }
 
        fixUpPermissions(header);
        return( TRUE);
@@ -414,7 +424,7 @@ tarExtractHardLink(TarInfo *header, int extractFlag, int tostdoutFlag)
                return( FALSE);
        }
 
-       /* Now set permissions etc for the new directory */
+       /* Now set permissions etc. for the new directory */
        fixUpPermissions(header);
        return( TRUE);
 }
@@ -467,7 +477,7 @@ tarExtractSpecial(TarInfo *header, int extractFlag, int tostdoutFlag)
                }
        }
 
-       /* Now set permissions etc for the new directory */
+       /* Now set permissions etc. for the new directory */
        fixUpPermissions(header);
        return( TRUE);
 }
@@ -486,7 +496,7 @@ readTarHeader(struct TarHeader *rawHeader, struct TarInfo *header)
                static int alreadyWarned=FALSE;
 
                while (*(header->name) == '/')
-                       ++*(header->name);
+                       header->name++;
 
                if (alreadyWarned == FALSE) {
                        error_msg("Removing leading '/' from member names");
@@ -577,10 +587,6 @@ static int readTarFile(int tarFd, int extractFlag, int listFlag,
        TarHeader rawHeader;
        TarInfo header;
 
-       /* Set the umask for this process so it doesn't 
-        * screw up permission setting for us later. */
-       umask(0);
-
        /* Read the tar file, and iterate over it one file at a time */
        while ( (status = full_read(tarFd, (char*)&rawHeader, TAR_BLOCK_SIZE)) == TAR_BLOCK_SIZE ) {
 
@@ -595,7 +601,7 @@ static int readTarFile(int tarFd, int extractFlag, int listFlag,
                        }
                }
                if ( *(header.name) == '\0' )
-                               goto endgame;
+                       continue;
                header.tarFd = tarFd;
 
                /* Skip funky extra GNU headers that precede long files */
@@ -922,6 +928,8 @@ writeTarHeader(struct TarBallInfo *tbInfo, const char *header_name,
                strncpy(header.linkname, tbInfo->hlInfo->name, sizeof(header.linkname));
        } else if (S_ISLNK(statbuf->st_mode)) {
                char *lpath = xreadlink(real_name);
+               if (!lpath) /* Already printed err msg inside xreadlink() */
+                       return ( FALSE);
                header.typeflag  = SYMTYPE;
                strncpy(header.linkname, lpath, sizeof(header.linkname)); 
                free(lpath);
@@ -946,7 +954,7 @@ writeTarHeader(struct TarBallInfo *tbInfo, const char *header_name,
                return ( FALSE);
        }
 
-       /* Calculate and store the checksum (i.e. the sum of all of the bytes of
+       /* Calculate and store the checksum (i.e., the sum of all of the bytes of
         * the header).  The checksum field must be filled with blanks for the
         * calculation.  The checksum field is formatted differently from the
         * other fields: it has [6] digits, a null, then a space -- rather than
@@ -1108,10 +1116,6 @@ static int writeTarFile(const char* tarName, int verboseFlag, char **argv,
        if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0)
                error_msg_and_die(io_error, tarName, strerror(errno)); 
 
-       /* Set the umask for this process so it doesn't 
-        * screw up permission setting for us later. */
-       umask(0);
-
        /* Read the directory/files and iterate over them one at a time */
        while (*argv != NULL) {
                if (recursive_action(*argv++, TRUE, FALSE, FALSE,