--unmount-idle x automatically unmounts the filesystem after x minutes without a filesystem operation.

This commit is contained in:
Sebastian Messmer 2015-11-12 15:06:53 -08:00
parent 4dab8c7426
commit 978e7dbc46
16 changed files with 90 additions and 14 deletions

View File

@ -11,3 +11,4 @@ Version 0.8.1
* Allow --cipher=xxx to specify cipher on command line. If cryfs is creating a new filesystem, it will use this cipher. If it is opening an existing filesystem, it will check whether this is the cipher used by it. * Allow --cipher=xxx to specify cipher on command line. If cryfs is creating a new filesystem, it will use this cipher. If it is opening an existing filesystem, it will check whether this is the cipher used by it.
* --show-ciphers shows a list of all supported ciphers * --show-ciphers shows a list of all supported ciphers
* --extpass allows using an external program for password input * --extpass allows using an external program for password input
* --unmount-idle x automatically unmounts the filesystem after x minutes without a filesystem operation.

View File

@ -21,6 +21,8 @@ namespace cryfs {
boost::chrono::time_point<boost::chrono::steady_clock> _start; boost::chrono::time_point<boost::chrono::steady_clock> _start;
cpputils::LoopThread _checkTimeoutThread; cpputils::LoopThread _checkTimeoutThread;
std::mutex _mutex; std::mutex _mutex;
DISALLOW_COPY_AND_ASSIGN(CallAfterTimeout);
}; };
inline CallAfterTimeout::CallAfterTimeout(boost::chrono::milliseconds timeout, std::function<void()> callback) inline CallAfterTimeout::CallAfterTimeout(boost::chrono::milliseconds timeout, std::function<void()> callback)

View File

