From 321b4b38cb22eb640619cebcb1230c604b2e9297 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 24 Sep 2022 03:57:46 +0200 Subject: Adding upstream version 0.23. Signed-off-by: Daniel Baumann --- extended.cc | 179 +++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 141 insertions(+), 38 deletions(-) (limited to 'extended.cc') diff --git a/extended.cc b/extended.cc index 721634a..f05d15f 100644 --- a/extended.cc +++ b/extended.cc @@ -18,6 +18,7 @@ #define _FILE_OFFSET_BITS 64 #include +#include #include #include @@ -29,21 +30,13 @@ const CRC32 crc32c( true ); namespace { -unsigned decimal_digits( unsigned long long value ) - { - unsigned digits = 1; - while( value >= 10 ) { value /= 10; ++digits; } - return digits; - } - - unsigned long long record_size( const unsigned keyword_size, const unsigned long value_size ) { - // size = ' ' + keyword + '=' + value + '\n' + /* length + ' ' + keyword + '=' + value + '\n' + minimize length; prefer "99<97_bytes>" to "100<97_bytes>" */ unsigned long long size = 1 + keyword_size + 1 + value_size + 1; - const unsigned d1 = decimal_digits( size ); - size += decimal_digits( d1 + size ); + size += decimal_digits( decimal_digits( size ) + size ); return size; } @@ -54,14 +47,14 @@ unsigned long long parse_decimal( const char * const ptr, { unsigned long long result = 0; unsigned long long i = 0; - while( i < size && std::isspace( ptr[i] ) ) ++i; + while( i < size && std::isspace( (unsigned char)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 > max_file_size ) // overflow + if( result < prev || result > LLONG_MAX ) // overflow { if( tailp ) *tailp = ptr; return 0; } } if( tailp ) *tailp = ptr + i; @@ -92,21 +85,25 @@ unsigned char xdigit( const unsigned value ) } void print_hex( char * const buf, int size, unsigned long long num ) - { - while( --size >= 0 ) { buf[size] = xdigit( num & 0x0F ); num >>= 4; } - } + { 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; } } - + { while( --size >= 0 ) { buf[size] = num % 10 + '0'; num /= 10; } } -bool print_record( char * const buf, const unsigned long long size, - const char * keyword, const std::string & value ) +unsigned long long print_size_keyword( char * const buf, + const unsigned long long size, const char * keyword ) { // "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++] = '='; + return pos; + } + +bool print_record( char * const buf, const unsigned long long size, + const char * keyword, const std::string & value ) + { + unsigned long long pos = print_size_keyword( buf, size, keyword ); std::memcpy( buf + pos, value.c_str(), value.size() ); pos += value.size(); buf[pos++] = '\n'; return pos == size; @@ -115,18 +112,71 @@ bool print_record( char * const buf, const unsigned long long 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 ); + int pos = print_size_keyword( buf, size, keyword ); + const int vd = decimal_digits( value ); print_decimal( buf + pos, vd, value ); pos += vd; buf[pos++] = '\n'; return pos == size; } +bool print_record( char * const buf, const int size, + const char * keyword, const Etime & value ) + { + int pos = print_size_keyword( buf, size, keyword ); + pos += value.print( buf + pos ); buf[pos++] = '\n'; + return pos == size; + } + } // end namespace +unsigned Etime::decimal_size() const + { + unsigned size = 1 + ( sec_ < 0 ); // first digit + negative sign + for( long long n = sec_; n >= 10 || n <= -10; n /= 10 ) ++size; + if( nsec_ > 0 && nsec_ <= 999999999 ) + { size += 2; // decimal point + first fractional digit + for( int n = nsec_; n >= 10; n /= 10 ) ++size; } + return size; + } + +unsigned Etime::print( char * const buf ) const + { + int len = 0; + if( nsec_ > 0 && nsec_ <= 999999999 ) + { for( int n = nsec_; n > 0; n /= 10 ) buf[len++] = n % 10 + '0'; + buf[len++] = '.'; } + long long n = sec_; + do { long long on = n; n /= 10; buf[len++] = llabs( on - 10 * n ) + '0'; } + while( n != 0 ); + if( sec_ < 0 ) buf[len++] = '-'; + for( int i = 0; i < len / 2; ++i ) std::swap( buf[i], buf[len-i-1] ); + return len; + } + +bool Etime::parse( const char * const ptr, const char ** const tailp, + const long long size ) + { + char * tail; + errno = 0; + long long s = strtoll( ptr, &tail, 10 ); + if( tail == ptr || errno || + ( *tail != 0 && *tail != '\n' && *tail != '.' ) ) return false; + int ns = 0; + if( *tail == '.' ) // parse nanoseconds and any extra digits + { + ++tail; + if( tail - ptr >= size || !std::isdigit( (unsigned char)*tail ) ) + return false; + for( int factor = 100000000; + tail - ptr < size && std::isdigit( (unsigned char)*tail ); + ++tail, factor /= 10 ) + ns += factor * ( *tail - '0' ); + } + sec_ = s; nsec_ = ns; if( tailp ) *tailp = tail; + return true; + } + + std::vector< std::string > Extended::unknown_keywords; const std::string Extended::crc_record( "22 GNU.crc32=00000000\n" ); @@ -136,7 +186,14 @@ void Extended::calculate_sizes() const path_recsize_ = path_.size() ? record_size( 4, path_.size() ) : 0; file_size_recsize_ = ( file_size_ > 0 ) ? record_size( 4, decimal_digits( file_size_ ) ) : 0; + uid_recsize_ = ( uid_ >= 0 ) ? record_size( 3, decimal_digits( uid_ ) ) : 0; + gid_recsize_ = ( gid_ >= 0 ) ? record_size( 3, decimal_digits( gid_ ) ) : 0; + atime_recsize_ = + atime_.out_of_ustar_range() ? record_size( 5, atime_.decimal_size() ) : 0; + mtime_recsize_ = + mtime_.out_of_ustar_range() ? record_size( 5, mtime_.decimal_size() ) : 0; edsize_ = linkpath_recsize_ + path_recsize_ + file_size_recsize_ + + uid_recsize_ + gid_recsize_ + atime_recsize_ + mtime_recsize_ + crc_record.size(); padded_edsize_ = round_up( edsize_ ); full_size_ = header_size + padded_edsize_; @@ -153,20 +210,19 @@ void Extended::unknown_keyword( const char * const buf, for( unsigned i = 0; i < unknown_keywords.size(); ++i ) if( keyword == unknown_keywords[i] ) return; unknown_keywords.push_back( keyword ); - std::string msg( "Ignoring unknown extended header keyword '" ); - msg += keyword; msg += '\''; - show_error( msg.c_str() ); + print_error( 0, "Ignoring unknown extended header keyword '%s'", + keyword.c_str() ); } -// Return the size of the extended block, or -1 if error. +// Return the size of the extended block, -1 if error, -2 if out of memory. 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 + if( !rbuf.resize( bufsize ) ) return -2; // extended block buffer uint8_t * const header = rbuf.u8(); // extended header char * const buf = rbuf() + header_size; // extended records init_tar_header( header ); @@ -185,13 +241,27 @@ long long Extended::format_block( Resizable_buffer & rbuf ) const !print_record( buf + pos, file_size_recsize_, "size", file_size_ ) ) return -1; pos += file_size_recsize_; + if( uid_recsize_ && !print_record( buf + pos, uid_recsize_, "uid", uid_ ) ) + return -1; + pos += uid_recsize_; + if( gid_recsize_ && !print_record( buf + pos, gid_recsize_, "gid", gid_ ) ) + return -1; + pos += gid_recsize_; + if( atime_recsize_ && + !print_record( buf + pos, atime_recsize_, "atime", atime_ ) ) + return -1; + pos += atime_recsize_; + if( mtime_recsize_ && + !print_record( buf + pos, mtime_recsize_, "mtime", mtime_ ) ) + return -1; + pos += mtime_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 + if( padded_edsize_ > edsize_ ) // set padding to zero std::memset( buf + edsize_, 0, padded_edsize_ - edsize_ ); crc_present_ = true; return bufsize; @@ -219,7 +289,7 @@ bool Extended::parse( const char * const buf, const unsigned long long edsize, while( len > 1 && tail[5+len-1] == '/' ) --len; // trailing '/' path_.assign( tail + 5, len ); // this also truncates path_ at the first embedded null character - path_.assign( remove_leading_dotslash( path_.c_str() ) ); + path_.assign( remove_leading_dotslash( path_.c_str(), &removed_prefix ) ); } else if( rest > 9 && std::memcmp( tail, "linkpath=", 9 ) == 0 ) { @@ -233,8 +303,34 @@ bool Extended::parse( const char * const buf, const unsigned long long edsize, 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_ < 1LL << 33 || tail != buf + ( pos + rsize - 1 ) ) - return false; + if( file_size_ < 1LL << 33 || file_size_ > max_file_size || + tail != buf + ( pos + rsize - 1 ) ) return false; + } + else if( rest > 4 && std::memcmp( tail, "uid=", 4 ) == 0 ) + { + if( uid_ >= 0 && !permissive ) return false; + uid_ = parse_decimal( tail + 4, &tail, rest - 4 ); + // parse error or uid fits in ustar header + if( uid_ < 1 << 21 || tail != buf + ( pos + rsize - 1 ) ) return false; + } + else if( rest > 4 && std::memcmp( tail, "gid=", 4 ) == 0 ) + { + if( gid_ >= 0 && !permissive ) return false; + gid_ = parse_decimal( tail + 4, &tail, rest - 4 ); + // parse error or gid fits in ustar header + if( gid_ < 1 << 21 || tail != buf + ( pos + rsize - 1 ) ) return false; + } + else if( rest > 6 && std::memcmp( tail, "atime=", 6 ) == 0 ) + { + if( atime_.isvalid() && !permissive ) return false; + if( !atime_.parse( tail + 6, &tail, rest - 6 ) || // parse error + tail != buf + ( pos + rsize - 1 ) ) return false; + } + else if( rest > 6 && std::memcmp( tail, "mtime=", 6 ) == 0 ) + { + if( mtime_.isvalid() && !permissive ) return false; + if( !mtime_.parse( tail + 6, &tail, rest - 6 ) || // parse error + tail != buf + ( pos + rsize - 1 ) ) return false; } else if( rest > 10 && std::memcmp( tail, "GNU.crc32=", 10 ) == 0 ) { @@ -259,7 +355,8 @@ bool Extended::parse( const char * const buf, const unsigned long long edsize, } -// if needed, copy linkpath, path and file_size from ustar header +/* If not already initialized, copy linkpath, path, file_size, uid, gid, + atime, and mtime from ustar header. */ void Extended::fill_from_ustar( const Tar_header header ) { if( linkpath_.empty() ) // copy linkpath from ustar header @@ -275,7 +372,7 @@ void Extended::fill_from_ustar( const Tar_header header ) } if( path_.empty() ) // copy path from ustar header - { + { // the entire path may be in prefix char stored_name[prefix_l+1+name_l+1]; int len = 0; while( len < prefix_l && header[prefix_o+len] ) @@ -285,13 +382,19 @@ void Extended::fill_from_ustar( const Tar_header header ) { stored_name[len] = header[name_o+i]; ++len; } while( len > 0 && stored_name[len-1] == '/' ) --len; // trailing '/' stored_name[len] = 0; - path( remove_leading_dotslash( stored_name ) ); + path( remove_leading_dotslash( stored_name, &removed_prefix ) ); } const Typeflag typeflag = (Typeflag)header[typeflag_o]; if( file_size_ == 0 && // copy file_size from ustar header ( typeflag == tf_regular || typeflag == tf_hiperf ) ) file_size( parse_octal( header + size_o, size_l ) ); + if( uid_ < 0 ) uid_ = parse_octal( header + uid_o, uid_l ); + if( gid_ < 0 ) gid_ = parse_octal( header + gid_o, gid_l ); + if( !atime_.isvalid() ) + atime_.set( parse_octal( header + mtime_o, mtime_l ) ); // 33 bits + if( !mtime_.isvalid() ) + mtime_.set( parse_octal( header + mtime_o, mtime_l ) ); // 33 bits } @@ -301,7 +404,7 @@ void Extended::fill_from_ustar( const Tar_header header ) long long Extended::get_file_size_and_reset( const Tar_header header ) { const long long tmp = file_size_; - file_size( 0 ); + file_size( 0 ); // reset full_size_ const Typeflag typeflag = (Typeflag)header[typeflag_o]; if( typeflag == tf_regular || typeflag == tf_hiperf ) { -- cgit v1.2.3