]> Dogcows Code - chaz/tar/blobdiff - src/extract.c
Update
[chaz/tar] / src / extract.c
index 26c83b1bb4e100b30a076a711e551b8a4f16ea4e..92f5452a000a9283fa92e001712e8a035bf59156 100644 (file)
@@ -7,7 +7,7 @@
 
    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the
 
    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the
-   Free Software Foundation; either version 2, or (at your option) any later
+   Free Software Foundation; either version 3, or (at your option) any later
    version.
 
    This program is distributed in the hope that it will be useful, but
    version.
 
    This program is distributed in the hope that it will be useful, but
@@ -37,7 +37,8 @@ enum permstatus
   /* This file may have existed already; its permissions are unknown.  */
   UNKNOWN_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
   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;
 
     {
       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.  */
         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;
          && typeflag != DIRTYPE
          && typeflag != GNUTYPE_DUMPDIR)
        return;
@@ -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
 /* 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.
    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
        }
 
       /* 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)
     }
 
   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.  */
         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 (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
        {
 #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)
 }
 
 /* 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 =
          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;
        }
          data->permstatus = ARCHIVED_PERMSTATUS;
          return;
        }
@@ -626,8 +638,9 @@ extract_dir (char *file_name, int typeflag)
   else if (typeflag == GNUTYPE_DUMPDIR)
     skip_member ();
 
   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)))
     {
 
   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,
     {
       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,
                        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
 
 
 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));
 {
   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
 
 #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;
   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.  */
 
 
   /* FIXME: deal with protection issues.  */
 
@@ -745,11 +761,12 @@ extract_file (char *file_name, int typeflag)
   else
     {
       do
   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)
        {
       while (fd < 0 && maybe_recoverable (file_name, &interdir_made));
 
       if (fd < 0)
        {
+         skip_member ();
          open_error (file_name);
          return 1;
        }
          open_error (file_name);
          return 1;
        }
@@ -810,7 +827,7 @@ extract_file (char *file_name, int typeflag)
   if (to_command_option)
     sys_wait_command ();
   else
   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);
              (old_files_option == OVERWRITE_OLD_FILES ?
               UNKNOWN_PERMSTATUS : ARCHIVED_PERMSTATUS),
              typeflag);
@@ -897,10 +914,12 @@ create_placeholder_file (char *file_name, bool is_symlink, int *interdir_made)
 static int
 extract_link (char *file_name, int typeflag)
 {
 static int
 extract_link (char *file_name, int typeflag)
 {
-  char const *link_name = safer_name_suffix (current_stat_info.link_name,
-                                             true, absolute_names_option);
   int interdir_made = 0;
   int interdir_made = 0;
+  char const *link_name;
 
 
+  transform_member_name (&current_stat_info.link_name, xform_link);
+  link_name = current_stat_info.link_name;
+  
   if (! absolute_names_option && contains_dot_dot (link_name))
     return create_placeholder_file (file_name, false, &interdir_made);
 
   if (! absolute_names_option && contains_dot_dot (link_name))
     return create_placeholder_file (file_name, false, &interdir_made);
 
@@ -955,6 +974,8 @@ extract_symlink (char *file_name, int typeflag)
   int status;
   int interdir_made = 0;
 
   int status;
   int interdir_made = 0;
 
+  transform_member_name (&current_stat_info.link_name, xform_symlink);
+
   if (! absolute_names_option
       && (IS_ABSOLUTE_FILE_NAME (current_stat_info.link_name)
          || contains_dot_dot (current_stat_info.link_name)))
   if (! absolute_names_option
       && (IS_ABSOLUTE_FILE_NAME (current_stat_info.link_name)
          || contains_dot_dot (current_stat_info.link_name)))
@@ -988,16 +1009,19 @@ extract_node (char *file_name, int typeflag)
 {
   int status;
   int interdir_made = 0;
 {
   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
 
   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
                    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;
 }
              ARCHIVED_PERMSTATUS, typeflag);
   return status;
 }
@@ -1009,13 +1033,16 @@ extract_fifo (char *file_name, int typeflag)
 {
   int status;
   int interdir_made = 0;
 {
   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)
     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);
              ARCHIVED_PERMSTATUS, typeflag);
   else
     mkfifo_error (file_name);
@@ -1023,13 +1050,6 @@ extract_fifo (char *file_name, int typeflag)
 }
 #endif
 
 }
 #endif
 
-static int
-extract_mangle_wrapper (char *file_name, int typeflag)
-{
-  extract_mangle ();
-  return 0;
-}
-
 static int
 extract_volhdr (char *file_name, int typeflag)
 {
 static int
 extract_volhdr (char *file_name, int typeflag)
 {
@@ -1121,10 +1141,6 @@ prepare_to_extract (char const *file_name, int typeflag, tar_extractor_t *fun)
       *fun = extract_volhdr;
       break;
 
       *fun = extract_volhdr;
       break;
 
-    case GNUTYPE_NAMES:
-      *fun = extract_mangle_wrapper;
-      break;
-
     case GNUTYPE_MULTIVOL:
       ERROR ((0, 0,
              _("%s: Cannot extract -- file is continued from another volume"),
     case GNUTYPE_MULTIVOL:
       ERROR ((0, 0,
              _("%s: Cannot extract -- file is continued from another volume"),
This page took 0.027545 seconds and 4 git commands to generate.