@ -48,7 +48,15 @@ using std::endl;
using std::vector; using std::vector;
using std::shared_ptr; using std::shared_ptr;
using std::make_shared; using std::make_shared;
using std::unique_ptr;
using std::make_unique;
using std::function;
using boost::optional;
using boost::none; using boost::none;
using boost::chrono::duration;
using boost::chrono::duration_cast;
using boost::chrono::minutes;
using boost::chrono::milliseconds;
//TODO Support files > 4GB //TODO Support files > 4GB
//TODO Improve parallelity. //TODO Improve parallelity.
@ -148,6 +156,13 @@ namespace cryfs {
_initLogfile(options); _initLogfile(options);
//TODO Test auto unmounting after idle timeout
//TODO This can fail due to a race condition if the filesystem isn't started yet (e.g. passing --unmount-idle 0").
auto idleUnmounter = _createIdleCallback(options.unmountAfterIdleMinutes(), [&fuse] {fuse.stop();});
if (idleUnmounter != none) {
device.onFsAction(std::bind(&CallAfterTimeout::resetTimer, idleUnmounter->get()));
}
std::cout << "\nMounting filesystem. To unmount, call:\n$ fusermount -u " << options.mountDir() << "\n" << std::endl; std::cout << "\nMounting filesystem. To unmount, call:\n$ fusermount -u " << options.mountDir() << "\n" << std::endl;
vector<char *> fuseOptions = options.fuseOptions(); vector<char *> fuseOptions = options.fuseOptions();
@ -159,6 +174,14 @@ namespace cryfs {
} }
} }
optional<unique_ref<CallAfterTimeout>> Cli::_createIdleCallback(optional<double> minutes, function<void()> callback) {
if (minutes == none) {
return none;
}
uint64_t millis = std::round(60000 * (*minutes));
return make_unique_ref<CallAfterTimeout>(milliseconds(millis), callback);
}
void Cli::_initLogfile(const ProgramOptions &options) { void Cli::_initLogfile(const ProgramOptions &options) {
spdlog::drop("cryfs"); spdlog::drop("cryfs");
//TODO Test that --logfile parameter works. Should be: file if specified, otherwise stderr if foreground, else syslog. //TODO Test that --logfile parameter works. Should be: file if specified, otherwise stderr if foreground, else syslog.

View File

@ -7,6 +7,7 @@
#include <boost/filesystem/path.hpp> #include <boost/filesystem/path.hpp>
#include <messmer/cpp-utils/tempfile/TempFile.h> #include <messmer/cpp-utils/tempfile/TempFile.h>
#include <messmer/cpp-utils/random/RandomGenerator.h> #include <messmer/cpp-utils/random/RandomGenerator.h>
#include "CallAfterTimeout.h"
namespace cryfs { namespace cryfs {
class Cli final { class Cli final {
@ -29,6 +30,7 @@ namespace cryfs {
void _checkDirAccessible(const boost::filesystem::path &dir, const std::string &name); void _checkDirAccessible(const boost::filesystem::path &dir, const std::string &name);
std::shared_ptr<cpputils::TempFile> _checkDirWriteable(const boost::filesystem::path &dir, const std::string &name); std::shared_ptr<cpputils::TempFile> _checkDirWriteable(const boost::filesystem::path &dir, const std::string &name);
void _checkDirReadable(const boost::filesystem::path &dir, std::shared_ptr<cpputils::TempFile> tempfile, const std::string &name); void _checkDirReadable(const boost::filesystem::path &dir, std::shared_ptr<cpputils::TempFile> tempfile, const std::string &name);
boost::optional<cpputils::unique_ref<CallAfterTimeout>> _createIdleCallback(boost::optional<double> minutes, std::function<void()> callback);
cpputils::RandomGenerator &_keyGenerator; cpputils::RandomGenerator &_keyGenerator;
cpputils::SCryptSettings _scryptSettings; cpputils::SCryptSettings _scryptSettings;

View File

@ -39,9 +39,9 @@ ProgramOptions Parser::parse(const vector<string> &supportedCiphers) const {
if (foreground) { if (foreground) {
options.second.push_back(const_cast<char*>("-f")); options.second.push_back(const_cast<char*>("-f"));
} }
optional<unsigned int> unmountAfterIdleMinutes = none; optional<double> unmountAfterIdleMinutes = none;
if (vm.count("unmount-idle")) { if (vm.count("unmount-idle")) {
unmountAfterIdleMinutes = vm["unmount-idle"].as<unsigned int>(); unmountAfterIdleMinutes = vm["unmount-idle"].as<double>();
} }
optional<string> logfile = none; optional<string> logfile = none;
if (vm.count("logfile")) { if (vm.count("logfile")) {
@ -104,7 +104,7 @@ void Parser::_addAllowedOptions(po::options_description *desc) {
("foreground,f", "Run CryFS in foreground.") ("foreground,f", "Run CryFS in foreground.")
("cipher", po::value<string>(), "Cipher to use for encryption. See possible values by calling cryfs with --show-ciphers") ("cipher", po::value<string>(), "Cipher to use for encryption. See possible values by calling cryfs with --show-ciphers")
("show-ciphers", "Show list of supported ciphers.") ("show-ciphers", "Show list of supported ciphers.")
("unmount-idle", po::value<unsigned int>(), "Automatically unmount after specified number of idle minutes.") ("unmount-idle", po::value<double>(), "Automatically unmount after specified number of idle minutes.")
("extpass", po::value<string>(), "External program to use for password input") ("extpass", po::value<string>(), "External program to use for password input")
("logfile", po::value<string>(), "Specify the file to write log messages to. If this is not specified, log messages will go to stdout, or syslog if CryFS is running in the background.") ("logfile", po::value<string>(), "Specify the file to write log messages to. If this is not specified, log messages will go to stdout, or syslog if CryFS is running in the background.")
; ;

View File

@ -8,7 +8,7 @@ using std::vector;
using boost::optional; using boost::optional;
ProgramOptions::ProgramOptions(const string &baseDir, const string &mountDir, const optional<string> &configFile, ProgramOptions::ProgramOptions(const string &baseDir, const string &mountDir, const optional<string> &configFile,
bool foreground, const optional<unsigned int> &unmountAfterIdleMinutes, bool foreground, const optional<double> &unmountAfterIdleMinutes,
const optional<string> &logFile, const optional<string> &cipher, const optional<string> &logFile, const optional<string> &cipher,
const optional<string> &extPass, const vector<char*> &fuseOptions) const optional<string> &extPass, const vector<char*> &fuseOptions)
:_baseDir(baseDir), _mountDir(new char[mountDir.size()+1]), _configFile(configFile), _foreground(foreground), :_baseDir(baseDir), _mountDir(new char[mountDir.size()+1]), _configFile(configFile), _foreground(foreground),
@ -50,7 +50,7 @@ bool ProgramOptions::foreground() const {
return _foreground; return _foreground;
} }
const optional<unsigned int> &ProgramOptions::unmountAfterIdleMinutes() const { const optional<double> &ProgramOptions::unmountAfterIdleMinutes() const {
return _unmountAfterIdleMinutes; return _unmountAfterIdleMinutes;
} }

View File

@ -12,7 +12,7 @@ namespace cryfs {
class ProgramOptions final { class ProgramOptions final {
public: public:
ProgramOptions(const std::string &baseDir, const std::string &mountDir, const boost::optional<std::string> &configFile, ProgramOptions(const std::string &baseDir, const std::string &mountDir, const boost::optional<std::string> &configFile,
bool foreground, const boost::optional<unsigned int> &unmountAfterIdleMinutes, bool foreground, const boost::optional<double> &unmountAfterIdleMinutes,
const boost::optional<std::string> &logFile, const boost::optional<std::string> &cipher, const boost::optional<std::string> &logFile, const boost::optional<std::string> &cipher,
const boost::optional<std::string> &extPass, const std::vector<char *> &fuseOptions); const boost::optional<std::string> &extPass, const std::vector<char *> &fuseOptions);
ProgramOptions(ProgramOptions &&rhs); ProgramOptions(ProgramOptions &&rhs);
@ -23,7 +23,7 @@ namespace cryfs {
const boost::optional<std::string> &configFile() const; const boost::optional<std::string> &configFile() const;
bool foreground() const; bool foreground() const;
const boost::optional<std::string> &cipher() const; const boost::optional<std::string> &cipher() const;
const boost::optional<unsigned int> &unmountAfterIdleMinutes() const; const boost::optional<double> &unmountAfterIdleMinutes() const;
const boost::optional<std::string> &logFile() const; const boost::optional<std::string> &logFile() const;
const boost::optional<std::string> &extPass() const; const boost::optional<std::string> &extPass() const;
const std::vector<char *> &fuseOptions() const; const std::vector<char *> &fuseOptions() const;
@ -34,7 +34,7 @@ namespace cryfs {
boost::optional<std::string> _configFile; boost::optional<std::string> _configFile;
bool _foreground; bool _foreground;
boost::optional<std::string> _cipher; boost::optional<std::string> _cipher;
boost::optional<unsigned int> _unmountAfterIdleMinutes; boost::optional<double> _unmountAfterIdleMinutes;
boost::optional<std::string> _logFile; boost::optional<std::string> _logFile;
boost::optional<std::string> _extPass; boost::optional<std::string> _extPass;
std::vector<char *> _fuseOptions; std::vector<char *> _fuseOptions;

View File

@ -57,7 +57,8 @@ CryDevice::CryDevice(CryConfigFile configFile, unique_ref<BlockStore> blockStore
), BLOCKSIZE_BYTES))) ), BLOCKSIZE_BYTES)))
) )
), ),
_rootKey(GetOrCreateRootKey(&configFile)) { _rootKey(GetOrCreateRootKey(&configFile)),
_onFsAction() {
} }
Key CryDevice::CreateRootBlobAndReturnKey() { Key CryDevice::CreateRootBlobAndReturnKey() {
@ -69,6 +70,8 @@ Key CryDevice::CreateRootBlobAndReturnKey() {
optional<unique_ref<fspp::Node>> CryDevice::Load(const bf::path &path) { optional<unique_ref<fspp::Node>> CryDevice::Load(const bf::path &path) {
ASSERT(path.is_absolute(), "Non absolute path given"); ASSERT(path.is_absolute(), "Non absolute path given");
callFsActionCallbacks();
if (path.parent_path().empty()) { if (path.parent_path().empty()) {
//We are asked to load the root directory '/'. //We are asked to load the root directory '/'.
return optional<unique_ref<fspp::Node>>(make_unique_ref<CryDir>(this, none, _rootKey)); return optional<unique_ref<fspp::Node>>(make_unique_ref<CryDir>(this, none, _rootKey));
@ -121,6 +124,7 @@ unique_ref<FsBlobRef> CryDevice::LoadBlob(const bf::path &path) {
} }
void CryDevice::statfs(const bf::path &path, struct statvfs *fsstat) { void CryDevice::statfs(const bf::path &path, struct statvfs *fsstat) {
callFsActionCallbacks();
throw FuseErrnoException(ENOTSUP); throw FuseErrnoException(ENOTSUP);
} }
@ -165,4 +169,14 @@ cpputils::unique_ref<blockstore::BlockStore> CryDevice::CreateEncryptedBlockStor
return CryCiphers::find(config.Cipher()).createEncryptedBlockstore(std::move(baseBlockStore), config.EncryptionKey()); return CryCiphers::find(config.Cipher()).createEncryptedBlockstore(std::move(baseBlockStore), config.EncryptionKey());
} }
void CryDevice::onFsAction(std::function<void()> callback) {
_onFsAction.push_back(callback);
}
void CryDevice::callFsActionCallbacks() const {
for (const auto &callback : _onFsAction) {
callback();
}
}
} }

View File

@ -31,14 +31,18 @@ public:
cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef> LoadDirBlob(const boost::filesystem::path &path); cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef> LoadDirBlob(const boost::filesystem::path &path);
void RemoveBlob(const blockstore::Key &key); void RemoveBlob(const blockstore::Key &key);
void onFsAction(std::function<void()> callback);
boost::optional<cpputils::unique_ref<fspp::Node>> Load(const boost::filesystem::path &path) override; boost::optional<cpputils::unique_ref<fspp::Node>> Load(const boost::filesystem::path &path) override;
void callFsActionCallbacks() const;
private: private:
cpputils::unique_ref<parallelaccessfsblobstore::ParallelAccessFsBlobStore> _fsBlobStore; cpputils::unique_ref<parallelaccessfsblobstore::ParallelAccessFsBlobStore> _fsBlobStore;
blockstore::Key _rootKey; blockstore::Key _rootKey;
std::vector<std::function<void()>> _onFsAction;
blockstore::Key GetOrCreateRootKey(CryConfigFile *config); blockstore::Key GetOrCreateRootKey(CryConfigFile *config);
blockstore::Key CreateRootBlobAndReturnKey(); blockstore::Key CreateRootBlobAndReturnKey();

View File

@ -37,12 +37,14 @@ CryDir::~CryDir() {
} }
unique_ref<fspp::OpenFile> CryDir::createAndOpenFile(const string &name, mode_t mode, uid_t uid, gid_t gid) { unique_ref<fspp::OpenFile> CryDir::createAndOpenFile(const string &name, mode_t mode, uid_t uid, gid_t gid) {
device()->callFsActionCallbacks();
auto child = device()->CreateFileBlob(); auto child = device()->CreateFileBlob();
LoadBlob()->AddChildFile(name, child->key(), mode, uid, gid); LoadBlob()->AddChildFile(name, child->key(), mode, uid, gid);
return make_unique_ref<CryOpenFile>(std::move(child)); return make_unique_ref<CryOpenFile>(device(), std::move(child));
} }
void CryDir::createDir(const string &name, mode_t mode, uid_t uid, gid_t gid) { void CryDir::createDir(const string &name, mode_t mode, uid_t uid, gid_t gid) {
device()->callFsActionCallbacks();
auto blob = LoadBlob(); auto blob = LoadBlob();
auto child = device()->CreateDirBlob(); auto child = device()->CreateDirBlob();
blob->AddChildDir(name, child->key(), mode, uid, gid); blob->AddChildDir(name, child->key(), mode, uid, gid);
@ -56,6 +58,7 @@ unique_ref<DirBlobRef> CryDir::LoadBlob() const {
} }
unique_ref<vector<fspp::Dir::Entry>> CryDir::children() const { unique_ref<vector<fspp::Dir::Entry>> CryDir::children() const {
device()->callFsActionCallbacks();
auto children = make_unique_ref<vector<fspp::Dir::Entry>>(); auto children = make_unique_ref<vector<fspp::Dir::Entry>>();
children->push_back(fspp::Dir::Entry(fspp::Dir::EntryType::DIR, ".")); children->push_back(fspp::Dir::Entry(fspp::Dir::EntryType::DIR, "."));
children->push_back(fspp::Dir::Entry(fspp::Dir::EntryType::DIR, "..")); children->push_back(fspp::Dir::Entry(fspp::Dir::EntryType::DIR, ".."));
@ -65,10 +68,12 @@ unique_ref<vector<fspp::Dir::Entry>> CryDir::children() const {
} }
fspp::Dir::EntryType CryDir::getType() const { fspp::Dir::EntryType CryDir::getType() const {
device()->callFsActionCallbacks();
return fspp::Dir::EntryType::DIR; return fspp::Dir::EntryType::DIR;
} }
void CryDir::createSymlink(const string &name, const bf::path &target, uid_t uid, gid_t gid) { void CryDir::createSymlink(const string &name, const bf::path &target, uid_t uid, gid_t gid) {
device()->callFsActionCallbacks();
auto blob = LoadBlob(); auto blob = LoadBlob();
auto child = device()->CreateSymlinkBlob(target); auto child = device()->CreateSymlinkBlob(target);
blob->AddChildSymlink(name, child->key(), uid, gid); blob->AddChildSymlink(name, child->key(), uid, gid);

View File

@ -35,16 +35,19 @@ unique_ref<parallelaccessfsblobstore::FileBlobRef> CryFile::LoadBlob() const {
} }
unique_ref<fspp::OpenFile> CryFile::open(int flags) const { unique_ref<fspp::OpenFile> CryFile::open(int flags) const {
device()->callFsActionCallbacks();
auto blob = LoadBlob(); auto blob = LoadBlob();
return make_unique_ref<CryOpenFile>(std::move(blob)); return make_unique_ref<CryOpenFile>(device(), std::move(blob));
} }
void CryFile::truncate(off_t size) const { void CryFile::truncate(off_t size) const {
device()->callFsActionCallbacks();
auto blob = LoadBlob(); auto blob = LoadBlob();
blob->resize(size); blob->resize(size);
} }
fspp::Dir::EntryType CryFile::getType() const { fspp::Dir::EntryType CryFile::getType() const {
device()->callFsActionCallbacks();
return fspp::Dir::EntryType::FILE; return fspp::Dir::EntryType::FILE;
} }

View File

@ -35,12 +35,14 @@ CryNode::~CryNode() {
} }
void CryNode::access(int mask) const { void CryNode::access(int mask) const {
device()->callFsActionCallbacks();
//TODO //TODO
return; return;
throw FuseErrnoException(ENOTSUP); throw FuseErrnoException(ENOTSUP);
} }
void CryNode::rename(const bf::path &to) { void CryNode::rename(const bf::path &to) {
device()->callFsActionCallbacks();
if (_parent == none) { if (_parent == none) {
//We are the root direcory. //We are the root direcory.
//TODO What should we do? //TODO What should we do?
@ -59,11 +61,13 @@ void CryNode::rename(const bf::path &to) {
} }
void CryNode::utimens(const timespec times[2]) { void CryNode::utimens(const timespec times[2]) {
device()->callFsActionCallbacks();
//TODO //TODO
throw FuseErrnoException(ENOTSUP); throw FuseErrnoException(ENOTSUP);
} }
void CryNode::remove() { void CryNode::remove() {
device()->callFsActionCallbacks();
//TODO Instead of all these if-else and having _parent being an optional, we could also introduce a CryRootDir which inherits from fspp::Dir. //TODO Instead of all these if-else and having _parent being an optional, we could also introduce a CryRootDir which inherits from fspp::Dir.
if (_parent == none) { if (_parent == none) {
//We are the root direcory. //We are the root direcory.
@ -87,6 +91,7 @@ unique_ref<FsBlobRef> CryNode::LoadBlob() const {
} }
void CryNode::stat(struct ::stat *result) const { void CryNode::stat(struct ::stat *result) const {
device()->callFsActionCallbacks();
if(_parent == none) { if(_parent == none) {
//We are the root directory. //We are the root directory.
//TODO What should we do? //TODO What should we do?
@ -97,6 +102,7 @@ void CryNode::stat(struct ::stat *result) const {
} }
void CryNode::chmod(mode_t mode) { void CryNode::chmod(mode_t mode) {
device()->callFsActionCallbacks();
if (_parent == none) { if (_parent == none) {
//We are the root direcory. //We are the root direcory.
//TODO What should we do? //TODO What should we do?
@ -106,6 +112,7 @@ void CryNode::chmod(mode_t mode) {
} }
void CryNode::chown(uid_t uid, gid_t gid) { void CryNode::chown(uid_t uid, gid_t gid) {
device()->callFsActionCallbacks();
if (_parent == none) { if (_parent == none) {
//We are the root direcory. //We are the root direcory.
//TODO What should we do? //TODO What should we do?

View File

@ -17,8 +17,8 @@ using fspp::fuse::FuseErrnoException;
namespace cryfs { namespace cryfs {
CryOpenFile::CryOpenFile(unique_ref<FileBlobRef> fileBlob) CryOpenFile::CryOpenFile(const CryDevice *device, unique_ref<FileBlobRef> fileBlob)
: _fileBlob(std::move(fileBlob)) { : _device(device), _fileBlob(std::move(fileBlob)) {
} }
CryOpenFile::~CryOpenFile() { CryOpenFile::~CryOpenFile() {
@ -26,32 +26,39 @@ CryOpenFile::~CryOpenFile() {
} }
void CryOpenFile::flush() { void CryOpenFile::flush() {
_device->callFsActionCallbacks();
_fileBlob->flush(); _fileBlob->flush();
} }
void CryOpenFile::stat(struct ::stat *result) const { void CryOpenFile::stat(struct ::stat *result) const {
_device->callFsActionCallbacks();
result->st_mode = S_IFREG | S_IRUSR | S_IXUSR | S_IWUSR; result->st_mode = S_IFREG | S_IRUSR | S_IXUSR | S_IWUSR;
result->st_size = _fileBlob->size(); result->st_size = _fileBlob->size();
return; return;
} }
void CryOpenFile::truncate(off_t size) const { void CryOpenFile::truncate(off_t size) const {
_device->callFsActionCallbacks();
_fileBlob->resize(size); _fileBlob->resize(size);
} }
ssize_t CryOpenFile::read(void *buf, size_t count, off_t offset) const { ssize_t CryOpenFile::read(void *buf, size_t count, off_t offset) const {
_device->callFsActionCallbacks();
return _fileBlob->read(buf, offset, count); return _fileBlob->read(buf, offset, count);
} }
void CryOpenFile::write(const void *buf, size_t count, off_t offset) { void CryOpenFile::write(const void *buf, size_t count, off_t offset) {
_device->callFsActionCallbacks();
_fileBlob->write(buf, offset, count); _fileBlob->write(buf, offset, count);
} }
void CryOpenFile::fsync() { void CryOpenFile::fsync() {
_device->callFsActionCallbacks();
//throw FuseErrnoException(ENOTSUP); //throw FuseErrnoException(ENOTSUP);
} }
void CryOpenFile::fdatasync() { void CryOpenFile::fdatasync() {
_device->callFsActionCallbacks();
//throw FuseErrnoException(ENOTSUP); //throw FuseErrnoException(ENOTSUP);
} }

View File

@ -10,7 +10,7 @@ class CryDevice;
class CryOpenFile: public fspp::OpenFile { class CryOpenFile: public fspp::OpenFile {
public: public:
explicit CryOpenFile(cpputils::unique_ref<parallelaccessfsblobstore::FileBlobRef> fileBlob); explicit CryOpenFile(const CryDevice *device, cpputils::unique_ref<parallelaccessfsblobstore::FileBlobRef> fileBlob);
virtual ~CryOpenFile(); virtual ~CryOpenFile();
void stat(struct ::stat *result) const override; void stat(struct ::stat *result) const override;
@ -22,6 +22,7 @@ public:
void fdatasync() override; void fdatasync() override;
private: private:
const CryDevice *_device;
cpputils::unique_ref<parallelaccessfsblobstore::FileBlobRef> _fileBlob; cpputils::unique_ref<parallelaccessfsblobstore::FileBlobRef> _fileBlob;
DISALLOW_COPY_AND_ASSIGN(CryOpenFile); DISALLOW_COPY_AND_ASSIGN(CryOpenFile);

View File

@ -40,10 +40,12 @@ unique_ref<SymlinkBlobRef> CrySymlink::LoadBlob() const {
} }
fspp::Dir::EntryType CrySymlink::getType() const { fspp::Dir::EntryType CrySymlink::getType() const {
device()->callFsActionCallbacks();
return fspp::Dir::EntryType::SYMLINK; return fspp::Dir::EntryType::SYMLINK;
} }
bf::path CrySymlink::target() const { bf::path CrySymlink::target() const {
device()->callFsActionCallbacks();
auto blob = LoadBlob(); auto blob = LoadBlob();
return blob->target(); return blob->target();
} }

View File

@ -81,6 +81,11 @@ TEST_F(ProgramOptionsParserTest, UnmountAfterIdleMinutesGiven) {
EXPECT_EQ(10, options.unmountAfterIdleMinutes().value()); EXPECT_EQ(10, options.unmountAfterIdleMinutes().value());
} }
TEST_F(ProgramOptionsParserTest, UnmountAfterIdleMinutesGiven_Float) {
ProgramOptions options = parse({"./myExecutable", "/home/user/baseDir", "--unmount-idle", "0.5", "/home/user/mountDir"});
EXPECT_EQ(0.5, options.unmountAfterIdleMinutes().value());
}
TEST_F(ProgramOptionsParserTest, InvalidCipher) { TEST_F(ProgramOptionsParserTest, InvalidCipher) {
EXPECT_DEATH( EXPECT_DEATH(
parse({"./myExecutable", "/home/user/baseDir", "--cipher", "invalid-cipher", "/home/user/mountDir"}), parse({"./myExecutable", "/home/user/baseDir", "--cipher", "invalid-cipher", "/home/user/mountDir"}),