+static void
+unknown_file_error (char *p)
+{
+ WARN ((0, 0, _("%s: Unknown file type; file ignored"),
+ quotearg_colon (p)));
+ if (!ignore_failed_read_option)
+ exit_status = TAREXIT_FAILURE;
+}
+
+\f
+/* Handling of hard links */
+
+/* Table of all non-directories that we've written so far. Any time
+ we see another, we check the table and avoid dumping the data
+ again if we've done it once already. */
+static Hash_table *link_table;
+
+/* Try to dump stat as a hard link to another file in the archive. If
+ succeeded returns true */
+static bool
+dump_hard_link (struct tar_stat_info *st)
+{
+ if (link_table && st->stat.st_nlink > 1)
+ {
+ struct link lp;
+ struct link *duplicate;
+ off_t block_ordinal;
+ union block *blk;
+
+ lp.ino = st->stat.st_ino;
+ lp.dev = st->stat.st_dev;
+
+ if ((duplicate = hash_lookup (link_table, &lp)))
+ {
+ /* We found a link. */
+ char const *link_name = safer_name_suffix (duplicate->name, true,
+ absolute_names_option);
+
+ duplicate->nlink--;
+
+ block_ordinal = current_block_ordinal ();
+ assign_string (&st->link_name, link_name);
+ if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT)
+ < strlen (link_name))
+ write_long_link (st);
+
+ st->stat.st_size = 0;
+ blk = start_header (st);
+ if (!blk)
+ return true;
+ tar_copy_str (blk->header.linkname, link_name, NAME_FIELD_SIZE);
+
+ blk->header.typeflag = LNKTYPE;
+ finish_header (st, blk, block_ordinal);
+
+ if (remove_files_option && unlink (st->orig_file_name) != 0)
+ unlink_error (st->orig_file_name);
+
+ return true;
+ }
+ }
+ return false;
+}
+
+static void
+file_count_links (struct tar_stat_info *st)
+{
+ if (st->stat.st_nlink > 1)
+ {
+ struct link *duplicate;
+ struct link *lp = xmalloc (offsetof (struct link, name)
+ + strlen (st->orig_file_name) + 1);
+ lp->ino = st->stat.st_ino;
+ lp->dev = st->stat.st_dev;
+ lp->nlink = st->stat.st_nlink;
+ strcpy (lp->name, st->orig_file_name);
+
+ if (! ((link_table
+ || (link_table = hash_initialize (0, 0, hash_link,
+ compare_links, 0)))
+ && (duplicate = hash_insert (link_table, lp))))
+ xalloc_die ();
+
+ if (duplicate != lp)
+ abort ();
+ lp->nlink--;
+ }
+}
+
+/* For each dumped file, check if all its links were dumped. Emit
+ warnings if it is not so. */
+void
+check_links (void)
+{
+ struct link *lp;
+
+ if (!link_table)
+ return;
+
+ for (lp = hash_get_first (link_table); lp;
+ lp = hash_get_next (link_table, lp))
+ {
+ if (lp->nlink)
+ {
+ WARN ((0, 0, _("Missing links to %s.\n"), quote (lp->name)));
+ }
+ }
+}
+
+
+/* Dump a single file, recursing on directories. P is the file name
+ to dump. TOP_LEVEL tells whether this is a top-level call; zero
+ means no, positive means yes, and negative means the top level
+ of an incremental dump. PARENT_DEVICE is the device of P's
+ parent directory; it is examined only if TOP_LEVEL is zero. */