]> Dogcows Code - chaz/tar/blobdiff - src/extract.c
* NEWS: Fix some race conditions with tar -x --same-owner.
[chaz/tar] / src / extract.c
index d346f79ac272a04344ed7e78ccafe55aa28a14fa..c699cb239abff2828ec24f44576f6d4b9588f0b6 100644 (file)
@@ -37,7 +37,8 @@ enum permstatus
   /* This file may have existed already; its permissions are unknown.  */
   UNKNOWN_PERMSTATUS,
 
-  /* This file was created using the permissions from the archive.  */
+  /* This file was created using the permissions from the archive,
+     except with S_IRWXG | S_IRWXO masked out if 0 < same_owner_option.  */
   ARCHIVED_PERMSTATUS,
 
   /* This is an intermediate directory; the archive did not specify
@@ -149,12 +150,15 @@ set_mode (char const *file_name,
     {
       mode = stat_info->st_mode;
 
-      /* If we created the file and it has a usual mode, then its mode
-        is normally set correctly already.  But on many hosts, some
+      /* If we created the file and it has a mode that we set already
+        with O_CREAT, then its mode is often set correctly already.
+        But if we are changing ownership, the mode's group and and
+        other permission bits were omitted originally, so it's less
+        likely that the mode is OK now.  Also, on many hosts, some
         directories inherit the setgid bits from their parents, so we
         we must set directories' modes explicitly.  */
