]> Dogcows Code - chaz/tar/blobdiff - src/extract.c
(name_next): If file names are given both in the
[chaz/tar] / src / extract.c
index 0728a99f8a8d16453c0507ce5bb9d8bab4ff194c..0aba5e7fa0af2ecf7d812332196a54fc875d0efe 100644 (file)
@@ -1,5 +1,5 @@
 /* Extract files from a tar archive.
-   Copyright (C) 1988, 92,93,94,96,97,98, 1999 Free Software Foundation, Inc.
+   Copyright 1988, 92,93,94,96,97,98, 1999 Free Software Foundation, Inc.
    Written by John Gilmore, on 1985-11-19.
 
    This program is free software; you can redistribute it and/or modify it
 
    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
-   59 Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include "system.h"
 
-#include <time.h>
-time_t time ();
-
 #if HAVE_UTIME_H
 # include <utime.h>
 #else
@@ -33,20 +30,10 @@ struct utimbuf
 
 #include "common.h"
 
-static time_t now;             /* current time */
 static int we_are_root;                /* true if our effective uid == 0 */
 static mode_t newdir_umask;    /* umask when creating new directories */
 static mode_t current_umask;   /* current umask (which is set to 0 if -p) */
 
-#if 0
-/* "Scratch" space to store the information about a sparse file before
-   writing the info into the header or extended header.  */
-struct sp_array *sparsearray;
-
-/* Number of elts storable in the sparsearray.  */
-int   sp_array_size = 10;
-#endif
-
 struct delayed_set_stat
   {
     struct delayed_set_stat *next;
@@ -63,15 +50,16 @@ static struct delayed_set_stat *delayed_set_stat_head;
 void
 extr_init (void)
 {
-  now = time ((time_t *) 0);
   we_are_root = geteuid () == 0;
+  same_permissions_option += we_are_root;
+  same_owner_option += we_are_root;
 
   /* Option -p clears the kernel umask, so it does not affect proper
      restoration of file permissions.  New intermediate directories will
      comply with umask at start of program.  */
 
   newdir_umask = umask (0);
-  if (same_permissions_option)
+  if (0 < same_permissions_option)
     current_umask = 0;
   else
     {
@@ -81,7 +69,7 @@ extr_init (void)
 
   /* FIXME: Just make sure we can add files in directories we create.  Maybe
      should we later remove permissions we are adding, here?  */
-  newdir_umask &= ~0300;
+  newdir_umask &= ~ MODE_WXUSR;
 }
 
 /*------------------------------------------------------------------.
@@ -91,20 +79,23 @@ extr_init (void)
 static void
 set_mode (char *file_name, struct stat *stat_info)
 {
-  /* We ought to force permission when -k is not selected, because if the
+  /* Do nothing unless we are restoring the original permissions.
+
+     We must force permission when -k and -U are not selected, because if the
      file already existed, open or creat would save the permission bits from
      the previously created file, ignoring the ones we specified.
 
-     But with -k selected, we know *we* created this file, so the mode
+     But with -k or -U selected, we know *we* created this file, so the mode
      bits were set by our open.  If the file has abnormal mode bits, we must
      chmod since writing or chown has probably reset them.  If the file is
      normal, we merely skip the chmod.  This works because we did umask (0)
      when -p, so umask will have left the specified mode alone.  */
 
-  if (!keep_old_files_option
-      || (stat_info->st_mode & (S_ISUID | S_ISGID | S_ISVTX)))
+  if (0 < same_permissions_option
+      && ((!keep_old_files_option && !unlink_first_option)
+         || (stat_info->st_mode & (S_ISUID | S_ISGID | S_ISVTX))))
     if (chmod (file_name, ~current_umask & stat_info->st_mode) < 0)
-      ERROR ((0, errno, _("%s: Cannot change mode to %0.4lo"),
+      ERROR ((0, errno, _("%s: Cannot change mode to %04lo"),
              file_name,
              (unsigned long) (~current_umask & stat_info->st_mode)));
 }
@@ -141,7 +132,7 @@ set_stat (char *file_name, struct stat *stat_info, int symlink_flag)
          if (incremental_option)
            utimbuf.actime = stat_info->st_atime;
          else
-           utimbuf.actime = now;
+           utimbuf.actime = start_time;
 
          utimbuf.modtime = stat_info->st_mtime;
 
@@ -162,10 +153,8 @@ set_stat (char *file_name, struct stat *stat_info, int symlink_flag)
      extract as the original owner.  Or else, if we are running as a user,
      leave the owner and group as they are, so we extract as that user.  */
 
-  if (we_are_root || same_owner_option)
+  if (0 < same_owner_option)
     {
-#if HAVE_LCHOWN
-
       /* When lchown exists, it should be used to change the attributes of
         the symbolic link itself.  In this case, a mere chown would change
         the attributes of the file the symbolic link is pointing to, and
@@ -173,11 +162,13 @@ set_stat (char *file_name, struct stat *stat_info, int symlink_flag)
 
       if (symlink_flag)
        {
+#if HAVE_LCHOWN
          if (lchown (file_name, stat_info->st_uid, stat_info->st_gid) < 0)
            ERROR ((0, errno, _("%s: Cannot lchown to uid %lu gid %lu"),
                    file_name,
                    (unsigned long) stat_info->st_uid,
                    (unsigned long) stat_info->st_gid));
+#endif
        }
       else
        {
@@ -186,28 +177,13 @@ set_stat (char *file_name, struct stat *stat_info, int symlink_flag)
                    file_name,
                    (unsigned long) stat_info->st_uid,
                    (unsigned long) stat_info->st_gid));
-       }
-
-#else /* not HAVE_LCHOWN */
-
-      if (!symlink_flag)
-
-       if (chown (file_name, stat_info->st_uid, stat_info->st_gid) < 0)
-         ERROR ((0, errno, _("%s: Cannot chown to uid %lu gid %lu"),
-                 file_name,
-                 (unsigned long) stat_info->st_uid,
-                 (unsigned long) stat_info->st_gid));
 
-#endif/* not HAVE_LCHOWN */
-
-      if (!symlink_flag)
-
-       /* On a few systems, and in particular, those allowing to give files
-          away, changing the owner or group destroys the suid or sgid bits.
-          So let's attempt setting these bits once more.  */
-
-       if (stat_info->st_mode & (S_ISUID | S_ISGID | S_ISVTX))
-         set_mode (file_name, stat_info);
+         /* On a few systems, and in particular, those allowing to give files
+            away, changing the owner or group destroys the suid or sgid bits.
+            So let's attempt setting these bits once more.  */
+         if (stat_info->st_mode & (S_ISUID | S_ISGID | S_ISVTX))
+           set_mode (file_name, stat_info);
+       }
     }
 }
 
@@ -226,7 +202,7 @@ make_directories (char *file_name)
   int status;
 
   for (cursor = strchr (file_name, '/');
-       cursor != NULL;
+       cursor;
        cursor = strchr (cursor + 1, '/'))
     {
       /* Avoid mkdir of empty string, if leading or double '/'.  */
@@ -240,22 +216,12 @@ make_directories (char *file_name)
        continue;
 
       *cursor = '\0';          /* truncate the path there */
-      status = mkdir (file_name, ~newdir_umask & 0777);
+      status = mkdir (file_name, ~newdir_umask & MODE_RWX);
 
       if (status == 0)
        {
-         /* Fix ownership.  */
-
-         if (we_are_root)
-           if (chown (file_name, current_stat.st_uid, current_stat.st_gid) < 0)
-             ERROR ((0, errno,
-                     _("%s: Cannot change owner to uid %lu, gid %lu"),
-                     file_name,
-                     (unsigned long) current_stat.st_uid,
-                     (unsigned long) current_stat.st_gid));
-
          print_for_mkdir (file_name, cursor - file_name,
-                          ~newdir_umask & 0777);
+                          ~newdir_umask & MODE_RWX);
          did_something = 1;
 
          *cursor = '/';
@@ -281,6 +247,25 @@ make_directories (char *file_name)
   return did_something;                /* tell them to retry if we made one */
 }
 
+/*--------------------------------------------------------------------.
+| Unlink the destination, if we are supposed to do so.               |
+| Return zero if extraction should not proceed.                              |
+`--------------------------------------------------------------------*/
+
+static int
+unlink_destination (char const *file_name)
+{
+  if (unlink_first_option
+      && !remove_any_file (file_name, recursive_unlink_option)
+      && errno != ENOENT)
+    {
+      ERROR ((0, errno, _("Cannot remove %s"), file_name));
+      return 0;
+    }
+
+  return 1;
+}
+
 /*--------------------------------------------------------------------.
 | Attempt repairing what went wrong with the extraction.  Delete an   |
 | already existing file or create missing intermediate directories.   |
@@ -294,10 +279,10 @@ maybe_recoverable (char *file_name)
   switch (errno)
     {
     case EEXIST:
-      /* Attempt deleting an existing file.  However, with -k, just stay
+      /* Attempt deleting an existing file.  However, with -k or -U, just stay
         quiet.  */
 
-      if (keep_old_files_option)
+      if (keep_old_files_option || unlink_first_option)
        return 0;
 
       return remove_any_file (file_name, 0);
@@ -321,25 +306,28 @@ maybe_recoverable (char *file_name)
 static void
 extract_sparse_file (int fd, off_t *sizeleft, off_t totalsize, char *name)
 {
-  union block *data_block;
   int sparse_ind = 0;
   size_t written;
   ssize_t count;
 
-  /* FIXME: `data_block' might be used uninitialized in this function.
-     Reported by Bruno Haible.  */
-
   /* assuming sizeleft is initially totalsize */
 
   while (*sizeleft > 0)
     {
-      data_block = find_next_block ();
-      if (data_block == NULL)
+      union block *data_block = find_next_block ();
+      if (! data_block)
        {
          ERROR ((0, 0, _("Unexpected EOF on archive file")));
          return;
        }
-      lseek (fd, sparsearray[sparse_ind].offset, 0);
+      if (lseek (fd, sparsearray[sparse_ind].offset, SEEK_SET) < 0)
+       {
+         char buf[UINTMAX_STRSIZE_BOUND];
+         ERROR ((0, errno, _("%s: lseek error at byte %s"),
+                 STRINGIFY_BIGINT (sparsearray[sparse_ind].offset, buf),
+                 name));
+         return;
+       }
       written = sparsearray[sparse_ind++].numbytes;
       while (written > BLOCKSIZE)
        {
@@ -350,6 +338,11 @@ extract_sparse_file (int fd, off_t *sizeleft, off_t totalsize, char *name)
          *sizeleft -= count;
          set_next_block_after (data_block);
          data_block = find_next_block ();
+         if (! data_block)
+           {
+             ERROR ((0, 0, _("Unexpected EOF on archive file")));
+             return;
+           }
        }
 
       count = full_write (fd, data_block->buffer, written);
@@ -371,8 +364,8 @@ extract_sparse_file (int fd, off_t *sizeleft, off_t totalsize, char *name)
       *sizeleft -= count;
       set_next_block_after (data_block);
     }
+
   free (sparsearray);
-  set_next_block_after (data_block);
 }
 
 /*----------------------------------.
@@ -422,15 +415,14 @@ extract_archive (void)
   skipcrud = 0;
   while (!absolute_names_option && CURRENT_FILE_NAME[0] == '/')
     {
-      static int warned_once = 0;
+      static int warned_once;
 
-      skipcrud++;              /* force relative path */
       if (!warned_once)
        {
          warned_once = 1;
-         WARN ((0, 0, _("\
-Removing leading `/' from absolute path names in the archive")));
+         WARN ((0, 0, _("Removing leading `/' from archive names")));
        }
+      skipcrud++;              /* force relative path */
     }
 
   /* Take a safety backup of a previously existing file.  */
