X-Git-Url: https://git.brokenzipper.com/gitweb?a=blobdiff_plain;f=src%2Fbuffer.c;h=e590e12834280f52400b3d2a797c7106b0033d39;hb=3af9cc0f1550e8c882f1a017727d41b54674c2d5;hp=2b53ec38d6d90d4c0ef1fed3e7373fe1b372a306;hpb=8c10111c66c83092f53a8ed029552afd780a8bca;p=chaz%2Ftar diff --git a/src/buffer.c b/src/buffer.c index 2b53ec3..e590e12 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -1,13 +1,13 @@ /* Buffer management for tar. Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001, - 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. Written by John Gilmore, on 1985-08-25. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any later + Free Software Foundation; either version 3, or (at your option) any later version. This program is distributed in the hope that it will be useful, but @@ -26,7 +26,6 @@ #include #include -#include #include #include @@ -75,9 +74,6 @@ static int read_error_count; /* Have we hit EOF yet? */ static bool hit_eof; -/* Checkpointing counter */ -static unsigned checkpoint; - static bool read_full_records = false; /* We're reading, but we just read the last block and it's time to update. @@ -121,6 +117,15 @@ static off_t save_totsize; /* total size of file we are writing, only static off_t save_sizeleft; /* where we are in the file we are writing, only valid if save_name is nonzero */ + +static struct tar_stat_info dummy; + +void +buffer_write_global_xheader () +{ + xheader_write_global (&dummy.xhdr); +} + void mv_begin (struct tar_stat_info *st) { @@ -193,10 +198,13 @@ compute_duration () /* Compression detection */ enum compress_type { - ct_none, + ct_tar, /* Plain tar file */ + ct_none, /* Unknown compression type */ ct_compress, ct_gzip, - ct_bzip2 + ct_bzip2, + ct_lzma, + ct_lzop }; struct zip_magic @@ -209,10 +217,13 @@ struct zip_magic }; static struct zip_magic const magic[] = { + { ct_tar }, { ct_none, }, { ct_compress, 2, "\037\235", "compress", "-Z" }, { ct_gzip, 2, "\037\213", "gzip", "-z" }, { ct_bzip2, 3, "BZh", "bzip2", "-j" }, + { ct_lzma, 6, "\xFFLZMA", "lzma", "-J" }, /* FIXME: ???? */ + { ct_lzop, 4, "\211LZO", "lzop", "--lzop" }, }; #define NMAGIC (sizeof(magic)/sizeof(magic[0])) @@ -222,26 +233,30 @@ static struct zip_magic const magic[] = { /* Check if the file ARCHIVE is a compressed archive. */ enum compress_type -check_compressed_archive () +check_compressed_archive (bool *pshort) { struct zip_magic const *p; bool sfr; + bool temp; + 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 */ - find_next_block (); - + *pshort = find_next_block () == 0; + /* Restore global values */ read_full_records = sfr; if (tar_checksum (record_start, true) == HEADER_SUCCESS) /* Probably a valid header */ - return ct_none; + return ct_tar; - for (p = magic + 1; p < magic + NMAGIC; p++) + for (p = magic + 2; p < magic + NMAGIC; p++) if (memcmp (record_start->buffer, p->magic, p->length) == 0) return p->type; @@ -261,11 +276,32 @@ open_compressed_archive () if (!multi_volume_option) { - enum compress_type type = check_compressed_archive (); + if (!use_compress_program_option) + { + bool shortfile; + enum compress_type type = check_compressed_archive (&shortfile); - if (type == ct_none) - return archive; + switch (type) + { + case ct_tar: + 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"))); + set_comression_program_by_suffix (archive_name_array[0], NULL); + if (!use_compress_program_option) + return archive; + break; + default: + use_compress_program_option = compress_program (type); + break; + } + } + /* FD is not needed any more */ rmtclose (archive); @@ -273,7 +309,6 @@ open_compressed_archive () check_compressed_archive */ /* Open compressed archive */ - use_compress_program_option = compress_program (type); child_pid = sys_child_open_for_uncompress (); read_full_records = true; } @@ -338,6 +373,9 @@ print_total_stats () print_stats (stderr, _("Total bytes read"), records_read * record_size); break; + + default: + abort (); } } @@ -433,16 +471,6 @@ _open_archive (enum access_mode wanted_access) { int backed_up_flag = 0; - if (index_file_name) - { - 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; - if (record_size == 0) FATAL_ERROR ((0, 0, _("Invalid value for record_size"))); @@ -482,7 +510,8 @@ _open_archive (enum access_mode wanted_access) break; } - if (wanted_access == ACCESS_WRITE + if (!index_file_name + && wanted_access == ACCESS_WRITE && strcmp (archive_name_array[0], "-") == 0) stdlis = stderr; } @@ -496,28 +525,33 @@ _open_archive (enum access_mode wanted_access) { case ACCESS_READ: { + bool shortfile; enum compress_type type; archive = STDIN_FILENO; - type = check_compressed_archive (); - if (type != ct_none) + type = check_compressed_archive (&shortfile); + if (type != ct_tar && type != ct_none) FATAL_ERROR ((0, 0, _("Archive is compressed. Use %s option"), compress_option (type))); + if (shortfile) + ERROR ((0, 0, _("This does not look like a tar archive"))); } break; case ACCESS_WRITE: archive = STDOUT_FILENO; - stdlis = stderr; + if (!index_file_name) + stdlis = stderr; break; case ACCESS_UPDATE: archive = STDIN_FILENO; - stdlis = stderr; write_archive_to_stdout = true; record_end = record_start; /* set up for 1st record = # 0 */ + if (!index_file_name) + stdlis = stderr; break; } } @@ -546,9 +580,16 @@ _open_archive (enum access_mode wanted_access) 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"))); + switch (check_compressed_archive (NULL)) + { + case ct_none: + case ct_tar: + break; + + default: + FATAL_ERROR ((0, 0, + _("Cannot update compressed archives"))); + } break; } @@ -580,43 +621,13 @@ _open_archive (enum access_mode wanted_access) } } -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; - do_checkpoint (true); + checkpoint_run (true); if (tape_length_option && tape_length_option <= bytes_written) { errno = ENOSPC; @@ -626,7 +637,7 @@ _flush_write (void) status = record_size; else status = sys_write_archive_buffer (); - + return status; } @@ -848,7 +859,7 @@ close_archive (void) verify_volume (); if (rmtclose (archive) != 0) - close_warn (*archive_name_cursor); + close_error (*archive_name_cursor); sys_wait_for_child (child_pid); @@ -1033,9 +1044,10 @@ new_volume (enum access_mode mode) assign_string (&volume_label, NULL); assign_string (&continued_file_name, NULL); continued_file_size = continued_file_offset = 0; - + current_block = record_start; + if (rmtclose (archive) != 0) - close_warn (*archive_name_cursor); + close_error (*archive_name_cursor); archive_name_cursor++; if (archive_name_cursor == archive_name_array + archive_names) @@ -1126,23 +1138,24 @@ try_new_volume () { size_t status; union block *header; - struct tar_stat_info dummy; - + enum access_mode acc; + switch (subcommand_option) { case APPEND_SUBCOMMAND: case CAT_SUBCOMMAND: case UPDATE_SUBCOMMAND: - if (!new_volume (ACCESS_UPDATE)) - return true; + acc = ACCESS_UPDATE; break; default: - if (!new_volume (ACCESS_READ)) - return true; + acc = ACCESS_READ; break; } + if (!new_volume (acc)) + return true; + while ((status = rmtread (archive, record_start->buffer, record_size)) == SAFE_READ_ERROR) archive_read_error (); @@ -1237,7 +1250,15 @@ try_new_volume () if (real_s_totsize - real_s_sizeleft != continued_file_offset) { - WARN ((0, 0, _("This volume is out of sequence"))); + 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 (continued_file_offset, s2buf))); + return false; } } @@ -1296,7 +1317,7 @@ static void _write_volume_label (const char *str) { if (archive_format == POSIX_FORMAT) - xheader_store ("GNU.volume.label", NULL, str); + xheader_store ("GNU.volume.label", &dummy, str); else { union block *label = find_next_block (); @@ -1413,9 +1434,9 @@ add_multi_volume_header (void) if (archive_format == POSIX_FORMAT) { off_t d = real_s_totsize - real_s_sizeleft; - xheader_store ("GNU.volume.filename", NULL, real_s_name); - xheader_store ("GNU.volume.size", NULL, &real_s_sizeleft); - xheader_store ("GNU.volume.offset", NULL, &d); + xheader_store ("GNU.volume.filename", &dummy, real_s_name); + xheader_store ("GNU.volume.size", &dummy, &real_s_sizeleft); + xheader_store ("GNU.volume.offset", &dummy, &d); } else gnu_add_multi_volume_header (); @@ -1453,7 +1474,7 @@ simple_flush_read (void) { size_t status; /* result from system call */ - do_checkpoint (false); + checkpoint_run (false); /* Clear the count of errors. This only applies to a single call to flush_read. */ @@ -1512,7 +1533,7 @@ _gnu_flush_read (void) { size_t status; /* result from system call */ - do_checkpoint (false); + checkpoint_run (false); /* Clear the count of errors. This only applies to a single call to flush_read. */ @@ -1578,13 +1599,15 @@ _gnu_flush_write (size_t buffer_level) char *copy_ptr; size_t copy_size; size_t bufsize; - + tarlong wrt; + status = _flush_write (); if (status != record_size && !multi_volume_option) archive_write_error (status); else { - records_written++; + if (status) + records_written++; bytes_written += status; } @@ -1594,15 +1617,22 @@ _gnu_flush_write (size_t buffer_level) return; } + if (status % BLOCKSIZE) + { + 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) archive_write_error (status); + real_s_sizeleft -= status; if (!new_volume (ACCESS_WRITE)) return; - xheader_destroy (&extended_header); + tar_stat_destroy (&dummy); increase_volume_number (); prev_written += bytes_written; @@ -1610,6 +1640,7 @@ _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 (); @@ -1620,9 +1651,12 @@ _gnu_flush_write (size_t buffer_level) if (real_s_name) add_multi_volume_header (); - write_extended (true, NULL, find_next_block ()); + write_extended (true, &dummy, find_next_block ()); + tar_stat_destroy (&dummy); + if (real_s_name) add_chunk_header (); + wrt = bytes_written; header = find_next_block (); bufsize = available_space_after (header); while (bufsize < copy_size) @@ -1637,6 +1671,16 @@ _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 (); }