diff options
Diffstat (limited to 'extended.cc')
-rw-r--r-- | extended.cc | 125 |
1 files changed, 103 insertions, 22 deletions
diff --git a/extended.cc b/extended.cc index 4b9e067..5440de7 100644 --- a/extended.cc +++ b/extended.cc @@ -19,10 +19,12 @@ #include <cctype> #include <climits> +#include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <vector> +#include <pthread.h> #include <stdint.h> #include "tarlz.h" @@ -38,13 +40,13 @@ unsigned decimal_digits( unsigned long long value ) } -int record_size( const unsigned keyword_size, const unsigned long value_size ) +unsigned long long 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; } @@ -89,45 +91,120 @@ uint32_t parse_record_crc( const char * const ptr ) const std::string Extended::crc_record( "22 GNU.crc32=00000000\n" ); -int Extended::recsize_linkpath() const +void Extended::calculate_sizes() const { - if( recsize_linkpath_ < 0 ) recsize_linkpath_ = - linkpath_.size() ? record_size( 8, linkpath_.size() ) : 0; - return recsize_linkpath_; + linkpath_recsize_ = linkpath_.size() ? record_size( 8, linkpath_.size() ) : 0; + path_recsize_ = path_.size() ? record_size( 4, path_.size() ) : 0; + file_size_recsize_ = + ( file_size_ > 0 ) ? record_size( 4, decimal_digits( file_size_ ) ) : 0; + edsize_ = linkpath_recsize_ + path_recsize_ + file_size_recsize_ + + crc_record.size(); + padded_edsize_ = round_up( edsize_ ); + full_size_ = header_size + padded_edsize_; } -int Extended::recsize_path() const + +unsigned char xdigit( const unsigned value ) { - if( recsize_path_ < 0 ) - recsize_path_ = path_.size() ? record_size( 4, path_.size() ) : 0; - return recsize_path_; + if( value <= 9 ) return '0' + value; + if( value <= 15 ) return 'A' + value - 10; + return 0; } -int Extended::recsize_file_size() const +void print_hex( char * const buf, int size, unsigned long long num ) { - if( recsize_file_size_ < 0 ) recsize_file_size_ = - ( file_size_ > 0 ) ? record_size( 4, decimal_digits( file_size_ ) ) : 0; - return recsize_file_size_; + while( --size >= 0 ) { buf[size] = xdigit( num & 0x0F ); num >>= 4; } + } + +void print_decimal( char * const buf, int size, unsigned long long num ) + { while( --size >= 0 ) { buf[size] = '0' + ( num % 10 ); num /= 10; } } + + +bool print_record( char * const buf, const unsigned long long size, + const char * keyword, const std::string & value ) + { + // "size keyword=value\n" + unsigned long long pos = decimal_digits( size ); + print_decimal( buf, pos, size ); buf[pos++] = ' '; + while( *keyword ) { buf[pos++] = *keyword; ++keyword; } buf[pos++] = '='; + std::memcpy( buf + pos, value.c_str(), value.size() ); + pos += value.size(); buf[pos++] = '\n'; + return pos == size; + } + +bool print_record( char * const buf, const int size, + const char * keyword, const unsigned long long value ) + { + // "size keyword=value\n" + int pos = decimal_digits( size ); + print_decimal( buf, pos, size ); buf[pos++] = ' '; + while( *keyword ) { buf[pos++] = *keyword; ++keyword; } buf[pos++] = '='; + const int vd = decimal_digits( value ); + print_decimal( buf + pos, vd, value ); pos += vd; buf[pos++] = '\n'; + return pos == size; + } + + +// Returns the extended block size, or -1 if error. +long long Extended::format_block( Resizable_buffer & rbuf ) const + { + if( empty() ) return 0; // no extended data + const unsigned long long bufsize = full_size(); // recalculate sizes + if( edsize_ <= 0 ) return 0; // no extended data + if( edsize_ >= 1LL << 33 ) return -1; // too much extended data + if( !rbuf.resize( bufsize ) ) return -1; // extended block buffer + uint8_t * const header = (uint8_t *)rbuf(); // extended header + char * const buf = rbuf() + header_size; // extended records + init_tar_header( header ); + header[typeflag_o] = tf_extended; // fill only required fields + print_octal( header + size_o, size_l - 1, edsize_ ); + print_octal( header + chksum_o, chksum_l - 1, ustar_chksum( header ) ); + + if( path_recsize_ && !print_record( buf, path_recsize_, "path", path_ ) ) + return -1; + long long pos = path_recsize_; + if( linkpath_recsize_ && + !print_record( buf + pos, linkpath_recsize_, "linkpath", linkpath_ ) ) + return -1; + pos += linkpath_recsize_; + if( file_size_recsize_ && + !print_record( buf + pos, file_size_recsize_, "size", file_size_ ) ) + return -1; + pos += file_size_recsize_; + const unsigned crc_size = Extended::crc_record.size(); + std::memcpy( buf + pos, Extended::crc_record.c_str(), crc_size ); + pos += crc_size; + if( pos != edsize_ ) return -1; + print_hex( buf + edsize_ - 9, 8, + crc32c.windowed_crc( (const uint8_t *)buf, edsize_ - 9, edsize_ ) ); + if( padded_edsize_ > edsize_ ) // wipe padding + std::memset( buf + edsize_, 0, padded_edsize_ - edsize_ ); + crc_present_ = true; + return bufsize; } bool Extended::parse( const char * const buf, const unsigned long long edsize, const bool permissive ) { - reset(); + reset(); full_size_ = -1; // invalidate cached sizes 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; + 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 ); } + { + if( path_.size() && !permissive ) return false; + path_.assign( tail + 5, rest - 5 ); + // this also truncates path_ at the first embedded null character + path_.assign( remove_leading_dotslash( path_.c_str() ) ); + } else if( rest > 9 && std::memcmp( tail, "linkpath=", 9 ) == 0 ) { if( linkpath_.size() && !permissive ) return false; linkpath_.assign( tail + 9, rest - 9 ); } @@ -143,14 +220,18 @@ bool Extended::parse( const char * const buf, const unsigned long long edsize, { if( crc_present_ && !permissive ) return false; if( rsize != crc_record.size() ) return false; + crc_present_ = true; 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; + if( stored_crc != computed_crc ) + { + if( verbosity >= 2 ) + std::fprintf( stderr, "CRC32C = %08X\n", (unsigned)computed_crc ); + return false; + } } pos += rsize; } - full_size_ = header_size + round_up( edsize ); return true; } |