return did_something; /* tell them to retry if we made one */
}
+static bool
+file_newer_p (const char *file_name, struct tar_stat_info *tar_stat)
+{
+ struct stat st;
+
+ if (stat (file_name, &st))
+ {
+ stat_warn (file_name);
+ return true; /* Be on the safe side */
+ }
+ if (!S_ISDIR (st.st_mode)
+ && st.st_mtime >= current_stat_info.stat.st_mtime)
+ {
+ return true;
+ }
+ return false;
+}
+
/* Prepare to extract a file.
Return zero if extraction should not proceed. */
if (to_stdout_option)
return 0;
- if (old_files_option == UNLINK_FIRST_OLD_FILES
- && !remove_any_file (file_name, recursive_unlink_option)
- && errno && errno != ENOENT)
+ switch (old_files_option)
{
- unlink_error (file_name);
- return 0;
+ case UNLINK_FIRST_OLD_FILES:
+ if (!remove_any_file (file_name, recursive_unlink_option)
+ && errno && errno != ENOENT)
+ {
+ unlink_error (file_name);
+ return 0;
+ }
+ break;
+
+ case KEEP_NEWER_FILES:
+ if (file_newer_p (file_name, ¤t_stat_info))
+ {
+ WARN ((0, 0, _("Current `%s' is newer"), file_name));
+ return 0;
+ }
+ break;
+
+ default:
+ break;
}
return 1;
static int
maybe_recoverable (char *file_name, int *interdir_made)
{
+ int e = errno;
+
if (*interdir_made)
return 0;
switch (old_files_option)
{
- default:
+ case KEEP_OLD_FILES:
return 0;
+ case KEEP_NEWER_FILES:
+ if (file_newer_p (file_name, ¤t_stat_info))
+ {
+ errno = e;
+ return 0;
+ }
+ /* FALL THROUGH */
+
case DEFAULT_OLD_FILES:
case NO_OVERWRITE_DIR_OLD_FILES:
case OVERWRITE_OLD_FILES:
errno = EEXIST;
return r;
}
+
+ case UNLINK_FIRST_OLD_FILES:
+ break;
}
case ENOENT:
}
}
-/* Translate the sparse information on the header, and in any
- subsequent extended headers, into an array of structures with true
- numbers, as opposed to character strings. Return nonzero if
- successful.
-
- This function invalidates current_header. */
-
-bool
-fill_in_sparse_array (void)
-{
- off_t sparse_data_size = current_stat_info.stat.st_size;
- off_t file_size = OFF_FROM_HEADER (current_header->oldgnu_header.realsize);
- int sparses;
- int counter;
- union block *h = current_header;
-
- init_sparsearray ();
-
- for (sparses = 0; sparses < SPARSES_IN_OLDGNU_HEADER; sparses++)
- {
- struct sparse const *s = &h->oldgnu_header.sp[sparses];
- off_t offset;
- size_t numbytes;
- if (s->numbytes[0] == '\0')
- break;
- sparsearray[sparses].offset = offset = OFF_FROM_HEADER (s->offset);
- sparsearray[sparses].numbytes = numbytes =
- SIZE_FROM_HEADER (s->numbytes);
- sparse_data_size -= numbytes;
- if (offset < 0 || file_size < offset + numbytes || sparse_data_size < 0)
- goto invalid_member;
- }
-
- if (h->oldgnu_header.isextended)
- do
- {
- h = find_next_block ();
- if (! h)
- {
- ERROR ((0, 0, _("Unexpected EOF in archive")));
- return 0;
- }
-
- for (counter = 0; counter < SPARSES_IN_SPARSE_HEADER; counter++)
- {
- struct sparse const *s = &h->sparse_header.sp[counter];
- off_t offset;
- size_t numbytes;
- if (s->numbytes[0] == '\0')
- break;
-
- if (sparses == sp_array_size)
- {
- sp_array_size *= 2;
- sparsearray =
- xrealloc (sparsearray,
- sp_array_size * sizeof *sparsearray);
- }
-
- sparsearray[sparses].offset = offset =
- OFF_FROM_HEADER (s->offset);
- sparsearray[sparses].numbytes = numbytes =
- SIZE_FROM_HEADER (s->numbytes);
- sparse_data_size -= numbytes;
- if (offset < 0 || file_size < offset + numbytes
- || sparse_data_size < 0)
- goto invalid_member;
- sparses++;
- }
-
- set_next_block_after (h);
-
- } while (h->sparse_header.isextended);
-
- return 1;
-
- invalid_member:
- ERROR ((0, 0, _("%s: invalid sparse archive member"),
- current_stat_info.file_name));
- return 0;
-}
-
-
-static off_t
-extract_sparse_file (int fd, char const *name,
- off_t sizeleft, off_t file_size)
-{
- int sparse_ind = 0;
-
- while (sizeleft != 0)
- {
- size_t written;
- size_t count;
- union block *data_block = find_next_block ();
- if (! data_block)
- {
- ERROR ((0, 0, _("Unexpected EOF in archive")));
- return sizeleft;
- }
- if (lseek (fd, sparsearray[sparse_ind].offset, SEEK_SET) < 0)
- {
- seek_error_details (name, sparsearray[sparse_ind].offset);
- return sizeleft;
- }
- written = sparsearray[sparse_ind++].numbytes;
- while (written > BLOCKSIZE)
- {
- count = full_write (fd, data_block->buffer, BLOCKSIZE);
- written -= count;
- sizeleft -= count;
- if (count != BLOCKSIZE)
- {
- write_error_details (name, count, BLOCKSIZE);
- return sizeleft;
- }
- set_next_block_after (data_block);
- data_block = find_next_block ();
- if (! data_block)
- {
- ERROR ((0, 0, _("Unexpected EOF in archive")));
- return sizeleft;
- }
- }
-
- count = full_write (fd, data_block->buffer, written);
- sizeleft -= count;
-
- if (count != written)
- {
- write_error_details (name, count, written);
- return sizeleft;
- }
-
- set_next_block_after (data_block);
- }
-
- if (ftruncate (fd, file_size) != 0)
- truncate_error (name);
-
- return 0;
-}
-
/* Fix the statuses of all directories whose statuses need fixing, and
which are not ancestors of FILE_NAME. If AFTER_SYMLINKS is
nonzero, do this for all such directories; otherwise, stop at the
int openflag;
mode_t mode;
off_t size;
- off_t file_size;
int interdir_made = 0;
char typeflag;
char *file_name;
/* Print the block from current_header and current_stat. */
if (verbose_option)
- print_header (-1);
+ print_header (¤t_stat_info, -1);
- file_name = safer_name_suffix (current_stat_info.file_name, 0);
+ file_name = safer_name_suffix (current_stat_info.file_name, false);
if (strip_path_elements)
{
size_t prefix_len = stripped_prefix_len (file_name, strip_path_elements);
/* Extract the archive entry according to its type. */
- typeflag = current_header->header.typeflag;
+ /* KLUDGE */
+ typeflag = sparse_member_p (¤t_stat_info) ?
+ GNUTYPE_SPARSE : current_header->header.typeflag;
+
switch (typeflag)
{
case GNUTYPE_SPARSE:
- file_size = OFF_FROM_HEADER (current_header->oldgnu_header.realsize);
- if (! fill_in_sparse_array ())
- return;
/* Fall through. */
case AREGTYPE:
}
extract_file:
- if (typeflag == GNUTYPE_SPARSE)
+ if (current_stat_info.is_sparse)
{
- char *name;
- size_t name_length_bis;
-
- /* Kludge alert. NAME is assigned to header.name because
- during the extraction, the space that contains the header
- will get scribbled on, and the name will get munged, so any
- error messages that happen to contain the filename will look
- REAL interesting unless we do this. */
-
- name_length_bis = strlen (file_name) + 1;
- name = xmalloc (name_length_bis);
- memcpy (name, file_name, name_length_bis);
- size = extract_sparse_file (fd, name,
- current_stat_info.stat.st_size, file_size);
- free (sparsearray);
+ sparse_extract_file (fd, ¤t_stat_info, &size);
}
else
for (size = current_stat_info.stat.st_size; size > 0; )
again_link:
{
- char const *link_name = safer_name_suffix (current_stat_info.link_name, 1);
+ char const *link_name = safer_name_suffix (current_stat_info.link_name,
+ true);
struct stat st1, st2;
int e;