+ else if (fstatat (parentfd, name, &st->stat, fstatat_flags) != 0)
+ diag = stat_diag;
+ else if (file_dumpable_p (&st->stat))
+ {
+ fd = subfile_open (parent, name, open_read_flags);
+ if (fd < 0)
+ diag = open_diag;
+ else
+ {
+ st->fd = fd;
+ if (fstat (fd, &st->stat) != 0)
+ diag = stat_diag;
+ }
+ }
+ if (diag)
+ {
+ file_removed_diag (p, top_level, diag);
+ return;
+ }
+
+ st->archive_file_size = original_size = st->stat.st_size;
+ st->atime = get_stat_atime (&st->stat);
+ st->mtime = get_stat_mtime (&st->stat);
+ st->ctime = original_ctime = get_stat_ctime (&st->stat);
+
+#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 explicitly listed in the command line. */
+
+ if (! (incremental_option && ! top_level)
+ && !S_ISDIR (st->stat.st_mode)
+ && OLDER_TAR_STAT_TIME (*st, m)
+ && (!after_date_option || OLDER_TAR_STAT_TIME (*st, c)))
+ {
+ if (!incremental_option && verbose_option)
+ WARNOPT (WARN_FILE_UNCHANGED,
+ (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))
+ {
+ WARNOPT (WARN_IGNORE_ARCHIVE,
+ (0, 0, _("%s: file is the archive; not dumped"),
+ quotearg_colon (p)));
+ return;
+ }
+
+ is_dir = S_ISDIR (st->stat.st_mode) != 0;
+
+ if (!is_dir && dump_hard_link (st))
+ return;
+
+ if (is_dir || S_ISREG (st->stat.st_mode) || S_ISCTG (st->stat.st_mode))
+ {
+ bool ok;
+ struct stat final_stat;
+
+ xattrs_acls_get (parentfd, name, st, 0, !is_dir);
+ xattrs_selinux_get (parentfd, name, st, fd);
+ xattrs_xattrs_get (parentfd, name, st, fd);
+
+ if (is_dir)
+ {
+ const char *tag_file_name;
+ ensure_slash (&st->orig_file_name);
+ ensure_slash (&st->file_name);
+
+ if (check_exclusion_tags (st, &tag_file_name) == exclusion_tag_all)
+ {
+ exclusion_tag_warning (st->orig_file_name, tag_file_name,
+ _("directory not dumped"));
+ return;
+ }
+
+ ok = dump_dir (st);
+
+ fd = st->fd;
+ parentfd = top_level ? chdir_fd : parent->fd;
+ }
+ else
+ {
+ enum dump_status status;
+
+ if (fd && sparse_option && ST_IS_SPARSE (st->stat))
+ {
+ 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:
+ case dump_status_short:
+ file_count_links (st);
+ break;
+
+ case dump_status_fail:
+ break;
+
+ case dump_status_not_implemented:
+ abort ();
+ }
+
+ ok = status == dump_status_ok;
+ }
+
+ if (ok)
+ {
+ if (fd < 0)
+ {
+ errno = - fd;
+ ok = false;
+ }
+ else if (fd == 0)
+ {
+ if (parentfd < 0 && ! top_level)
+ {
+ errno = - parentfd;
+ ok = false;
+ }
+ else
+ ok = fstatat (parentfd, name, &final_stat, fstatat_flags) == 0;
+ }
+ else
+ ok = fstat (fd, &final_stat) == 0;
+
+ if (! ok)
+ file_removed_diag (p, top_level, stat_diag);
+ }
+
+ if (ok)
+ {
+ if ((timespec_cmp (get_stat_ctime (&final_stat), original_ctime) != 0
+ /* Original ctime will change if the file is a directory and
+ --remove-files is given */
+ && !(remove_files_option && is_dir))
+ || original_size < final_stat.st_size)
+ {
+ WARNOPT (WARN_FILE_CHANGED,
+ (0, 0, _("%s: file changed as we read it"),
+ quotearg_colon (p)));
+ set_exit_status (TAREXIT_DIFFERS);
+ }
+ else if (atime_preserve_option == replace_atime_preserve
+ && fd && (is_dir || original_size != 0)
+ && set_file_atime (fd, parentfd, name, st->atime) != 0)
+ utime_error (p);
+ }
+
+ ok &= tar_stat_close (st);
+ if (ok && remove_files_option)
+ queue_deferred_unlink (p, is_dir);
+
+ 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 = readlinkat (parentfd, name, buffer, linklen + 1);
+ if (size < 0)
+ {
+ file_removed_diag (p, top_level, readlink_diag);
+ return;
+ }
+ buffer[size] = '\0';
+ assign_string (&st->link_name, buffer);
+ transform_name (&st->link_name, XFORM_SYMLINK);
+ if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) < size)
+ write_long_link (st);
+
+ xattrs_selinux_get (parentfd, name, st, 0);
+ xattrs_xattrs_get (parentfd, name, st, 0);
+
+ 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, st->link_name, NAME_FIELD_SIZE);
+ header->header.typeflag = SYMTYPE;
+ finish_header (st, header, block_ordinal);
+ /* nothing more to do to it */
+
+ if (remove_files_option)
+ queue_deferred_unlink (p, false);
+
+ file_count_links (st);
+ return;
+ }
+#endif
+ else if (S_ISCHR (st->stat.st_mode))
+ {
+ type = CHRTYPE;
+ xattrs_acls_get (parentfd, name, st, 0, true);
+ xattrs_selinux_get (parentfd, name, st, 0);
+ xattrs_xattrs_get (parentfd, name, st, 0);
+ }
+ else if (S_ISBLK (st->stat.st_mode))
+ {
+ type = BLKTYPE;
+ xattrs_acls_get (parentfd, name, st, 0, true);
+ xattrs_selinux_get (parentfd, name, st, 0);
+ xattrs_xattrs_get (parentfd, name, st, 0);
+ }
+ else if (S_ISFIFO (st->stat.st_mode))
+ {
+ type = FIFOTYPE;
+ xattrs_acls_get (parentfd, name, st, 0, true);
+ xattrs_selinux_get (parentfd, name, st, 0);
+ xattrs_xattrs_get (parentfd, name, st, 0);
+ }
+ else if (S_ISSOCK (st->stat.st_mode))
+ {
+ WARNOPT (WARN_FILE_IGNORED,
+ (0, 0, _("%s: socket ignored"), quotearg_colon (p)));
+ return;
+ }
+ else if (S_ISDOOR (st->stat.st_mode))
+ {
+ WARNOPT (WARN_FILE_IGNORED,
+ (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)
+ queue_deferred_unlink (p, false);
+}
+
+/* Dump a file, recursively. PARENT describes the file's parent
+ directory, NAME is the file's name relative to PARENT, and FULLNAME
+ its full name, possibly relative to the working directory. NAME
+ may contain slashes at the top level of invocation. */
+
+void
+dump_file (struct tar_stat_info *parent, char const *name,
+ char const *fullname)
+{
+ struct tar_stat_info st;
+ tar_stat_init (&st);
+ st.parent = parent;
+ dump_file0 (&st, name, fullname);
+ if (parent && listed_incremental_option)
+ update_parent_directory (parent);
+ tar_stat_destroy (&st);