2019-09-11 13:43:07 +03:00
# include "rust-ffi.hh"
2019-09-11 16:25:43 +03:00
# include "compression.hh"
2019-12-07 17:35:14 +02:00
# include <archive.h>
# include <archive_entry.h>
# include "finally.hh"
2019-03-27 15:12:20 +02:00
2019-09-11 14:10:46 +03:00
namespace nix {
2019-12-07 20:08:33 +02:00
struct TarArchive {
struct archive * archive ;
Source * source ;
unsigned char buffer [ 4096 ] ;
void check ( int err , const char * reason = " Failed to extract archive (%s) " ) {
if ( err = = ARCHIVE_EOF )
throw EndOfFile ( " reached end of archive " ) ;
else if ( err ! = ARCHIVE_OK )
throw Error ( reason , archive_error_string ( this - > archive ) ) ;
2019-12-07 18:10:27 +02:00
}
2019-12-07 20:08:33 +02:00
TarArchive ( Source & source ) {
this - > archive = archive_read_new ( ) ;
this - > source = & source ;
archive_read_support_filter_all ( archive ) ;
archive_read_support_format_all ( archive ) ;
check ( archive_read_open ( archive , ( void * ) this , TarArchive : : callback_open , TarArchive : : callback_read , TarArchive : : callback_close ) , " Failed to open archive (%s) " ) ;
2019-12-07 18:10:27 +02:00
}
2019-12-07 20:08:33 +02:00
TarArchive ( const Path & path ) {
this - > archive = archive_read_new ( ) ;
archive_read_support_filter_all ( archive ) ;
archive_read_support_format_all ( archive ) ;
check ( archive_read_open_filename ( archive , path . c_str ( ) , 16384 ) , " Failed to open archive (%s) " ) ;
}
void close ( ) {
check ( archive_read_close ( archive ) , " Failed to close archive (%s) " ) ;
}
~ TarArchive ( ) {
if ( this - > archive ) archive_read_free ( this - > archive ) ;
}
private :
static int callback_open ( struct archive * , void * self ) {
return ARCHIVE_OK ;
}
static ssize_t callback_read ( struct archive * archive , void * _self , const void * * buffer ) {
TarArchive * self = ( TarArchive * ) _self ;
* buffer = self - > buffer ;
try {
return self - > source - > read ( self - > buffer , 4096 ) ;
} catch ( EndOfFile & ) {
return 0 ;
} catch ( std : : exception & err ) {
archive_set_error ( archive , EIO , " Source threw exception: %s " , err . what ( ) ) ;
return - 1 ;
}
}
static int callback_close ( struct archive * , void * self ) {
return ARCHIVE_OK ;
}
} ;
2019-12-07 18:23:11 +02:00
struct PushD {
char * oldDir ;
2019-12-07 20:08:33 +02:00
PushD ( const std : : string & newDir ) {
2019-12-07 18:23:11 +02:00
oldDir = getcwd ( 0 , 0 ) ;
if ( ! oldDir ) throw SysError ( " getcwd " ) ;
int r = chdir ( newDir . c_str ( ) ) ;
if ( r ! = 0 ) throw SysError ( " changing directory to tar output path " ) ;
}
2019-12-07 20:08:33 +02:00
2019-12-07 18:23:11 +02:00
~ PushD ( ) {
int r = chdir ( oldDir ) ;
free ( oldDir ) ;
if ( r ! = 0 )
std : : cerr < < " warning: popd failed to chdir " ;
/* can't throw out of a destructor */
}
} ;
2019-12-07 20:08:33 +02:00
static void extract_archive ( TarArchive & archive , const Path & destDir ) {
2019-12-07 18:23:11 +02:00
// need to chdir back *after* archive closing
PushD newDir ( destDir ) ;
2019-12-07 17:35:14 +02:00
struct archive_entry * entry ;
2019-12-07 20:08:33 +02:00
int flags = ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_PERM ;
2019-12-07 17:35:14 +02:00
for ( ; ; ) {
2019-12-07 20:08:33 +02:00
int r = archive_read_next_header ( archive . archive , & entry ) ;
2019-12-07 17:35:14 +02:00
if ( r = = ARCHIVE_EOF ) break ;
2019-12-07 20:08:33 +02:00
else if ( r = = ARCHIVE_WARN )
std : : cerr < < " warning: " < < archive_error_string ( archive . archive ) < < std : : endl ;
else
archive . check ( r ) ;
archive . check ( archive_read_extract ( archive . archive , entry , flags ) ) ;
2019-12-07 17:35:14 +02:00
}
2019-12-07 20:08:33 +02:00
archive . close ( ) ;
2019-12-07 17:35:14 +02:00
}
2019-12-07 20:08:33 +02:00
2019-12-07 17:35:14 +02:00
void unpackTarfile ( Source & source , const Path & destDir )
{
2019-12-07 20:08:33 +02:00
auto archive = TarArchive ( source ) ;
2019-12-07 18:23:11 +02:00
2019-12-07 17:35:14 +02:00
createDirs ( destDir ) ;
2019-12-07 20:08:33 +02:00
extract_archive ( archive , destDir ) ;
2019-12-07 17:35:14 +02:00
}
2019-12-07 20:08:33 +02:00
2019-12-07 17:35:14 +02:00
void unpackTarfile ( const Path & tarFile , const Path & destDir )
{
2019-12-07 20:08:33 +02:00
auto archive = TarArchive ( tarFile ) ;
2019-12-07 18:23:11 +02:00
2019-12-07 17:35:14 +02:00
createDirs ( destDir ) ;
2019-12-07 20:08:33 +02:00
extract_archive ( archive , destDir ) ;
2019-09-11 16:25:43 +03:00
}
2019-09-11 14:10:46 +03:00
}