Răsfoiți Sursa

Further rewrite

Sergey Poznyakoff 20 ani în urmă
părinte
comite
5179c0b5e6
1 a modificat fișierele cu 219 adăugiri și 222 ștergeri
  1. 219 222
      src/extract.c

+ 219 - 222
src/extract.c

@@ -450,43 +450,6 @@ file_newer_p (const char *file_name, struct tar_stat_info *tar_stat)
   return false;
   return false;
 }
 }
 
 
-/* Prepare to extract a file.
-   Return zero if extraction should not proceed.  */
-
-static int
-prepare_to_extract (char const *file_name)
-{
-  if (to_stdout_option)
-    return 0;
-
-  switch (old_files_option)
-    {
-    case UNLINK_FIRST_OLD_FILES:
-      if (!remove_any_file (file_name, 
-                            recursive_unlink_option ? RECURSIVE_REMOVE_OPTION 
-                                                      : ORDINARY_REMOVE_OPTION)
-	  && errno && errno != ENOENT)
-	{
-	  unlink_error (file_name);
-	  return 0;
-	}
-      break;
-
-    case KEEP_NEWER_FILES:
-      if (file_newer_p (file_name, &current_stat_info))
-	{
-	  WARN ((0, 0, _("Current %s is newer"), quote (file_name)));
-	  return 0;
-	}
-      break;
-
-    default:
-      break;
-    }
-
-  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.
    Return nonzero if we somewhat increased our chances at a successful
    Return nonzero if we somewhat increased our chances at a successful
@@ -602,8 +565,11 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_symlinks)
     }
     }
 }
 }
 
 
+
 
 
-void
+/* Extractor functions for various member types */
+
+static int
 extract_dir (char *file_name, int typeflag)
 extract_dir (char *file_name, int typeflag)
 {
 {
   int status;
   int status;
@@ -618,48 +584,41 @@ extract_dir (char *file_name, int typeflag)
 
 
   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)) & MODE_RWX;
 
 
-  status = prepare_to_extract (file_name);
-  if (status == 0)
-    return;
+  while ((status = mkdir (file_name, mode)))
+    {
+      if (errno == EEXIST
+	  && (interdir_made
+	      || old_files_option == DEFAULT_OLD_FILES
+	      || old_files_option == OVERWRITE_OLD_FILES))
+	{
+	  struct stat st;
+	  if (stat (file_name, &st) == 0)
+	    {
+	      if (interdir_made)
+		{
+		  repair_delayed_set_stat (file_name, &st);
+		  return 0;
+		}
+	      if (S_ISDIR (st.st_mode))
+		{
+		  mode = st.st_mode & ~ current_umask;
+		  break;
+		}
+	    }
+	  errno = EEXIST;
+	}
+      
+      if (maybe_recoverable (file_name, &interdir_made))
+	continue;
+      
+      if (errno != EEXIST)
+	{
+	  mkdir_error (file_name);
+	  return 1;
+	}
+      break;
+    }
   
   
-  if (status > 0)
-    while ((status = mkdir (file_name, mode)))
-      {
-	if (errno == EEXIST
-	    && (interdir_made
-		|| old_files_option == DEFAULT_OLD_FILES
-		|| old_files_option == OVERWRITE_OLD_FILES))
-	  {
-	    struct stat st;
-	    if (stat (file_name, &st) == 0)
-	      {
-		if (interdir_made)
-		  {
-		    repair_delayed_set_stat (file_name, &st);
-		    return;
-		  }
-		if (S_ISDIR (st.st_mode))
-		  {
-		    mode = st.st_mode & ~ current_umask;
-		    break;
-		  }
-	      }
-	    errno = EEXIST;
-	  }
-	
-	if (maybe_recoverable (file_name, &interdir_made))
-	  continue;
-
-	if (errno != EEXIST)
-	  {
-	    mkdir_error (file_name);
-	    if (backup_option)
-	    undo_last_backup ();
-	    return;
-	  }
-	break;
-      }
-
   if (status == 0
   if (status == 0
       || old_files_option == DEFAULT_OLD_FILES
       || old_files_option == DEFAULT_OLD_FILES
       || old_files_option == OVERWRITE_OLD_FILES)
       || old_files_option == OVERWRITE_OLD_FILES)
@@ -668,6 +627,8 @@ extract_dir (char *file_name, int typeflag)
 		    (status == 0
 		    (status == 0
 		     ? ARCHIVED_PERMSTATUS
 		     ? ARCHIVED_PERMSTATUS
 		     : UNKNOWN_PERMSTATUS));
 		     : UNKNOWN_PERMSTATUS));
