2015-10-29 10:47:14 +01:00
# include "Cli.h"
2017-07-18 23:49:51 +02:00
# include <blockstore/implementations/ondisk/OnDiskBlockStore2.h>
2015-10-29 10:47:14 +01:00
# include <cmath>
# include <cstdio>
# include <cstdlib>
2016-02-11 16:39:42 +01:00
# include <cpp-utils/assert/backtrace.h>
2015-10-29 10:47:14 +01:00
2016-02-11 16:39:42 +01:00
# include <fspp/fuse/Fuse.h>
# include <fspp/impl/FilesystemImpl.h>
# include <cpp-utils/process/subprocess.h>
2016-02-12 23:18:13 +01:00
# include <cpp-utils/io/DontEchoStdinToStdoutRAII.h>
2019-01-26 08:38:34 +01:00
# include <cryfs/impl/filesystem/CryDevice.h>
# include <cryfs/impl/config/CryConfigLoader.h>
# include <cryfs/impl/config/CryPasswordBasedKeyProvider.h>
2015-10-29 10:47:14 +01:00
# include "program_options/Parser.h"
2015-11-17 10:49:35 +01:00
# include <boost/filesystem.hpp>
2015-10-29 10:47:14 +01:00
2019-01-26 08:38:34 +01:00
# include <cryfs/impl/filesystem/CryDir.h>
2016-03-02 13:53:37 +01:00
# include <gitversion/gitversion.h>
2015-10-29 10:47:14 +01:00
2015-11-23 17:43:21 +01:00
# include "VersionChecker.h"
2016-03-26 17:09:07 +01:00
# include <gitversion/VersionCompare.h>
2016-09-24 20:28:56 +02:00
# include <cpp-utils/io/NoninteractiveConsole.h>
2019-01-26 08:38:34 +01:00
# include <cryfs/impl/localstate/LocalStateDir.h>
# include <cryfs/impl/localstate/BasedirMetadata.h>
2016-02-21 22:15:27 +01:00
# include "Environment.h"
2019-01-26 08:38:34 +01:00
# include <cryfs/impl/CryfsException.h>
2019-01-20 12:25:21 +01:00
# include <cpp-utils/thread/debugging.h>
2015-11-23 17:43:21 +01:00
2015-10-30 17:23:08 +01:00
//TODO Many functions accessing the ProgramOptions object. Factor out into class that stores it as a member.
2015-11-19 10:08:09 +01:00
//TODO Factor out class handling askPassword
2015-10-30 17:23:08 +01:00
2019-01-19 22:02:41 +01:00
using namespace cryfs_cli ;
2015-10-29 10:47:14 +01:00
using namespace cryfs ;
namespace bf = boost : : filesystem ;
2015-10-30 20:32:25 +01:00
using namespace cpputils : : logging ;
2015-10-29 10:47:14 +01:00
2017-07-18 23:49:51 +02:00
using blockstore : : ondisk : : OnDiskBlockStore2 ;
2015-10-29 10:47:14 +01:00
using program_options : : ProgramOptions ;
using cpputils : : make_unique_ref ;
2016-09-24 20:28:56 +02:00
using cpputils : : NoninteractiveConsole ;
2015-10-30 18:28:33 +01:00
using cpputils : : TempFile ;
2015-11-04 05:27:00 +01:00
using cpputils : : RandomGenerator ;
using cpputils : : unique_ref ;
2018-10-22 04:31:08 +02:00
using cpputils : : SCrypt ;
2020-07-19 22:26:13 +02:00
using cpputils : : either ;
2015-11-04 05:27:00 +01:00
using cpputils : : SCryptSettings ;
2016-01-25 15:01:34 +01:00
using cpputils : : Console ;
2016-01-28 18:55:26 +01:00
using cpputils : : HttpClient ;
2015-10-29 10:47:14 +01:00
using std : : cout ;
using std : : string ;
using std : : endl ;
2015-10-30 18:28:33 +01:00
using std : : shared_ptr ;
using std : : make_shared ;
2018-12-11 06:20:18 +01:00
using std : : unique_ptr ;
2018-12-09 18:27:53 +01:00
using std : : make_unique ;
2015-11-13 00:06:53 +01:00
using std : : function ;
using boost : : optional ;
2015-10-29 10:47:14 +01:00
using boost : : none ;
2015-11-13 00:06:53 +01:00
using boost : : chrono : : minutes ;
using boost : : chrono : : milliseconds ;
2016-02-19 02:10:10 +01:00
using cpputils : : dynamic_pointer_move ;
2016-03-26 17:09:07 +01:00
using gitversion : : VersionCompare ;
2015-10-29 10:47:14 +01:00
2015-12-11 00:27:26 +01:00
//TODO Delete a large file in parallel possible? Takes a long time right now...
2015-10-29 10:47:14 +01:00
//TODO Improve parallelity.
//TODO Replace ASSERTs with other error handling when it is not a programming error but an environment influence (e.g. a block is missing)
2016-03-02 01:36:04 +01:00
//TODO Can we improve performance by setting compiler parameter -maes for scrypt?
2015-10-29 10:47:14 +01:00
2019-01-19 22:02:41 +01:00
namespace cryfs_cli {
2015-10-29 10:47:14 +01:00
2018-12-03 07:57:21 +01:00
Cli : : Cli ( RandomGenerator & keyGenerator , const SCryptSettings & scryptSettings , shared_ptr < Console > console ) :
_keyGenerator ( keyGenerator ) , _scryptSettings ( scryptSettings ) , _console ( ) , _noninteractive ( false ) , _idleUnmounter ( none ) , _device ( none ) {
2016-02-21 22:15:27 +01:00
_noninteractive = Environment : : isNoninteractive ( ) ;
2016-09-24 20:28:56 +02:00
if ( _noninteractive ) {
2016-09-25 02:50:28 +02:00
_console = make_shared < NoninteractiveConsole > ( console ) ;
2016-09-24 20:28:56 +02:00
} else {
2016-09-25 02:50:28 +02:00
_console = console ;
2016-09-24 20:28:56 +02:00
}
2016-02-21 01:34:21 +01:00
}
2015-11-03 21:22:35 +01:00
2017-09-30 19:53:03 +02:00
void Cli : : _showVersion ( unique_ref < HttpClient > httpClient ) {
2016-03-02 13:53:37 +01:00
cout < < " CryFS Version " < < gitversion : : VersionString ( ) < < endl ;
if ( gitversion : : IsDevVersion ( ) ) {
cout < < " WARNING! This is a development version based on git commit " < < gitversion : : GitCommitId ( ) < <
2015-10-29 10:47:14 +01:00
" . Please do not use in production! " < < endl ;
2016-03-02 13:53:37 +01:00
} else if ( ! gitversion : : IsStableVersion ( ) ) {
2015-10-29 10:47:14 +01:00
cout < < " WARNING! This is an experimental version. Please backup your data frequently! " < < endl ;
}
# ifndef NDEBUG
cout < < " WARNING! This is a debug build. Performance might be slow. " < < endl ;
# endif
2016-09-24 13:16:26 +02:00
# ifndef CRYFS_NO_UPDATE_CHECKS
2016-09-25 11:40:01 +02:00
if ( Environment : : noUpdateCheck ( ) ) {
2016-02-22 15:52:41 +01:00
cout < < " Automatic checking for security vulnerabilities and updates is disabled. " < < endl ;
2016-09-25 11:40:01 +02:00
} else if ( Environment : : isNoninteractive ( ) ) {
cout < < " Automatic checking for security vulnerabilities and updates is disabled in noninteractive mode. " < < endl ;
} else {
2017-09-30 19:53:03 +02:00
_checkForUpdates ( std : : move ( httpClient ) ) ;
2016-02-21 22:15:27 +01:00
}
2016-09-24 13:16:26 +02:00
# else
# warning Update checks are disabled. The resulting executable will not go online to check for newer versions or known security vulnerabilities.
2020-06-30 09:09:31 +02:00
UNUSED ( httpClient ) ;
2016-09-24 13:16:26 +02:00
# endif
2016-02-21 22:15:27 +01:00
cout < < endl ;
}
2017-09-30 19:53:03 +02:00
void Cli : : _checkForUpdates ( unique_ref < HttpClient > httpClient ) {
2017-09-30 23:24:33 +02:00
VersionChecker versionChecker ( httpClient . get ( ) ) ;
2015-11-23 17:43:21 +01:00
optional < string > newestVersion = versionChecker . newestVersion ( ) ;
if ( newestVersion = = none ) {
2016-02-20 18:23:46 +01:00
cout < < " Could not check for updates. " < < endl ;
2016-03-02 13:53:37 +01:00
} else if ( VersionCompare : : isOlderThan ( gitversion : : VersionString ( ) , * newestVersion ) ) {
2015-11-23 17:43:21 +01:00
cout < < " CryFS " < < * newestVersion < < " is released. Please update. " < < endl ;
}
2016-03-02 13:53:37 +01:00
optional < string > securityWarning = versionChecker . securityWarningFor ( gitversion : : VersionString ( ) ) ;
2015-11-23 17:43:21 +01:00
if ( securityWarning ! = none ) {
cout < < * securityWarning < < endl ;
}
2015-10-29 10:47:14 +01:00
}
bool Cli : : _checkPassword ( const string & password ) {
if ( password = = " " ) {
std : : cerr < < " Empty password not allowed. Please try again. " < < std : : endl ;
return false ;
}
return true ;
}
2018-09-04 01:51:59 +02:00
function < string ( ) > Cli : : _askPasswordForExistingFilesystem ( std : : shared_ptr < cpputils : : Console > console ) {
return [ console ] ( ) {
string password = console - > askPassword ( " Password: " ) ;
while ( ! _checkPassword ( password ) ) {
password = console - > askPassword ( " Password: " ) ;
}
return password ;
} ;
2015-10-29 10:47:14 +01:00
} ;
2018-09-04 01:51:59 +02:00
function < string ( ) > Cli : : _askPasswordForNewFilesystem ( std : : shared_ptr < cpputils : : Console > console ) {
2018-10-22 04:31:08 +02:00
//TODO Ask confirmation if using insecure password (<8 characters)
2018-09-04 01:51:59 +02:00
return [ console ] ( ) {
string password ;
bool again = false ;
do {
password = console - > askPassword ( " Password: " ) ;
if ( ! _checkPassword ( password ) ) {
again = true ;
continue ;
}
if ( ! _confirmPassword ( console . get ( ) , password ) ) {
again = true ;
continue ;
}
again = false ;
} while ( again ) ;
return password ;
} ;
2015-11-19 10:08:09 +01:00
}
2018-09-04 01:51:59 +02:00
bool Cli : : _confirmPassword ( cpputils : : Console * console , const string & password ) {
string confirmPassword = console - > askPassword ( " Confirm Password: " ) ;
2015-11-19 10:08:09 +01:00
if ( password ! = confirmPassword ) {
std : : cout < < " Passwords don't match " < < std : : endl ;
return false ;
}
return true ;
}
2018-09-04 01:51:59 +02:00
function < string ( ) > Cli : : _askPasswordNoninteractive ( std : : shared_ptr < cpputils : : Console > console ) {
2016-02-21 01:34:21 +01:00
//TODO Test
2018-09-04 01:51:59 +02:00
return [ console ] ( ) {
string password = console - > askPassword ( " Password: " ) ;
if ( ! _checkPassword ( password ) ) {
throw CryfsException ( " Invalid password. Password cannot be empty. " , ErrorCode : : EmptyPassword ) ;
}
return password ;
} ;
2016-02-12 23:18:13 +01:00
}
2015-10-29 10:47:14 +01:00
bf : : path Cli : : _determineConfigFile ( const ProgramOptions & options ) {
auto configFile = options . configFile ( ) ;
if ( configFile = = none ) {
return bf : : path ( options . baseDir ( ) ) / " cryfs.config " ;
}
return * configFile ;
}
2018-04-22 07:04:21 +02:00
void Cli : : _checkConfigIntegrity ( const bf : : path & basedir , const LocalStateDir & localStateDir , const CryConfigFile & config , bool allowReplacedFilesystem ) {
auto basedirMetadata = BasedirMetadata : : load ( localStateDir ) ;
2018-02-03 17:33:59 +01:00
if ( ! allowReplacedFilesystem & & ! basedirMetadata . filesystemIdForBasedirIsCorrect ( basedir , config . config ( ) - > FilesystemId ( ) ) ) {
2017-09-28 08:41:08 +02:00
if ( ! _console - > askYesNo ( " The filesystem id in the config file is different to the last time we loaded a filesystem from this basedir. This can be genuine if you replaced the filesystem with a different one. If you didn't do that, it is possible that an attacker did. Do you want to continue loading the file system? " , false ) ) {
2018-02-02 03:09:28 +01:00
throw CryfsException (
" The filesystem id in the config file is different to the last time we loaded a filesystem from this basedir. " , ErrorCode : : FilesystemIdChanged ) ;
2017-09-28 08:41:08 +02:00
}
}
// Update local state (or create it if it didn't exist yet)
2017-09-28 09:19:30 +02:00
basedirMetadata . updateFilesystemIdForBasedir ( basedir , config . config ( ) - > FilesystemId ( ) ) ;
basedirMetadata . save ( ) ;
2017-09-28 08:41:08 +02:00
}
2018-04-22 07:04:21 +02:00
CryConfigLoader : : ConfigLoadResult Cli : : _loadOrCreateConfig ( const ProgramOptions & options , const LocalStateDir & localStateDir ) {
2018-02-02 01:08:01 +01:00
auto configFile = _determineConfigFile ( options ) ;
2018-04-22 07:04:21 +02:00
auto config = _loadOrCreateConfigFile ( std : : move ( configFile ) , localStateDir , options . cipher ( ) , options . blocksizeBytes ( ) , options . allowFilesystemUpgrade ( ) , options . missingBlockIsIntegrityViolation ( ) , options . allowReplacedFilesystem ( ) ) ;
2020-07-19 22:26:13 +02:00
if ( config . is_left ( ) ) {
switch ( config . left ( ) ) {
case CryConfigFile : : LoadError : : DecryptionFailed :
throw CryfsException ( " Failed to decrypt the config file. Did you enter the correct password? " , ErrorCode : : WrongPassword ) ;
case CryConfigFile : : LoadError : : ConfigFileNotFound :
throw CryfsException ( " Could not find the cryfs.config file. Are you sure this is a valid CryFS file system? " , ErrorCode : : InvalidFilesystem ) ;
}
2015-10-29 10:47:14 +01:00
}
2020-07-19 22:26:13 +02:00
_checkConfigIntegrity ( options . baseDir ( ) , localStateDir , * config . right ( ) . configFile , options . allowReplacedFilesystem ( ) ) ;
return std : : move ( config . right ( ) ) ;
2015-10-29 10:47:14 +01:00
}
2020-07-19 22:26:13 +02:00
either < CryConfigFile : : LoadError , CryConfigLoader : : ConfigLoadResult > Cli : : _loadOrCreateConfigFile ( bf : : path configFilePath , LocalStateDir localStateDir , const optional < string > & cipher , const optional < uint32_t > & blocksizeBytes , bool allowFilesystemUpgrade , const optional < bool > & missingBlockIsIntegrityViolation , bool allowReplacedFilesystem ) {
2018-10-22 04:31:08 +02:00
// TODO Instead of passing in _askPasswordXXX functions to KeyProvider, only pass in console and move logic to the key provider,
// for example by having a separate CryPasswordBasedKeyProvider / CryNoninteractivePasswordBasedKeyProvider.
auto keyProvider = make_unique_ref < CryPasswordBasedKeyProvider > (
_console ,
_noninteractive ? Cli : : _askPasswordNoninteractive ( _console ) : Cli : : _askPasswordForExistingFilesystem ( _console ) ,
_noninteractive ? Cli : : _askPasswordNoninteractive ( _console ) : Cli : : _askPasswordForNewFilesystem ( _console ) ,
make_unique_ref < SCrypt > ( _scryptSettings )
) ;
return CryConfigLoader ( _console , _keyGenerator , std : : move ( keyProvider ) , std : : move ( localStateDir ) ,
cipher , blocksizeBytes , missingBlockIsIntegrityViolation ) . loadOrCreate ( std : : move ( configFilePath ) , allowFilesystemUpgrade , allowReplacedFilesystem ) ;
2016-02-21 01:34:21 +01:00
}
2020-07-20 01:57:37 +02:00
namespace {
2021-08-14 19:34:41 +02:00
void printConfig ( const CryConfig & oldConfig , const CryConfig & updatedConfig ) {
auto printValue = [ & ] ( const char * prefix , const char * suffix , auto member ) {
std : : cout < < prefix ;
auto oldConfigValue = member ( oldConfig ) ;
auto updatedConfigValue = member ( updatedConfig ) ;
if ( oldConfigValue = = updatedConfigValue ) {
std : : cout < < oldConfigValue ;
} else {
std : : cout < < oldConfigValue < < " -> " < < updatedConfigValue ;
}
std : : cout < < suffix ;
} ;
2020-07-20 01:57:37 +02:00
std : : cout
< < " \n ---------------------------------------------------- "
< < " \n Filesystem configuration: "
2021-08-14 19:34:41 +02:00
< < " \n ---------------------------------------------------- " ;
printValue ( " \n - Filesystem format version: " , " " , [ ] ( const CryConfig & config ) { return config . Version ( ) ; } ) ;
printValue ( " \n - Created with: CryFS " , " " , [ ] ( const CryConfig & config ) { return config . CreatedWithVersion ( ) ; } ) ;
printValue ( " \n - Last opened with: CryFS " , " " , [ ] ( const CryConfig & config ) { return config . LastOpenedWithVersion ( ) ; } ) ;
printValue ( " \n - Cipher: " , " " , [ ] ( const CryConfig & config ) { return config . Cipher ( ) ; } ) ;
printValue ( " \n - Blocksize: " , " bytes " , [ ] ( const CryConfig & config ) { return config . BlocksizeBytes ( ) ; } ) ;
printValue ( " \n - Filesystem Id: " , " " , [ ] ( const CryConfig & config ) { return config . FilesystemId ( ) . ToString ( ) ; } ) ;
std : : cout < < " \n ---------------------------------------------------- \n " ;
2020-07-20 01:57:37 +02:00
}
}
2018-12-09 18:27:53 +01:00
void Cli : : _runFilesystem ( const ProgramOptions & options , std : : function < void ( ) > onMounted ) {
2015-10-30 20:32:25 +01:00
try {
2018-12-03 07:57:21 +01:00
LocalStateDir localStateDir ( Environment : : localStateDir ( ) ) ;
auto blockStore = make_unique_ref < OnDiskBlockStore2 > ( options . baseDir ( ) ) ;
auto config = _loadOrCreateConfig ( options , localStateDir ) ;
2021-08-14 19:34:41 +02:00
printConfig ( config . oldConfig , * config . configFile - > config ( ) ) ;
2018-12-11 06:20:18 +01:00
unique_ptr < fspp : : fuse : : Fuse > fuse = nullptr ;
2018-12-19 06:40:03 +01:00
bool stoppedBecauseOfIntegrityViolation = false ;
auto onIntegrityViolation = [ & fuse , & stoppedBecauseOfIntegrityViolation ] ( ) {
2018-12-11 06:20:18 +01:00
if ( fuse . get ( ) ! = nullptr ) {
LOG ( ERR , " Integrity violation detected. Unmounting. " ) ;
2018-12-19 06:40:03 +01:00
stoppedBecauseOfIntegrityViolation = true ;
2018-12-11 06:20:18 +01:00
fuse - > stop ( ) ;
} else {
2018-12-19 06:40:03 +01:00
// Usually on an integrity violation, the file system is unmounted.
// Here, the file system isn't initialized yet, i.e. we failed in the initial steps when
2018-12-11 06:20:18 +01:00
// setting up _device before running initFilesystem.
// We can't unmount a not-mounted file system, but we can make sure it doesn't get mounted.
2018-12-19 06:40:03 +01:00
throw CryfsException ( " Integrity violation detected. Unmounting. " , ErrorCode : : IntegrityViolation ) ;
2018-12-11 06:20:18 +01:00
}
} ;
2018-12-22 00:58:30 +01:00
const bool missingBlockIsIntegrityViolation = config . configFile - > config ( ) - > missingBlockIsIntegrityViolation ( ) ;
2019-10-13 13:29:59 +02:00
_device = optional < unique_ref < CryDevice > > ( make_unique_ref < CryDevice > ( std : : move ( config . configFile ) , std : : move ( blockStore ) , std : : move ( localStateDir ) , config . myClientId , options . allowIntegrityViolations ( ) , missingBlockIsIntegrityViolation , std : : move ( onIntegrityViolation ) ) ) ;
2018-12-03 07:57:21 +01:00
_sanityCheckFilesystem ( _device - > get ( ) ) ;
2015-10-30 20:32:25 +01:00
2018-12-09 18:27:53 +01:00
auto initFilesystem = [ & ] ( fspp : : fuse : : Fuse * fs ) {
2018-12-03 07:57:21 +01:00
ASSERT ( _device ! = none , " File system not ready to be initialized. Was it already initialized before? " ) ;
2015-10-30 20:32:25 +01:00
2018-12-03 07:57:21 +01:00
//TODO Test auto unmounting after idle timeout
2019-02-28 11:29:10 +01:00
const boost : : optional < double > idle_minutes = options . unmountAfterIdleMinutes ( ) ;
_idleUnmounter = _createIdleCallback ( idle_minutes , [ fs , idle_minutes ] {
LOG ( INFO , " Unmounting because file system was idle for {} minutes " , * idle_minutes ) ;
fs - > stop ( ) ;
} ) ;
2018-12-03 07:57:21 +01:00
if ( _idleUnmounter ! = none ) {
( * _device ) - > onFsAction ( std : : bind ( & CallAfterTimeout : : resetTimer , _idleUnmounter - > get ( ) ) ) ;
}
return make_shared < fspp : : FilesystemImpl > ( std : : move ( * _device ) ) ;
} ;
2018-12-11 06:20:18 +01:00
fuse = make_unique < fspp : : fuse : : Fuse > ( initFilesystem , std : : move ( onMounted ) , " cryfs " , " cryfs@ " + options . baseDir ( ) . string ( ) ) ;
2018-12-03 07:57:21 +01:00
_initLogfile ( options ) ;
2015-11-13 00:06:53 +01:00
2019-01-21 05:23:49 +01:00
std : : cout < < " \n Mounting filesystem. To unmount, call: \n $ cryfs-unmount " < < options . mountDir ( ) < < " \n "
< < std : : endl ;
2018-12-19 06:40:03 +01:00
2018-12-22 02:26:38 +01:00
if ( options . foreground ( ) ) {
fuse - > runInForeground ( options . mountDir ( ) , options . fuseOptions ( ) ) ;
} else {
fuse - > runInBackground ( options . mountDir ( ) , options . fuseOptions ( ) ) ;
}
2018-12-19 06:40:03 +01:00
2018-12-22 02:26:38 +01:00
if ( stoppedBecauseOfIntegrityViolation ) {
2018-12-19 06:40:03 +01:00
throw CryfsException ( " Integrity violation detected. Unmounting. " , ErrorCode : : IntegrityViolation ) ;
2018-12-22 02:26:38 +01:00
}
2018-02-02 03:09:28 +01:00
} catch ( const CryfsException & e ) {
throw ; // CryfsException is only thrown if setup goes wrong. Throw it through so that we get the correct process exit code.
2015-10-30 20:32:25 +01:00
} catch ( const std : : exception & e ) {
2018-05-17 08:03:03 +02:00
LOG ( ERR , " Crashed: {} " , e . what ( ) ) ;
2015-10-30 20:32:25 +01:00
} catch ( . . . ) {
2018-05-17 08:03:03 +02:00
LOG ( ERR , " Crashed " ) ;
2015-10-30 20:32:25 +01:00
}
2015-10-29 10:47:14 +01:00
}
2016-02-19 02:10:10 +01:00
void Cli : : _sanityCheckFilesystem ( CryDevice * device ) {
//Try to list contents of base directory
auto _rootDir = device - > Load ( " / " ) ; // this might throw an exception if the root blob doesn't exist
if ( _rootDir = = none ) {
2018-02-02 01:08:01 +01:00
throw CryfsException ( " Couldn't find root blob " , ErrorCode : : InvalidFilesystem ) ;
2016-02-19 02:10:10 +01:00
}
auto rootDir = dynamic_pointer_move < CryDir > ( * _rootDir ) ;
if ( rootDir = = none ) {
2018-02-02 01:08:01 +01:00
throw CryfsException ( " Base directory blob doesn't contain a directory " , ErrorCode : : InvalidFilesystem ) ;
2016-02-19 02:10:10 +01:00
}
( * rootDir ) - > children ( ) ; // Load children
}
2015-11-13 00:06:53 +01:00
optional < unique_ref < CallAfterTimeout > > Cli : : _createIdleCallback ( optional < double > minutes , function < void ( ) > callback ) {
if ( minutes = = none ) {
return none ;
}
2018-10-14 22:26:30 +02:00
uint64_t millis = std : : llround ( 60000 * ( * minutes ) ) ;
2019-01-20 12:25:21 +01:00
return make_unique_ref < CallAfterTimeout > ( milliseconds ( millis ) , callback , " idlecallback " ) ;
2015-11-13 00:06:53 +01:00
}
2015-10-29 15:52:49 +01:00
void Cli : : _initLogfile ( const ProgramOptions & options ) {
2015-11-02 21:20:10 +01:00
spdlog : : drop ( " cryfs " ) ;
2015-10-29 15:52:49 +01:00
//TODO Test that --logfile parameter works. Should be: file if specified, otherwise stderr if foreground, else syslog.
if ( options . logFile ( ) ! = none ) {
cpputils : : logging : : setLogger (
2019-06-02 06:05:26 +02:00
spdlog : : create < spdlog : : sinks : : basic_file_sink_mt > ( " cryfs " , options . logFile ( ) - > string ( ) ) ) ;
2015-10-30 21:40:38 +01:00
} else if ( options . foreground ( ) ) {
cpputils : : logging : : setLogger ( spdlog : : stderr_logger_mt ( " cryfs " ) ) ;
} else {
2018-08-08 03:27:34 +02:00
cpputils : : logging : : setLogger ( cpputils : : logging : : system_logger ( " cryfs " ) ) ;
2015-10-29 15:52:49 +01:00
}
}
2019-01-14 01:29:28 +01:00
void Cli : : _sanityChecks ( const ProgramOptions & options ) {
2020-08-01 03:08:19 +02:00
_checkDirAccessible ( bf : : absolute ( options . baseDir ( ) ) , " base directory " , options . createMissingBasedir ( ) , ErrorCode : : InaccessibleBaseDir ) ;
2019-01-14 01:29:28 +01:00
if ( ! options . mountDirIsDriveLetter ( ) ) {
2020-08-01 03:08:19 +02:00
_checkDirAccessible ( options . mountDir ( ) , " mount directory " , options . createMissingMountpoint ( ) , ErrorCode : : InaccessibleMountDir ) ;
2019-01-14 01:29:28 +01:00
_checkMountdirDoesntContainBasedir ( options ) ;
} else {
if ( bf : : exists ( options . mountDir ( ) ) ) {
throw CryfsException ( " Drive " + options . mountDir ( ) . string ( ) + " already exists. " , ErrorCode : : InaccessibleMountDir ) ;
}
}
2015-10-30 17:23:08 +01:00
}
2020-08-01 03:08:19 +02:00
void Cli : : _checkDirAccessible ( const bf : : path & dir , const std : : string & name , bool createMissingDir , ErrorCode errorCode ) {
2015-10-30 22:01:42 +01:00
if ( ! bf : : exists ( dir ) ) {
2020-08-01 03:08:19 +02:00
bool create = createMissingDir ;
if ( create ) {
LOG ( INFO , " Automatically creating {} " , name ) ;
} else {
create = _console - > askYesNo ( " Could not find " + name + " . Do you want to create it? " , false ) ;
}
2016-01-25 15:01:34 +01:00
if ( create ) {
if ( ! bf : : create_directory ( dir ) ) {
2018-02-02 01:08:01 +01:00
throw CryfsException ( " Error creating " + name , errorCode ) ;
2016-01-25 15:01:34 +01:00
}
} else {
2018-02-02 01:08:01 +01:00
//std::cerr << "Exit code: " << exitCode(errorCode) << std::endl;
throw CryfsException ( name + " not found. " , errorCode ) ;
2016-01-25 15:01:34 +01:00
}
2015-10-30 17:23:08 +01:00
}
2015-10-30 22:01:42 +01:00
if ( ! bf : : is_directory ( dir ) ) {
2018-02-02 01:08:01 +01:00
throw CryfsException ( name + " is not a directory. " , errorCode ) ;
2015-10-30 18:28:33 +01:00
}
2018-02-02 01:08:01 +01:00
auto file = _checkDirWriteable ( dir , name , errorCode ) ;
_checkDirReadable ( dir , file , name , errorCode ) ;
2015-10-30 18:28:33 +01:00
}
2018-02-02 01:08:01 +01:00
shared_ptr < TempFile > Cli : : _checkDirWriteable ( const bf : : path & dir , const std : : string & name , ErrorCode errorCode ) {
2015-10-30 22:01:42 +01:00
auto path = dir / " tempfile " ;
2015-10-30 18:28:33 +01:00
try {
return make_shared < TempFile > ( path ) ;
} catch ( const std : : runtime_error & e ) {
2018-02-02 01:08:01 +01:00
throw CryfsException ( " Could not write to " + name + " . " , errorCode ) ;
2015-10-30 18:28:33 +01:00
}
}
2018-02-02 01:08:01 +01:00
void Cli : : _checkDirReadable ( const bf : : path & dir , shared_ptr < TempFile > tempfile , const std : : string & name , ErrorCode errorCode ) {
2015-10-30 22:01:42 +01:00
ASSERT ( bf : : equivalent ( dir , tempfile - > path ( ) . parent_path ( ) ) , " This function should be called with a file inside the directory " ) ;
2015-10-30 18:28:33 +01:00
try {
bool found = false ;
bf : : directory_iterator end ;
2015-10-30 22:01:42 +01:00
for ( auto iter = bf : : directory_iterator ( dir ) ; iter ! = end ; + + iter ) {
2015-10-30 18:28:33 +01:00
if ( bf : : equivalent ( * iter , tempfile - > path ( ) ) ) {
found = true ;
}
}
if ( ! found ) {
//This should not happen. Can only happen if the written temp file got deleted inbetween or maybe was not written at all.
2015-10-30 22:01:42 +01:00
throw std : : runtime_error ( " Error accessing " + name + " . " ) ;
2015-10-30 18:28:33 +01:00
}
} catch ( const boost : : filesystem : : filesystem_error & e ) {
2018-02-02 01:08:01 +01:00
throw CryfsException ( " Could not read from " + name + " . " , errorCode ) ;
2015-10-30 18:28:33 +01:00
}
2015-10-30 17:23:08 +01:00
}
void Cli : : _checkMountdirDoesntContainBasedir ( const ProgramOptions & options ) {
if ( _pathContains ( options . mountDir ( ) , options . baseDir ( ) ) ) {
2018-02-02 01:08:01 +01:00
throw CryfsException ( " base directory can't be inside the mount directory. " , ErrorCode : : BaseDirInsideMountDir ) ;
2015-10-30 17:23:08 +01:00
}
}
bool Cli : : _pathContains ( const bf : : path & parent , const bf : : path & child ) {
bf : : path absParent = bf : : canonical ( parent ) ;
bf : : path current = bf : : canonical ( child ) ;
if ( absParent . empty ( ) & & current . empty ( ) ) {
return true ;
}
while ( ! current . empty ( ) ) {
if ( bf : : equivalent ( current , absParent ) ) {
return true ;
}
current = current . parent_path ( ) ;
2015-10-29 20:17:52 +01:00
}
2015-10-30 17:23:08 +01:00
return false ;
2015-10-29 20:17:52 +01:00
}
2019-06-08 22:06:17 +02:00
int Cli : : main ( int argc , const char * * argv , unique_ref < HttpClient > httpClient , std : : function < void ( ) > onMounted ) {
2018-05-19 07:29:41 +02:00
cpputils : : showBacktraceOnCrash ( ) ;
2019-01-20 12:25:21 +01:00
cpputils : : set_thread_name ( " cryfs " ) ;
2015-10-29 15:52:49 +01:00
try {
2018-02-02 01:21:51 +01:00
_showVersion ( std : : move ( httpClient ) ) ;
2018-02-02 01:08:01 +01:00
ProgramOptions options = program_options : : Parser ( argc , argv ) . parse ( CryCiphers : : supportedCipherNames ( ) ) ;
2015-10-29 20:17:52 +01:00
_sanityChecks ( options ) ;
2018-12-09 18:27:53 +01:00
_runFilesystem ( options , std : : move ( onMounted ) ) ;
2018-02-02 01:08:01 +01:00
} catch ( const CryfsException & e ) {
2018-12-11 06:20:18 +01:00
if ( e . what ( ) ! = string ( ) ) {
2018-12-19 06:23:16 +01:00
std : : cerr < < " Error " < < static_cast < int > ( e . errorCode ( ) ) < < " : " < < e . what ( ) < < std : : endl ;
2018-02-02 01:08:01 +01:00
}
return exitCode ( e . errorCode ( ) ) ;
2015-10-29 15:52:49 +01:00
} catch ( const std : : runtime_error & e ) {
std : : cerr < < " Error: " < < e . what ( ) < < std : : endl ;
2018-02-02 01:08:01 +01:00
return exitCode ( ErrorCode : : UnspecifiedError ) ;
2015-10-29 10:47:14 +01:00
}
2018-02-02 01:08:01 +01:00
return exitCode ( ErrorCode : : Success ) ;
2015-10-29 10:47:14 +01:00
}
2015-10-30 18:28:33 +01:00
}