+ 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);