@@ -467,15 +459,14 @@ Removing leading `/' from absolute path names in the archive")));
 
     case GNUTYPE_SPARSE:
       sp_array_size = 10;
-      sparsearray = (struct sp_array *)
+      sparsearray =
        xmalloc (sp_array_size * sizeof (struct sp_array));
 
       for (counter = 0; counter < SPARSES_IN_OLDGNU_HEADER; counter++)
        {
-         sparsearray[counter].offset =
-           OFF_FROM_OCT (current_header->oldgnu_header.sp[counter].offset);
-         sparsearray[counter].numbytes =
-           SIZE_FROM_OCT (current_header->oldgnu_header.sp[counter].numbytes);
+         struct sparse const *s = &current_header->oldgnu_header.sp[counter];
+         sparsearray[counter].offset = OFF_FROM_HEADER (s->offset);
+         sparsearray[counter].numbytes = SIZE_FROM_HEADER (s->numbytes);
          if (!sparsearray[counter].numbytes)
            break;
        }
@@ -491,26 +482,30 @@ Removing leading `/' from absolute path names in the archive")));
          while (1)
            {
              exhdr = find_next_block ();
+             if (! exhdr)
+               {
+                 ERROR ((0, 0, _("Unexpected EOF on archive file")));
+                 return;
+               }
              for (counter = 0; counter < SPARSES_IN_SPARSE_HEADER; counter++)
                {
+                 struct sparse const *s = &exhdr->sparse_header.sp[counter];
                  if (counter + ind > sp_array_size - 1)
                    {
                      /* Realloc the scratch area since we've run out of
                         room.  */
 
                      sp_array_size *= 2;
-                     sparsearray = (struct sp_array *)
+                     sparsearray =
                        xrealloc (sparsearray,
-                                 sp_array_size * (sizeof (struct sp_array)));
+                                 sp_array_size * sizeof (struct sp_array));
                    }
-                 /* Compare to 0, or use !(int)..., for Pyramid's dumb
-                    compiler.  */
-                 if (exhdr->sparse_header.sp[counter].numbytes == 0)
+                 if (s->numbytes[0] == 0)
                    break;
                  sparsearray[counter + ind].offset =
-                   OFF_FROM_OCT (exhdr->sparse_header.sp[counter].offset);
+                   OFF_FROM_HEADER (s->offset);
                  sparsearray[counter + ind].numbytes =
-                   SIZE_FROM_OCT (exhdr->sparse_header.sp[counter].numbytes);
+                   SIZE_FROM_HEADER (s->numbytes);
                }
              if (!exhdr->sparse_header.isextended)
                break;
