Ver Fonte

this matches historical practice.
(unlink_destination): New function, which checks for unlink failures.
(maybe_recoverable): Stay quiet if -U.
(extract_archive): Use O_EXCL if unlink_first_option.
Report unlink failures.
Use HAVE_SYMLINK, not S_ISLNK, to determine whether symlink exists.
Use HAVE_MKFIFO || defined mkfifo, not S_ISFIFO, to determine whether
mkfifo exists.

Paul Eggert há 26 anos atrás
pai
commit
9cb6f5c466
1 ficheiros alterados com 50 adições e 21 exclusões
  1. 50 21
      src/extract.c

+ 50 - 21
src/extract.c

@@ -91,18 +91,21 @@ extr_init (void)
 static void
 static void
 set_mode (char *file_name, struct stat *stat_info)
 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
      file already existed, open or creat would save the permission bits from
      the previously created file, ignoring the ones we specified.
      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
      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
      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)
      normal, we merely skip the chmod.  This works because we did umask (0)
      when -p, so umask will have left the specified mode alone.  */
      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 ((we_are_root || 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)
     if (chmod (file_name, ~current_umask & stat_info->st_mode) < 0)
       ERROR ((0, errno, _("%s: Cannot change mode to %04lo"),
       ERROR ((0, errno, _("%s: Cannot change mode to %04lo"),
 	      file_name,
 	      file_name,
@@ -281,6 +284,25 @@ make_directories (char *file_name)
   return did_something;		/* tell them to retry if we made one */
   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   |
 | Attempt repairing what went wrong with the extraction.  Delete an   |
 | already existing file or create missing intermediate directories.   |
 | already existing file or create missing intermediate directories.   |
@@ -294,10 +316,10 @@ maybe_recoverable (char *file_name)
   switch (errno)
   switch (errno)
     {
     {
     case EEXIST:
     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.  */
 	 quiet.  */
 
 
-      if (keep_old_files_option)
+      if (keep_old_files_option || unlink_first_option)
 	return 0;
 	return 0;
 
 
       return remove_any_file (file_name, 0);
       return remove_any_file (file_name, 0);
@@ -541,7 +563,7 @@ Removing leading `/' from absolute path names in the archive")));
       /* FIXME: deal with protection issues.  */
       /* FIXME: deal with protection issues.  */
 
 
     again_file:
     again_file:
-      openflag = (keep_old_files_option ?
+      openflag = (keep_old_files_option || unlink_first_option ?
 		  O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_EXCL :
 		  O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_EXCL :
 		  O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_TRUNC)
 		  O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_TRUNC)
 	| ((typeflag == GNUTYPE_SPARSE) ? 0 : O_APPEND);
 	| ((typeflag == GNUTYPE_SPARSE) ? 0 : O_APPEND);
@@ -561,8 +583,15 @@ Removing leading `/' from absolute path names in the archive")));
 	  goto extract_file;
 	  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
 #if O_CTG
       /* Contiguous files (on the Masscomp) have to specify the size in
       /* Contiguous files (on the Masscomp) have to specify the size in
@@ -696,9 +725,9 @@ Removing leading `/' from absolute path names in the archive")));
       if (to_stdout_option)
       if (to_stdout_option)
 	break;
 	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),
       while (status = symlink (current_link_name, CURRENT_FILE_NAME),
 	     status != 0)
 	     status != 0)
@@ -723,7 +752,7 @@ Removing leading `/' from absolute path names in the archive")));
 	}
 	}
       break;
       break;
 
 
-#else /* not S_ISLNK */
+#else
       {
       {
 	static int warned_once = 0;
 	static int warned_once = 0;
 
 
@@ -736,14 +765,14 @@ Attempting extraction of symbolic links as hard links")));
       }
       }
       /* Fall through.  */
       /* Fall through.  */
 
 
-#endif /* not S_ISLNK */
+#endif
 
 
     case LNKTYPE:
     case LNKTYPE:
       if (to_stdout_option)
       if (to_stdout_option)
 	break;
 	break;
 
 
-      if (unlink_first_option)
-	remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option);
+      if (!unlink_destination (CURRENT_FILE_NAME))
+	break;
 
 
     again_link:
     again_link:
       {
       {
@@ -789,8 +818,8 @@ Attempting extraction of symbolic links as hard links")));
       if (to_stdout_option)
       if (to_stdout_option)
 	break;
 	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,
       status = mknod (CURRENT_FILE_NAME, current_stat.st_mode,
 		      current_stat.st_rdev);
 		      current_stat.st_rdev);
@@ -808,13 +837,13 @@ Attempting extraction of symbolic links as hard links")));
       break;
       break;
 #endif
 #endif
 
 
-#ifdef S_ISFIFO
+#if HAVE_MKFIFO || defined mkfifo
     case FIFOTYPE:
     case FIFOTYPE:
       if (to_stdout_option)
       if (to_stdout_option)
 	break;
 	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),
       while (status = mkfifo (CURRENT_FILE_NAME, current_stat.st_mode),
 	     status != 0)
 	     status != 0)