+ }
+ if (save_typeflag == GNUTYPE_SPARSE)
+ {
+ if (f < 0
+ || finish_sparse_file (f, &sizeleft, current_stat.st_size, p))
+ goto padit;
+ }
+ else
+ while (sizeleft > 0)
+ {
+ if (multi_volume_option)
+ {
+ assign_string (&save_name, p);
+ save_sizeleft = sizeleft;
+ save_totsize = current_stat.st_size;
+ }
+ start = find_next_block ();
+
+ bufsize = available_space_after (start);
+
+ if (sizeleft < bufsize)
+ {
+ /* Last read -- zero out area beyond. */
+
+ bufsize = sizeleft;
+ count = bufsize % BLOCKSIZE;
+ if (count)
+ memset (start->buffer + sizeleft, 0,
+ (size_t) (BLOCKSIZE - count));
+ }
+ if (f < 0)
+ count = bufsize;
+ else
+ count = safe_read (f, start->buffer, bufsize);
+ if (count < 0)
+ {
+ char buf[UINTMAX_STRSIZE_BOUND];
+ ERROR ((0, errno, _("\
+Read error at byte %s, reading %lu bytes, in file %s"),
+ STRINGIFY_BIGINT (current_stat.st_size - sizeleft,
+ buf),
+ (unsigned long) bufsize, p));
+ goto padit;
+ }
+ sizeleft -= count;
+
+ /* This is nonportable (the type of set_next_block_after's arg). */
+
+ set_next_block_after (start + (count - 1) / BLOCKSIZE);
+
+ if (count == bufsize)
+ continue;
+ else
+ {
+ char buf[UINTMAX_STRSIZE_BOUND];
+ ERROR ((0, 0,
+ _("File %s shrunk by %s bytes, padding with zeros"),
+ p, STRINGIFY_BIGINT (sizeleft, buf)));
+ goto padit; /* short read */
+ }
+ }
+
+ if (multi_volume_option)
+ assign_string (&save_name, NULL);
+
+ if (f >= 0)
+ {
+ struct stat final_stat;
+ if (fstat (f, &final_stat) != 0)
+ ERROR ((0, errno, "%s: fstat", p));
+ else if (final_stat.st_mtime != restore_times.modtime
+ || final_stat.st_size != restore_size)
+ ERROR ((0, errno, _("%s: file changed as we read it"), p));
+ if (close (f) != 0)
+ ERROR ((0, errno, _("%s: close"), p));
+ if (atime_preserve_option)
+ utime (p, &restore_times);
+ }
+ if (remove_files_option)
+ {
+ if (unlink (p) == -1)
+ ERROR ((0, errno, _("Cannot remove %s"), p));
+ }
+ return;
+
+ /* File shrunk or gave error, pad out tape to match the size we
+ specified in the header. */
+
+ padit:
+ while (sizeleft > 0)
+ {
+ save_sizeleft = sizeleft;
+ start = find_next_block ();
+ memset (start->buffer, 0, BLOCKSIZE);
+ set_next_block_after (start);
+ sizeleft -= BLOCKSIZE;
+ }
+ if (multi_volume_option)
+ assign_string (&save_name, NULL);
+ if (f >= 0)
+ {
+ close (f);
+ if (atime_preserve_option)
+ utime (p, &restore_times);
+ }
+ return;
+ }
+
+#ifdef HAVE_READLINK
+ else if (S_ISLNK (current_stat.st_mode))
+ {
+ int size;
+ char *buffer = (char *) alloca (PATH_MAX + 1);
+
+ size = readlink (p, buffer, PATH_MAX + 1);
+ if (size < 0)
+ {
+ WARN ((0, errno, _("Cannot add file %s"), p));
+ if (!ignore_failed_read_option)
+ exit_status = TAREXIT_FAILURE;
+ return;
+ }
+ buffer[size] = '\0';
+ if (size >= NAME_FIELD_SIZE)
+ write_long (buffer, GNUTYPE_LONGLINK);
+ assign_string (¤t_link_name, buffer);
+
+ current_stat.st_size = 0; /* force 0 size on symlink */
+ header = start_header (p, ¤t_stat);
+ if (header == NULL)
+ {
+ exit_status = TAREXIT_FAILURE;
+ return;
+ }
+ strncpy (header->header.linkname, buffer, NAME_FIELD_SIZE);
+ header->header.linkname[NAME_FIELD_SIZE - 1] = '\0';
+ header->header.typeflag = SYMTYPE;
+ finish_header (header); /* nothing more to do to it */
+ if (remove_files_option)
+ {
+ if (unlink (p) == -1)
+ ERROR ((0, errno, _("Cannot remove %s"), p));
+ }
+ return;
+ }
+#endif
+
+ else if (S_ISDIR (current_stat.st_mode))
+ {
+ DIR *directory;
+ struct dirent *entry;
+ char *namebuf;
+ size_t buflen;
+ size_t len;
+ dev_t our_device = current_stat.st_dev;
+
+ /* If this tar program is installed suid root, like for Amanda, the
+ access might look like denied, while it is not really.
+
+ FIXME: I have the feeling this test is done too early. Couldn't it
+ just be bundled in later actions? I guess that the proper support
+ of --ignore-failed-read is the key of the current writing. */
+
+ if (access (p, R_OK) == -1 && geteuid () != 0)
+ {
+ WARN ((0, errno, _("Cannot add directory %s"), p));
+ if (!ignore_failed_read_option)
+ exit_status = TAREXIT_FAILURE;
+ return;
+ }
+
+ /* Build new prototype name. Ensure exactly one trailing slash. */
+
+ len = strlen (p);
+ buflen = len + NAME_FIELD_SIZE;
+ namebuf = xmalloc (buflen + 1);
+ strncpy (namebuf, p, buflen);
+ while (len >= 1 && namebuf[len - 1] == '/')
+ len--;
+ namebuf[len++] = '/';
+ namebuf[len] = '\0';
+
+ if (1)
+ {
+ /* The "1" above used to be "archive_format != V7_FORMAT", GNU tar
+ was just not writing directory blocks at all. Daniel Trinkle
+ writes: ``All old versions of tar I have ever seen have
+ correctly archived an empty directory. The really old ones I
+ checked included HP-UX 7 and Mt. Xinu More/BSD. There may be
+ some subtle reason for the exclusion that I don't know, but the
+ current behavior is broken.'' I do not know those subtle
+ reasons either, so until these are reported (anew?), just allow
+ directory blocks to be written even with old archives. */
+
+ current_stat.st_size = 0; /* force 0 size on dir */
+
+ /* FIXME: If people could really read standard archives, this
+ should be:
+
+ header
+ = start_header (standard_option ? p : namebuf, ¤t_stat);
+
+ but since they'd interpret DIRTYPE blocks as regular
+ files, we'd better put the / on the name. */
+
+ header = start_header (namebuf, ¤t_stat);
+ if (header == NULL)
+ {
+ exit_status = TAREXIT_FAILURE;
+ return; /* eg name too long */
+ }
+
+ if (incremental_option)
+ header->header.typeflag = GNUTYPE_DUMPDIR;
+ else /* if (standard_option) */
+ header->header.typeflag = DIRTYPE;
+
+ /* If we're gnudumping, we aren't done yet so don't close it. */
+
+ if (!incremental_option)
+ finish_header (header); /* done with directory header */