diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2019-01-31 23:06:40 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2019-01-31 23:06:40 +0000 |
commit | 01348f04bc92f307f5f61dd9f9f4c8d7746336f5 (patch) | |
tree | 90ea2c52891816e8d5c095fd79f599e23659e092 /extended.cc | |
parent | Adding upstream version 0.9. (diff) | |
download | tarlz-01348f04bc92f307f5f61dd9f9f4c8d7746336f5.tar.xz tarlz-01348f04bc92f307f5f61dd9f9f4c8d7746336f5.zip |
Adding upstream version 0.10.upstream/0.10
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'extended.cc')
-rw-r--r-- | extended.cc | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/extended.cc b/extended.cc new file mode 100644 index 0000000..7e0cb30 --- /dev/null +++ b/extended.cc @@ -0,0 +1,156 @@ +/* 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 <climits> +#include <cstdlib> +#include <cstring> +#include <string> +#include <vector> +#include <stdint.h> + +#include "tarlz.h" + + +namespace { + +unsigned decimal_digits( unsigned long long value ) + { + unsigned digits = 1; + while( value >= 10 ) { value /= 10; ++digits; } + return digits; + } + + +int record_size( const unsigned keyword_size, const unsigned long value_size ) + { + // size = ' ' + keyword + '=' + value + '\n' + unsigned long long size = 1 + keyword_size + 1 + value_size + 1; + const unsigned d1 = decimal_digits( size ); + size += decimal_digits( d1 + size ); + if( size >= INT_MAX ) size = 0; // overflows snprintf size + return size; + } + + +unsigned long long parse_decimal( const char * const ptr, + const char ** const tailp, + const unsigned long long size ) + { + unsigned long long result = 0; + unsigned long long i = 0; + while( i < size && std::isspace( ptr[i] ) ) ++i; + if( !std::isdigit( (unsigned char)ptr[i] ) ) + { if( tailp ) *tailp = ptr; return 0; } + for( ; i < size && std::isdigit( (unsigned char)ptr[i] ); ++i ) + { + const unsigned long long prev = result; + result *= 10; result += ptr[i] - '0'; + if( result < prev || result > LLONG_MAX ) // overflow + { if( tailp ) *tailp = ptr; return 0; } + } + if( tailp ) *tailp = ptr + i; + return result; + } + + +uint32_t parse_record_crc( const char * const ptr ) + { + uint32_t crc = 0; + for( int i = 0; i < 8; ++i ) + { + crc <<= 4; + if( ptr[i] >= '0' && ptr[i] <= '9' ) crc += ptr[i] - '0'; + else if( ptr[i] >= 'A' && ptr[i] <= 'F' ) crc += ptr[i] + 10 - 'A'; + else if( ptr[i] >= 'a' && ptr[i] <= 'f' ) crc += ptr[i] + 10 - 'a'; + else { crc = 0; break; } // invalid digit in crc string + } + return crc; + } + +} // end namespace + + +const std::string Extended::crc_record( "22 GNU.crc32=00000000\n" ); + +int Extended::recsize_linkpath() const + { + if( recsize_linkpath_ < 0 ) recsize_linkpath_ = + linkpath_.size() ? record_size( 8, linkpath_.size() ) : 0; + return recsize_linkpath_; + } + +int Extended::recsize_path() const + { + if( recsize_path_ < 0 ) + recsize_path_ = path_.size() ? record_size( 4, path_.size() ) : 0; + return recsize_path_; + } + +int Extended::recsize_file_size() const + { + if( recsize_file_size_ < 0 ) recsize_file_size_ = + ( file_size_ > 0 ) ? record_size( 4, file_size_ ) : 0; + return recsize_file_size_; + } + + +bool Extended::parse( const char * const buf, const unsigned long long edsize, + const bool permissive ) + { + reset(); + for( unsigned long long pos = 0; pos < edsize; ) // parse records + { + const char * tail; + const unsigned long long rsize = + parse_decimal( buf + pos, &tail, edsize - pos ); + if( rsize == 0 || rsize > edsize - pos || tail[0] != ' ' || + buf[pos+rsize-1] != '\n' ) return false; + ++tail; // point to keyword + // rest = length of (keyword + '=' + value) without the final newline + const unsigned long long rest = ( buf + ( pos + rsize - 1 ) ) - tail; + if( rest > 5 && std::memcmp( tail, "path=", 5 ) == 0 ) + { if( path_.size() && !permissive ) return false; + path_.assign( tail + 5, rest - 5 ); } + else if( rest > 9 && std::memcmp( tail, "linkpath=", 9 ) == 0 ) + { if( linkpath_.size() && !permissive ) return false; + linkpath_.assign( tail + 9, rest - 9 ); } + else if( rest > 5 && std::memcmp( tail, "size=", 5 ) == 0 ) + { + if( file_size_ != 0 && !permissive ) return false; + file_size_ = parse_decimal( tail + 5, &tail, rest - 5 ); + // parse error or size fits in ustar header + if( file_size_ < 1ULL << 33 || tail != buf + ( pos + rsize - 1 ) ) + return false; + } + else if( rest > 10 && std::memcmp( tail, "GNU.crc32=", 10 ) == 0 ) + { + if( crc_present_ && !permissive ) return false; + if( rsize != crc_record.size() ) return false; + const uint32_t stored_crc = parse_record_crc( tail + 10 ); + const uint32_t computed_crc = + crc32c.windowed_crc( (const uint8_t *)buf, pos + rsize - 9, edsize ); + crc_present_ = true; + if( stored_crc != computed_crc ) return false; + } + pos += rsize; + } + full_size_ = header_size + round_up( edsize ); + return true; + } |