4
0
Эх сурвалжийг харах

(extract_archive): Rewritten

Sergey Poznyakoff 20 жил өмнө
parent
commit
c8764440d9
1 өөрчлөгдсөн 435 нэмэгдсэн , 401 устгасан
  1. 435 401
      src/extract.c

+ 435 - 401
src/extract.c

@@ -602,19 +602,435 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_symlinks)
     }
 }
 
-/* Extract a file from the archive.  */
+
 void
-extract_archive (void)
+extract_dir (char *file_name, int typeflag)
+{
+  int status;
+  mode_t mode;
+  int interdir_made = 0;
+  
+  if (incremental_option)
+    /* Read the entry and delete files that aren't listed in the archive.  */
+    purge_directory (file_name);
+  else if (typeflag == GNUTYPE_DUMPDIR)
+    skip_member ();
+
+  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;
+  
+  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
+      || old_files_option == DEFAULT_OLD_FILES
+      || old_files_option == OVERWRITE_OLD_FILES)
+    delay_set_stat (file_name, &current_stat_info.stat,
+		    MODE_RWX & (mode ^ current_stat_info.stat.st_mode),
+		    (status == 0
+		     ? ARCHIVED_PERMSTATUS
+		     : UNKNOWN_PERMSTATUS));
+}
+
+
+static int
+open_output_file (char *file_name, int typeflag)
 {
-  union block *data_block;
   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
+     the open call that creates them.  */
+
+  if (typeflag == CONTTYPE)
+    fd = open (file_name, openflag | O_CTG, mode, current_stat_info.stat.st_size);
+  else
+    fd = open (file_name, openflag, mode);
+
+#else /* not O_CTG */
+  if (typeflag == CONTTYPE)
+    {
+      static int conttype_diagnosed;
+
+      if (!conttype_diagnosed)
+	{
+	  conttype_diagnosed = 1;
+	  WARN ((0, 0, _("Extracting contiguous files as regular files")));
+	}
+    }
+  fd = open (file_name, openflag, mode);
+
+#endif /* not O_CTG */
+  
+  return fd;
+}
+
+static void
+extract_file (char *file_name, int typeflag)
+{
+  int fd;
+  off_t size;
+  union block *data_block;
   int status;
   size_t count;
   size_t written;
-  int openflag;
-  mode_t mode;
-  off_t size;
   int interdir_made = 0;
+  
+  /* FIXME: deal with protection issues.  */
+
+  do
+    {
+      if (to_stdout_option)
+	fd = STDOUT_FILENO;
+      else
+	{
+	  if (! prepare_to_extract (file_name))
+	    {
+	      skip_member ();
+	      if (backup_option)
+		undo_last_backup ();
+	      return;
+	    }
+	  fd = open_output_file (file_name, typeflag);
+	}
+    }
+  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)
+    sparse_extract_file (fd, &current_stat_info, &size);
+  else
+    for (size = current_stat_info.stat.st_size; size > 0; )
+      {
+	if (multi_volume_option)
+	  {
+	    assign_string (&save_name, current_stat_info.file_name);
+	    save_totsize = current_stat_info.stat.st_size;
+	    save_sizeleft = size;
+	  }
+
+	/* Locate data, determine max length writeable, write it,
+	   block that we have used the data, then check if the write
+	   worked.  */
+	
+	data_block = find_next_block ();
+	if (! data_block)
+	  {
+	    ERROR ((0, 0, _("Unexpected EOF in archive")));
+	    break;		/* FIXME: What happens, then?  */
+	  }
+
+	written = available_space_after (data_block);
+	
+	if (written > size)
+	  written = size;
+	errno = 0;
+	count = full_write (fd, data_block->buffer, written);
+	size -= count;
+	
+	set_next_block_after ((union block *)
+			      (data_block->buffer + written - 1));
+	if (count != written)
+	  {
+	    write_error_details (file_name, count, written);
+	    break;
+	  }
+      }
+  
+  skip_file (size);
+
+  if (multi_volume_option)
+    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.  */
+
+  if (to_stdout_option)
+    return;
+
+  status = close (fd);
+  if (status < 0)
+    {
+      close_error (file_name);
+      if (backup_option)
+	undo_last_backup ();
+    }
+
+  set_stat (file_name, &current_stat_info.stat, 0, 0,
+	    (old_files_option == OVERWRITE_OLD_FILES
+	     ? UNKNOWN_PERMSTATUS
+	     : ARCHIVED_PERMSTATUS),
+	    typeflag);
+}  
+
+static void
+extract_link (char *file_name)
+{
+  char const *link_name;
+  int interdir_made = 0;
+  
+  if (!prepare_to_extract (file_name))
+    return;
+
+  do
+    {
+      struct stat st1, st2;
+      int e;
+      link_name = safer_name_suffix (current_stat_info.link_name, true);
+      
+      int status = link (link_name, file_name);
+      e = errno;
+
+      if (status == 0)
+	{
+	  struct delayed_symlink *ds = delayed_symlink_head;
+	  if (ds && lstat (link_name, &st1) == 0)
+	    for (; ds; ds = ds->next)
+	      if (ds->dev == st1.st_dev
+		  && ds->ino == st1.st_ino
+		  && ds->mtime == st1.st_mtime)
+		{
+		  struct string_list *p =  xmalloc (offsetof (struct string_list, string)
+						    + strlen (file_name) + 1);
+		  strcpy (p->string, file_name);
+		  p->next = ds->sources;
+		  ds->sources = p;
+		  break;
+		}
+	  return;
+	}
+      else if ((e == EEXIST && strcmp (link_name, file_name) == 0)
+	       || (lstat (link_name, &st1) == 0
+		   && lstat (file_name, &st2) == 0
+		   && st1.st_dev == st2.st_dev
+		   && st1.st_ino == st2.st_ino))
+	return;
+      
+      errno = e;
+    }
+  while (maybe_recoverable (file_name, &interdir_made));
+
+  if (!(incremental_option && errno == EEXIST))
+    {
+      link_error (link_name, file_name);
+      if (backup_option)
+	undo_last_backup ();
+    }
+}  
+
+static void
+extract_symlink (char *file_name)
+{
+#ifdef HAVE_SYMLINK
+  int status, fd;
+  int interdir_made = 0;
+  
+  if (! prepare_to_extract (file_name))
+    return;
+
+  if (absolute_names_option
+      || ! (IS_ABSOLUTE_FILE_NAME (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)
+	if (!maybe_recoverable (file_name, &interdir_made))
+	  break;
+
+      if (status == 0)
+	set_stat (file_name, &current_stat_info.stat, 0, 0, 0, SYMTYPE);
+      else
+	symlink_error (current_stat_info.link_name, file_name);
+    }
+  else
+    {
+      /* This symbolic link is potentially dangerous.  Don't
+	 create it now; instead, create a placeholder file, which
+	 will be replaced after other extraction is done.  */
+      struct stat st;
+
+      while ((fd = open (file_name, O_WRONLY | O_CREAT | O_EXCL, 0)) < 0)
+	if (! maybe_recoverable (file_name, &interdir_made))
+	  break;
+
+      status = -1;
+      if (fd < 0)
+	open_error (file_name);
+      else if (fstat (fd, &st) != 0)
+	{
+	  stat_error (file_name);
+	  close (fd);
+	}
+      else if (close (fd) != 0)
+	close_error (file_name);
+      else
+	{
+	  struct delayed_set_stat *h;
+	  struct delayed_symlink *p = xmalloc (offsetof (struct delayed_symlink, target)
+					       + strlen (current_stat_info.link_name)
+					       + 1);
+	  p->next = delayed_symlink_head;
+	  delayed_symlink_head = p;
+	  p->dev = st.st_dev;
+	  p->ino = st.st_ino;
+	  p->mtime = st.st_mtime;
+	  p->uid = current_stat_info.stat.st_uid;
+	  p->gid = current_stat_info.stat.st_gid;
+	  p->sources = xmalloc (offsetof (struct string_list, string)
+				+ strlen (file_name) + 1);
+	  p->sources->next = 0;
+	  strcpy (p->sources->string, file_name);
+	  strcpy (p->target, current_stat_info.link_name);
+
+	  h = delayed_set_stat_head;
+	  if (h && ! h->after_symlinks
+	      && 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))
+	    {
+	      do
+		{
+		  h->after_symlinks = 1;
+		  
+		  if (stat (h->file_name, &st) != 0)
+		    stat_error (h->file_name);
+		  else
+		    {
+		      h->stat_info.st_dev = st.st_dev;
+		      h->stat_info.st_ino = st.st_ino;
+		    }
+		}
+	      while ((h = h->next) && ! h->after_symlinks);
+	    }
+	  
+	  status = 0;
+	}
+    }
+
+  if (status != 0 && backup_option)
+    undo_last_backup ();
+
+#else
+  static int warned_once;
+
+  if (!warned_once)
+    {
+      warned_once = 1;
+      WARN ((0, 0, _("Attempting extraction of symbolic links as hard links")));
+    }
+  extract_link (file_name);  
+#endif
+}  
+
+#if S_IFCHR || S_IFBLK
+static void
+extract_node (char *file_name, int typeflag)
+{
+  int status;
+  int interdir_made = 0;
+  
+  do
+    {
+      if (! prepare_to_extract (file_name))
+	return;
+  
+      status = mknod (file_name, current_stat_info.stat.st_mode,
+		      current_stat_info.stat.st_rdev);
+    }
+  while (status && maybe_recoverable (file_name, &interdir_made));
+  
+  if (status != 0)
+    {
+      mknod_error (file_name);
+      if (backup_option)
+	undo_last_backup ();
+    }
+  else
+    set_stat (file_name, &current_stat_info.stat, 0, 0, ARCHIVED_PERMSTATUS, typeflag);
+}
+#endif
+
+#if HAVE_MKFIFO || defined mkfifo
+static void
+extract_fifo (char *file_name, int typeflag)
+{
+  int status;
+  int interdir_made = 0;
+  
+  if (! prepare_to_extract (file_name))
+    return;
+
+  while ((status = mkfifo (file_name, current_stat_info.stat.st_mode)))
+    if (!maybe_recoverable (file_name, &interdir_made))
+      break;
+
+  if (status == 0)
+    set_stat (file_name, &current_stat_info.stat, NULL, 0,
+	      ARCHIVED_PERMSTATUS, typeflag);
+  else
+    {
+      mkfifo_error (file_name);
+      if (backup_option)
+	undo_last_backup ();
+    }
+}  
+#endif
+
+/* Extract a file from the archive.  */
+void
+extract_archive (void)
+{
   char typeflag;
   char *file_name;
 
@@ -666,9 +1082,7 @@ extract_archive (void)
 
   switch (typeflag)
     {
-    case GNUTYPE_SPARSE:
-      /* Fall through.  */
-
+    case GNUTYPE_SPARSE: /* FIXME: Shouldn't we call extract_file at once? */
     case AREGTYPE:
     case REGTYPE:
     case CONTTYPE:
@@ -677,422 +1091,42 @@ extract_archive (void)
 	 suffix means a directory.  */
 
       if (current_stat_info.had_trailing_slash)
-	goto really_dir;
-
-      /* FIXME: deal with protection issues.  */
-
-    again_file:
-      openflag = (O_WRONLY | O_BINARY | O_CREAT
-		  | (old_files_option == OVERWRITE_OLD_FILES
-		     ? O_TRUNC
-		     : O_EXCL));
-      mode = current_stat_info.stat.st_mode & MODE_RWX & ~ current_umask;
-
-      if (to_stdout_option)
-	{
-	  fd = STDOUT_FILENO;
-	  goto extract_file;
-	}
-
-      if (! prepare_to_extract (file_name))
-	{
-	  skip_member ();
-	  if (backup_option)
-	    undo_last_backup ();
-	  break;
-	}
-
-#if O_CTG
-      /* Contiguous files (on the Masscomp) have to specify the size in
-	 the open call that creates them.  */
-
-      if (typeflag == CONTTYPE)
-	fd = open (file_name, openflag | O_CTG, mode, current_stat_info.stat.st_size);
+	extract_dir (file_name, typeflag);
       else
-	fd = open (file_name, openflag, mode);
-
-#else /* not O_CTG */
-      if (typeflag == CONTTYPE)
-	{
-	  static int conttype_diagnosed;
-
-	  if (!conttype_diagnosed)
-	    {
-	      conttype_diagnosed = 1;
-	      WARN ((0, 0, _("Extracting contiguous files as regular files")));
-	    }
-	}
-      fd = open (file_name, openflag, mode);
-
-#endif /* not O_CTG */
-
-      if (fd < 0)
-	{
-	  if (maybe_recoverable (file_name, &interdir_made))
-	    goto again_file;
-
-	  open_error (file_name);
-	  skip_member ();
-	  if (backup_option)
-	    undo_last_backup ();
-	  break;
-	}
-
-    extract_file:
-      if (current_stat_info.is_sparse)
-	{
-	  sparse_extract_file (fd, &current_stat_info, &size);
-	}
-      else
-	for (size = current_stat_info.stat.st_size; size > 0; )
-	  {
-	    if (multi_volume_option)
-	      {
-		assign_string (&save_name, current_stat_info.file_name);
-		save_totsize = current_stat_info.stat.st_size;
-		save_sizeleft = size;
-	      }
-
-	    /* Locate data, determine max length writeable, write it,
-	       block that we have used the data, then check if the write
-	       worked.  */
-
-	    data_block = find_next_block ();
-	    if (! data_block)
-	      {
-		ERROR ((0, 0, _("Unexpected EOF in archive")));
-		break;		/* FIXME: What happens, then?  */
-	      }
-
-	    written = available_space_after (data_block);
-
-	    if (written > size)
-	      written = size;
-	    errno = 0;
-	    count = full_write (fd, data_block->buffer, written);
-	    size -= count;
-
-	    set_next_block_after ((union block *)
-				  (data_block->buffer + written - 1));
-	    if (count != written)
-	      {
-		write_error_details (file_name, count, written);
-		break;
-	      }
-	  }
-
-      skip_file (size);
-
-      if (multi_volume_option)
-	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.  */
-
-      if (to_stdout_option)
-	break;
-
-      status = close (fd);
-      if (status < 0)
-	{
-	  close_error (file_name);
-	  if (backup_option)
-	    undo_last_backup ();
-	}
-
-      set_stat (file_name, &current_stat_info.stat, 0, 0,
-		(old_files_option == OVERWRITE_OLD_FILES
-		 ? UNKNOWN_PERMSTATUS
-		 : ARCHIVED_PERMSTATUS),
-		typeflag);
+	extract_file (file_name, typeflag);
       break;
 
     case SYMTYPE:
-#ifdef HAVE_SYMLINK
-      if (! prepare_to_extract (file_name))
-	break;
-
-      if (absolute_names_option
-	  || ! (IS_ABSOLUTE_FILE_NAME (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)
-	    if (!maybe_recoverable (file_name, &interdir_made))
-	      break;
-
-	  if (status == 0)
-	    set_stat (file_name, &current_stat_info.stat, 0, 0, 0, SYMTYPE);
-	  else
-	    symlink_error (current_stat_info.link_name, file_name);
-	}
-      else
-	{
-	  /* This symbolic link is potentially dangerous.  Don't
-	     create it now; instead, create a placeholder file, which
-	     will be replaced after other extraction is done.  */
-	  struct stat st;
-
-	  while (fd = open (file_name, O_WRONLY | O_CREAT | O_EXCL, 0),
-		 fd < 0)
-	    if (! maybe_recoverable (file_name, &interdir_made))
-	      break;
-
-	  status = -1;
-	  if (fd < 0)
-	    open_error (file_name);
-	  else if (fstat (fd, &st) != 0)
-	    {
-	      stat_error (file_name);
-	      close (fd);
-	    }
-	  else if (close (fd) != 0)
-	    close_error (file_name);
-	  else
-	    {
-	      struct delayed_set_stat *h;
-	      struct delayed_symlink *p =
-		xmalloc (offsetof (struct delayed_symlink, target)
-			 + strlen (current_stat_info.link_name) + 1);
-	      p->next = delayed_symlink_head;
-	      delayed_symlink_head = p;
-	      p->dev = st.st_dev;
-	      p->ino = st.st_ino;
-	      p->mtime = st.st_mtime;
-	      p->uid = current_stat_info.stat.st_uid;
-	      p->gid = current_stat_info.stat.st_gid;
-	      p->sources = xmalloc (offsetof (struct string_list, string)
-				    + strlen (file_name) + 1);
-	      p->sources->next = 0;
-	      strcpy (p->sources->string, file_name);
-	      strcpy (p->target, current_stat_info.link_name);
-
-	      h = delayed_set_stat_head;
-	      if (h && ! h->after_symlinks
-		  && 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))
-		{
-		  do
-		    {
-		      h->after_symlinks = 1;
-
-		      if (stat (h->file_name, &st) != 0)
-			stat_error (h->file_name);
-		      else
-			{
-			  h->stat_info.st_dev = st.st_dev;
-			  h->stat_info.st_ino = st.st_ino;
-			}
-		    }
-		  while ((h = h->next) && ! h->after_symlinks);
-		}
-
-	      status = 0;
-	    }
-	}
-
-      if (status != 0 && backup_option)
-	undo_last_backup ();
+      extract_symlink (file_name);
       break;
-
-#else
-      {
-	static int warned_once;
-
-	if (!warned_once)
-	  {
-	    warned_once = 1;
-	    WARN ((0, 0,
-		   _("Attempting extraction of symbolic links as hard links")));
-	  }
-      }
-      typeflag = LNKTYPE;
-      /* Fall through.  */
-#endif
-
+      
     case LNKTYPE:
-      if (! prepare_to_extract (file_name))
-	break;
-
-    again_link:
-      {
-	char const *link_name = safer_name_suffix (current_stat_info.link_name,
-	                                           true);
-	struct stat st1, st2;
-	int e;
-
-	/* MSDOS does not implement links.  However, djgpp's link() actually
-	   copies the file.  */
-	status = link (link_name, file_name);
-	e = errno;
-
-	if (status == 0)
-	  {
-	    struct delayed_symlink *ds = delayed_symlink_head;
-	    if (ds && lstat (link_name, &st1) == 0)
-	      for (; ds; ds = ds->next)
-		if (ds->dev == st1.st_dev
-		    && ds->ino == st1.st_ino
-		    && ds->mtime == st1.st_mtime)
-		  {
-		    struct string_list *p =
-		      xmalloc (offsetof (struct string_list, string)
-			       + strlen (file_name) + 1);
-		    strcpy (p->string, file_name);
-		    p->next = ds->sources;
-		    ds->sources = p;
-		    break;
-		  }
-	    break;
-	  }
-
-	if ((e == EEXIST && strcmp (link_name, file_name) == 0)
-	    || (lstat (link_name, &st1) == 0
-		&& lstat (file_name, &st2) == 0
-		&& st1.st_dev == st2.st_dev
-		&& st1.st_ino == st2.st_ino))
-	  break;
-
-	errno = e;
-	if (maybe_recoverable (file_name, &interdir_made))
-	  goto again_link;
-
-	if (incremental_option && errno == EEXIST)
-	  break;
-
-	link_error (link_name, file_name);
-	if (backup_option)
-	  undo_last_backup ();
-      }
+      extract_link (file_name);
       break;
 
 #if S_IFCHR
     case CHRTYPE:
       current_stat_info.stat.st_mode |= S_IFCHR;
-      goto make_node;
+      extract_node (file_name, typeflag);
+      break;
 #endif
 
 #if S_IFBLK
     case BLKTYPE:
       current_stat_info.stat.st_mode |= S_IFBLK;
-#endif
-
-#if S_IFCHR || S_IFBLK
-    make_node:
-      if (! prepare_to_extract (file_name))
-	break;
-
-      status = mknod (file_name, current_stat_info.stat.st_mode,
-		      current_stat_info.stat.st_rdev);
-      if (status != 0)
-	{
-	  if (maybe_recoverable (file_name, &interdir_made))
-	    goto make_node;
-	  mknod_error (file_name);
-	  if (backup_option)
-	    undo_last_backup ();
-	  break;
-	};
-      set_stat (file_name, &current_stat_info.stat, 0, 0,
-		ARCHIVED_PERMSTATUS, typeflag);
+      extract_node (file_name, typeflag);
       break;
 #endif
 
 #if HAVE_MKFIFO || defined mkfifo
     case FIFOTYPE:
-      if (! prepare_to_extract (file_name))
-	break;
-
-      while (status = mkfifo (file_name, current_stat_info.stat.st_mode),
-	     status != 0)
-	if (!maybe_recoverable (file_name, &interdir_made))
-	  break;
-
-      if (status == 0)
-	set_stat (file_name, &current_stat_info.stat, NULL, 0,
-		  ARCHIVED_PERMSTATUS, typeflag);
-      else
-	{
-	  mkfifo_error (file_name);
-	  if (backup_option)
-	    undo_last_backup ();
-	}
+      extract_fifo (file_name, typeflag);
       break;
 #endif
 
     case DIRTYPE:
     case GNUTYPE_DUMPDIR:
-    really_dir:
-      if (incremental_option)
-	{
-	  /* Read the entry and delete files that aren't listed in the
-	     archive.  */
-
-	  purge_directory (file_name);
-	}
-      else if (typeflag == GNUTYPE_DUMPDIR)
-	skip_member ();
-
-      mode = ((current_stat_info.stat.st_mode
-	       | (we_are_root ? 0 : MODE_WXUSR))
-	      & MODE_RWX);
-
-      status = prepare_to_extract (file_name);
-      if (status == 0)
-	break;
-      if (status < 0)
-	goto directory_exists;
-
-    again_dir:
-      status = mkdir (file_name, mode);
-
-      if (status != 0)
-	{
-	  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);
-		      break;
-		    }
-		  if (S_ISDIR (st.st_mode))
-		    {
-		      mode = st.st_mode & ~ current_umask;
-		      goto directory_exists;
-		    }
-		}
-	      errno = EEXIST;
-	    }
-
-	  if (maybe_recoverable (file_name, &interdir_made))
-	    goto again_dir;
-
-	  if (errno != EEXIST)
-	    {
-	      mkdir_error (file_name);
-	      if (backup_option)
-		undo_last_backup ();
-	      break;
-	    }
-	}
-
-    directory_exists:
-      if (status == 0
-	  || old_files_option == DEFAULT_OLD_FILES
-	  || old_files_option == OVERWRITE_OLD_FILES)
-	delay_set_stat (file_name, &current_stat_info.stat,
-			MODE_RWX & (mode ^ current_stat_info.stat.st_mode),
-			(status == 0
-			 ? ARCHIVED_PERMSTATUS
-			 : UNKNOWN_PERMSTATUS));
+      extract_dir (file_name, typeflag);
       break;
 
     case GNUTYPE_VOLHDR:
@@ -1115,7 +1149,7 @@ extract_archive (void)
 
     case GNUTYPE_LONGNAME:
     case GNUTYPE_LONGLINK:
-      ERROR ((0, 0, _("Visible long name error")));
+      ERROR ((0, 0, _("Unexpected long name header")));
       skip_member ();
       if (backup_option)
 	undo_last_backup ();
@@ -1125,7 +1159,7 @@ extract_archive (void)
       WARN ((0, 0,
 	     _("%s: Unknown file type '%c', extracted as normal file"),
 	     quotearg_colon (file_name), typeflag));
-      goto again_file;
+      extract_file (file_name, typeflag);
     }
 }