+
+  return status;
 }
 }
 
 
 
 
@@ -708,7 +669,7 @@ open_output_file (char *file_name, int typeflag)
   return fd;
   return fd;
 }
 }
 
 
-static void
+static int
 extract_file (char *file_name, int typeflag)
 extract_file (char *file_name, int typeflag)
 {
 {
   int fd;
   int fd;
@@ -721,32 +682,20 @@ extract_file (char *file_name, int typeflag)
   
   
   /* FIXME: deal with protection issues.  */
   /* FIXME: deal with protection issues.  */
 
 
-  do
+  if (to_stdout_option)
+    fd = STDOUT_FILENO;
+  else
     {
     {
-      if (to_stdout_option)
-	fd = STDOUT_FILENO;
-      else
+      do
+	fd = open_output_file (file_name, typeflag);
+      while (fd < 0 && maybe_recoverable (file_name, &interdir_made));
+    
+      if (fd < 0)
 	{
 	{
-	  if (! prepare_to_extract (file_name))
-	    {
-	      skip_member ();
-	      if (backup_option)
-		undo_last_backup ();
-	      return;
-	    }
-	  fd = open_output_file (file_name, typeflag);
+	  open_error (file_name);
+	  return 1;
 	}
 	}
     }
     }
-  while (fd < 0 && maybe_recoverable (file_name, &interdir_made));
-    
-  if (fd < 0)
-    {
-      open_error (file_name);
-      skip_member ();
-      if (backup_option)
-	undo_last_backup ();
-      return;
-    }
     
     
   if (current_stat_info.is_sparse)
   if (current_stat_info.is_sparse)
     sparse_extract_file (fd, &current_stat_info, &size);
     sparse_extract_file (fd, &current_stat_info, &size);
@@ -783,7 +732,8 @@ extract_file (char *file_name, int typeflag)
 			      (data_block->buffer + written - 1));
 			      (data_block->buffer + written - 1));
 	if (count != written)
 	if (count != written)
 	  {
 	  {
-	    write_error_details (file_name, count, written);
+	    write_error_details (file_name, count, written); /* FIXME: shouldn't we
+								restore from backup? */
 	    break;
 	    break;
 	  }
 	  }
       }
       }
@@ -797,38 +747,30 @@ extract_file (char *file_name, int typeflag)
      it doesn't exist, or we don't want to touch it anyway.  */
      it doesn't exist, or we don't want to touch it anyway.  */
 
 
   if (to_stdout_option)
   if (to_stdout_option)
-    return;
+    return 0;
 
 
   status = close (fd);
   status = close (fd);
   if (status < 0)
   if (status < 0)
-    {
-      close_error (file_name);
-      if (backup_option)
-	undo_last_backup ();
-    }
+    close_error (file_name);
 
 
   set_stat (file_name, &current_stat_info.stat, 0, 0,
   set_stat (file_name, &current_stat_info.stat, 0, 0,
-	    (old_files_option == OVERWRITE_OLD_FILES
-	     ? UNKNOWN_PERMSTATUS
-	     : ARCHIVED_PERMSTATUS),
+	    (old_files_option == OVERWRITE_OLD_FILES ?
+	          UNKNOWN_PERMSTATUS : ARCHIVED_PERMSTATUS),
 	    typeflag);
 	    typeflag);
+
+  return status;
 }  
 }  
 
 
-static void
-extract_link (char *file_name)
+static int
+extract_link (char *file_name, int typeflag)
 {
 {
-  char const *link_name;
+  char const *link_name = safer_name_suffix (current_stat_info.link_name, true);
   int interdir_made = 0;
   int interdir_made = 0;
   
   
-  if (!prepare_to_extract (file_name))
-    return;
-
   do
   do
     {
     {
       struct stat st1, st2;
       struct stat st1, st2;
       int e;
       int e;
-      link_name = safer_name_suffix (current_stat_info.link_name, true);
-      
       int status = link (link_name, file_name);
       int status = link (link_name, file_name);
       e = errno;
       e = errno;
 
 
@@ -848,14 +790,14 @@ extract_link (char *file_name)
 		  ds->sources = p;
 		  ds->sources = p;
 		  break;
 		  break;
 		}
 		}
-	  return;
+	  return 0;
 	}
 	}
       else if ((e == EEXIST && strcmp (link_name, file_name) == 0)
       else if ((e == EEXIST && strcmp (link_name, file_name) == 0)
 	       || (lstat (link_name, &st1) == 0
 	       || (lstat (link_name, &st1) == 0
 		   && lstat (file_name, &st2) == 0
 		   && lstat (file_name, &st2) == 0
 		   && st1.st_dev == st2.st_dev
 		   && st1.st_dev == st2.st_dev
 		   && st1.st_ino == st2.st_ino))
 		   && st1.st_ino == st2.st_ino))
