X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fbuffer.c;h=1b36051e19c784f7e5588162c8f7b06a92ea2f3c;hb=84378fa99ae3ce256fea9eb25d8ae999d1c91fdf;hp=367a93a0796d75c9561646560f5b7deaf50a46ae;hpb=737b0251de867074882619e7304be721749fcfd9;p=chaz%2Ftar diff --git a/src/buffer.c b/src/buffer.c index 367a93a..1b36051 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, 2005 Free Software Foundation, Inc. + 2003, 2004, 2005, 2006 Free Software Foundation, Inc. Written by John Gilmore, on 1985-08-25. @@ -23,6 +23,7 @@ #include +#include #include #include #include @@ -84,6 +85,9 @@ extern bool time_to_start_writing; bool write_archive_to_stdout; +void (*flush_write_ptr) (size_t); +void (*flush_read_ptr) (void); + char *volume_label; char *continued_file_name; @@ -365,7 +369,7 @@ init_buffer () { if (!record_buffer[record_index]) page_aligned_alloc (&record_buffer[record_index], record_size); - + record_start = record_buffer[record_index]; current_block = record_start; record_end = record_start + blocking_factor; @@ -380,9 +384,10 @@ _open_archive (enum access_mode wanted_access) 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; @@ -399,7 +404,7 @@ _open_archive (enum access_mode wanted_access) record_index = 0; init_buffer (); - + /* When updating the archive, we start with reading. */ access_mode = wanted_access == ACCESS_UPDATE ? ACCESS_READ : wanted_access; @@ -674,6 +679,7 @@ _flush_read (void) 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; @@ -692,7 +698,7 @@ flush_archive (void) break; case ACCESS_WRITE: - flush_write (); + flush_write_ptr (buffer_level); break; case ACCESS_UPDATE: @@ -784,7 +790,11 @@ void 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 (); @@ -855,6 +865,99 @@ increase_volume_number () volno++; } +void +change_tape_menu (FILE *read_file) +{ + char *input_buffer = NULL; + size_t size = 0; + + while (1) + { + 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'; + + /* FIXME: the following allocation is never reclaimed. */ + *archive_name_cursor = xstrdup (name); + } + 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. */ @@ -876,7 +979,7 @@ new_volume (enum access_mode mode) 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); @@ -896,88 +999,12 @@ new_volume (enum access_mode mode) { 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) @@ -1021,14 +1048,27 @@ new_volume (enum access_mode mode) return true; } - + +static bool +read_header0 () +{ + enum read_header rc = read_header (false); + + 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; + switch (subcommand_option) { case APPEND_SUBCOMMAND: @@ -1051,56 +1091,58 @@ try_new_volume () 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: + { + struct tar_stat_info dummy; + if (!read_header0 ()) + return false; + tar_stat_init (&dummy); + 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 ()) + return false; + 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 ()) + return false; + 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 - && strcmp (continued_file_name, real_s_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))); @@ -1122,7 +1164,7 @@ try_new_volume () 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"))); @@ -1188,7 +1230,7 @@ _write_volume_label (const char *str) else { union block *label = find_next_block (); - + memset (label, 0, BLOCKSIZE); strcpy (label->header.name, volume_label_option); @@ -1219,6 +1261,38 @@ add_volume_label (void) 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); + free (st.orig_file_name); + if (!blk) + abort (); /* FIXME */ + finish_header (&st, blk, block_ordinal); + } +} + + /* Add a volume label to the current archive */ static void write_volume_label (void) @@ -1235,25 +1309,25 @@ gnu_add_multi_volume_header (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); @@ -1269,6 +1343,7 @@ 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); } @@ -1328,7 +1403,7 @@ simple_flush_read (void) if (status != record_size) archive_write_error (status); } - + for (;;) { status = rmtread (archive, record_start->buffer, record_size); @@ -1349,7 +1424,7 @@ simple_flush_read (void) /* 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; @@ -1420,7 +1495,7 @@ _gnu_flush_read (void) else if (status == SAFE_READ_ERROR) { archive_read_error (); - continue; + continue; } break; } @@ -1430,20 +1505,20 @@ _gnu_flush_read (void) 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); @@ -1468,25 +1543,28 @@ _gnu_flush_write (void) return; xheader_destroy (&extended_header); - + increase_volume_number (); if (totals_option) 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) { @@ -1504,18 +1582,30 @@ _gnu_flush_write (void) } 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) @@ -1530,5 +1620,8 @@ open_archive (enum access_mode wanted_access) if (volume_label_option) write_volume_label (); break; + + default: + break; } }