off_t records_read; /* number of records read from this archive */
off_t records_written; /* likewise, for records written */
extern off_t records_skipped; /* number of records skipped at the start
- of the archive, defined in delete.c */
+ of the archive, defined in delete.c */
static off_t record_start_block; /* block ordinal at record_start */
last_stat_time = start_time;
}
-void
-set_volume_start_time ()
+static void
+set_volume_start_time (void)
{
gettime (&volume_start_time);
last_stat_time = volume_start_time;
ct_compress,
ct_gzip,
ct_bzip2,
+ ct_lzip,
ct_lzma,
ct_lzop,
ct_xz
{
enum compress_type type;
size_t length;
- char *magic;
- char *program;
- char *option;
+ char const *magic;
+ char const *program;
+ char const *option;
};
static struct zip_magic const magic[] = {
{ ct_compress, 2, "\037\235", COMPRESS_PROGRAM, "-Z" },
{ ct_gzip, 2, "\037\213", GZIP_PROGRAM, "-z" },
{ ct_bzip2, 3, "BZh", BZIP2_PROGRAM, "-j" },
+ { ct_lzip, 4, "LZIP", LZIP_PROGRAM, "--lzip" },
{ ct_lzma, 6, "\xFFLZMA", LZMA_PROGRAM, "--lzma" },
{ ct_lzop, 4, "\211LZO", LZOP_PROGRAM, "--lzop" },
{ ct_xz, 6, "\0xFD7zXZ", XZ_PROGRAM, "-J" },
#define compress_program(t) magic[t].program
/* Check if the file ARCHIVE is a compressed archive. */
-enum compress_type
+static enum compress_type
check_compressed_archive (bool *pshort)
{
struct zip_magic const *p;
if (!pshort)
pshort = &temp;
-
+
/* Prepare global data needed for find_next_block: */
record_end = record_start; /* set up for 1st record = # 0 */
sfr = read_full_records;
read_full_records = true; /* Suppress fatal error on reading a partial
record */
*pshort = find_next_block () == 0;
-
+
/* Restore global values */
read_full_records = sfr;
/* Guess if the archive is seekable. */
static void
-guess_seekable_archive ()
+guess_seekable_archive (void)
{
struct stat st;
seekable_archive = !!seek_option;
return;
}
-
+
if (!multi_volume_option && !use_compress_program_option
&& fstat (archive, &st) == 0)
seekable_archive = S_ISREG (st.st_mode);
/* Open an archive named archive_name_array[0]. Detect if it is
a compressed archive of known type and use corresponding decompression
program if so */
-int
-open_compressed_archive ()
+static int
+open_compressed_archive (void)
{
archive = rmtopen (archive_name_array[0], O_RDONLY | O_BINARY,
MODE_RW, rsh_command_option);
if (shortfile)
ERROR ((0, 0, _("This does not look like a tar archive")));
return archive;
-
+
case ct_none:
if (shortfile)
ERROR ((0, 0, _("This does not look like a tar archive")));
break;
}
}
-
+
/* FD is not needed any more */
rmtclose (archive);
-
+
hit_eof = false; /* It might have been set by find_next_block in
check_compressed_archive */
}
static void
-init_buffer ()
+init_buffer (void)
{
if (! record_buffer_aligned[record_index])
record_buffer_aligned[record_index] =
}
/* Perform a write to flush the buffer. */
-ssize_t
+static ssize_t
_flush_write (void)
{
ssize_t status;
status = record_size;
else
status = sys_write_archive_buffer ();
-
+
return status;
}
}
static bool
-archive_is_dev ()
+archive_is_dev (void)
{
struct stat st;
off_t start = current_block_ordinal ();
off_t offset;
off_t nrec, nblk;
- off_t skipped = (blocking_factor - (current_block - record_start));
+ off_t skipped = (blocking_factor - (current_block - record_start))
+ * BLOCKSIZE;
- size -= skipped * BLOCKSIZE;
-
- if (size < record_size)
+ if (size <= skipped)
return 0;
- /* FIXME: flush? */
/* Compute number of records to skip */
- nrec = size / record_size;
+ nrec = (size - skipped) / record_size;
+ if (nrec == 0)
+ return 0;
offset = rmtlseek (archive, nrec * record_size, SEEK_CUR);
if (offset < 0)
return offset;
\f
static void
-increase_volume_number ()
+increase_volume_number (void)
{
global_volno++;
if (global_volno < 0)
volno++;
}
-void
+static void
change_tape_menu (FILE *read_file)
{
char *input_buffer = NULL;
size_t size = 0;
bool stop = false;
-
+
while (!stop)
{
fputc ('\007', stderr);
assign_string (&continued_file_name, NULL);
continued_file_size = continued_file_offset = 0;
current_block = record_start;
-
+
if (rmtclose (archive) != 0)
close_error (*archive_name_cursor);
enum read_header rc;
tar_stat_init (info);
- rc = read_header (¤t_header, info, false);
+ rc = read_header (¤t_header, info, read_header_auto);
if (rc == HEADER_SUCCESS)
{
set_next_block_after (current_header);
return false;
}
-bool
-try_new_volume ()
+static bool
+try_new_volume (void)
{
size_t status;
union block *header;
enum access_mode acc;
-
+
switch (subcommand_option)
{
case APPEND_SUBCOMMAND:
if (!new_volume (acc))
return true;
-
+
while ((status = rmtread (archive, record_start->buffer, record_size))
== SAFE_READ_ERROR)
archive_read_error ();
{
case XGLTYPE:
{
- if (!read_header0 (&dummy))
- return false;
+ tar_stat_init (&dummy);
+ if (read_header (&header, &dummy, read_header_x_global)
+ != HEADER_SUCCESS_EXTENDED)
+ {
+ ERROR ((0, 0, _("This does not look like a tar archive")));
+ return false;
+ }
+
xheader_decode (&dummy); /* decodes values from the global header */
tar_stat_destroy (&dummy);
- if (!real_s_name)
- {
- /* We have read the extended header of the first member in
- this volume. Put it back, so next read_header works as
- expected. */
- current_block = record_start;
- }
+
+ /* The initial global header must be immediately followed by
+ an extended PAX header for the first member in this volume.
+ However, in some cases tar may split volumes in the middle
+ of a PAX header. This is incorrect, and should be fixed
+ in the future versions. In the meantime we must be
+ prepared to correctly list and extract such archives.
+
+ If this happens, the following call to read_header returns
+ HEADER_FAILURE, which is ignored.
+
+ See also tests/multiv07.at */
+
+ switch (read_header (&header, &dummy, read_header_auto))
+ {
+ case HEADER_SUCCESS:
+ set_next_block_after (header);
+ break;
+
+ case HEADER_FAILURE:
+ break;
+
+ default:
+ ERROR ((0, 0, _("This does not look like a tar archive")));
+ return false;
+ }
break;
}
STRINGIFY_BIGINT (real_s_totsize, totsizebuf),
STRINGIFY_BIGINT (real_s_sizeleft, s1buf),
STRINGIFY_BIGINT (continued_file_offset, s2buf)));
-
+
return false;
}
}
if (len < 1)
return NULL;
-
+
for (p = label + len - 1; p > label && isdigit ((unsigned char) *p); p--)
;
if (p > label && p - (VOLUME_TEXT_LEN - 1) > label)
return NULL;
}
-
+
/* Check LABEL against the volume label, seen as a globbing
pattern. Return true if the pattern matches. In case of failure,
retry matching a volume sequence number before giving up in
check_label_pattern (const char *label)
{
char *string;
- bool result;
+ bool result = false;
if (fnmatch (volume_label_option, label, 0) == 0)
return true;
if (!volume_label)
{
union block *label = find_next_block ();
-
+
if (!label)
FATAL_ERROR ((0, 0, _("Archive not labeled to match %s"),
quote (volume_label_option)));
tar_stat_destroy (&st);
}
}
-
+
if (!volume_label)
FATAL_ERROR ((0, 0, _("Archive not labeled to match %s"),
quote (volume_label_option)));
-
+
if (!check_label_pattern (volume_label))
FATAL_ERROR ((0, 0, _("Volume %s does not match %s"),
quote_n (0, volume_label),
}
static void
-add_chunk_header ()
+add_chunk_header (void)
{
if (archive_format == POSIX_FORMAT)
{
/* Synchronize multi-volume globals */
static void
-multi_volume_sync ()
+multi_volume_sync (void)
{
if (multi_volume_option)
{
size_t status; /* result from system call */
checkpoint_run (false);
-
+
/* Clear the count of errors. This only applies to a single call to
flush_read. */
size_t status; /* result from system call */
checkpoint_run (false);
-
+
/* Clear the count of errors. This only applies to a single call to
flush_read. */
size_t copy_size;
size_t bufsize;
tarlong wrt;
-
+
status = _flush_write ();
if (status != record_size && !multi_volume_option)
archive_write_error (status);
else
{
if (status)
- records_written++;
+ records_written++;
bytes_written += status;
}
ERROR ((0, 0, _("write did not end on a block boundary")));
archive_write_error (status);
}
-
+
/* In multi-volume mode. */
/* ENXIO is for the UNIX PC. */
if (status < 0 && errno != ENOSPC && errno != EIO && errno != ENXIO)
copy_ptr = record_start->buffer + status;
copy_size = buffer_level - status;
-
+
/* Switch to the next buffer */
record_index = !record_index;
init_buffer ();
write_extended (true, &dummy, find_next_block ());
tar_stat_destroy (&dummy);
-
+
if (real_s_name)
add_chunk_header ();
wrt = bytes_written;
switch (wanted_access)
{
case ACCESS_READ:
+ case ACCESS_UPDATE:
if (volume_label_option)
match_volume_label ();
break;
if (volume_label_option)
write_volume_label ();
break;
-
- default:
- break;
}
set_volume_start_time ();
}