-	return;
+	return 0;
       
       
       errno = e;
       errno = e;
     }
     }
@@ -864,27 +806,23 @@ extract_link (char *file_name)
   if (!(incremental_option && errno == EEXIST))
   if (!(incremental_option && errno == EEXIST))
     {
     {
       link_error (link_name, file_name);
       link_error (link_name, file_name);
-      if (backup_option)
-	undo_last_backup ();
+      return 1;
     }
     }
+  return 0;
 }  
 }  
 
 
-static void
-extract_symlink (char *file_name)
+static int
+extract_symlink (char *file_name, int typeflag)
 {
 {
 #ifdef HAVE_SYMLINK
 #ifdef HAVE_SYMLINK
   int status, fd;
   int status, fd;
   int interdir_made = 0;
   int interdir_made = 0;
   
   
-  if (! prepare_to_extract (file_name))
-    return;
-
   if (absolute_names_option
   if (absolute_names_option
       || ! (IS_ABSOLUTE_FILE_NAME (current_stat_info.link_name)
       || ! (IS_ABSOLUTE_FILE_NAME (current_stat_info.link_name)
 	    || contains_dot_dot (current_stat_info.link_name)))
 	    || contains_dot_dot (current_stat_info.link_name)))
     {
     {
-      while (status = symlink (current_stat_info.link_name, file_name),
-	     status != 0)
+      while ((status = symlink (current_stat_info.link_name, file_name)))
 	if (!maybe_recoverable (file_name, &interdir_made))
 	if (!maybe_recoverable (file_name, &interdir_made))
 	  break;
 	  break;
 
 
@@ -958,8 +896,7 @@ extract_symlink (char *file_name)
 	}
 	}
     }
     }
 
 
-  if (status != 0 && backup_option)
-    undo_last_backup ();
+  return status;
 
 
 #else
 #else
   static int warned_once;
   static int warned_once;
@@ -969,48 +906,37 @@ extract_symlink (char *file_name)
       warned_once = 1;
       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")));
     }
     }
-  extract_link (file_name);  
+  return extract_link (file_name, typeflag);  
 #endif
 #endif
 }  
 }  
 
 
 #if S_IFCHR || S_IFBLK
 #if S_IFCHR || S_IFBLK
-static void
+static int
 extract_node (char *file_name, int typeflag)
 extract_node (char *file_name, int typeflag)
 {
 {
   int status;
   int status;
   int interdir_made = 0;
   int interdir_made = 0;
   
   
   do
   do
-    {
-      if (! prepare_to_extract (file_name))
-	return;
-  
-      status = mknod (file_name, current_stat_info.stat.st_mode,
-		      current_stat_info.stat.st_rdev);
-    }
+    status = mknod (file_name, current_stat_info.stat.st_mode,
+		    current_stat_info.stat.st_rdev);
   while (status && maybe_recoverable (file_name, &interdir_made));
   while (status && maybe_recoverable (file_name, &interdir_made));
   
   
   if (status != 0)
   if (status != 0)
-    {
-      mknod_error (file_name);
-      if (backup_option)
-	undo_last_backup ();
-    }
+    mknod_error (file_name);
   else
   else
     set_stat (file_name, &current_stat_info.stat, 0, 0, ARCHIVED_PERMSTATUS, typeflag);
     set_stat (file_name, &current_stat_info.stat, 0, 0, ARCHIVED_PERMSTATUS, typeflag);
+  return status;
 }
 }
 #endif
 #endif
 
 
 #if HAVE_MKFIFO || defined mkfifo
 #if HAVE_MKFIFO || defined mkfifo
