|
@@ -76,8 +76,7 @@ static bool read_full_records = false;
|
|
|
|
|
|
/* We're reading, but we just read the last block and it's time to update.
|
|
|
Declared in update.c
|
|
|
-
|
|
|
- As least EXTERN like this one as possible. (?? --gray)
|
|
|
+
|
|
|
FIXME: Either eliminate it or move it to common.h.
|
|
|
*/
|
|
|
extern bool time_to_start_writing;
|
|
@@ -101,19 +100,94 @@ static int global_volno = 1; /* volume number to print in external
|
|
|
|
|
|
bool write_archive_to_stdout;
|
|
|
|
|
|
-/* Used by flush_read and flush_write to store the real info about saved
|
|
|
- names. */
|
|
|
-static char *real_s_name;
|
|
|
-static off_t real_s_totsize;
|
|
|
-static off_t real_s_sizeleft;
|
|
|
-
|
|
|
|
|
|
/* Multi-volume tracking support */
|
|
|
-static char *save_name; /* name of the file we are currently writing */
|
|
|
-static off_t save_totsize; /* total size of file we are writing, only
|
|
|
- valid if save_name is nonzero */
|
|
|
-static off_t save_sizeleft; /* where we are in the file we are writing,
|
|
|
- only valid if save_name is nonzero */
|
|
|
+
|
|
|
+/* When creating a multi-volume archive, each `bufmap' represents
|
|
|
+ a member stored (perhaps partly) in the current record buffer.
|
|
|
+ After flushing the record to the output media, all bufmaps that
|
|
|
+ represent fully written members are removed from the list, then
|
|
|
+ the sizeleft and start numbers in the remaining bufmaps are updated.
|
|
|
+
|
|
|
+ When reading from a multi-volume archive, the list degrades to a
|
|
|
+ single element, which keeps information about the member currently
|
|
|
+ being read.
|
|
|
+*/
|
|
|
+
|
|
|
+struct bufmap
|
|
|
+{
|
|
|
+ struct bufmap *next; /* Pointer to the next map entry */
|
|
|
+ size_t start; /* Offset of the first data block */
|
|
|
+ char *file_name; /* Name of the stored file */
|
|
|
+ off_t sizetotal; /* Size of the stored file */
|
|
|
+ off_t sizeleft; /* Size left to read/write */
|
|
|
+};
|
|
|
+static struct bufmap *bufmap_head, *bufmap_tail;
|
|
|
+
|
|
|
+/* This variable, when set, inhibits updating the bufmap chain after
|
|
|
+ a write. This is necessary when writing extended POSIX headers. */
|
|
|
+static int inhibit_map;
|
|
|
+
|
|
|
+void
|
|
|
+mv_begin_write (const char *file_name, off_t totsize, off_t sizeleft)
|
|
|
+{
|
|
|
+ if (multi_volume_option)
|
|
|
+ {
|
|
|
+ struct bufmap *bp = xmalloc (sizeof bp[0]);
|
|
|
+ if (bufmap_tail)
|
|
|
+ bufmap_tail->next = bp;
|
|
|
+ else
|
|
|
+ bufmap_head = bp;
|
|
|
+ bufmap_tail = bp;
|
|
|
+
|
|
|
+ bp->next = NULL;
|
|
|
+ bp->start = current_block - record_start;
|
|
|
+ bp->file_name = xstrdup (file_name);
|
|
|
+ bp->sizetotal = totsize;
|
|
|
+ bp->sizeleft = sizeleft;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static struct bufmap *
|
|
|
+bufmap_locate (size_t off)
|
|
|
+{
|
|
|
+ struct bufmap *map;
|
|
|
+
|
|
|
+ for (map = bufmap_head; map; map = map->next)
|
|
|
+ {
|
|
|
+ if (!map->next
|
|
|
+ || off < map->next->start * BLOCKSIZE)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return map;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+bufmap_free (struct bufmap *mark)
|
|
|
+{
|
|
|
+ struct bufmap *map;
|
|
|
+ for (map = bufmap_head; map && map != mark; )
|
|
|
+ {
|
|
|
+ struct bufmap *next = map->next;
|
|
|
+ free (map->file_name);
|
|
|
+ free (map);
|
|
|
+ map = next;
|
|
|
+ }
|
|
|
+ bufmap_head = map;
|
|
|
+ if (!bufmap_head)
|
|
|
+ bufmap_tail = bufmap_head;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+bufmap_reset (struct bufmap *map, ssize_t fixup)
|
|
|
+{
|
|
|
+ bufmap_free (map);
|
|
|
+ if (map)
|
|
|
+ {
|
|
|
+ for (; map; map = map->next)
|
|
|
+ map->start += fixup;
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
|
|
|
static struct tar_stat_info dummy;
|
|
@@ -125,32 +199,23 @@ buffer_write_global_xheader ()
|
|
|
}
|
|
|
|
|
|
void
|
|
|
-mv_begin (struct tar_stat_info *st)
|
|
|
+mv_begin_read (struct tar_stat_info *st)
|
|
|
{
|
|
|
- if (multi_volume_option)
|
|
|
- {
|
|
|
- assign_string (&save_name, st->orig_file_name);
|
|
|
- save_totsize = save_sizeleft = st->stat.st_size;
|
|
|
- }
|
|
|
+ mv_begin_write (st->orig_file_name, st->stat.st_size, st->stat.st_size);
|
|
|
}
|
|
|
|
|
|
void
|
|
|
mv_end ()
|
|
|
{
|
|
|
if (multi_volume_option)
|
|
|
- assign_string (&save_name, 0);
|
|
|
-}
|
|
|
-
|
|
|
-void
|
|
|
-mv_total_size (off_t size)
|
|
|
-{
|
|
|
- save_totsize = size;
|
|
|
+ bufmap_free (NULL);
|
|
|
}
|
|
|
|
|
|
void
|
|
|
mv_size_left (off_t size)
|
|
|
{
|
|
|
- save_sizeleft = size;
|
|
|
+ if (bufmap_head)
|
|
|
+ bufmap_head->sizeleft = size;
|
|
|
}
|
|
|
|
|
|
|
|
@@ -511,9 +576,7 @@ _open_archive (enum access_mode wanted_access)
|
|
|
FATAL_ERROR ((0, 0, _("No archive name given")));
|
|
|
|
|
|
tar_stat_destroy (¤t_stat_info);
|
|
|
- save_name = 0;
|
|
|
- real_s_name = 0;
|
|
|
-
|
|
|
+
|
|
|
record_index = 0;
|
|
|
init_buffer ();
|
|
|
|
|
@@ -673,6 +736,20 @@ _flush_write (void)
|
|
|
else
|
|
|
status = sys_write_archive_buffer ();
|
|
|
|
|
|
+ if (status && multi_volume_option && !inhibit_map)
|
|
|
+ {
|
|
|
+ struct bufmap *map = bufmap_locate (status);
|
|
|
+ if (map)
|
|
|
+ {
|
|
|
+ size_t delta = status - map->start * BLOCKSIZE;
|
|
|
+ if (delta > map->sizeleft)
|
|
|
+ delta = map->sizeleft;
|
|
|
+ map->sizeleft -= delta;
|
|
|
+ if (map->sizeleft == 0)
|
|
|
+ map = map->next;
|
|
|
+ bufmap_reset (map, map ? (- map->start) : 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
return status;
|
|
|
}
|
|
|
|
|
@@ -907,12 +984,9 @@ close_archive (void)
|
|
|
sys_wait_for_child (child_pid, hit_eof);
|
|
|
|
|
|
tar_stat_destroy (¤t_stat_info);
|
|
|
- if (save_name)
|
|
|
- free (save_name);
|
|
|
- if (real_s_name)
|
|
|
- free (real_s_name);
|
|
|
free (record_buffer[0]);
|
|
|
free (record_buffer[1]);
|
|
|
+ bufmap_free (NULL);
|
|
|
}
|
|
|
|
|
|
/* Called to initialize the global volume number. */
|
|
@@ -1280,30 +1354,30 @@ try_new_volume (void)
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- if (real_s_name)
|
|
|
+ if (bufmap_head)
|
|
|
{
|
|
|
uintmax_t s;
|
|
|
if (!continued_file_name
|
|
|
- || strcmp (continued_file_name, real_s_name))
|
|
|
+ || strcmp (continued_file_name, bufmap_head->file_name))
|
|
|
{
|
|
|
if ((archive_format == GNU_FORMAT || archive_format == OLDGNU_FORMAT)
|
|
|
- && strlen (real_s_name) >= NAME_FIELD_SIZE
|
|
|
- && strncmp (continued_file_name, real_s_name,
|
|
|
+ && strlen (bufmap_head->file_name) >= NAME_FIELD_SIZE
|
|
|
+ && strncmp (continued_file_name, bufmap_head->file_name,
|
|
|
NAME_FIELD_SIZE) == 0)
|
|
|
WARN ((0, 0,
|
|
|
_("%s is possibly continued on this volume: header contains truncated name"),
|
|
|
- quote (real_s_name)));
|
|
|
+ quote (bufmap_head->file_name)));
|
|
|
else
|
|
|
{
|
|
|
WARN ((0, 0, _("%s is not continued on this volume"),
|
|
|
- quote (real_s_name)));
|
|
|
+ quote (bufmap_head->file_name)));
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
s = continued_file_size + continued_file_offset;
|
|
|
|
|
|
- if (real_s_totsize != s || s < continued_file_offset)
|
|
|
+ if (bufmap_head->sizetotal != s || s < continued_file_offset)
|
|
|
{
|
|
|
char totsizebuf[UINTMAX_STRSIZE_BOUND];
|
|
|
char s1buf[UINTMAX_STRSIZE_BOUND];
|
|
@@ -1311,21 +1385,22 @@ try_new_volume (void)
|
|
|
|
|
|
WARN ((0, 0, _("%s is the wrong size (%s != %s + %s)"),
|
|
|
quote (continued_file_name),
|
|
|
- STRINGIFY_BIGINT (save_totsize, totsizebuf),
|
|
|
+ STRINGIFY_BIGINT (bufmap_head->sizetotal, totsizebuf),
|
|
|
STRINGIFY_BIGINT (continued_file_size, s1buf),
|
|
|
STRINGIFY_BIGINT (continued_file_offset, s2buf)));
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- if (real_s_totsize - real_s_sizeleft != continued_file_offset)
|
|
|
+ if (bufmap_head->sizetotal - bufmap_head->sizeleft !=
|
|
|
+ continued_file_offset)
|
|
|
{
|
|
|
char totsizebuf[UINTMAX_STRSIZE_BOUND];
|
|
|
char s1buf[UINTMAX_STRSIZE_BOUND];
|
|
|
char s2buf[UINTMAX_STRSIZE_BOUND];
|
|
|
|
|
|
WARN ((0, 0, _("This volume is out of sequence (%s - %s != %s)"),
|
|
|
- STRINGIFY_BIGINT (real_s_totsize, totsizebuf),
|
|
|
- STRINGIFY_BIGINT (real_s_sizeleft, s1buf),
|
|
|
+ STRINGIFY_BIGINT (bufmap_head->sizetotal, totsizebuf),
|
|
|
+ STRINGIFY_BIGINT (bufmap_head->sizeleft, s1buf),
|
|
|
STRINGIFY_BIGINT (continued_file_offset, s2buf)));
|
|
|
|
|
|
return false;
|
|
@@ -1477,26 +1552,24 @@ add_volume_label (void)
|
|
|
}
|
|
|
|
|
|
static void
|
|
|
-add_chunk_header (void)
|
|
|
+add_chunk_header (struct bufmap *map)
|
|
|
{
|
|
|
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.orig_file_name = st.file_name = map->file_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);
|
|
|
+ volno);
|
|
|
st.file_name = st.orig_file_name;
|
|
|
- st.archive_file_size = st.stat.st_size = real_s_sizeleft;
|
|
|
+ st.archive_file_size = st.stat.st_size = map->sizeleft;
|
|
|
|
|
|
block_ordinal = current_block_ordinal ();
|
|
|
blk = start_header (&st);
|
|
@@ -1520,27 +1593,23 @@ write_volume_label (void)
|
|
|
|
|
|
/* Write GNU multi-volume header */
|
|
|
static void
|
|
|
-gnu_add_multi_volume_header (void)
|
|
|
+gnu_add_multi_volume_header (struct bufmap *map)
|
|
|
{
|
|
|
int tmp;
|
|
|
union block *block = find_next_block ();
|
|
|
|
|
|
- if (strlen (real_s_name) > NAME_FIELD_SIZE)
|
|
|
+ if (strlen (map->file_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)));
|
|
|
+ quotearg_colon (map->file_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);
|
|
|
+ strncpy (block->header.name, map->file_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,
|
|
|
+ OFF_TO_CHARS (map->sizeleft, block->header.size);
|
|
|
+ OFF_TO_CHARS (map->sizetotal - map->sizeleft,
|
|
|
block->oldgnu_header.offset);
|
|
|
|
|
|
tmp = verbose_option;
|
|
@@ -1553,40 +1622,17 @@ gnu_add_multi_volume_header (void)
|
|
|
/* Add a multi volume header to the current archive. The exact header format
|
|
|
depends on the archive format. */
|
|
|
static void
|
|
|
-add_multi_volume_header (void)
|
|
|
+add_multi_volume_header (struct bufmap *map)
|
|
|
{
|
|
|
if (archive_format == POSIX_FORMAT)
|
|
|
{
|
|
|
- off_t d = real_s_totsize - real_s_sizeleft;
|
|
|
- xheader_store ("GNU.volume.filename", &dummy, real_s_name);
|
|
|
- xheader_store ("GNU.volume.size", &dummy, &real_s_sizeleft);
|
|
|
+ off_t d = map->sizetotal - map->sizeleft;
|
|
|
+ xheader_store ("GNU.volume.filename", &dummy, map->file_name);
|
|
|
+ xheader_store ("GNU.volume.size", &dummy, &map->sizeleft);
|
|
|
xheader_store ("GNU.volume.offset", &dummy, &d);
|
|
|
}
|
|
|
else
|
|
|
- gnu_add_multi_volume_header ();
|
|
|
-}
|
|
|
-
|
|
|
-/* Synchronize multi-volume globals */
|
|
|
-static void
|
|
|
-multi_volume_sync (void)
|
|
|
-{
|
|
|
- if (multi_volume_option)
|
|
|
- {
|
|
|
- if (save_name)
|
|
|
- {
|
|
|
- assign_string (&real_s_name,
|
|
|
- safer_name_suffix (save_name, false,
|
|
|
- absolute_names_option));
|
|
|
- real_s_totsize = save_totsize;
|
|
|
- real_s_sizeleft = save_sizeleft;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- assign_string (&real_s_name, 0);
|
|
|
- real_s_totsize = 0;
|
|
|
- real_s_sizeleft = 0;
|
|
|
- }
|
|
|
- }
|
|
|
+ gnu_add_multi_volume_header (map);
|
|
|
}
|
|
|
|
|
|
|
|
@@ -1673,8 +1719,6 @@ _gnu_flush_read (void)
|
|
|
archive_write_error (status);
|
|
|
}
|
|
|
|
|
|
- multi_volume_sync ();
|
|
|
-
|
|
|
for (;;)
|
|
|
{
|
|
|
status = rmtread (archive, record_start->buffer, record_size);
|
|
@@ -1726,8 +1770,8 @@ _gnu_flush_write (size_t buffer_level)
|
|
|
char *copy_ptr;
|
|
|
size_t copy_size;
|
|
|
size_t bufsize;
|
|
|
- tarlong wrt;
|
|
|
-
|
|
|
+ struct bufmap *map;
|
|
|
+
|
|
|
status = _flush_write ();
|
|
|
if (status != record_size && !multi_volume_option)
|
|
|
archive_write_error (status);
|
|
@@ -1740,10 +1784,11 @@ _gnu_flush_write (size_t buffer_level)
|
|
|
|
|
|
if (status == record_size)
|
|
|
{
|
|
|
- multi_volume_sync ();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ map = bufmap_locate (status);
|
|
|
+
|
|
|
if (status % BLOCKSIZE)
|
|
|
{
|
|
|
ERROR ((0, 0, _("write did not end on a block boundary")));
|
|
@@ -1755,7 +1800,6 @@ _gnu_flush_write (size_t buffer_level)
|
|
|
if (status < 0 && errno != ENOSPC && errno != EIO && errno != ENXIO)
|
|
|
archive_write_error (status);
|
|
|
|
|
|
- real_s_sizeleft -= status;
|
|
|
if (!new_volume (ACCESS_WRITE))
|
|
|
return;
|
|
|
|
|
@@ -1767,25 +1811,28 @@ _gnu_flush_write (size_t buffer_level)
|
|
|
|
|
|
copy_ptr = record_start->buffer + status;
|
|
|
copy_size = buffer_level - status;
|
|
|
-
|
|
|
+
|
|
|
/* Switch to the next buffer */
|
|
|
record_index = !record_index;
|
|
|
init_buffer ();
|
|
|
|
|
|
+ inhibit_map = 1;
|
|
|
+
|
|
|
if (volume_label_option)
|
|
|
add_volume_label ();
|
|
|
|
|
|
- if (real_s_name)
|
|
|
- add_multi_volume_header ();
|
|
|
+ if (map)
|
|
|
+ add_multi_volume_header (map);
|
|
|
|
|
|
write_extended (true, &dummy, find_next_block ());
|
|
|
tar_stat_destroy (&dummy);
|
|
|
|
|
|
- if (real_s_name)
|
|
|
- add_chunk_header ();
|
|
|
- wrt = bytes_written;
|
|
|
+ if (map)
|
|
|
+ add_chunk_header (map);
|
|
|
header = find_next_block ();
|
|
|
+ bufmap_reset (map, header - record_start);
|
|
|
bufsize = available_space_after (header);
|
|
|
+ inhibit_map = 0;
|
|
|
while (bufsize < copy_size)
|
|
|
{
|
|
|
memcpy (header->buffer, copy_ptr, bufsize);
|
|
@@ -1798,16 +1845,6 @@ _gnu_flush_write (size_t buffer_level)
|
|
|
memcpy (header->buffer, copy_ptr, copy_size);
|
|
|
memset (header->buffer + copy_size, 0, bufsize - copy_size);
|
|
|
set_next_block_after (header + (copy_size - 1) / BLOCKSIZE);
|
|
|
- if (multi_volume_option && wrt < bytes_written)
|
|
|
- {
|
|
|
- /* The value of bytes_written has changed while moving data;
|
|
|
- that means that flush_archive was executed at least once in
|
|
|
- between, and, as a consequence, copy_size bytes were not written
|
|
|
- to disk. We need to update sizeleft variables to compensate for
|
|
|
- that. */
|
|
|
- save_sizeleft += copy_size;
|
|
|
- multi_volume_sync ();
|
|
|
- }
|
|
|
find_next_block ();
|
|
|
}
|
|
|
|