--unmount-idle x automatically unmounts the filesystem after x minutes without a filesystem operation.
This commit is contained in:
parent
4dab8c7426
commit
978e7dbc46
@ -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.
|
||||
* --show-ciphers shows a list of all supported ciphers
|
||||
* --extpass allows using an external program for password input
|
||||
* --unmount-idle x automatically unmounts the filesystem after x minutes without a filesystem operation.
|
||||
|
@ -21,6 +21,8 @@ namespace cryfs {
|
||||
boost::chrono::time_point<boost::chrono::steady_clock> _start;
|
||||
cpputils::LoopThread _checkTimeoutThread;
|
||||
std::mutex _mutex;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CallAfterTimeout);
|
||||
};
|
||||
|
||||
inline CallAfterTimeout::CallAfterTimeout(boost::chrono::milliseconds timeout, std::function<void()> callback)
|
||||
|
@ -48,7 +48,15 @@ using std::endl;
|
||||
using std::vector;
|
||||
using std::shared_ptr;
|
||||
using std::make_shared;
|
||||
using std::unique_ptr;
|
||||
using std::make_unique;
|
||||
using std::function;
|
||||
using boost::optional;
|
||||
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 Improve parallelity.
|
||||
@ -148,6 +156,13 @@ namespace cryfs {
|
||||
|
||||
_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;
|
||||
|
||||
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) {
|
||||
spdlog::drop("cryfs");
|
||||
//TODO Test that --logfile parameter works. Should be: file if specified, otherwise stderr if foreground, else syslog.
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <messmer/cpp-utils/tempfile/TempFile.h>
|
||||
#include <messmer/cpp-utils/random/RandomGenerator.h>
|
||||
#include "CallAfterTimeout.h"
|
||||
|
||||
namespace cryfs {
|
||||
class Cli final {
|
||||
@ -29,6 +30,7 @@ namespace cryfs {
|
||||
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);
|
||||
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::SCryptSettings _scryptSettings;
|
||||
|
@ -39,9 +39,9 @@ ProgramOptions Parser::parse(const vector<string> &supportedCiphers) const {
|
||||
if (foreground) {
|
||||
options.second.push_back(const_cast<char*>("-f"));
|
||||
}
|
||||
optional<unsigned int> unmountAfterIdleMinutes = none;
|
||||
optional<double> unmountAfterIdleMinutes = none;
|
||||
if (vm.count("unmount-idle")) {
|
||||
unmountAfterIdleMinutes = vm["unmount-idle"].as<unsigned int>();
|
||||
unmountAfterIdleMinutes = vm["unmount-idle"].as<double>();
|
||||
}
|
||||
optional<string> logfile = none;
|
||||
if (vm.count("logfile")) {
|
||||
@ -104,7 +104,7 @@ void Parser::_addAllowedOptions(po::options_description *desc) {
|
||||
("foreground,f", "Run CryFS in foreground.")
|
||||
("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.")
|
||||
("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")
|
||||
("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.")
|
||||
;
|
||||
|
@ -8,7 +8,7 @@ using std::vector;
|
||||
using boost::optional;
|
||||
|
||||
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> &extPass, const vector<char*> &fuseOptions)
|
||||
:_baseDir(baseDir), _mountDir(new char[mountDir.size()+1]), _configFile(configFile), _foreground(foreground),
|
||||
@ -50,7 +50,7 @@ bool ProgramOptions::foreground() const {
|
||||
return _foreground;
|
||||
}
|
||||
|
||||
const optional<unsigned int> &ProgramOptions::unmountAfterIdleMinutes() const {
|
||||
const optional<double> &ProgramOptions::unmountAfterIdleMinutes() const {
|
||||
return _unmountAfterIdleMinutes;
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ namespace cryfs {
|
||||
class ProgramOptions final {
|
||||
public:
|
||||
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> &extPass, const std::vector<char *> &fuseOptions);
|
||||
ProgramOptions(ProgramOptions &&rhs);
|
||||
@ -23,7 +23,7 @@ namespace cryfs {
|
||||
const boost::optional<std::string> &configFile() const;
|
||||
bool foreground() 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> &extPass() const;
|
||||
const std::vector<char *> &fuseOptions() const;
|
||||
@ -34,7 +34,7 @@ namespace cryfs {
|
||||
boost::optional<std::string> _configFile;
|
||||
bool _foreground;
|
||||
boost::optional<std::string> _cipher;
|
||||
boost::optional<unsigned int> _unmountAfterIdleMinutes;
|
||||
boost::optional<double> _unmountAfterIdleMinutes;
|
||||
boost::optional<std::string> _logFile;
|
||||
boost::optional<std::string> _extPass;
|
||||
std::vector<char *> _fuseOptions;
|
||||
|
@ -57,7 +57,8 @@ CryDevice::CryDevice(CryConfigFile configFile, unique_ref<BlockStore> blockStore
|
||||
), BLOCKSIZE_BYTES)))
|
||||
)
|
||||
),
|
||||
_rootKey(GetOrCreateRootKey(&configFile)) {
|
||||
_rootKey(GetOrCreateRootKey(&configFile)),
|
||||
_onFsAction() {
|
||||
}
|
||||
|
||||
Key CryDevice::CreateRootBlobAndReturnKey() {
|
||||
@ -69,6 +70,8 @@ Key CryDevice::CreateRootBlobAndReturnKey() {
|
||||
optional<unique_ref<fspp::Node>> CryDevice::Load(const bf::path &path) {
|
||||
ASSERT(path.is_absolute(), "Non absolute path given");
|
||||
|
||||
callFsActionCallbacks();
|
||||
|
||||
if (path.parent_path().empty()) {
|
||||
//We are asked to load the root directory '/'.
|
||||
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) {
|
||||
callFsActionCallbacks();
|
||||
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());
|
||||
}
|
||||
|
||||
void CryDevice::onFsAction(std::function<void()> callback) {
|
||||
_onFsAction.push_back(callback);
|
||||
}
|
||||
|
||||
void CryDevice::callFsActionCallbacks() const {
|
||||
for (const auto &callback : _onFsAction) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -31,14 +31,18 @@ public:
|
||||
cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef> LoadDirBlob(const boost::filesystem::path &path);
|
||||
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;
|
||||
|
||||
void callFsActionCallbacks() const;
|
||||
|
||||
private:
|
||||
|
||||
cpputils::unique_ref<parallelaccessfsblobstore::ParallelAccessFsBlobStore> _fsBlobStore;
|
||||
|
||||
blockstore::Key _rootKey;
|
||||
std::vector<std::function<void()>> _onFsAction;
|
||||
|
||||
blockstore::Key GetOrCreateRootKey(CryConfigFile *config);
|
||||
blockstore::Key CreateRootBlobAndReturnKey();
|
||||
|
@ -37,12 +37,14 @@ CryDir::~CryDir() {
|
||||
}
|
||||
|
||||
unique_ref<fspp::OpenFile> CryDir::createAndOpenFile(const string &name, mode_t mode, uid_t uid, gid_t gid) {
|
||||
device()->callFsActionCallbacks();
|
||||
auto child = device()->CreateFileBlob();
|
||||
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) {
|
||||
device()->callFsActionCallbacks();
|
||||
auto blob = LoadBlob();
|
||||
auto child = device()->CreateDirBlob();
|
||||
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 {
|
||||
device()->callFsActionCallbacks();
|
||||
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, ".."));
|
||||
@ -65,10 +68,12 @@ unique_ref<vector<fspp::Dir::Entry>> CryDir::children() const {
|
||||
}
|
||||
|
||||
fspp::Dir::EntryType CryDir::getType() const {
|
||||
device()->callFsActionCallbacks();
|
||||
return fspp::Dir::EntryType::DIR;
|
||||
}
|
||||
|
||||
void CryDir::createSymlink(const string &name, const bf::path &target, uid_t uid, gid_t gid) {
|
||||
device()->callFsActionCallbacks();
|
||||
auto blob = LoadBlob();
|
||||
auto child = device()->CreateSymlinkBlob(target);
|
||||
blob->AddChildSymlink(name, child->key(), uid, gid);
|
||||
|
@ -35,16 +35,19 @@ unique_ref<parallelaccessfsblobstore::FileBlobRef> CryFile::LoadBlob() const {
|
||||
}
|
||||
|
||||
unique_ref<fspp::OpenFile> CryFile::open(int flags) const {
|
||||
device()->callFsActionCallbacks();
|
||||
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 {
|
||||
device()->callFsActionCallbacks();
|
||||
auto blob = LoadBlob();
|
||||
blob->resize(size);
|
||||
}
|
||||
|
||||
fspp::Dir::EntryType CryFile::getType() const {
|
||||
device()->callFsActionCallbacks();
|
||||
return fspp::Dir::EntryType::FILE;
|
||||
}
|
||||
|
||||
|
@ -35,12 +35,14 @@ CryNode::~CryNode() {
|
||||
}
|
||||
|
||||
void CryNode::access(int mask) const {
|
||||
device()->callFsActionCallbacks();
|
||||
//TODO
|
||||
return;
|
||||
throw FuseErrnoException(ENOTSUP);
|
||||
}
|
||||
|
||||
void CryNode::rename(const bf::path &to) {
|
||||
device()->callFsActionCallbacks();
|
||||
if (_parent == none) {
|
||||
//We are the root direcory.
|
||||
//TODO What should we do?
|
||||
@ -59,11 +61,13 @@ void CryNode::rename(const bf::path &to) {
|
||||
}
|
||||
|
||||
void CryNode::utimens(const timespec times[2]) {
|
||||
device()->callFsActionCallbacks();
|
||||
//TODO
|
||||
throw FuseErrnoException(ENOTSUP);
|
||||
}
|
||||
|
||||
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.
|
||||
if (_parent == none) {
|
||||
//We are the root direcory.
|
||||
@ -87,6 +91,7 @@ unique_ref<FsBlobRef> CryNode::LoadBlob() const {
|
||||
}
|
||||
|
||||
void CryNode::stat(struct ::stat *result) const {
|
||||
device()->callFsActionCallbacks();
|
||||
if(_parent == none) {
|
||||
//We are the root directory.
|
||||
//TODO What should we do?
|
||||
@ -97,6 +102,7 @@ void CryNode::stat(struct ::stat *result) const {
|
||||
}
|
||||
|
||||
void CryNode::chmod(mode_t mode) {
|
||||
device()->callFsActionCallbacks();
|
||||
if (_parent == none) {
|
||||
//We are the root direcory.
|
||||
//TODO What should we do?
|
||||
@ -106,6 +112,7 @@ void CryNode::chmod(mode_t mode) {
|
||||
}
|
||||
|
||||
void CryNode::chown(uid_t uid, gid_t gid) {
|
||||
device()->callFsActionCallbacks();
|
||||
if (_parent == none) {
|
||||
//We are the root direcory.
|
||||
//TODO What should we do?
|
||||
|
@ -17,8 +17,8 @@ using fspp::fuse::FuseErrnoException;
|
||||
|
||||
namespace cryfs {
|
||||
|
||||
CryOpenFile::CryOpenFile(unique_ref<FileBlobRef> fileBlob)
|
||||
: _fileBlob(std::move(fileBlob)) {
|
||||
CryOpenFile::CryOpenFile(const CryDevice *device, unique_ref<FileBlobRef> fileBlob)
|
||||
: _device(device), _fileBlob(std::move(fileBlob)) {
|
||||
}
|
||||
|
||||
CryOpenFile::~CryOpenFile() {
|
||||
@ -26,32 +26,39 @@ CryOpenFile::~CryOpenFile() {
|
||||
}
|
||||
|
||||
void CryOpenFile::flush() {
|
||||
_device->callFsActionCallbacks();
|
||||
_fileBlob->flush();
|
||||
}
|
||||
|
||||
void CryOpenFile::stat(struct ::stat *result) const {
|
||||
_device->callFsActionCallbacks();
|
||||
result->st_mode = S_IFREG | S_IRUSR | S_IXUSR | S_IWUSR;
|
||||
result->st_size = _fileBlob->size();
|
||||
return;
|
||||
}
|
||||
|
||||
void CryOpenFile::truncate(off_t size) const {
|
||||
_device->callFsActionCallbacks();
|
||||
_fileBlob->resize(size);
|
||||
}
|
||||
|
||||
ssize_t CryOpenFile::read(void *buf, size_t count, off_t offset) const {
|
||||
_device->callFsActionCallbacks();
|
||||
return _fileBlob->read(buf, offset, count);
|
||||
}
|
||||
|
||||
void CryOpenFile::write(const void *buf, size_t count, off_t offset) {
|
||||
_device->callFsActionCallbacks();
|
||||
_fileBlob->write(buf, offset, count);
|
||||
}
|
||||
|
||||
void CryOpenFile::fsync() {
|
||||
_device->callFsActionCallbacks();
|
||||
//throw FuseErrnoException(ENOTSUP);
|
||||
}
|
||||
|
||||
void CryOpenFile::fdatasync() {
|
||||
_device->callFsActionCallbacks();
|
||||
//throw FuseErrnoException(ENOTSUP);
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ class CryDevice;
|
||||
|
||||
class CryOpenFile: public fspp::OpenFile {
|
||||
public:
|
||||
explicit CryOpenFile(cpputils::unique_ref<parallelaccessfsblobstore::FileBlobRef> fileBlob);
|
||||
explicit CryOpenFile(const CryDevice *device, cpputils::unique_ref<parallelaccessfsblobstore::FileBlobRef> fileBlob);
|
||||
virtual ~CryOpenFile();
|
||||
|
||||
void stat(struct ::stat *result) const override;
|
||||
@ -22,6 +22,7 @@ public:
|
||||
void fdatasync() override;
|
||||
|
||||
private:
|
||||
const CryDevice *_device;
|
||||
cpputils::unique_ref<parallelaccessfsblobstore::FileBlobRef> _fileBlob;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CryOpenFile);
|
||||
|
@ -40,10 +40,12 @@ unique_ref<SymlinkBlobRef> CrySymlink::LoadBlob() const {
|
||||
}
|
||||
|
||||
fspp::Dir::EntryType CrySymlink::getType() const {
|
||||
device()->callFsActionCallbacks();
|
||||
return fspp::Dir::EntryType::SYMLINK;
|
||||
}
|
||||
|
||||
bf::path CrySymlink::target() const {
|
||||
device()->callFsActionCallbacks();
|
||||
auto blob = LoadBlob();
|
||||
return blob->target();
|
||||
}
|
||||
|
@ -81,6 +81,11 @@ TEST_F(ProgramOptionsParserTest, UnmountAfterIdleMinutesGiven) {
|
||||
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) {
|
||||
EXPECT_DEATH(
|
||||
parse({"./myExecutable", "/home/user/baseDir", "--cipher", "invalid-cipher", "/home/user/mountDir"}),
|
||||
|
Loading…
Reference in New Issue
Block a user