-static void
+static int
 extract_fifo (char *file_name, int typeflag)
 extract_fifo (char *file_name, int typeflag)
 {
 {
   int status;
   int status;
   int interdir_made = 0;
   int interdir_made = 0;
   
   
-  if (! prepare_to_extract (file_name))
-    return;
-
   while ((status = mkfifo (file_name, current_stat_info.stat.st_mode)))
   while ((status = mkfifo (file_name, current_stat_info.stat.st_mode)))
     if (!maybe_recoverable (file_name, &interdir_made))
     if (!maybe_recoverable (file_name, &interdir_made))
       break;
       break;
@@ -1019,148 +945,219 @@ extract_fifo (char *file_name, int typeflag)
     set_stat (file_name, &current_stat_info.stat, NULL, 0,
     set_stat (file_name, &current_stat_info.stat, NULL, 0,
 	      ARCHIVED_PERMSTATUS, typeflag);
 	      ARCHIVED_PERMSTATUS, typeflag);
   else
   else
-    {
-      mkfifo_error (file_name);
-      if (backup_option)
-	undo_last_backup ();
-    }
+    mkfifo_error (file_name);
+  return status;
 }  
 }  
 #endif
 #endif
 
 
-/* Extract a file from the archive.  */
-void
-extract_archive (void)
+static int
+extract_mangle_wrapper (char *file_name, int typeflag)
 {
 {
-  char typeflag;
-  char *file_name;
-
-  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))
-    {
-      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);
-  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;
-    }
+  extract_mangle ();
+  return 0;
+}
 
 
-  apply_nonancestor_delayed_set_stat (file_name, 0);
+     
+static int
+extract_failure (char *file_name, int typeflag)
+{
+  return 1;
+}
 
 
-  /* Take a safety backup of a previously existing file.  */
+typedef int (*tar_extractor_t) (char *file_name, int typeflag);
 
 
-  if (backup_option && !to_stdout_option)
-    if (!maybe_backup_file (file_name, 0))
-      {
-	int e = errno;
-	ERROR ((0, e, _("%s: Was unable to backup this file"),
-		quotearg_colon (file_name)));
-	skip_member ();
-	return;
-      }
+
 
 
-  /* Extract the archive entry according to its type.  */
+/* Prepare to extract a file. Find extractor function.
+   Return zero if extraction should not proceed.  */
 
 
-  /* KLUDGE */
-  typeflag = sparse_member_p (&current_stat_info) ?
-                  GNUTYPE_SPARSE : current_header->header.typeflag;
+static int
+prepare_to_extract (char const *file_name, int typeflag, tar_extractor_t *fun)
+{
+  int rc = 1;
+  
+  if (to_stdout_option)
+    rc = 0;
 
 
+  /* Select the extractor */
   switch (typeflag)
   switch (typeflag)
     {
     {
-    case GNUTYPE_SPARSE: /* FIXME: Shouldn't we call extract_file at once? */
+    case GNUTYPE_SPARSE:
+      *fun = extract_file;
+      rc = 1;
+      break;
+      
     case AREGTYPE:
     case AREGTYPE:
     case REGTYPE:
     case REGTYPE:
     case CONTTYPE:
     case CONTTYPE:
-
       /* Appears to be a file.  But BSD tar uses the convention that a slash
       /* Appears to be a file.  But BSD tar uses the convention that a slash
 	 suffix means a directory.  */
 	 suffix means a directory.  */
-
       if (current_stat_info.had_trailing_slash)
       if (current_stat_info.had_trailing_slash)
-	extract_dir (file_name, typeflag);
+	*fun = extract_dir;
       else
       else
-	extract_file (file_name, typeflag);
+	{
+	  *fun = extract_file;
+	  rc = 1;
+	}
       break;
       break;
 
 
     case SYMTYPE:
     case SYMTYPE:
-      extract_symlink (file_name);
+      *fun = extract_symlink;
       break;
       break;
-      
+
     case LNKTYPE:
     case LNKTYPE:
-      extract_link (file_name);
+      *fun = extract_link;
       break;
       break;
 
 
 #if S_IFCHR
 #if S_IFCHR
     case CHRTYPE:
     case CHRTYPE:
       current_stat_info.stat.st_mode |= S_IFCHR;
       current_stat_info.stat.st_mode |= S_IFCHR;
-      extract_node (file_name, typeflag);
+      *fun = extract_node;
       break;
       break;
 #endif
 #endif
 
 
 #if S_IFBLK
 #if S_IFBLK
     case BLKTYPE:
     case BLKTYPE:
       current_stat_info.stat.st_mode |= S_IFBLK;
       current_stat_info.stat.st_mode |= S_IFBLK;
-      extract_node (file_name, typeflag);
+      *fun = extract_node;
       break;
       break;
 #endif
 #endif
 
 
 #if HAVE_MKFIFO || defined mkfifo
 #if HAVE_MKFIFO || defined mkfifo
     case FIFOTYPE:
     case FIFOTYPE:
-      extract_fifo (file_name, typeflag);
+      *fun = extract_fifo;
       break;
       break;
 #endif
 #endif
 
 
     case DIRTYPE:
     case DIRTYPE:
     case GNUTYPE_DUMPDIR:
     case GNUTYPE_DUMPDIR:
-      extract_dir (file_name, typeflag);
+      *fun = extract_dir;
       break;
       break;
 
 
     case GNUTYPE_VOLHDR:
     case GNUTYPE_VOLHDR:
       if (verbose_option)
       if (verbose_option)
 	fprintf (stdlis, _("Reading %s\n"), quote (current_stat_info.file_name));
 	fprintf (stdlis, _("Reading %s\n"), quote (current_stat_info.file_name));
+      *fun = NULL;
       break;
       break;
 
 
     case GNUTYPE_NAMES:
     case GNUTYPE_NAMES:
-      extract_mangle ();
+      *fun = extract_mangle_wrapper;
       break;
       break;
 
 
     case GNUTYPE_MULTIVOL:
     case GNUTYPE_MULTIVOL:
       ERROR ((0, 0,
       ERROR ((0, 0,
 	      _("%s: Cannot extract -- file is continued from another volume"),
 	      _("%s: Cannot extract -- file is continued from another volume"),
 	      quotearg_colon (current_stat_info.file_name)));
 	      quotearg_colon (current_stat_info.file_name)));
-      skip_member ();
-      if (backup_option)
-	undo_last_backup ();
+      *fun = extract_failure;
       break;
       break;
 
 
     case GNUTYPE_LONGNAME:
     case GNUTYPE_LONGNAME:
     case GNUTYPE_LONGLINK:
     case GNUTYPE_LONGLINK:
       ERROR ((0, 0, _("Unexpected long name header")));
       ERROR ((0, 0, _("Unexpected long name header")));
-      skip_member ();
-      if (backup_option)
-	undo_last_backup ();
+      *fun = extract_failure;
       break;
       break;
 
 
     default:
     default:
       WARN ((0, 0,
       WARN ((0, 0,
 	     _("%s: Unknown file type '%c', extracted as normal file"),
 	     _("%s: Unknown file type '%c', extracted as normal file"),
 	     quotearg_colon (file_name), typeflag));
 	     quotearg_colon (file_name), typeflag));
-      extract_file (file_name, typeflag);
+      *fun = extract_file;
     }
     }
+
+  /* Determine whether the extraction should proceed */
+  if (rc == 0)
+    return 0;
+  
+  switch (old_files_option)
+    {
+    case UNLINK_FIRST_OLD_FILES:
+      if (!remove_any_file (file_name, 
+                            recursive_unlink_option ? RECURSIVE_REMOVE_OPTION 
+                                                      : ORDINARY_REMOVE_OPTION)
+	  && errno && errno != ENOENT)
+	{
+	  unlink_error (file_name);
+	  return 0;
+	}
+      break;
+
+    case KEEP_NEWER_FILES:
+      if (file_newer_p (file_name, &current_stat_info))
+	{
+	  WARN ((0, 0, _("Current %s is newer"), quote (file_name)));
+	  return 0;
+	}
+      break;
+
+    default:
+      break;
+    }
+
+  return 1;
+}
+
+/* Extract a file from the archive.  */
+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))
+    {
+      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);
+  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;
+    }
+
+  apply_nonancestor_delayed_set_stat (file_name, 0);
+
+  /* Take a safety backup of a previously existing file.  */
+
+  if (backup_option && !to_stdout_option)
+    if (!maybe_backup_file (file_name, 0))
+      {
+	int e = errno;
+	ERROR ((0, e, _("%s: Was unable to backup this file"),
+		quotearg_colon (file_name)));
+	skip_member ();
+	return;
+      }
+
+  /* Extract the archive entry according to its type.  */
+
+  /* KLUDGE */
+  typeflag = sparse_member_p (&current_stat_info) ?
+                  GNUTYPE_SPARSE : current_header->header.typeflag;
+
+  if (prepare_to_extract (file_name, typeflag, &fun))
+    {
+      if (fun && (*fun) (file_name, typeflag) && backup_option)
+	undo_last_backup ();
+    }
+  else
+    skip_member ();
+  
 }
 }
 
 
 /* Extract the symbolic links whose final extraction were delayed.  */
 /* Extract the symbolic links whose final extraction were delayed.  */