+ if (rc == RECOVER_SKIP)
+ return 0;
+ if (!(incremental_option && errno == EEXIST))
+ {
+ link_error (link_name, file_name);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+extract_symlink (char *file_name, int typeflag)
+{
+#ifdef HAVE_SYMLINK
+ bool interdir_made = false;
+
+ if (! absolute_names_option
+ && (IS_ABSOLUTE_FILE_NAME (current_stat_info.link_name)
+ || contains_dot_dot (current_stat_info.link_name)))
+ return create_placeholder_file (file_name, true, &interdir_made);
+
+ while (symlinkat (current_stat_info.link_name, chdir_fd, file_name) != 0)
+ switch (maybe_recoverable (file_name, false, &interdir_made))
+ {
+ case RECOVER_OK:
+ continue;
+
+ case RECOVER_SKIP:
+ return 0;
+
+ case RECOVER_NO:
+ symlink_error (current_stat_info.link_name, file_name);
+ return -1;
+ }
+
+ set_stat (file_name, ¤t_stat_info, -1, 0, 0,
+ SYMTYPE, false, AT_SYMLINK_NOFOLLOW);
+ return 0;
+
+#else
+ static int warned_once;
+
+ if (!warned_once)
+ {
+ warned_once = 1;
+ WARNOPT (WARN_SYMLINK_CAST,
+ (0, 0,
+ _("Attempting extraction of symbolic links as hard links")));
+ }
+ return extract_link (file_name, typeflag);
+#endif
+}
+
+#if S_IFCHR || S_IFBLK
+static int
+extract_node (char *file_name, int typeflag)
+{
+ bool interdir_made = false;
+ mode_t mode = (current_stat_info.stat.st_mode & (MODE_RWX | S_IFBLK | S_IFCHR)
+ & ~ (0 < same_owner_option ? S_IRWXG | S_IRWXO : 0));
+
+ while (mknodat (chdir_fd, file_name, mode, current_stat_info.stat.st_rdev)
+ != 0)
+ switch (maybe_recoverable (file_name, false, &interdir_made))
+ {
+ case RECOVER_OK:
+ continue;
+
+ case RECOVER_SKIP:
+ return 0;
+
+ case RECOVER_NO:
+ mknod_error (file_name);
+ return -1;
+ }
+
+ set_stat (file_name, ¤t_stat_info, -1,
+ mode & ~ current_umask, MODE_RWX,
+ typeflag, false, AT_SYMLINK_NOFOLLOW);
+ return 0;
+}
+#endif
+
+#if HAVE_MKFIFO || defined mkfifo
+static int
+extract_fifo (char *file_name, int typeflag)
+{
+ bool interdir_made = false;
+ mode_t mode = (current_stat_info.stat.st_mode & MODE_RWX
+ & ~ (0 < same_owner_option ? S_IRWXG | S_IRWXO : 0));
+
+ while (mkfifoat (chdir_fd, file_name, mode) != 0)
+ switch (maybe_recoverable (file_name, false, &interdir_made))
+ {
+ case RECOVER_OK:
+ continue;
+
+ case RECOVER_SKIP:
+ return 0;
+
+ case RECOVER_NO:
+ mkfifo_error (file_name);
+ return -1;
+ }
+
+ set_stat (file_name, ¤t_stat_info, -1,
+ mode & ~ current_umask, MODE_RWX,
+ typeflag, false, AT_SYMLINK_NOFOLLOW);
+ return 0;
+}
+#endif
+
+static int
+extract_volhdr (char *file_name, int typeflag)
+{
+ skip_member ();
+ return 0;
+}
+
+static int
+extract_failure (char *file_name, int typeflag)
+{
+ return 1;
+}
+
+static int
+extract_skip (char *file_name, int typeflag)
+{
+ skip_member ();
+ return 0;
+}
+
+typedef int (*tar_extractor_t) (char *file_name, int typeflag);
+
+\f
+
+/* Prepare to extract a file. Find extractor function.
+ Return zero if extraction should not proceed. */
+
+static int
+prepare_to_extract (char const *file_name, int typeflag, tar_extractor_t *fun)
+{
+ int rc = 1;
+
+ if (EXTRACT_OVER_PIPE)
+ rc = 0;
+
+ /* Select the extractor */
+ switch (typeflag)
+ {
+ case GNUTYPE_SPARSE:
+ *fun = extract_file;
+ rc = 1;
+ break;
+
+ case AREGTYPE:
+ case REGTYPE:
+ case CONTTYPE:
+ /* Appears to be a file. But BSD tar uses the convention that a slash
+ suffix means a directory. */
+ if (current_stat_info.had_trailing_slash)
+ *fun = extract_dir;
+ else
+ {
+ *fun = extract_file;
+ rc = 1;