@@ -538,9 +533,9 @@ Removing leading `/' from absolute path names in the archive")));
       /* FIXME: deal with protection issues.  */
 
     again_file:
-      openflag = (keep_old_files_option ?
-                 O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_EXCL :
-                 O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_TRUNC)
+      openflag = (keep_old_files_option || unlink_first_option ?
+                 O_WRONLY | O_BINARY | O_NONBLOCK | O_CREAT | O_EXCL :
+                 O_WRONLY | O_BINARY | O_NONBLOCK | O_CREAT | O_TRUNC)
        | ((typeflag == GNUTYPE_SPARSE) ? 0 : O_APPEND);
 
       /* JK - The last | is a kludge to solve the problem the O_APPEND
@@ -558,8 +553,15 @@ Removing leading `/' from absolute path names in the archive")));
          goto extract_file;
        }
 
-      if (unlink_first_option)
-       remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option);
+      if (!unlink_destination (CURRENT_FILE_NAME))
+       {
+         if (current_header->oldgnu_header.isextended)
+           skip_extended_headers ();
+         skip_file (current_stat.st_size);
+         if (backup_option)
+           undo_last_backup ();
+         break;
+       }
 
 #if O_CTG
       /* Contiguous files (on the Masscomp) have to specify the size in
@@ -574,7 +576,7 @@ Removing leading `/' from absolute path names in the archive")));
 #else /* not O_CTG */
       if (typeflag == CONTTYPE)
        {
-         static int conttype_diagnosed = 0;
+         static int conttype_diagnosed;
 
          if (!conttype_diagnosed)
            {
@@ -614,7 +616,7 @@ Removing leading `/' from absolute path names in the archive")));
             REAL interesting unless we do this.  */
 
          name_length_bis = strlen (CURRENT_FILE_NAME) + 1;
-         name = (char *) xmalloc (name_length_bis);
+         name = xmalloc (name_length_bis);
          memcpy (name, CURRENT_FILE_NAME, name_length_bis);
          size = current_stat.st_size;
          extract_sparse_file (fd, &size, current_stat.st_size, name);
@@ -636,25 +638,13 @@ Removing leading `/' from absolute path names in the archive")));
               worked.  */
 
            data_block = find_next_block ();
-           if (data_block == NULL)
+           if (! data_block)
              {
                ERROR ((0, 0, _("Unexpected EOF on archive file")));
                break;          /* FIXME: What happens, then?  */
              }
 
-           /* If the file is sparse, use the sparsearray that we created
-              before to lseek into the new file the proper amount, and to
-              see how many bytes we want to write at that position.  */
-
-#if 0
-           if (typeflag == GNUTYPE_SPARSE)
-             {
-               lseek (fd, sparsearray[sparse_ind].offset, 0);
-               written = sparsearray[sparse_ind++].numbytes;
-             }
-           else
-#endif
-             written = available_space_after (data_block);
+           written = available_space_after (data_block);
 
            if (written > size)
              written = size;
@@ -682,7 +672,7 @@ Removing leading `/' from absolute path names in the archive")));
          }
 
       if (multi_volume_option)
-       assign_string (&save_name, NULL);
+       assign_string (&save_name, 0);
 
       /* If writing to stdout, don't try to do anything to the filename;
         it doesn't exist, or we don't want to touch it anyway.  */
@@ -690,28 +680,6 @@ Removing leading `/' from absolute path names in the archive")));
       if (to_stdout_option)
        break;
 
-#if 0
-      if (current_header->header.isextended)
-       {
-         union block *exhdr;
-         int counter;
-
-         for (counter = 0; counter < 21; counter++)
-           {
-             off_t offset;
-
-             if (!exhdr->sparse_header.sp[counter].numbytes)
-               break;
-             offset = OFF_FROM_OCT (exhdr->sparse_header.sp[counter].offset);
-             written
-               = SIZE_FROM_OCT (exhdr->sparse_header.sp[counter].numbytes);
-             lseek (fd, offset, 0);
-             sstatus = full_write (fd, data_block->buffer, written);
-             if (sstatus == written)
-               continue;
-           }
-       }
-#endif
       status = close (fd);
       if (status < 0)
        {
@@ -727,9 +695,9 @@ Removing leading `/' from absolute path names in the archive")));
       if (to_stdout_option)
        break;
 
-#ifdef S_ISLNK
-      if (unlink_first_option)
-       remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option);
+#ifdef HAVE_SYMLINK
+      if (!unlink_destination (CURRENT_FILE_NAME))
+       break;
 
       while (status = symlink (current_link_name, CURRENT_FILE_NAME),
             status != 0)
@@ -754,27 +722,27 @@ Removing leading `/' from absolute path names in the archive")));
        }
       break;
 
-#else /* not S_ISLNK */
+#else
       {
-       static int warned_once = 0;
+       static int warned_once;
 
        if (!warned_once)
          {
            warned_once = 1;
-           WARN ((0, 0, _("\
-Attempting extraction of symbolic links as hard links")));
+           WARN ((0, 0,
+                  _("Attempting extraction of symbolic links as hard links")));
          }
       }
       /* Fall through.  */
 
-#endif /* not S_ISLNK */
+#endif
 
     case LNKTYPE:
       if (to_stdout_option)
        break;
 
-      if (unlink_first_option)
-       remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option);
+      if (!unlink_destination (CURRENT_FILE_NAME))
+       break;
 
     again_link:
       {
@@ -815,13 +783,13 @@ Attempting extraction of symbolic links as hard links")));
       current_stat.st_mode |= S_IFBLK;
 #endif
 
-#if defined(S_IFCHR) || defined(S_IFBLK)
+#if S_IFCHR || S_IFBLK
     make_node:
       if (to_stdout_option)
        break;
 
-      if (unlink_first_option)
-       remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option);
+      if (!unlink_destination (CURRENT_FILE_NAME))
+       break;
 
       status = mknod (CURRENT_FILE_NAME, current_stat.st_mode,
                      current_stat.st_rdev);
@@ -839,13 +807,13 @@ Attempting extraction of symbolic links as hard links")));
       break;
 #endif
 
-#ifdef S_ISFIFO
+#if HAVE_MKFIFO || defined mkfifo
     case FIFOTYPE:
       if (to_stdout_option)
        break;
 
-      if (unlink_first_option)
-       remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option);
+      if (!unlink_destination (CURRENT_FILE_NAME))
+       break;
 
       while (status = mkfifo (CURRENT_FILE_NAME, current_stat.st_mode),
             status != 0)
