+ write_directory_file ();
+}
+
+
+/* Calculate the hash of a link. */
+static unsigned
+hash_link (void const *entry, unsigned n_buckets)
+{
+ struct link const *link = entry;
+ return (uintmax_t) (link->dev ^ link->ino) % n_buckets;
+}
+
+/* Compare two links for equality. */
+static bool
+compare_links (void const *entry1, void const *entry2)
+{
+ struct link const *link1 = entry1;
+ struct link const *link2 = entry2;
+ return ((link1->dev ^ link2->dev) | (link1->ino ^ link2->ino)) == 0;
+}
+
+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 *stat)
+{
+ if (link_table && stat->stat.st_nlink > 1)
+ {
+ struct link lp;
+ struct link *dup;
+ off_t block_ordinal;
+ union block *blk;
+
+ lp.ino = stat->stat.st_ino;
+ lp.dev = stat->stat.st_dev;
+
+ if ((dup = hash_lookup (link_table, &lp)))
+ {
+ /* We found a link. */
+ char const *link_name = safer_name_suffix (dup->name, true);
+
+ dup->nlink--;
+
+ block_ordinal = current_block_ordinal ();
+ assign_string (&stat->link_name, link_name);
+ if (NAME_FIELD_SIZE < strlen (link_name))
+ write_long_link (stat);
+
+ stat->stat.st_size = 0;
+ blk = start_header (stat);
+ if (!blk)
+ return true;
+ tar_copy_str (blk->header.linkname, link_name, NAME_FIELD_SIZE);
+
+ blk->header.typeflag = LNKTYPE;
+ finish_header (stat, blk, block_ordinal);
+
+ if (remove_files_option && unlink (stat->orig_file_name) != 0)
+ unlink_error (stat->orig_file_name);
+
+ return true;
+ }
+ }
+ return false;
+}
+
+static void
+file_count_links (struct tar_stat_info *stat)
+{
+ if (stat->stat.st_nlink > 1)
+ {
+ struct link *dup;
+ struct link *lp = xmalloc (offsetof (struct link, name)
+ + strlen (stat->orig_file_name) + 1);
+ lp->ino = stat->stat.st_ino;
+ lp->dev = stat->stat.st_dev;
+ lp->nlink = stat->stat.st_nlink;
+ strcpy (lp->name, stat->orig_file_name);
+
+ if (! ((link_table
+ || (link_table = hash_initialize (0, 0, hash_link,
+ compare_links, 0)))
+ && (dup = hash_insert (link_table, lp))))
+ xalloc_die ();
+
+ if (dup != 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 ()
+{
+ 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"), lp->name));
+ }
+ }