/* Buffer management for tar.
Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
- 2003, 2004, 2005 Free Software Foundation, Inc.
+ 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
Written by John Gilmore, on 1985-08-25.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
#include <system.h>
+#include <system-ioctl.h>
#include <signal.h>
+#include <closeout.h>
#include <fnmatch.h>
+#include <getline.h>
#include <human.h>
#include <quotearg.h>
static tarlong prev_written; /* bytes written on previous volumes */
static tarlong bytes_written; /* bytes written on this volume */
static void *record_buffer[2]; /* allocated memory */
+union block *record_buffer_aligned[2];
static int record_index;
/* FIXME: The following variables should ideally be static to this
enum access_mode access_mode; /* how do we handle the archive */
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 */
static off_t record_start_block; /* block ordinal at record_start */
static bool hit_eof;
/* Checkpointing counter */
-static int checkpoint;
+static unsigned checkpoint;
static bool read_full_records = false;
bool write_archive_to_stdout;
+void (*flush_write_ptr) (size_t);
+void (*flush_read_ptr) (void);
+
\f
char *volume_label;
char *continued_file_name;
set_start_time ()
{
gettime (&start_time);
+ volume_start_time = start_time;
+ last_stat_time = start_time;
+}
+
+void
+set_volume_start_time ()
+{
+ gettime (&volume_start_time);
+ last_stat_time = volume_start_time;
}
void
{
struct timespec now;
gettime (&now);
- duration += ((now.tv_sec - start_time.tv_sec)
- + (now.tv_nsec - start_time.tv_nsec) / 1e9);
- set_start_time ();
+ duration += ((now.tv_sec - last_stat_time.tv_sec)
+ + (now.tv_nsec - last_stat_time.tv_nsec) / 1e9);
+ gettime (&last_stat_time);
}
\f
}
\f
-void
-print_total_written (void)
+static void
+print_stats (FILE *fp, const char *text, tarlong numbytes)
{
- tarlong written = prev_written + bytes_written;
char bytes[sizeof (tarlong) * CHAR_BIT];
char abbr[LONGEST_HUMAN_READABLE + 1];
char rate[LONGEST_HUMAN_READABLE + 1];
int human_opts = human_autoscale | human_base_1024 | human_SI | human_B;
- sprintf (bytes, TARLONG_FORMAT, written);
+ sprintf (bytes, TARLONG_FORMAT, numbytes);
- /* Amanda 2.4.1p1 looks for "Total bytes written: [0-9][0-9]*". */
- fprintf (stderr, _("Total bytes written: %s (%s, %s/s)\n"), bytes,
- human_readable (written, abbr, human_opts, 1, 1),
- (0 < duration && written / duration < (uintmax_t) -1
- ? human_readable (written / duration, rate, human_opts, 1, 1)
+ fprintf (fp, "%s: %s (%s, %s/s)\n",
+ text, bytes,
+ human_readable (numbytes, abbr, human_opts, 1, 1),
+ (0 < duration && numbytes / duration < (uintmax_t) -1
+ ? human_readable (numbytes / duration, rate, human_opts, 1, 1)
: "?"));
}
+void
+print_total_stats ()
+{
+ switch (subcommand_option)
+ {
+ case CREATE_SUBCOMMAND:
+ case CAT_SUBCOMMAND:
+ case UPDATE_SUBCOMMAND:
+ case APPEND_SUBCOMMAND:
+ /* Amanda 2.4.1p1 looks for "Total bytes written: [0-9][0-9]*". */
+ print_stats (stderr, _("Total bytes written"),
+ prev_written + bytes_written);
+ break;
+
+ case DELETE_SUBCOMMAND:
+ {
+ char buf[UINTMAX_STRSIZE_BOUND];
+ print_stats (stderr, _("Total bytes read"),
+ records_read * record_size);
+ print_stats (stderr, _("Total bytes written"),
+ prev_written + bytes_written);
+ fprintf (stderr, _("Total bytes deleted: %s\n"),
+ STRINGIFY_BIGINT ((records_read - records_skipped)
+ * record_size
+ - (prev_written + bytes_written), buf));
+ }
+ break;
+
+ case EXTRACT_SUBCOMMAND:
+ case LIST_SUBCOMMAND:
+ case DIFF_SUBCOMMAND:
+ print_stats (stderr, _("Total bytes read"),
+ records_read * record_size);
+ break;
+ }
+}
+
/* Compute and return the block ordinal at current_block. */
off_t
current_block_ordinal (void)
static void
init_buffer ()
{
- if (!record_buffer[record_index])
- page_aligned_alloc (&record_buffer[record_index], record_size);
-
- record_start = record_buffer[record_index];
+ if (! record_buffer_aligned[record_index])
+ record_buffer_aligned[record_index] =
+ page_aligned_alloc (&record_buffer[record_index], record_size);
+
+ record_start = record_buffer_aligned[record_index];
current_block = record_start;
record_end = record_start + blocking_factor;
}
if (index_file_name)
{
- stdlis = fopen (index_file_name, "w");
+ stdlis = freopen (index_file_name, "w", stdout);
if (! stdlis)
open_error (index_file_name);
+ close_stdout_set_file_name (index_file_name);
}
else
stdlis = to_stdout_option ? stderr : stdout;
record_index = 0;
init_buffer ();
-
+
/* When updating the archive, we start with reading. */
access_mode = wanted_access == ACCESS_UPDATE ? ACCESS_READ : wanted_access;
archive = STDIN_FILENO;
- type = check_compressed_archive (archive);
+ type = check_compressed_archive ();
if (type != ct_none)
FATAL_ERROR ((0, 0,
_("Archive is compressed. Use %s option"),
archive = STDIN_FILENO;
stdlis = stderr;
write_archive_to_stdout = true;
+ record_end = record_start; /* set up for 1st record = # 0 */
break;
}
}
break;
case ACCESS_UPDATE:
- archive = rmtopen (archive_name_array[0], O_RDWR | O_CREAT | O_BINARY,
+ archive = rmtopen (archive_name_array[0],
+ O_RDWR | O_CREAT | O_BINARY,
MODE_RW, rsh_command_option);
+
+ if (check_compressed_archive () != ct_none)
+ FATAL_ERROR ((0, 0,
+ _("Cannot update compressed archives")));
break;
}
switch (wanted_access)
{
- case ACCESS_UPDATE:
- records_written = 0;
- record_end = record_start; /* set up for 1st record = # 0 */
-
case ACCESS_READ:
find_next_block (); /* read it in, check for EOF */
break;
+ case ACCESS_UPDATE:
case ACCESS_WRITE:
records_written = 0;
break;
}
}
+static void
+do_checkpoint (bool write)
+{
+ if (checkpoint_option && !(++checkpoint % checkpoint_option))
+ {
+ switch (checkpoint_style)
+ {
+ case checkpoint_dot:
+ fputc ('.', stdlis);
+ fflush (stdlis);
+ break;
+
+ case checkpoint_text:
+ if (write)
+ /* TRANSLATORS: This is a ``checkpoint of write operation'',
+ *not* ``Writing a checkpoint''.
+ E.g. in Spanish ``Punto de comprobaci@'on de escritura'',
+ *not* ``Escribiendo un punto de comprobaci@'on'' */
+ WARN ((0, 0, _("Write checkpoint %u"), checkpoint));
+ else
+ /* TRANSLATORS: This is a ``checkpoint of read operation'',
+ *not* ``Reading a checkpoint''.
+ E.g. in Spanish ``Punto de comprobaci@'on de lectura'',
+ *not* ``Leyendo un punto de comprobaci@'on'' */
+ WARN ((0, 0, _("Read checkpoint %u"), checkpoint));
+ break;
+ }
+ }
+}
+
/* Perform a write to flush the buffer. */
ssize_t
_flush_write (void)
{
ssize_t status;
- if (checkpoint_option && !(++checkpoint % 10))
- /* TRANSLATORS: This is a ``checkpoint of write operation'',
- *not* ``Writing a checkpoint''.
- E.g. in Spanish ``Punto de comprobaci@'on de escritura'',
- *not* ``Escribiendo un punto de comprobaci@'on'' */
- WARN ((0, 0, _("Write checkpoint %d"), checkpoint));
-
+ do_checkpoint (true);
if (tape_length_option && tape_length_option <= bytes_written)
{
errno = ENOSPC;
if (totals_option)
{
int e = errno;
- print_total_written ();
+ print_total_stats ();
errno = e;
}
records_read++;
}
-/* Perform a read to flush the buffer. */
-size_t
-_flush_read (void)
-{
- size_t status; /* result from system call */
-
- if (checkpoint_option && !(++checkpoint % 10))
- /* TRANSLATORS: This is a ``checkpoint of read operation'',
- *not* ``Reading a checkpoint''.
- E.g. in Spanish ``Punto de comprobaci@'on de lectura'',
- *not* ``Leyendo un punto de comprobaci@'on'' */
- WARN ((0, 0, _("Read checkpoint %d"), checkpoint));
-
- /* Clear the count of errors. This only applies to a single call to
- flush_read. */
-
- read_error_count = 0; /* clear error count */
-
- if (write_archive_to_stdout && record_start_block != 0)
- {
- archive = STDOUT_FILENO;
- status = sys_write_archive_buffer ();
- archive = STDIN_FILENO;
- if (status != record_size)
- archive_write_error (status);
- }
-
- status = rmtread (archive, record_start->buffer, record_size);
- if (status == record_size)
- records_read++;
- return status;
-}
-
/* Flush the current buffer to/from the archive. */
void
flush_archive (void)
{
+ size_t buffer_level = current_block->buffer - record_start->buffer;
record_start_block += record_end - record_start;
current_block = record_start;
record_end = record_start + blocking_factor;
break;
case ACCESS_WRITE:
- flush_write ();
+ flush_write_ptr (buffer_level);
break;
case ACCESS_UPDATE:
close_archive (void)
{
if (time_to_start_writing || access_mode == ACCESS_WRITE)
- flush_archive ();
+ {
+ flush_archive ();
+ if (current_block > record_start)
+ flush_archive ();
+ }
sys_drain_input_pipe ();
volno++;
}
+void
+change_tape_menu (FILE *read_file)
+{
+ char *input_buffer = NULL;
+ size_t size = 0;
+ bool stop = false;
+
+ while (!stop)
+ {
+ fputc ('\007', stderr);
+ fprintf (stderr,
+ _("Prepare volume #%d for %s and hit return: "),
+ global_volno + 1, quote (*archive_name_cursor));
+ fflush (stderr);
+
+ if (getline (&input_buffer, &size, read_file) <= 0)
+ {
+ WARN ((0, 0, _("EOF where user reply was expected")));
+
+ if (subcommand_option != EXTRACT_SUBCOMMAND
+ && subcommand_option != LIST_SUBCOMMAND
+ && subcommand_option != DIFF_SUBCOMMAND)
+ WARN ((0, 0, _("WARNING: Archive is incomplete")));
+
+ fatal_exit ();
+ }
+
+ if (input_buffer[0] == '\n'
+ || input_buffer[0] == 'y'
+ || input_buffer[0] == 'Y')
+ break;
+
+ switch (input_buffer[0])
+ {
+ case '?':
+ {
+ fprintf (stderr, _("\
+ n name Give a new file name for the next (and subsequent) volume(s)\n\
+ q Abort tar\n\
+ y or newline Continue operation\n"));
+ if (!restrict_option)
+ fprintf (stderr, _(" ! Spawn a subshell\n"));
+ fprintf (stderr, _(" ? Print this list\n"));
+ }
+ break;
+
+ case 'q':
+ /* Quit. */
+
+ WARN ((0, 0, _("No new volume; exiting.\n")));
+
+ if (subcommand_option != EXTRACT_SUBCOMMAND
+ && subcommand_option != LIST_SUBCOMMAND
+ && subcommand_option != DIFF_SUBCOMMAND)
+ WARN ((0, 0, _("WARNING: Archive is incomplete")));
+
+ fatal_exit ();
+
+ case 'n':
+ /* Get new file name. */
+
+ {
+ char *name;
+ char *cursor;
+
+ for (name = input_buffer + 1;
+ *name == ' ' || *name == '\t';
+ name++)
+ ;
+
+ for (cursor = name; *cursor && *cursor != '\n'; cursor++)
+ ;
+ *cursor = '\0';
+
+ if (name[0])
+ {
+ /* FIXME: the following allocation is never reclaimed. */
+ *archive_name_cursor = xstrdup (name);
+ stop = true;
+ }
+ else
+ fprintf (stderr, "%s",
+ _("File name not specified. Try again.\n"));
+ }
+ break;
+
+ case '!':
+ if (!restrict_option)
+ {
+ sys_spawn_shell ();
+ break;
+ }
+ /* FALL THROUGH */
+
+ default:
+ fprintf (stderr, _("Invalid input. Type ? for help.\n"));
+ }
+ }
+ free (input_buffer);
+}
+
/* We've hit the end of the old volume. Close it and open the next one.
Return nonzero on success.
*/
{
static FILE *read_file;
static int looped;
+ int prompt;
if (!read_file && !info_script_option)
/* FIXME: if fopen is used, it will never be closed. */
assign_string (&volume_label, NULL);
assign_string (&continued_file_name, NULL);
continued_file_size = continued_file_offset = 0;
-
+
if (rmtclose (archive) != 0)
close_warn (*archive_name_cursor);
archive_name_cursor = archive_name_array;
looped = 1;
}
+ prompt = looped;
tryagain:
- if (looped)
+ if (prompt)
{
/* We have to prompt from now on. */
{
if (volno_file_option)
closeout_volume_number ();
- if (system (info_script_option) != 0)
+ if (sys_exec_info_script (archive_name_cursor, global_volno+1))
FATAL_ERROR ((0, 0, _("%s command failed"),
quote (info_script_option)));
}
else
- while (1)
- {
- char input_buffer[80];
-
- fputc ('\007', stderr);
- fprintf (stderr,
- _("Prepare volume #%d for %s and hit return: "),
- global_volno + 1, quote (*archive_name_cursor));
- fflush (stderr);
-
- if (fgets (input_buffer, sizeof input_buffer, read_file) == 0)
- {
- WARN ((0, 0, _("EOF where user reply was expected")));
-
- if (subcommand_option != EXTRACT_SUBCOMMAND
- && subcommand_option != LIST_SUBCOMMAND
- && subcommand_option != DIFF_SUBCOMMAND)
- WARN ((0, 0, _("WARNING: Archive is incomplete")));
-
- fatal_exit ();
- }
- if (input_buffer[0] == '\n'
- || input_buffer[0] == 'y'
- || input_buffer[0] == 'Y')
- break;
-
- switch (input_buffer[0])
- {
- case '?':
- {
- /* FIXME: Might it be useful to disable the '!' command? */
- fprintf (stderr, _("\
- n [name] Give a new file name for the next (and subsequent) volume(s)\n\
- q Abort tar\n\
- ! Spawn a subshell\n\
- ? Print this list\n"));
- }
- break;
-
- case 'q':
- /* Quit. */
-
- WARN ((0, 0, _("No new volume; exiting.\n")));
-
- if (subcommand_option != EXTRACT_SUBCOMMAND
- && subcommand_option != LIST_SUBCOMMAND
- && subcommand_option != DIFF_SUBCOMMAND)
- WARN ((0, 0, _("WARNING: Archive is incomplete")));
-
- fatal_exit ();
-
- case 'n':
- /* Get new file name. */
-
- {
- char *name = &input_buffer[1];
- char *cursor;
-
- for (name = input_buffer + 1;
- *name == ' ' || *name == '\t';
- name++)
- ;
-
- for (cursor = name; *cursor && *cursor != '\n'; cursor++)
- ;
- *cursor = '\0';
-
- /* FIXME: the following allocation is never reclaimed. */
- *archive_name_cursor = xstrdup (name);
- }
- break;
-
- case '!':
- sys_spawn_shell ();
- break;
- }
- }
+ change_tape_menu (read_file);
}
if (strcmp (archive_name_cursor[0], "-") == 0)
open_warn (*archive_name_cursor);
if (!verify_option && mode == ACCESS_WRITE && backup_option)
undo_last_backup ();
+ prompt = 1;
goto tryagain;
}
return true;
}
-
+
+static bool
+read_header0 (struct tar_stat_info *info)
+{
+ enum read_header rc;
+
+ tar_stat_init (info);
+ rc = read_header_primitive (false, info);
+ if (rc == HEADER_SUCCESS)
+ {
+ set_next_block_after (current_header);
+ return true;
+ }
+ ERROR ((0, 0, _("This does not look like a tar archive")));
+ return false;
+}
+
bool
try_new_volume ()
{
size_t status;
- enum read_header rc;
- union block *block;
-
+ union block *header;
+ struct tar_stat_info dummy;
+
switch (subcommand_option)
{
case APPEND_SUBCOMMAND:
if (status != record_size)
short_read (status);
- again:
- block = current_block;
- rc = read_header (true);
- switch (rc)
+ header = find_next_block ();
+ if (!header)
+ return false;
+
+ switch (header->header.typeflag)
{
- case HEADER_SUCCESS_EXTENDED:
- if (current_header->header.typeflag == XGLTYPE)
- {
- struct tar_stat_info dummy;
- xheader_read (current_header,
- OFF_FROM_HEADER (current_header->header.size));
- xheader_decode_global ();
- xheader_destroy (&extended_header);
- tar_stat_init (&dummy);
- xheader_decode (&dummy); /* decodes values from the global header */
- tar_stat_destroy (&dummy);
- }
- break;
-
- case HEADER_SUCCESS:
- if (current_header->header.typeflag == GNUTYPE_VOLHDR)
- assign_string (&volume_label, current_header->header.name);
- else if (current_header->header.typeflag == GNUTYPE_MULTIVOL)
- {
- assign_string (&continued_file_name, current_header->header.name);
- continued_file_size =
- UINTMAX_FROM_HEADER (current_header->header.size);
- continued_file_offset =
- UINTMAX_FROM_HEADER (current_header->oldgnu_header.offset);
- }
- else
+ case XGLTYPE:
+ {
+ if (!read_header0 (&dummy))
+ 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;
+ }
break;
- set_next_block_after (current_header);
- goto again;
+ }
- case HEADER_ZERO_BLOCK:
- case HEADER_END_OF_FILE:
- case HEADER_FAILURE:
- current_block = block;
+ case GNUTYPE_VOLHDR:
+ if (!read_header0 (&dummy))
+ return false;
+ tar_stat_destroy (&dummy);
+ assign_string (&volume_label, current_header->header.name);
+ set_next_block_after (header);
+ header = find_next_block ();
+ if (header->header.typeflag != GNUTYPE_MULTIVOL)
+ break;
+ /* FALL THROUGH */
+
+ case GNUTYPE_MULTIVOL:
+ if (!read_header0 (&dummy))
+ return false;
+ tar_stat_destroy (&dummy);
+ assign_string (&continued_file_name, current_header->header.name);
+ continued_file_size =
+ UINTMAX_FROM_HEADER (current_header->header.size);
+ continued_file_offset =
+ UINTMAX_FROM_HEADER (current_header->oldgnu_header.offset);
break;
default:
- abort ();
+ break;
}
if (real_s_name)
{
uintmax_t s;
- if (!continued_file_name
+ if (!continued_file_name
|| strcmp (continued_file_name, real_s_name))
{
- WARN ((0, 0, _("%s is not continued on this volume"),
- quote (real_s_name)));
- return false;
+ if ((archive_format == GNU_FORMAT || archive_format == OLDGNU_FORMAT)
+ && strlen (real_s_name) >= NAME_FIELD_SIZE
+ && strncmp (continued_file_name, real_s_name,
+ NAME_FIELD_SIZE) == 0)
+ WARN ((0, 0,
+ _("%s is possibly continued on this volume: header contains truncated name"),
+ quote (real_s_name)));
+ else
+ {
+ WARN ((0, 0, _("%s is not continued on this volume"),
+ quote (real_s_name)));
+ return false;
+ }
}
s = continued_file_size + continued_file_offset;
STRINGIFY_BIGINT (continued_file_offset, s2buf)));
return false;
}
-
+
if (real_s_totsize - real_s_sizeleft != continued_file_offset)
{
WARN ((0, 0, _("This volume is out of sequence")));
else
{
union block *label = find_next_block ();
-
+
memset (label, 0, BLOCKSIZE);
strcpy (label->header.name, volume_label_option);
free (s);
}
+static void
+add_chunk_header ()
+{
+ if (archive_format == POSIX_FORMAT)
+ {
+ off_t block_ordinal;
+ union block *blk;
+ struct tar_stat_info st;
+ static size_t real_s_part_no; /* FIXME */
+
+ real_s_part_no++;
+ memset (&st, 0, sizeof st);
+ st.orig_file_name = st.file_name = real_s_name;
+ st.stat.st_mode = S_IFREG|S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
+ st.stat.st_uid = getuid ();
+ st.stat.st_gid = getgid ();
+ st.orig_file_name = xheader_format_name (&st,
+ "%d/GNUFileParts.%p/%f.%n",
+ real_s_part_no);
+ st.file_name = st.orig_file_name;
+ st.archive_file_size = st.stat.st_size = real_s_sizeleft;
+
+ block_ordinal = current_block_ordinal ();
+ blk = start_header (&st);
+ if (!blk)
+ abort (); /* FIXME */
+ finish_header (&st, blk, block_ordinal);
+ free (st.orig_file_name);
+ }
+}
+
+
/* Add a volume label to the current archive */
static void
write_volume_label (void)
{
int tmp;
union block *block = find_next_block ();
-
+
if (strlen (real_s_name) > NAME_FIELD_SIZE)
WARN ((0, 0,
_("%s: file name too long to be stored in a GNU multivolume header, truncated"),
quotearg_colon (real_s_name)));
-
+
memset (block, 0, BLOCKSIZE);
-
+
/* FIXME: Michael P Urban writes: [a long name file] is being written
when a new volume rolls around [...] Looks like the wrong value is
being preserved in real_s_name, though. */
-
+
strncpy (block->header.name, real_s_name, NAME_FIELD_SIZE);
block->header.typeflag = GNUTYPE_MULTIVOL;
-
+
OFF_TO_CHARS (real_s_sizeleft, block->header.size);
OFF_TO_CHARS (real_s_totsize - real_s_sizeleft,
block->oldgnu_header.offset);
-
+
tmp = verbose_option;
verbose_option = 0;
finish_header (¤t_stat_info, block, -1);
{
size_t status; /* result from system call */
- if (checkpoint_option && !(++checkpoint % 10))
- /* TRANSLATORS: This is a ``checkpoint of read operation'',
- *not* ``Reading a checkpoint''.
- E.g. in Spanish ``Punto de comprobaci@'on de lectura'',
- *not* ``Leyendo un punto de comprobaci@'on'' */
- WARN ((0, 0, _("Read checkpoint %d"), checkpoint));
-
+ do_checkpoint (false);
+
/* Clear the count of errors. This only applies to a single call to
flush_read. */
if (status != record_size)
archive_write_error (status);
}
-
+
for (;;)
{
status = rmtread (archive, record_start->buffer, record_size);
/* Simple flush write (no multi-volume or label extensions) */
static void
-simple_flush_write (void)
+simple_flush_write (size_t level __attribute__((unused)))
{
ssize_t status;
{
size_t status; /* result from system call */
- if (checkpoint_option && !(++checkpoint % 10))
- /* TRANSLATORS: This is a ``checkpoint of read operation'',
- *not* ``Reading a checkpoint''.
- E.g. in Spanish ``Punto de comprobaci@'on de lectura'',
- *not* ``Leyendo un punto de comprobaci@'on'' */
- WARN ((0, 0, _("Read checkpoint %d"), checkpoint));
-
+ do_checkpoint (false);
+
/* Clear the count of errors. This only applies to a single call to
flush_read. */
else if (status == SAFE_READ_ERROR)
{
archive_read_error ();
- continue;
+ continue;
}
break;
}
static void
gnu_flush_read (void)
{
- flush_read = simple_flush_read; /* Avoid recursion */
+ flush_read_ptr = simple_flush_read; /* Avoid recursion */
_gnu_flush_read ();
- flush_read = gnu_flush_read;
+ flush_read_ptr = gnu_flush_read;
}
static void
-_gnu_flush_write (void)
+_gnu_flush_write (size_t buffer_level)
{
ssize_t status;
union block *header;
char *copy_ptr;
size_t copy_size;
size_t bufsize;
-
+
status = _flush_write ();
if (status != record_size && !multi_volume_option)
archive_write_error (status);
return;
xheader_destroy (&extended_header);
-
+
increase_volume_number ();
- if (totals_option)
- prev_written += bytes_written;
+ prev_written += bytes_written;
bytes_written = 0;
copy_ptr = record_start->buffer + status;
- copy_size = record_size - status;
+ copy_size = buffer_level - status;
/* Switch to the next buffer */
record_index = !record_index;
init_buffer ();
-
+
if (volume_label_option)
add_volume_label ();
if (real_s_name)
add_multi_volume_header ();
- header = write_extended (XGLTYPE, NULL, find_next_block ());
+ write_extended (true, NULL, find_next_block ());
+ if (real_s_name)
+ add_chunk_header ();
+ header = find_next_block ();
bufsize = available_space_after (header);
while (bufsize < copy_size)
{
}
static void
-gnu_flush_write (void)
+gnu_flush_write (size_t buffer_level)
{
- flush_write = simple_flush_write; /* Avoid recursion */
- _gnu_flush_write ();
- flush_write = gnu_flush_write;
+ flush_write_ptr = simple_flush_write; /* Avoid recursion */
+ _gnu_flush_write (buffer_level);
+ flush_write_ptr = gnu_flush_write;
}
-
+
+void
+flush_read ()
+{
+ flush_read_ptr ();
+}
+
+void
+flush_write ()
+{
+ flush_write_ptr (record_size);
+}
+
void
open_archive (enum access_mode wanted_access)
{
- flush_read = gnu_flush_read;
- flush_write = gnu_flush_write;
+ flush_read_ptr = gnu_flush_read;
+ flush_write_ptr = gnu_flush_write;
_open_archive (wanted_access);
switch (wanted_access)
if (volume_label_option)
write_volume_label ();
break;
+
+ default:
+ break;
}
+ set_volume_start_time ();
}