summaryrefslogtreecommitdiffstats
path: root/extended.cc
diff options
context:
space:
mode:
Diffstat (limited to 'extended.cc')
-rw-r--r--extended.cc125
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;
}