+ 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));
+ }
+ }
+}
+
+
+/* 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. */
+
+/* FIXME: One should make sure that for *every* path leading to setting
+ exit_status to failure, a clear diagnostic has been issued. */
+
+static void
+dump_file0 (struct tar_stat_info *st, char *p,
+ int top_level, dev_t parent_device)
+{
+ union block *header;
+ char type;
+ time_t original_ctime;
+ struct utimbuf restore_times;
+ off_t block_ordinal = -1;
+
+ if (interactive_option && !confirm ("add", p))
+ return;
+
+ assign_string (&st->orig_file_name, p);
+ assign_string (&st->file_name, safer_name_suffix (p, false));
+
+ if (deref_stat (dereference_option, p, &st->stat) != 0)
+ {
+ stat_diag (p);
+ return;
+ }
+ st->archive_file_size = st->stat.st_size;
+ sys_stat_nanoseconds (st);
+ original_ctime = st->stat.st_ctime;
+ restore_times.actime = st->stat.st_atime;
+ restore_times.modtime = st->stat.st_mtime;
+
+#ifdef S_ISHIDDEN
+ if (S_ISHIDDEN (st->stat.st_mode))
+ {
+ char *new = (char *) alloca (strlen (p) + 2);
+ if (new)
+ {
+ strcpy (new, p);
+ strcat (new, "@");
+ p = new;
+ }
+ }
+#endif
+
+ /* See if we want only new files, and check if this one is too old to
+ put in the archive.
+
+ This check is omitted if incremental_option is set *and* the
+ requested file is not explicitely listed in the command line. */
+
+ if (!(incremental_option && !is_individual_file (p))
+ && !S_ISDIR (st->stat.st_mode)
+ && OLDER_STAT_TIME (st->stat, m)
+ && (!after_date_option || OLDER_STAT_TIME (st->stat, c)))
+ {
+ if (!incremental_option)
+ WARN ((0, 0, _("%s: file is unchanged; not dumped"),
+ quotearg_colon (p)));
+ return;
+ }
+
+ /* See if we are trying to dump the archive. */
+ if (sys_file_is_archive (st))
+ {
+ WARN ((0, 0, _("%s: file is the archive; not dumped"),
+ quotearg_colon (p)));
+ return;
+ }
+
+ if (S_ISDIR (st->stat.st_mode))
+ {
+ dump_dir (st, top_level, parent_device);
+ if (atime_preserve_option)
+ utime (p, &restore_times);
+ return;
+ }
+ else if (is_avoided_name (p))
+ return;
+ else
+ {
+ /* Check for multiple links. */
+ if (dump_hard_link (st))
+ return;
+
+ /* This is not a link to a previously dumped file, so dump it. */
+
+ if (S_ISREG (st->stat.st_mode)
+ || S_ISCTG (st->stat.st_mode))
+ {
+ int fd;
+ enum dump_status status;
+
+ if (file_dumpable_p (st))
+ {
+ fd = open (st->orig_file_name,
+ O_RDONLY | O_BINARY);
+ if (fd < 0)
+ {
+ if (!top_level && errno == ENOENT)
+ WARN ((0, 0, _("%s: File removed before we read it"),
+ quotearg_colon (st->orig_file_name)));
+ else
+ open_diag (st->orig_file_name);
+ return;
+ }
+ }
+ else
+ fd = -1;
+
+ if (sparse_option && sparse_file_p (st))
+ {
+ status = sparse_dump_file (fd, st);
+ if (status == dump_status_not_implemented)
+ status = dump_regular_file (fd, st);
+ }
+ else
+ status = dump_regular_file (fd, st);
+
+ switch (status)
+ {
+ case dump_status_ok:
+ if (multi_volume_option)
+ assign_string (&save_name, 0);
+ dump_regular_finish (fd, st, original_ctime);
+ break;
+
+ case dump_status_short:
+ if (multi_volume_option)
+ assign_string (&save_name, 0);
+ close (fd);
+ break;
+
+ case dump_status_fail:
+ close (fd);
+ return;
+
+ case dump_status_not_implemented:
+ abort ();
+ }
+
+ if (atime_preserve_option)
+ utime (st->orig_file_name, &restore_times);
+ file_count_links (st);
+ return;
+ }
+#ifdef HAVE_READLINK
+ else if (S_ISLNK (st->stat.st_mode))
+ {
+ char *buffer;
+ int size;
+ size_t linklen = st->stat.st_size;
+ if (linklen != st->stat.st_size || linklen + 1 == 0)
+ xalloc_die ();
+ buffer = (char *) alloca (linklen + 1);
+ size = readlink (p, buffer, linklen + 1);
+ if (size < 0)
+ {
+ readlink_diag (p);
+ return;
+ }
+ buffer[size] = '\0';
+ assign_string (&st->link_name, buffer);
+ if (size > NAME_FIELD_SIZE)
+ write_long_link (st);
+
+ block_ordinal = current_block_ordinal ();
+ st->stat.st_size = 0; /* force 0 size on symlink */
+ header = start_header (st);
+ if (!header)
+ return;
+ tar_copy_str (header->header.linkname, buffer, NAME_FIELD_SIZE);
+ header->header.typeflag = SYMTYPE;
+ finish_header (st, header, block_ordinal);
+ /* nothing more to do to it */
+
+ if (remove_files_option)
+ {
+ if (unlink (p) == -1)
+ unlink_error (p);
+ }
+ file_count_links (st);
+ return;
+ }
+#endif
+ else if (S_ISCHR (st->stat.st_mode))
+ type = CHRTYPE;
+ else if (S_ISBLK (st->stat.st_mode))
+ type = BLKTYPE;
+ else if (S_ISFIFO (st->stat.st_mode))
+ type = FIFOTYPE;
+ else if (S_ISSOCK (st->stat.st_mode))
+ {
+ WARN ((0, 0, _("%s: socket ignored"), quotearg_colon (p)));
+ return;
+ }
+ else if (S_ISDOOR (st->stat.st_mode))
+ {
+ WARN ((0, 0, _("%s: door ignored"), quotearg_colon (p)));
+ return;
+ }
+ else
+ {
+ unknown_file_error (p);
+ return;
+ }
+ }
+
+ if (archive_format == V7_FORMAT)
+ {
+ unknown_file_error (p);
+ return;
+ }
+
+ block_ordinal = current_block_ordinal ();
+ st->stat.st_size = 0; /* force 0 size */
+ header = start_header (st);
+ if (!header)
+ return;
+ header->header.typeflag = type;
+
+ if (type != FIFOTYPE)
+ {
+ MAJOR_TO_CHARS (major (st->stat.st_rdev),
+ header->header.devmajor);
+ MINOR_TO_CHARS (minor (st->stat.st_rdev),
+ header->header.devminor);
+ }
+
+ finish_header (st, header, block_ordinal);
+ if (remove_files_option)
+ {
+ if (unlink (p) == -1)
+ unlink_error (p);
+ }