@@ -887,7 +855,8 @@ Attempting extraction of symbolic links as hard links")));
 
     again_dir:
       status = mkdir (CURRENT_FILE_NAME,
-                    (we_are_root ? 0 : 0300) | current_stat.st_mode);
+                     ((we_are_root ? 0 : MODE_WXUSR)
+                      | current_stat.st_mode));
       if (status != 0)
        {
          /* If the directory creation fails, let's consider immediately the
@@ -940,15 +909,15 @@ Attempting extraction of symbolic links as hard links")));
        }
 
     check_perms:
-      if (!we_are_root && 0300 != (0300 & current_stat.st_mode))
+      if (!we_are_root && MODE_WXUSR != (MODE_WXUSR & current_stat.st_mode))
        {
-         current_stat.st_mode |= 0300;
+         current_stat.st_mode |= MODE_WXUSR;
          WARN ((0, 0, _("Added write and execute permission to directory %s"),
                 CURRENT_FILE_NAME));
        }
 
 #if !MSDOS
-      /* MSDOS does not associate timestamps with directories.   In this
+      /* MSDOS does not associate time stamps with directories.   In this
         case, no need to try delaying their restoration.  */
 
       if (touch_option)
@@ -961,8 +930,7 @@ Attempting extraction of symbolic links as hard links")));
 
       else
        {
-         data = ((struct delayed_set_stat *)
-                     xmalloc (sizeof (struct delayed_set_stat)));
+         data = xmalloc (sizeof (struct delayed_set_stat));
          data->file_name = xstrdup (CURRENT_FILE_NAME);
          data->stat_info = current_stat;
          data->next = delayed_set_stat_head;
@@ -981,8 +949,8 @@ Attempting extraction of symbolic links as hard links")));
       break;
 
     case GNUTYPE_MULTIVOL:
-      ERROR ((0, 0, _("\
-Cannot extract `%s' -- file is continued from another volume"),
+      ERROR ((0, 0,
+             _("Cannot extract `%s' -- file is continued from another volume"),
              current_file_name));
       skip_file (current_stat.st_size);
       if (backup_option)
@@ -1016,7 +984,7 @@ apply_delayed_set_stat (void)
 {
   struct delayed_set_stat *data;
 
-  while (delayed_set_stat_head != NULL)
+  while (delayed_set_stat_head)
     {
       data = delayed_set_stat_head;
       delayed_set_stat_head = delayed_set_stat_head->next;
This page took 0.039324 seconds and 4 git commands to generate.