X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fdelete.c;h=337b922fd8089aa17a60c762df9ac96ba37db827;hb=e7c99a4dd16685012e42519f1b4fb2b37b19dec5;hp=4cb73b3c95453fb0893921a8c60b928212910823;hpb=3e3d9b7e3962df782fd733ea9dd2fe2be37f58d6;p=chaz%2Ftar diff --git a/src/delete.c b/src/delete.c index 4cb73b3..337b922 100644 --- a/src/delete.c +++ b/src/delete.c @@ -1,109 +1,110 @@ /* Delete entries from a tar archive. - Copyright (C) 1988, 1992, 1994, 1996, 1997 Free Software Foundation, Inc. - 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 - version. + Copyright 1988, 1992, 1994, 1996-1997, 2000-2001, 2003-2006, 2010, + 2013-2014 Free Software Foundation, Inc. - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General - Public License for more details. + This file is part of GNU tar. - 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 Place - Suite 330, Boston, MA 02111-1307, USA. */ + GNU tar 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 3 of the License, or + (at your option) any later version. -#include "system.h" + GNU tar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. -#define STDIN 0 -#define STDOUT 1 + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include +#include #include "common.h" -#include "rmt.h" +#include -static union block *new_record = NULL; -static union block *save_record = NULL; -static int records_read = 0; -static int new_blocks = 0; -static int blocks_needed = 0; +static union block *new_record; +static int new_blocks; +static bool acting_as_filter; -/* FIXME: This module should not directly handle the following three - variables, instead, this should be done in buffer.c only. */ +/* FIXME: This module should not directly handle the following + variables, instead, the interface should be cleaned up. */ extern union block *record_start; extern union block *record_end; extern union block *current_block; - -/*-------------------------------------------------------------------------. -| Move archive descriptor by COUNT records worth. If COUNT is positive we | -| move forward, else we move negative. If its a tape, MTIOCTOP had better | -| work. If its something else, we try to seek on it. If we can't seek, | -| we loose! | -`-------------------------------------------------------------------------*/ - +extern union block *recent_long_name; +extern union block *recent_long_link; +extern off_t records_read; + +/* The number of records skipped at the start of the archive, when + passing over members that are not deleted. */ +off_t records_skipped; + +/* Move archive descriptor by COUNT records worth. If COUNT is + positive we move forward, else we move negative. If it's a tape, + MTIOCTOP had better work. If it's something else, we try to seek + on it. If we can't seek, we lose! */ static void -move_archive (int count) +move_archive (off_t count) { + if (count == 0) + return; + #ifdef MTIOCTOP { struct mtop operation; - int status; - if (count > 0) + if (count < 0 + ? (operation.mt_op = MTBSR, + operation.mt_count = -count, + operation.mt_count == -count) + : (operation.mt_op = MTFSR, + operation.mt_count = count, + operation.mt_count == count)) { - operation.mt_op = MTFSR; - operation.mt_count = count; - } - else - { - operation.mt_op = MTBSR; - operation.mt_count = -count; - } + if (0 <= rmtioctl (archive, MTIOCTOP, (char *) &operation)) + return; - if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation), - status >= 0) - return; - - if (errno == EIO) - if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation), - status >= 0) - return; + if (errno == EIO + && 0 <= rmtioctl (archive, MTIOCTOP, (char *) &operation)) + return; + } } #endif /* MTIOCTOP */ { - off_t position = rmtlseek (archive, 0L, 1); + off_t position0 = rmtlseek (archive, (off_t) 0, SEEK_CUR); + off_t increment = record_size * (off_t) count; + off_t position = position0 + increment; - position += record_size * count; - - if (rmtlseek (archive, position, 0) != position) - FATAL_ERROR ((0, 0, _("Could not re-position archive file"))); + if (increment / count != record_size + || (position < position0) != (increment < 0) + || (position = position < 0 ? 0 : position, + rmtlseek (archive, position, SEEK_SET) != position)) + seek_error_details (archive_name_array[0], position); return; } } -/*----------------------------------------------------------------. -| Write out the record which has been filled. If MOVE_BACK_FLAG, | -| backspace to where we started. | -`----------------------------------------------------------------*/ - +/* Write out the record which has been filled. If MOVE_BACK_FLAG, + backspace to where we started. */ static void write_record (int move_back_flag) { - save_record = record_start; + union block *save_record = record_start; record_start = new_record; - if (archive == STDIN) + if (acting_as_filter) { - archive = STDOUT; + archive = STDOUT_FILENO; flush_write (); - archive = STDIN; + archive = STDIN_FILENO; } else { - move_archive (-(records_read + 1)); + move_archive ((records_written + records_skipped) - records_read); flush_write (); } @@ -113,19 +114,39 @@ write_record (int move_back_flag) { /* Move the tape head back to where we were. */ - if (archive != STDIN) - move_archive (records_read); - - records_read--; + if (! acting_as_filter) + move_archive (records_read - (records_written + records_skipped)); } - blocks_needed = blocking_factor; new_blocks = 0; } -/*---. -| ? | -`---*/ +static void +write_recent_blocks (union block *h, size_t blocks) +{ + size_t i; + for (i = 0; i < blocks; i++) + { + new_record[new_blocks++] = h[i]; + if (new_blocks == blocking_factor) + write_record (1); + } +} + +static void +write_recent_bytes (char *data, size_t bytes) +{ + size_t blocks = bytes / BLOCKSIZE; + size_t rest = bytes - blocks * BLOCKSIZE; + + write_recent_blocks ((union block *)data, blocks); + memcpy (new_record[new_blocks].buffer, data + blocks * BLOCKSIZE, rest); + if (rest < BLOCKSIZE) + memset (new_record[new_blocks].buffer + rest, 0, BLOCKSIZE - rest); + new_blocks++; + if (new_blocks == blocking_factor) + write_record (1); +} void delete_archive_members (void) @@ -135,16 +156,19 @@ delete_archive_members (void) /* FIXME: Should clean the routine before cleaning these variables :-( */ struct name *name; - int blocks_to_skip = 0; - int blocks_to_keep = 0; + off_t blocks_to_skip = 0; + off_t blocks_to_keep = 0; int kept_blocks_in_record; name_gather (); open_archive (ACCESS_UPDATE); + acting_as_filter = strcmp (archive_name_array[0], "-") == 0; - while (logical_status == HEADER_STILL_UNREAD) + do { - enum read_header status = read_header (); + enum read_header status = read_header (¤t_header, + ¤t_stat_info, + read_header_x_raw); switch (status) { @@ -152,19 +176,30 @@ delete_archive_members (void) abort (); case HEADER_SUCCESS: - if (name = name_scan (current_file_name), !name) + if ((name = name_scan (current_stat_info.file_name)) == NULL) { - set_next_block_after (current_header); - if (current_header->oldgnu_header.isextended) - skip_extended_headers (); - skip_file ((long) (current_stat.st_size)); + skip_member (); break; } - name->found = 1; - logical_status = HEADER_SUCCESS; + name->found_count++; + if (!ISFOUND(name)) + { + skip_member (); + break; + } + + /* Fall through. */ + case HEADER_SUCCESS_EXTENDED: + logical_status = status; break; case HEADER_ZERO_BLOCK: + if (ignore_zeros_option) + { + set_next_block_after (current_header); + break; + } + /* Fall through. */ case HEADER_END_OF_FILE: logical_status = HEADER_END_OF_FILE; break; @@ -178,6 +213,7 @@ delete_archive_members (void) /* Fall through. */ case HEADER_SUCCESS: + case HEADER_SUCCESS_EXTENDED: case HEADER_ZERO_BLOCK: ERROR ((0, 0, _("Skipping to next header"))); /* Fall through. */ @@ -193,144 +229,166 @@ delete_archive_members (void) previous_status = status; } + while (logical_status == HEADER_STILL_UNREAD); - if (logical_status != HEADER_SUCCESS) - { - write_eot (); - close_archive (); - names_notfound (); - return; - } + records_skipped = records_read - 1; + new_record = xmalloc (record_size); - write_archive_to_stdout = 0; - new_record = (union block *) xmalloc ((size_t) record_size); - - /* Save away blocks before this one in this record. */ - - new_blocks = current_block - record_start; - blocks_needed = blocking_factor - new_blocks; - if (new_blocks) - memcpy ((void *) new_record, (void *) record_start, - (size_t) (new_blocks * BLOCKSIZE)); - -#if 0 - /* FIXME: Old code, before the goto was inserted. To be redesigned. */ - set_next_block_after (current_header); - if (current_header->oldgnu_header.isextended) - skip_extended_headers (); - skip_file ((long) (current_stat.st_size)); -#endif - logical_status = HEADER_STILL_UNREAD; - goto flush_file; - - /* FIXME: Solaris 2.4 Sun cc (the ANSI one, not the old K&R) says: - "delete.c", line 223: warning: loop not entered at top - Reported by Bruno Haible. */ - while (1) + if (logical_status == HEADER_SUCCESS + || logical_status == HEADER_SUCCESS_EXTENDED) { - enum read_header status; + write_archive_to_stdout = false; - /* Fill in a record. */ + /* Save away blocks before this one in this record. */ - if (current_block == record_end) - { - flush_archive (); - records_read++; - } - status = read_header (); + new_blocks = current_block - record_start; + if (new_blocks) + memcpy (new_record, record_start, new_blocks * BLOCKSIZE); - if (status == HEADER_ZERO_BLOCK && ignore_zeros_option) - { - set_next_block_after (current_header); - continue; - } - if (status == HEADER_END_OF_FILE || status == HEADER_ZERO_BLOCK) + if (logical_status == HEADER_SUCCESS) { - logical_status = HEADER_END_OF_FILE; - memset (new_record[new_blocks].buffer, 0, - (size_t) (BLOCKSIZE * blocks_needed)); - new_blocks += blocks_needed; - blocks_needed = 0; - write_record (0); - break; + /* FIXME: Pheew! This is crufty code! */ + logical_status = HEADER_STILL_UNREAD; + goto flush_file; } - if (status == HEADER_FAILURE) + /* FIXME: Solaris 2.4 Sun cc (the ANSI one, not the old K&R) says: + "delete.c", line 223: warning: loop not entered at top + Reported by Bruno Haible. */ + while (1) { - ERROR ((0, 0, _("Deleting non-header from archive"))); - set_next_block_after (current_header); - continue; - } + enum read_header status; - /* Found another header. */ + /* Fill in a record. */ - if (name = name_scan (current_file_name), name) - { - name->found = 1; - flush_file: - set_next_block_after (current_header); - blocks_to_skip = (current_stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE; + if (current_block == record_end) + flush_archive (); + status = read_header (¤t_header, ¤t_stat_info, + read_header_auto); - while (record_end - current_block <= blocks_to_skip) + xheader_decode (¤t_stat_info); + + if (status == HEADER_ZERO_BLOCK && ignore_zeros_option) { - blocks_to_skip -= (record_end - current_block); - flush_archive (); - records_read++; + set_next_block_after (current_header); + continue; + } + if (status == HEADER_END_OF_FILE || status == HEADER_ZERO_BLOCK) + { + logical_status = HEADER_END_OF_FILE; + break; } - current_block += blocks_to_skip; - blocks_to_skip = 0; - continue; - } - /* Copy header. */ + if (status == HEADER_FAILURE) + { + ERROR ((0, 0, _("Deleting non-header from archive"))); + set_next_block_after (current_header); + continue; + } - new_record[new_blocks] = *current_header; - new_blocks++; - blocks_needed--; - blocks_to_keep - = (current_stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE; - set_next_block_after (current_header); - if (blocks_needed == 0) - write_record (1); + /* Found another header. */ + + if ((name = name_scan (current_stat_info.file_name)) != NULL) + { + name->found_count++; + if (ISFOUND(name)) + { + flush_file: + set_next_block_after (current_header); + blocks_to_skip = (current_stat_info.stat.st_size + + BLOCKSIZE - 1) / BLOCKSIZE; + + while (record_end - current_block <= blocks_to_skip) + { + blocks_to_skip -= (record_end - current_block); + flush_archive (); + } + current_block += blocks_to_skip; + blocks_to_skip = 0; + continue; + } + } + /* Copy header. */ + + if (current_stat_info.xhdr.size) + { + write_recent_bytes (current_stat_info.xhdr.buffer, + current_stat_info.xhdr.size); + } + else + { + write_recent_blocks (recent_long_name, recent_long_name_blocks); + write_recent_blocks (recent_long_link, recent_long_link_blocks); + } + new_record[new_blocks] = *current_header; + new_blocks++; + blocks_to_keep + = (current_stat_info.stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE; + set_next_block_after (current_header); + if (new_blocks == blocking_factor) + write_record (1); - /* Copy data. */ + /* Copy data. */ - kept_blocks_in_record = record_end - current_block; - if (kept_blocks_in_record > blocks_to_keep) - kept_blocks_in_record = blocks_to_keep; + kept_blocks_in_record = record_end - current_block; + if (kept_blocks_in_record > blocks_to_keep) + kept_blocks_in_record = blocks_to_keep; - while (blocks_to_keep) + while (blocks_to_keep) + { + int count; + + if (current_block == record_end) + { + flush_read (); + current_block = record_start; + kept_blocks_in_record = blocking_factor; + if (kept_blocks_in_record > blocks_to_keep) + kept_blocks_in_record = blocks_to_keep; + } + count = kept_blocks_in_record; + if (blocking_factor - new_blocks < count) + count = blocking_factor - new_blocks; + + if (! count) + abort (); + + memcpy (new_record + new_blocks, current_block, count * BLOCKSIZE); + new_blocks += count; + current_block += count; + blocks_to_keep -= count; + kept_blocks_in_record -= count; + + if (new_blocks == blocking_factor) + write_record (1); + } + } + + if (logical_status == HEADER_END_OF_FILE) { - int count; + /* Write the end of tape. FIXME: we can't use write_eot here, + as it gets confused when the input is at end of file. */ - if (current_block == record_end) + int total_zero_blocks = 0; + + do { - flush_read (); - records_read++; - current_block = record_start; - kept_blocks_in_record = blocking_factor; - if (kept_blocks_in_record > blocks_to_keep) - kept_blocks_in_record = blocks_to_keep; + int zero_blocks = blocking_factor - new_blocks; + memset (new_record + new_blocks, 0, BLOCKSIZE * zero_blocks); + total_zero_blocks += zero_blocks; + write_record (total_zero_blocks < 2); } - count = kept_blocks_in_record; - if (count > blocks_needed) - count = blocks_needed; - - memcpy ((void *) (new_record + new_blocks), - (void *) current_block, - (size_t) (count * BLOCKSIZE)); - new_blocks += count; - blocks_needed -= count; - current_block += count; - blocks_to_keep -= count; - kept_blocks_in_record -= count; - - if (blocks_needed == 0) - write_record (1); + while (total_zero_blocks < 2); + } + + if (! acting_as_filter && ! _isrmt (archive)) + { + if (sys_truncate (archive)) + truncate_warn (archive_name_array[0]); } } + free (new_record); - write_eot (); close_archive (); names_notfound (); }