2019-12-07 16:35:14 +01:00
# include <archive.h>
# include <archive_entry.h>
2019-12-09 11:21:46 +01:00
# include "serialise.hh"
2019-03-27 14:12:20 +01:00
2019-09-11 13:10:46 +02:00
namespace nix {
2019-12-07 19:08:33 +01:00
struct TarArchive {
struct archive * archive ;
Source * source ;
2019-12-09 11:21:46 +01:00
std : : vector < unsigned char > buffer ;
2019-12-07 19:08:33 +01:00
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 17:10:27 +01:00
}
2019-12-07 19:08:33 +01:00
2019-12-09 11:21:46 +01:00
TarArchive ( Source & source ) : buffer ( 4096 ) {
2019-12-07 19:08:33 +01:00
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 17:10:27 +01:00
}
2019-12-07 19:08:33 +01: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) " ) ;
}
2019-12-09 11:21:46 +01:00
// disable copy constructor
TarArchive ( const TarArchive & ) = delete ;
2019-12-07 19:08:33 +01:00
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 ;
}
2019-12-09 11:21:46 +01:00
2019-12-07 19:08:33 +01:00
static ssize_t callback_read ( struct archive * archive , void * _self , const void * * buffer ) {
2019-12-09 11:21:46 +01:00
TarArchive * self = ( TarArchive * ) _self ;
* buffer = self - > buffer . data ( ) ;
2019-12-07 19:08:33 +01:00
try {
2019-12-09 11:21:46 +01:00
return self - > source - > read ( self - > buffer . data ( ) , 4096 ) ;
2019-12-07 19:08:33 +01:00
} 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 17:23:11 +01:00
struct PushD {
char * oldDir ;
2019-12-07 19:08:33 +01:00
PushD ( const std : : string & newDir ) {
2019-12-07 17:23:11 +01: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 19:08:33 +01:00
2019-12-07 17:23:11 +01:00
~ PushD ( ) {
int r = chdir ( oldDir ) ;
free ( oldDir ) ;
if ( r ! = 0 )
2019-12-09 11:21:46 +01:00
std : : cerr < < " warning: failed to change directory back after tar extraction " ;
2019-12-07 17:23:11 +01:00
/* can't throw out of a destructor */
}
} ;
2019-12-07 19:08:33 +01:00
static void extract_archive ( TarArchive & archive , const Path & destDir ) {
2019-12-07 17:23:11 +01:00
// need to chdir back *after* archive closing
PushD newDir ( destDir ) ;
2019-12-07 16:35:14 +01:00
struct archive_entry * entry ;
2019-12-09 11:21:46 +01:00
int flags = ARCHIVE_EXTRACT_FFLAGS
| ARCHIVE_EXTRACT_PERM
| ARCHIVE_EXTRACT_SECURE_SYMLINKS
| ARCHIVE_EXTRACT_SECURE_NODOTDOT
| ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS ;
2019-12-07 19:08:33 +01:00
2019-12-07 16:35:14 +01:00
for ( ; ; ) {
2019-12-07 19:08:33 +01:00
int r = archive_read_next_header ( archive . archive , & entry ) ;
2019-12-07 16:35:14 +01:00
if ( r = = ARCHIVE_EOF ) break ;
2019-12-07 19:08:33 +01: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 16:35:14 +01:00
}
2019-12-07 19:08:33 +01:00
archive . close ( ) ;
2019-12-07 16:35:14 +01:00
}
2019-12-07 19:08:33 +01:00
2019-12-07 16:35:14 +01:00
void unpackTarfile ( Source & source , const Path & destDir )
{
2019-12-07 19:08:33 +01:00
auto archive = TarArchive ( source ) ;
2019-12-07 17:23:11 +01:00
2019-12-07 16:35:14 +01:00
createDirs ( destDir ) ;
2019-12-07 19:08:33 +01:00
extract_archive ( archive , destDir ) ;
2019-12-07 16:35:14 +01:00
}
2019-12-07 19:08:33 +01:00
2019-12-07 16:35:14 +01:00
void unpackTarfile ( const Path & tarFile , const Path & destDir )
{
2019-12-07 19:08:33 +01:00
auto archive = TarArchive ( tarFile ) ;
2019-12-07 17:23:11 +01:00
2019-12-07 16:35:14 +01:00
createDirs ( destDir ) ;
2019-12-07 19:08:33 +01:00
extract_archive ( archive , destDir ) ;
2019-09-11 15:25:43 +02:00
}
2019-09-11 13:10:46 +02:00
}