diff options
Diffstat (limited to 'delete_lz.cc')
-rw-r--r-- | delete_lz.cc | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/delete_lz.cc b/delete_lz.cc new file mode 100644 index 0000000..11c3a14 --- /dev/null +++ b/delete_lz.cc @@ -0,0 +1,167 @@ +/* Tarlz - Archiver with multimember lzip compression + Copyright (C) 2013-2019 Antonio Diaz Diaz. + + 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 of the License, or + (at your option) any later version. + + 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define _FILE_OFFSET_BITS 64 + +#include <cctype> +#include <cerrno> +#include <climits> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <string> +#include <vector> +#include <pthread.h> +#include <stdint.h> +#include <unistd.h> +#include <lzlib.h> + +#include "arg_parser.h" +#include "lzip_index.h" +#include "tarlz.h" + + +/* Deleting from a corrupt archive must not worsen the corruption. Stop and + tail-copy as soon as corruption is found. */ +int delete_members_lz( const char * const archive_namep, + const Arg_parser & parser, + std::vector< char > & name_pending, + const Lzip_index & lzip_index, + const int filenames, const int infd, const int outfd, + const bool missing_crc, const bool permissive ) + { + Resizable_buffer rbuf; + LZ_Decoder * const decoder = LZ_decompress_open(); + if( !rbuf.size() || !decoder || LZ_decompress_errno( decoder ) != LZ_ok ) + { show_error( mem_msg ); return 1; } + + long long istream_pos = 0; // source of next data move + const long long cdata_size = lzip_index.cdata_size(); + int retval = 0; + for( long i = 0; i < lzip_index.members(); ++i ) + { + const long long mdata_pos = lzip_index.dblock( i ).pos(); + long long data_pos = mdata_pos; + const long long mdata_end = lzip_index.dblock( i ).end(); + if( data_pos >= mdata_end ) continue; // empty lzip member + const long long member_pos = lzip_index.mblock( i ).pos(); + long long file_pos = member_pos; + const long long member_end = lzip_index.mblock( i ).end(); + + long long member_begin = 0; // first pos of current tar member + Extended extended; // metadata from extended records + bool prev_extended = false; // prev header was extended + LZ_decompress_reset( decoder ); // prepare for new member + if( !safe_seek( infd, member_pos ) ) { retval = 1; break; } + while( true ) // process one tar header per iteration + { + if( data_pos >= mdata_end ) + { + if( data_pos == mdata_end && !prev_extended ) break; + // member end exceeded or ends in extended + show_file_error( archive_namep, "Member misalignment found." ); + retval = 2; goto done; + } + if( !prev_extended ) member_begin = data_pos; + Tar_header header; + const char * msg = 0; + retval = archive_read_lz( decoder, infd, file_pos, member_end, + cdata_size, header, header_size, &msg ); + if( retval != 0 ) { show_file_error( archive_namep, msg ); goto done; } + data_pos += header_size; + if( !verify_ustar_chksum( header ) ) + { + if( block_is_zero( header, header_size ) ) // EOF + { + if( prev_extended && !permissive ) + { show_file_error( archive_namep, fv_msg1 ); retval = 2; } + goto done; + } + show_file_error( archive_namep, ( data_pos > header_size ) ? + bad_hdr_msg : posix_lz_msg ); + retval = 2; + goto done; + } + + const Typeflag typeflag = (Typeflag)header[typeflag_o]; + if( typeflag == tf_global ) + { + if( prev_extended && !permissive ) + { show_file_error( archive_namep, fv_msg2 ); retval = 2; goto done; } + Extended dummy; // global headers are parsed and ignored + retval = parse_records_lz( decoder, infd, file_pos, member_end, + cdata_size, data_pos, dummy, header, + rbuf, &msg, true ); + if( retval == 0 ) continue; + show_file_error( archive_namep, msg ? msg : gblrec_msg ); + goto done; + } + if( typeflag == tf_extended ) + { + if( prev_extended && !permissive ) { msg = fv_msg3; retval = 2; } + else retval = parse_records_lz( decoder, infd, file_pos, member_end, + cdata_size, data_pos, extended, header, + rbuf, &msg, permissive ); + if( retval == 0 && !extended.crc_present() && missing_crc ) + { msg = mcrc_msg; retval = 2; } + if( retval == 0 ) { prev_extended = true; continue; } + show_file_error( archive_namep, msg ? msg : extrec_msg ); + goto done; + } + prev_extended = false; + + extended.fill_from_ustar( header ); // copy metadata from header + + long long rest = extended.file_size(); + const int rem = rest % header_size; + if( rem ) rest += header_size - rem; // padding + if( data_pos + rest >= mdata_end ) data_pos += rest; + else // skip tar member + if( ( retval = skip_member_lz( decoder, infd, file_pos, member_end, + cdata_size, data_pos, rest, &msg ) ) != 0 ) + goto done; + + if( !check_skip_filename( parser, name_pending, extended.path().c_str(), + filenames ) ) // delete tar member + { + // verify that members match + if( member_begin != mdata_pos || data_pos != mdata_end ) + { show_file_error( extended.path().c_str(), + "Can't delete: not individually compressed." ); + retval = 2; extended.reset(); continue; } + if( !show_member_name( extended, header, 1, rbuf ) ) + { retval = 1; goto done; } + const long long size = member_pos - istream_pos; + if( size > 0 ) // move pending data each time a member is deleted + { + if( istream_pos == 0 ) + { if( !safe_seek( outfd, size ) ) { retval = 1; break; } } + else if( !safe_seek( infd, istream_pos ) || + !copy_file( infd, outfd, size ) ) { retval = 1; break; } + } + istream_pos = member_end; + } + extended.reset(); + } + } +done: + if( LZ_decompress_close( decoder ) < 0 && !retval ) + { show_error( "LZ_decompress_close failed." ); retval = 1; } + // tail copy keeps trailing data + return tail_copy( archive_namep, parser, name_pending, lzip_index, + istream_pos, infd, outfd, retval ); + } |