-      if (permstatus == ARCHIVED_PERMSTATUS
-         && ! (mode & ~ MODE_RWX)
+      if ((permstatus == ARCHIVED_PERMSTATUS
+          && ! (mode & ~ (0 < same_owner_option ? S_IRWXU : MODE_RWX)))
          && typeflag != DIRTYPE
          && typeflag != GNUTYPE_DUMPDIR)
        return;
@@ -193,7 +197,7 @@ check_time (char const *file_name, struct timespec t)
   if (t.tv_sec <= 0)
     WARN ((0, 0, _("%s: implausibly old time stamp %s"),
           file_name, tartime (t, true)));
-  else if (timespec_cmp (start_time, t) < 0)
+  else if (timespec_cmp (volume_start_time, t) < 0)
     {
       struct timespec now;
       gettime (&now);
@@ -217,7 +221,7 @@ check_time (char const *file_name, struct timespec t)
 /* Restore stat attributes (owner, group, mode and times) for
    FILE_NAME, using information given in *ST.
    If CUR_INFO is nonzero, *CUR_INFO is the
-   file's currernt status.
+   file's current status.
    If not restoring permissions, invert the
    INVERT_PERMISSIONS bits from the file's current permissions.
    PERMSTATUS specifies the status of the file's permissions.
@@ -265,11 +269,11 @@ set_stat (char const *file_name,
        }
 
       /* Some systems allow non-root users to give files away.  Once this
-        done, it is not possible anymore to change file permissions, so we
-        have to set permissions prior to possibly giving files away.  */
-
-      set_mode (file_name, &st->stat, cur_info,
-               invert_permissions, permstatus, typeflag);
+        done, it is not possible anymore to change file permissions.
+        However, setting file permissions now would be incorrect, since
+        they would apply to the wrong user, and there would be a race
+        condition.  So, don't use systems that allow non-root users to
+        give files away.  */
     }
 
   if (0 < same_owner_option && permstatus != INTERDIR_PERMSTATUS)
@@ -278,29 +282,36 @@ set_stat (char const *file_name,
         the symbolic link itself.  In this case, a mere chown would change
         the attributes of the file the symbolic link is pointing to, and
         should be avoided.  */
+      int chown_result = 1;
 
       if (typeflag == SYMTYPE)
        {
 #if HAVE_LCHOWN
-         if (lchown (file_name, st->stat.st_uid, st->stat.st_gid) < 0)
-           chown_error_details (file_name,
-                                st->stat.st_uid, st->stat.st_gid);
+         chown_result = lchown (file_name, st->stat.st_uid, st->stat.st_gid);
 #endif
        }
       else
        {
-         if (chown (file_name, st->stat.st_uid, st->stat.st_gid) < 0)
-           chown_error_details (file_name,
-                                st->stat.st_uid, st->stat.st_gid);
-
-         /* 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 (st->stat.st_mode & (S_ISUID | S_ISGID | S_ISVTX))
-           set_mode (file_name, &st->stat, 0,
-                     invert_permissions, permstatus, typeflag);
+         chown_result = chown (file_name, st->stat.st_uid, st->stat.st_gid);
+       }
+
+      if (chown_result == 0)
+       {
+         /* Changing the owner can flip st_mode bits in some cases, so
+            ignore cur_info if it might be obsolete now.  */
+         if (cur_info
+             && cur_info->st_mode & S_IXUGO
+             && cur_info->st_mode & (S_ISUID | S_ISGID))
+           cur_info = NULL;
        }
+      else if (chown_result < 0)
+       chown_error_details (file_name,
+                            st->stat.st_uid, st->stat.st_gid);
     }
+
+  if (typeflag != SYMTYPE)
+    set_mode (file_name, &st->stat, cur_info,
+             invert_permissions, permstatus, typeflag);
 }
 
 /* Remember to restore stat attributes (owner, group, mode and times)
@@ -374,7 +385,8 @@ repair_delayed_set_stat (char const *dir,
          data->atime = current_stat_info.atime;
          data->mtime = current_stat_info.mtime;
          data->invert_permissions =
-           (MODE_RWX & (current_stat_info.stat.st_mode ^ st.st_mode));
+           ((current_stat_info.stat.st_mode ^ st.st_mode)
+            & MODE_RWX & ~ current_umask);
          data->permstatus = ARCHIVED_PERMSTATUS;
          return;
        }
@@ -626,8 +638,9 @@ extract_dir (char *file_name, int typeflag)
   else if (typeflag == GNUTYPE_DUMPDIR)
     skip_member ();
 
-  mode = (current_stat_info.stat.st_mode |
-          (we_are_root ? 0 : MODE_WXUSR)) & MODE_RWX;
+  mode = current_stat_info.stat.st_mode | (we_are_root ? 0 : MODE_WXUSR);
+  if (0 < same_owner_option || current_stat_info.stat.st_mode & ~ MODE_RWX)
+    mode &= S_IRWXU;
 
   while ((status = mkdir (file_name, mode)))
     {
@@ -670,7 +683,8 @@ extract_dir (char *file_name, int typeflag)
     {
       if (status == 0)
        delay_set_stat (file_name, &current_stat_info,
-                       MODE_RWX & (mode ^ current_stat_info.stat.st_mode),
+                       ((mode ^ current_stat_info.stat.st_mode)
+                        & MODE_RWX & ~ current_umask),
                        ARCHIVED_PERMSTATUS);
       else /* For an already existing directory, invert_perms must be 0 */
        delay_set_stat (file_name, &current_stat_info,
@@ -682,14 +696,13 @@ extract_dir (char *file_name, int typeflag)
 
 
 static int
-open_output_file (char *file_name, int typeflag)
+open_output_file (char *file_name, int typeflag, mode_t mode)
 {
   int fd;
   int openflag = (O_WRONLY | O_BINARY | O_CREAT
                  | (old_files_option == OVERWRITE_OLD_FILES
                     ? O_TRUNC
                     : O_EXCL));
-  mode_t mode = current_stat_info.stat.st_mode & MODE_RWX & ~ current_umask;
 
 #if O_CTG
   /* Contiguous files (on the Masscomp) have to specify the size in
@@ -728,6 +741,9 @@ extract_file (char *file_name, int typeflag)
   size_t count;
   size_t written;
   int interdir_made = 0;
+  mode_t mode = current_stat_info.stat.st_mode & MODE_RWX & ~ current_umask;
+  mode_t invert_permissions =
+    0 < same_owner_option ? mode & (S_IRWXG | S_IRWXO) : 0;
 
   /* FIXME: deal with protection issues.  */
 
@@ -745,7 +761,7 @@ extract_file (char *file_name, int typeflag)
   else
     {
       do
-       fd = open_output_file (file_name, typeflag);
+       fd = open_output_file (file_name, typeflag, mode ^ invert_permissions);
       while (fd < 0 && maybe_recoverable (file_name, &interdir_made));
 
       if (fd < 0)
@@ -810,7 +826,7 @@ extract_file (char *file_name, int typeflag)
   if (to_command_option)
     sys_wait_command ();
   else
-    set_stat (file_name, &current_stat_info, NULL, 0,
+    set_stat (file_name, &current_stat_info, NULL, invert_permissions,
              (old_files_option == OVERWRITE_OLD_FILES ?
               UNKNOWN_PERMSTATUS : ARCHIVED_PERMSTATUS),
              typeflag);
@@ -871,7 +887,7 @@ create_placeholder_file (char *file_name, bool is_symlink, int *interdir_made)
       if (h && ! h->after_links
          && strncmp (file_name, h->file_name, h->file_name_len) == 0
          && ISSLASH (file_name[h->file_name_len])
-         && (base_name (file_name) == file_name + h->file_name_len + 1))
+         && (last_component (file_name) == file_name + h->file_name_len + 1))
        {
          do
            {
@@ -988,16 +1004,19 @@ extract_node (char *file_name, int typeflag)
 {
   int status;
   int interdir_made = 0;
+  mode_t mode = current_stat_info.stat.st_mode & ~ current_umask;
+  mode_t invert_permissions =
+    0 < same_owner_option ? mode & (S_IRWXG | S_IRWXO) : 0;
 
   do
-    status = mknod (file_name, current_stat_info.stat.st_mode,
+    status = mknod (file_name, mode ^ invert_permissions,
                    current_stat_info.stat.st_rdev);
   while (status && maybe_recoverable (file_name, &interdir_made));
 
   if (status != 0)
     mknod_error (file_name);
   else
-    set_stat (file_name, &current_stat_info, NULL, 0,
+    set_stat (file_name, &current_stat_info, NULL, invert_permissions,
              ARCHIVED_PERMSTATUS, typeflag);
   return status;
 }
@@ -1009,13 +1028,16 @@ extract_fifo (char *file_name, int typeflag)
 {
   int status;
   int interdir_made = 0;
+  mode_t mode = current_stat_info.stat.st_mode & ~ current_umask;
+  mode_t invert_permissions =
+    0 < same_owner_option ? mode & (S_IRWXG | S_IRWXO) : 0;
 
-  while ((status = mkfifo (file_name, current_stat_info.stat.st_mode)))
+  while ((status = mkfifo (file_name, mode)) != 0)
     if (!maybe_recoverable (file_name, &interdir_made))
       break;
 
   if (status == 0)
-    set_stat (file_name, &current_stat_info, NULL, 0,
+    set_stat (file_name, &current_stat_info, NULL, invert_permissions,
              ARCHIVED_PERMSTATUS, typeflag);
   else
     mkfifo_error (file_name);
@@ -1024,13 +1046,14 @@ extract_fifo (char *file_name, int typeflag)
 #endif
 
 static int
-extract_mangle_wrapper (char *file_name, int typeflag)
+extract_volhdr (char *file_name, int typeflag)
 {
-  extract_mangle ();
+  if (verbose_option)
+    fprintf (stdlis, _("Reading %s\n"), quote (current_stat_info.file_name));
+  skip_member ();
   return 0;
 }
 
-
 static int
 extract_failure (char *file_name, int typeflag)
 {
@@ -1110,13 +1133,7 @@ prepare_to_extract (char const *file_name, int typeflag, tar_extractor_t *fun)
       break;
 
     case GNUTYPE_VOLHDR:
-      if (verbose_option)
-       fprintf (stdlis, _("Reading %s\n"), quote (current_stat_info.file_name));
-      *fun = NULL;
-      break;
-
-    case GNUTYPE_NAMES:
-      *fun = extract_mangle_wrapper;
+      *fun = extract_volhdr;
       break;
 
     case GNUTYPE_MULTIVOL:
@@ -1177,51 +1194,36 @@ void
 extract_archive (void)
 {
   char typeflag;
-  char *file_name;
   tar_extractor_t fun;
 
   set_next_block_after (current_header);
   decode_header (current_header, &current_stat_info, &current_format, 1);
-
-  if (interactive_option && !confirm ("extract", current_stat_info.file_name))
+  if (!current_stat_info.file_name[0]
+      || (interactive_option
+         && !confirm ("extract", current_stat_info.file_name)))
     {
       skip_member ();
       return;
     }
 
   /* Print the block from current_header and current_stat.  */
-
   if (verbose_option)
     print_header (&current_stat_info, -1);
 
-  file_name = safer_name_suffix (current_stat_info.file_name,
-                                 false, absolute_names_option);
-  if (strip_name_components)
-    {
-      size_t prefix_len = stripped_prefix_len (file_name,
-                                              strip_name_components);
-      if (prefix_len == (size_t) -1)
-       {
-         skip_member ();
-         return;
-       }
-      file_name += prefix_len;
-    }
-
   /* Restore stats for all non-ancestor directories, unless
      it is an incremental archive.
      (see NOTICE in the comment to delay_set_stat above) */
   if (!delay_directory_restore_option)
-    apply_nonancestor_delayed_set_stat (file_name, 0);
+    apply_nonancestor_delayed_set_stat (current_stat_info.file_name, 0);
 
   /* Take a safety backup of a previously existing file.  */
 
   if (backup_option)
-    if (!maybe_backup_file (file_name, 0))
+    if (!maybe_backup_file (current_stat_info.file_name, 0))
       {
        int e = errno;
        ERROR ((0, e, _("%s: Was unable to backup this file"),
-               quotearg_colon (file_name)));
+               quotearg_colon (current_stat_info.file_name)));
        skip_member ();
        return;
       }
@@ -1231,9 +1233,10 @@ extract_archive (void)
   typeflag = sparse_member_p (&current_stat_info) ?
                   GNUTYPE_SPARSE : current_header->header.typeflag;
 
-  if (prepare_to_extract (file_name, typeflag, &fun))
+  if (prepare_to_extract (current_stat_info.file_name, typeflag, &fun))
     {
-      if (fun && (*fun) (file_name, typeflag) && backup_option)
+      if (fun && (*fun) (current_stat_info.file_name, typeflag)
+         && backup_option)
        undo_last_backup ();
     }
   else
@@ -1339,10 +1342,10 @@ rename_directory (char *src, char *dst)
              e = errno;
            }
          break;
-                   
+
        case EXDEV:
          /* FIXME: Fall back to recursive copying */
-         
+
        default:
          break;
        }
@@ -1354,7 +1357,7 @@ rename_directory (char *src, char *dst)
     }
   return true;
 }
-      
+
 void
 fatal_exit (void)
 {
This page took 0.038607 seconds and 4 git commands to generate.