X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fbuffer.c;h=5977f5a56ed80b17ad6723646fcb46b405928d64;hb=65a7536d1fccd2890ec7d9e806cb5d1ad438282d;hp=80bbe829b4d031ed0ea7b47a527f818515550e4b;hpb=488430f739e8230e852f88bdd5086f0a47b8eba0;p=chaz%2Ftar diff --git a/src/buffer.c b/src/buffer.c index 80bbe82..5977f5a 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -1,7 +1,7 @@ /* Buffer management for tar. Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001, - 2003, 2004 Free Software Foundation, Inc. + 2003, 2004, 2005 Free Software Foundation, Inc. Written by John Gilmore, on 1985-08-25. @@ -17,9 +17,9 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "system.h" +#include #include @@ -28,7 +28,7 @@ #include #include "common.h" -#include "rmt.h" +#include /* Number of retries before giving up on read. */ #define READ_ERROR_MAX 10 @@ -40,6 +40,7 @@ static tarlong prev_written; /* bytes written on previous volumes */ static tarlong bytes_written; /* bytes written on this volume */ +static void *record_buffer; /* allocated memory */ /* FIXME: The following variables should ideally be static to this module. However, this cannot be done yet. The cleanup continues! */ @@ -66,11 +67,13 @@ static pid_t child_pid; static int read_error_count; /* Have we hit EOF yet? */ -static int hit_eof; +static bool hit_eof; /* Checkpointing counter */ static int checkpoint; +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 @@ -114,6 +117,123 @@ clear_read_error_count (void) read_error_count = 0; } + +/* Time-related functions */ + +double duration; + +void +set_start_time () +{ + gettime (&start_time); +} + +void +compute_duration () +{ + struct timespec now; + gettime (&now); + duration += ((now.tv_sec - start_time.tv_sec) + + (now.tv_nsec - start_time.tv_nsec) / 1e9); + set_start_time (); +} + + +/* Compression detection */ + +enum compress_type { + ct_none, + ct_compress, + ct_gzip, + ct_bzip2 +}; + +struct zip_magic +{ + enum compress_type type; + size_t length; + char *magic; + char *program; + char *option; +}; + +static struct zip_magic const magic[] = { + { ct_none, }, + { ct_compress, 2, "\037\235", "compress", "-Z" }, + { ct_gzip, 2, "\037\213", "gzip", "-z" }, + { ct_bzip2, 3, "BZh", "bzip2", "-j" }, +}; + +#define NMAGIC (sizeof(magic)/sizeof(magic[0])) + +#define compress_option(t) magic[t].option +#define compress_program(t) magic[t].program + +/* Check if the file ARCHIVE is a compressed archive. */ +enum compress_type +check_compressed_archive () +{ + struct zip_magic const *p; + bool sfr; + + /* 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 */ + find_next_block (); + + /* Restore global values */ + read_full_records = sfr; + + if (tar_checksum (record_start, true) == HEADER_SUCCESS) + /* Probably a valid header */ + return ct_none; + + for (p = magic + 1; p < magic + NMAGIC; p++) + if (memcmp (record_start->buffer, p->magic, p->length) == 0) + return p->type; + + return ct_none; +} + +/* 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 () +{ + archive = rmtopen (archive_name_array[0], O_RDONLY | O_BINARY, + MODE_RW, rsh_command_option); + if (archive == -1) + return archive; + + if (!multi_volume_option) + { + enum compress_type type = check_compressed_archive (); + + if (type == ct_none) + return archive; + + /* FD is not needed any more */ + rmtclose (archive); + + hit_eof = false; /* It might have been set by find_next_block in + check_compressed_archive */ + + /* Open compressed archive */ + use_compress_program_option = compress_program (type); + child_pid = sys_child_open_for_uncompress (); + read_full_records = true; + } + + records_read = 0; + record_end = record_start; /* set up for 1st record = # 0 */ + + return archive; +} + + void print_total_written (void) { @@ -121,25 +241,16 @@ print_total_written (void) char bytes[sizeof (tarlong) * CHAR_BIT]; char abbr[LONGEST_HUMAN_READABLE + 1]; char rate[LONGEST_HUMAN_READABLE + 1]; - double seconds; - int human_opts = human_autoscale | human_base_1024 | human_SI | human_B; -#if HAVE_CLOCK_GETTIME - struct timespec now; - if (clock_gettime (CLOCK_REALTIME, &now) == 0) - seconds = ((now.tv_sec - start_timespec.tv_sec) - + (now.tv_nsec - start_timespec.tv_nsec) / 1e9); - else -#endif - seconds = time (0) - start_time; + int human_opts = human_autoscale | human_base_1024 | human_SI | human_B; sprintf (bytes, TARLONG_FORMAT, written); /* 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 < seconds && written / seconds < (uintmax_t) -1 - ? human_readable (written / seconds, rate, human_opts, 1, 1) + (0 < duration && written / duration < (uintmax_t) -1 + ? human_readable (written / duration, rate, human_opts, 1, 1) : "?")); } @@ -156,7 +267,7 @@ reset_eof (void) { if (hit_eof) { - hit_eof = 0; + hit_eof = false; current_block = record_start; record_end = record_start + blocking_factor; access_mode = ACCESS_WRITE; @@ -176,7 +287,7 @@ find_next_block (void) flush_archive (); if (current_block == record_end) { - hit_eof = 1; + hit_eof = true; return 0; } } @@ -270,29 +381,30 @@ open_archive (enum access_mode wanted_access) save_name = 0; real_s_name = 0; + record_start = + page_aligned_alloc (&record_buffer, + (record_size + + (multi_volume_option ? 2 * BLOCKSIZE : 0))); if (multi_volume_option) - { - record_start = valloc (record_size + (2 * BLOCKSIZE)); - if (record_start) - record_start += 2; - } - else - record_start = valloc (record_size); - if (!record_start) - FATAL_ERROR ((0, 0, _("Cannot allocate memory for blocking factor %d"), - blocking_factor)); + record_start += 2; current_block = record_start; record_end = record_start + blocking_factor; /* When updating the archive, we start with reading. */ access_mode = wanted_access == ACCESS_UPDATE ? ACCESS_READ : wanted_access; + read_full_records = read_full_records_option; + + records_read = 0; + if (use_compress_program_option) { switch (wanted_access) { case ACCESS_READ: child_pid = sys_child_open_for_uncompress (); + read_full_records = true; + record_end = record_start; /* set up for 1st record = # 0 */ break; case ACCESS_WRITE: @@ -310,14 +422,24 @@ open_archive (enum access_mode wanted_access) } else if (strcmp (archive_name_array[0], "-") == 0) { - read_full_records_option = true; /* could be a pipe, be safe */ + read_full_records = true; /* could be a pipe, be safe */ if (verify_option) FATAL_ERROR ((0, 0, _("Cannot verify stdin/stdout archive"))); switch (wanted_access) { case ACCESS_READ: - archive = STDIN_FILENO; + { + enum compress_type type; + + archive = STDIN_FILENO; + + type = check_compressed_archive (archive); + if (type != ct_none) + FATAL_ERROR ((0, 0, + _("Archive is compressed. Use %s option"), + compress_option (type))); + } break; case ACCESS_WRITE: @@ -339,8 +461,7 @@ open_archive (enum access_mode wanted_access) switch (wanted_access) { case ACCESS_READ: - archive = rmtopen (archive_name_array[0], O_RDONLY | O_BINARY, - MODE_RW, rsh_command_option); + archive = open_compressed_archive (); break; case ACCESS_WRITE: @@ -378,9 +499,9 @@ open_archive (enum access_mode wanted_access) { case ACCESS_UPDATE: records_written = 0; - case ACCESS_READ: - records_read = 0; record_end = record_start; /* set up for 1st record = # 0 */ + + case ACCESS_READ: find_next_block (); /* read it in, check for EOF */ if (volume_label_option) @@ -414,7 +535,7 @@ open_archive (enum access_mode wanted_access) strip_trailing_slashes (current_stat_info.file_name); record_start->header.typeflag = GNUTYPE_VOLHDR; - TIME_TO_CHARS (start_time, record_start->header.mtime); + TIME_TO_CHARS (start_time.tv_sec, record_start->header.mtime); finish_header (¤t_stat_info, record_start, -1); } break; @@ -429,6 +550,10 @@ 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)); if (tape_length_option && tape_length_option <= bytes_written) @@ -455,7 +580,9 @@ flush_write (void) { if (save_name) { - assign_string (&real_s_name, safer_name_suffix (save_name, false)); + assign_string (&real_s_name, + safer_name_suffix (save_name, false, + absolute_names_option)); real_s_totsize = save_totsize; real_s_sizeleft = save_sizeleft; } @@ -502,7 +629,7 @@ flush_write (void) memset (record_start, 0, BLOCKSIZE); sprintf (record_start->header.name, "%s Volume %d", volume_label_option, volno); - TIME_TO_CHARS (start_time, record_start->header.mtime); + TIME_TO_CHARS (start_time.tv_sec, record_start->header.mtime); record_start->header.typeflag = GNUTYPE_VOLHDR; finish_header (¤t_stat_info, record_start, -1); } @@ -514,17 +641,24 @@ flush_write (void) if (volume_label_option) record_start++; + 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 (record_start, 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. */ - strcpy (record_start->header.name, real_s_name); + strncpy (record_start->header.name, real_s_name, NAME_FIELD_SIZE); record_start->header.typeflag = GNUTYPE_MULTIVOL; + OFF_TO_CHARS (real_s_sizeleft, record_start->header.size); OFF_TO_CHARS (real_s_totsize - real_s_sizeleft, record_start->oldgnu_header.offset); + tmp = verbose_option; verbose_option = 0; finish_header (¤t_stat_info, record_start, -1); @@ -554,7 +688,9 @@ flush_write (void) assign_string (&real_s_name, 0); else { - assign_string (&real_s_name, safer_name_suffix (save_name, false)); + assign_string (&real_s_name, + safer_name_suffix (save_name, false, + absolute_names_option)); real_s_sizeleft = save_sizeleft; real_s_totsize = save_totsize; } @@ -608,23 +744,16 @@ short_read (size_t status) left = record_size - status; while (left % BLOCKSIZE != 0 - || (left && status && read_full_records_option)) + || (left && status && read_full_records)) { if (status) while ((status = rmtread (archive, more, left)) == SAFE_READ_ERROR) archive_read_error (); if (status == 0) - { - char buf[UINTMAX_STRSIZE_BOUND]; - - WARN((0, 0, _("Read %s bytes from %s"), - STRINGIFY_BIGINT (record_size - left, buf), - *archive_name_cursor)); - break; - } + break; - if (! read_full_records_option) + if (! read_full_records) { unsigned long rest = record_size - left; @@ -644,7 +773,7 @@ short_read (size_t status) /* FIXME: for size=0, multi-volume support. On the first record, warn about the problem. */ - if (!read_full_records_option && verbose_option > 1 + if (!read_full_records && verbose_option > 1 && record_start_block == 0 && status != 0) { unsigned long rsize = (record_size - left) / BLOCKSIZE; @@ -666,6 +795,10 @@ 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 @@ -685,7 +818,9 @@ flush_read (void) { if (save_name) { - assign_string (&real_s_name, safer_name_suffix (save_name, false)); + assign_string (&real_s_name, + safer_name_suffix (save_name, false, + absolute_names_option)); real_s_sizeleft = save_sizeleft; real_s_totsize = save_totsize; } @@ -706,7 +841,7 @@ flush_read (void) } /* The condition below used to include - || (status > 0 && !read_full_records_option) + || (status > 0 && !read_full_records) This is incorrect since even if new_volume() succeeds, the subsequent call to rmtread will overwrite the chunk of data already read in the buffer, so the processing will fail */ @@ -767,7 +902,7 @@ flush_read (void) { uintmax_t s1, s2; if (cursor->header.typeflag != GNUTYPE_MULTIVOL - || strcmp (cursor->header.name, real_s_name)) + || strncmp (cursor->header.name, real_s_name, NAME_FIELD_SIZE)) { WARN ((0, 0, _("%s is not continued on this volume"), quote (real_s_name))); @@ -895,13 +1030,13 @@ seek_archive (off_t size) off_t offset; off_t nrec, nblk; off_t skipped = (blocking_factor - (current_block - record_start)); - + size -= skipped * BLOCKSIZE; - + if (size < record_size) return 0; /* FIXME: flush? */ - + /* Compute number of records to skip */ nrec = size / record_size; offset = rmtlseek (archive, nrec * record_size, SEEK_CUR); @@ -920,7 +1055,7 @@ seek_archive (off_t size) records_read += nblk / blocking_factor; record_start_block = offset - blocking_factor; current_block = record_end; - + return nblk; } @@ -933,6 +1068,7 @@ close_archive (void) sys_drain_input_pipe (); + compute_duration (); if (verify_option) verify_volume (); @@ -946,7 +1082,7 @@ close_archive (void) free (save_name); if (real_s_name) free (real_s_name); - free (multi_volume_option ? record_start - 2 : record_start); + free (record_buffer); } /* Called to initialize the global volume number. */ @@ -1030,7 +1166,8 @@ new_volume (enum access_mode mode) if (volno_file_option) closeout_volume_number (); if (system (info_script_option) != 0) - FATAL_ERROR ((0, 0, _("`%s' command failed"), info_script_option)); + FATAL_ERROR ((0, 0, _("%s command failed"), + quote (info_script_option))); } else while (1) @@ -1114,7 +1251,7 @@ new_volume (enum access_mode mode) if (strcmp (archive_name_cursor[0], "-") == 0) { - read_full_records_option = true; + read_full_records = true; archive = STDIN_FILENO; } else if (verify_option) @@ -1153,4 +1290,3 @@ new_volume (enum access_mode mode) return true; } -