Fuse offers an onMounted callback that is called when everything is ready.
This commit is contained in:
parent
449133e3da
commit
64150f294e
@ -51,8 +51,8 @@ using std::string;
|
|||||||
using std::endl;
|
using std::endl;
|
||||||
using std::shared_ptr;
|
using std::shared_ptr;
|
||||||
using std::make_shared;
|
using std::make_shared;
|
||||||
|
using std::make_unique;
|
||||||
using std::function;
|
using std::function;
|
||||||
using std::make_shared;
|
|
||||||
using boost::optional;
|
using boost::optional;
|
||||||
using boost::none;
|
using boost::none;
|
||||||
using boost::chrono::minutes;
|
using boost::chrono::minutes;
|
||||||
@ -219,7 +219,7 @@ namespace cryfs {
|
|||||||
cipher, blocksizeBytes, missingBlockIsIntegrityViolation).loadOrCreate(std::move(configFilePath), allowFilesystemUpgrade, allowReplacedFilesystem);
|
cipher, blocksizeBytes, missingBlockIsIntegrityViolation).loadOrCreate(std::move(configFilePath), allowFilesystemUpgrade, allowReplacedFilesystem);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cli::_runFilesystem(const ProgramOptions &options) {
|
void Cli::_runFilesystem(const ProgramOptions &options, std::function<void()> onMounted) {
|
||||||
try {
|
try {
|
||||||
LocalStateDir localStateDir(Environment::localStateDir());
|
LocalStateDir localStateDir(Environment::localStateDir());
|
||||||
auto blockStore = make_unique_ref<OnDiskBlockStore2>(options.baseDir());
|
auto blockStore = make_unique_ref<OnDiskBlockStore2>(options.baseDir());
|
||||||
@ -229,11 +229,11 @@ namespace cryfs {
|
|||||||
options.allowIntegrityViolations(), missingBlockIsIntegrityViolation));
|
options.allowIntegrityViolations(), missingBlockIsIntegrityViolation));
|
||||||
_sanityCheckFilesystem(_device->get());
|
_sanityCheckFilesystem(_device->get());
|
||||||
|
|
||||||
auto initFilesystem = [this, &options] (fspp::fuse::Fuse *fuse){
|
auto initFilesystem = [&] (fspp::fuse::Fuse *fs){
|
||||||
ASSERT(_device != none, "File system not ready to be initialized. Was it already initialized before?");
|
ASSERT(_device != none, "File system not ready to be initialized. Was it already initialized before?");
|
||||||
|
|
||||||
//TODO Test auto unmounting after idle timeout
|
//TODO Test auto unmounting after idle timeout
|
||||||
_idleUnmounter = _createIdleCallback(options.unmountAfterIdleMinutes(), [fuse] {fuse->stop();});
|
_idleUnmounter = _createIdleCallback(options.unmountAfterIdleMinutes(), [fs] {fs->stop();});
|
||||||
if (_idleUnmounter != none) {
|
if (_idleUnmounter != none) {
|
||||||
(*_device)->onFsAction(std::bind(&CallAfterTimeout::resetTimer, _idleUnmounter->get()));
|
(*_device)->onFsAction(std::bind(&CallAfterTimeout::resetTimer, _idleUnmounter->get()));
|
||||||
}
|
}
|
||||||
@ -241,7 +241,7 @@ namespace cryfs {
|
|||||||
return make_shared<fspp::FilesystemImpl>(std::move(*_device));
|
return make_shared<fspp::FilesystemImpl>(std::move(*_device));
|
||||||
};
|
};
|
||||||
|
|
||||||
auto fuse = make_unique_ref<fspp::fuse::Fuse>(initFilesystem, "cryfs", "cryfs@" + options.baseDir().string());
|
auto fuse = make_unique<fspp::fuse::Fuse>(initFilesystem, std::move(onMounted), "cryfs", "cryfs@" + options.baseDir().string());
|
||||||
|
|
||||||
_initLogfile(options);
|
_initLogfile(options);
|
||||||
|
|
||||||
@ -369,14 +369,14 @@ namespace cryfs {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Cli::main(int argc, const char *argv[], unique_ref<HttpClient> httpClient) {
|
int Cli::main(int argc, const char *argv[], unique_ref<HttpClient> httpClient, std::function<void()> onMounted) {
|
||||||
cpputils::showBacktraceOnCrash();
|
cpputils::showBacktraceOnCrash();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
_showVersion(std::move(httpClient));
|
_showVersion(std::move(httpClient));
|
||||||
ProgramOptions options = program_options::Parser(argc, argv).parse(CryCiphers::supportedCipherNames());
|
ProgramOptions options = program_options::Parser(argc, argv).parse(CryCiphers::supportedCipherNames());
|
||||||
_sanityChecks(options);
|
_sanityChecks(options);
|
||||||
_runFilesystem(options);
|
_runFilesystem(options, std::move(onMounted));
|
||||||
} catch (const CryfsException &e) {
|
} catch (const CryfsException &e) {
|
||||||
if (e.errorCode() != ErrorCode::Success) {
|
if (e.errorCode() != ErrorCode::Success) {
|
||||||
std::cerr << "Error: " << e.what() << std::endl;
|
std::cerr << "Error: " << e.what() << std::endl;
|
||||||
|
@ -18,11 +18,11 @@ namespace cryfs {
|
|||||||
class Cli final {
|
class Cli final {
|
||||||
public:
|
public:
|
||||||
Cli(cpputils::RandomGenerator &keyGenerator, const cpputils::SCryptSettings& scryptSettings, std::shared_ptr<cpputils::Console> console);
|
Cli(cpputils::RandomGenerator &keyGenerator, const cpputils::SCryptSettings& scryptSettings, std::shared_ptr<cpputils::Console> console);
|
||||||
int main(int argc, const char *argv[], cpputils::unique_ref<cpputils::HttpClient> httpClient);
|
int main(int argc, const char *argv[], cpputils::unique_ref<cpputils::HttpClient> httpClient, std::function<void()> onMounted);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _checkForUpdates(cpputils::unique_ref<cpputils::HttpClient> httpClient);
|
void _checkForUpdates(cpputils::unique_ref<cpputils::HttpClient> httpClient);
|
||||||
void _runFilesystem(const program_options::ProgramOptions &options);
|
void _runFilesystem(const program_options::ProgramOptions &options, std::function<void()> onMounted);
|
||||||
CryConfigLoader::ConfigLoadResult _loadOrCreateConfig(const program_options::ProgramOptions &options, const LocalStateDir& localStateDir);
|
CryConfigLoader::ConfigLoadResult _loadOrCreateConfig(const program_options::ProgramOptions &options, const LocalStateDir& localStateDir);
|
||||||
void _checkConfigIntegrity(const boost::filesystem::path& basedir, const LocalStateDir& localStateDir, const CryConfigFile& config, bool allowReplacedFilesystem);
|
void _checkConfigIntegrity(const boost::filesystem::path& basedir, const LocalStateDir& localStateDir, const CryConfigFile& config, bool allowReplacedFilesystem);
|
||||||
boost::optional<CryConfigLoader::ConfigLoadResult> _loadOrCreateConfigFile(boost::filesystem::path configFilePath, LocalStateDir localStateDir, const boost::optional<std::string> &cipher, const boost::optional<uint32_t> &blocksizeBytes, bool allowFilesystemUpgrade, const boost::optional<bool> &missingBlockIsIntegrityViolation, bool allowReplacedFilesystem);
|
boost::optional<CryConfigLoader::ConfigLoadResult> _loadOrCreateConfigFile(boost::filesystem::path configFilePath, LocalStateDir localStateDir, const boost::optional<std::string> &cipher, const boost::optional<uint32_t> &blocksizeBytes, bool allowFilesystemUpgrade, const boost::optional<bool> &missingBlockIsIntegrityViolation, bool allowReplacedFilesystem);
|
||||||
|
@ -26,7 +26,7 @@ int main(int argc, const char *argv[]) {
|
|||||||
auto httpClient = make_unique_ref<cpputils::CurlHttpClient>();
|
auto httpClient = make_unique_ref<cpputils::CurlHttpClient>();
|
||||||
#endif
|
#endif
|
||||||
return Cli(keyGenerator, SCrypt::DefaultSettings, make_shared<IOStreamConsole>())
|
return Cli(keyGenerator, SCrypt::DefaultSettings, make_shared<IOStreamConsole>())
|
||||||
.main(argc, argv, std::move(httpClient));
|
.main(argc, argv, std::move(httpClient), []{});
|
||||||
} catch (const CryfsException &e) {
|
} catch (const CryfsException &e) {
|
||||||
if (e.errorCode() != ErrorCode::Success) {
|
if (e.errorCode() != ErrorCode::Success) {
|
||||||
std::cerr << "Error: " << e.what() << std::endl;
|
std::cerr << "Error: " << e.what() << std::endl;
|
||||||
|
@ -229,8 +229,10 @@ Fuse::~Fuse() {
|
|||||||
_argv.clear();
|
_argv.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
Fuse::Fuse(std::function<shared_ptr<Filesystem> (Fuse *fuse)> init, std::string fstype, boost::optional<std::string> fsname)
|
Fuse::Fuse(std::function<shared_ptr<Filesystem> (Fuse *fuse)> init, std::function<void()> onMounted, std::string fstype, boost::optional<std::string> fsname)
|
||||||
:_init(std::move(init)), _fs(make_shared<InvalidFilesystem>()), _mountdir(), _running(false), _fstype(std::move(fstype)), _fsname(std::move(fsname)) {
|
:_init(std::move(init)), _onMounted(std::move(onMounted)), _fs(make_shared<InvalidFilesystem>()), _mountdir(), _running(false), _fstype(std::move(fstype)), _fsname(std::move(fsname)) {
|
||||||
|
ASSERT(static_cast<bool>(_init), "Invalid init given");
|
||||||
|
ASSERT(static_cast<bool>(_onMounted), "Invalid onMounted given");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fuse::_logException(const std::exception &e) {
|
void Fuse::_logException(const std::exception &e) {
|
||||||
@ -866,6 +868,7 @@ void Fuse::init(fuse_conn_info *conn) {
|
|||||||
LOG(INFO, "Filesystem started.");
|
LOG(INFO, "Filesystem started.");
|
||||||
|
|
||||||
_running = true;
|
_running = true;
|
||||||
|
_onMounted();
|
||||||
|
|
||||||
#ifdef FSPP_LOG
|
#ifdef FSPP_LOG
|
||||||
cpputils::logging::setLevel(DEBUG);
|
cpputils::logging::setLevel(DEBUG);
|
||||||
|
@ -21,7 +21,7 @@ class Filesystem;
|
|||||||
|
|
||||||
class Fuse final {
|
class Fuse final {
|
||||||
public:
|
public:
|
||||||
explicit Fuse(std::function<std::shared_ptr<Filesystem> (Fuse *fuse)> init, std::string fstype, boost::optional<std::string> fsname);
|
explicit Fuse(std::function<std::shared_ptr<Filesystem> (Fuse *fuse)> init, std::function<void()> onMounted, std::string fstype, boost::optional<std::string> fsname);
|
||||||
~Fuse();
|
~Fuse();
|
||||||
|
|
||||||
void run(const boost::filesystem::path &mountdir, const std::vector<std::string> &fuseOptions);
|
void run(const boost::filesystem::path &mountdir, const std::vector<std::string> &fuseOptions);
|
||||||
@ -69,6 +69,7 @@ private:
|
|||||||
void _add_fuse_option_if_not_exists(std::vector<char *> *argv, const std::string &key, const std::string &value);
|
void _add_fuse_option_if_not_exists(std::vector<char *> *argv, const std::string &key, const std::string &value);
|
||||||
|
|
||||||
std::function<std::shared_ptr<Filesystem> (Fuse *fuse)> _init;
|
std::function<std::shared_ptr<Filesystem> (Fuse *fuse)> _init;
|
||||||
|
std::function<void()> _onMounted;
|
||||||
std::shared_ptr<Filesystem> _fs;
|
std::shared_ptr<Filesystem> _fs;
|
||||||
boost::filesystem::path _mountdir;
|
boost::filesystem::path _mountdir;
|
||||||
std::vector<char*> _argv;
|
std::vector<char*> _argv;
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include <cpp-utils/logging/logging.h>
|
#include <cpp-utils/logging/logging.h>
|
||||||
#include <cpp-utils/process/subprocess.h>
|
#include <cpp-utils/process/subprocess.h>
|
||||||
#include <cpp-utils/network/FakeHttpClient.h>
|
#include <cpp-utils/network/FakeHttpClient.h>
|
||||||
|
#include <cpp-utils/lock/ConditionBarrier.h>
|
||||||
#include "../../cryfs/testutils/MockConsole.h"
|
#include "../../cryfs/testutils/MockConsole.h"
|
||||||
#include "../../cryfs/testutils/TestWithFakeHomeDirectory.h"
|
#include "../../cryfs/testutils/TestWithFakeHomeDirectory.h"
|
||||||
#include <cryfs/ErrorCodes.h>
|
#include <cryfs/ErrorCodes.h>
|
||||||
@ -41,7 +42,7 @@ public:
|
|||||||
return std::move(httpClient);
|
return std::move(httpClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
int run(const std::vector<std::string>& args) {
|
int run(const std::vector<std::string>& args, std::function<void()> onMounted) {
|
||||||
std::vector<const char*> _args;
|
std::vector<const char*> _args;
|
||||||
_args.reserve(args.size() + 1);
|
_args.reserve(args.size() + 1);
|
||||||
_args.emplace_back("cryfs");
|
_args.emplace_back("cryfs");
|
||||||
@ -52,7 +53,7 @@ public:
|
|||||||
ON_CALL(*console, askPassword(testing::StrEq("Password: "))).WillByDefault(testing::Return("pass"));
|
ON_CALL(*console, askPassword(testing::StrEq("Password: "))).WillByDefault(testing::Return("pass"));
|
||||||
ON_CALL(*console, askPassword(testing::StrEq("Confirm Password: "))).WillByDefault(testing::Return("pass"));
|
ON_CALL(*console, askPassword(testing::StrEq("Confirm Password: "))).WillByDefault(testing::Return("pass"));
|
||||||
// Run Cryfs
|
// Run Cryfs
|
||||||
return cryfs::Cli(keyGenerator, cpputils::SCrypt::TestSettings, console).main(_args.size(), _args.data(), _httpClient());
|
return cryfs::Cli(keyGenerator, cpputils::SCrypt::TestSettings, console).main(_args.size(), _args.data(), _httpClient(), std::move(onMounted));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EXPECT_EXIT_WITH_HELP_MESSAGE(const std::vector<std::string>& args, const std::string &message, cryfs::ErrorCode errorCode) {
|
void EXPECT_EXIT_WITH_HELP_MESSAGE(const std::vector<std::string>& args, const std::string &message, cryfs::ErrorCode errorCode) {
|
||||||
@ -60,7 +61,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EXPECT_RUN_ERROR(const std::vector<std::string>& args, const std::string& message, cryfs::ErrorCode errorCode) {
|
void EXPECT_RUN_ERROR(const std::vector<std::string>& args, const std::string& message, cryfs::ErrorCode errorCode) {
|
||||||
FilesystemOutput filesystem_output = _run_filesystem(args, boost::none);
|
FilesystemOutput filesystem_output = _run_filesystem(args, boost::none, []{});
|
||||||
|
|
||||||
EXPECT_EQ(exitCode(errorCode), filesystem_output.exit_code);
|
EXPECT_EQ(exitCode(errorCode), filesystem_output.exit_code);
|
||||||
EXPECT_TRUE(std::regex_search(filesystem_output.stderr_, std::regex(message)));
|
EXPECT_TRUE(std::regex_search(filesystem_output.stderr_, std::regex(message)));
|
||||||
@ -70,7 +71,7 @@ public:
|
|||||||
//TODO Make this work when run in background
|
//TODO Make this work when run in background
|
||||||
ASSERT(std::find(args.begin(), args.end(), string("-f")) != args.end(), "Currently only works if run in foreground");
|
ASSERT(std::find(args.begin(), args.end(), string("-f")) != args.end(), "Currently only works if run in foreground");
|
||||||
|
|
||||||
FilesystemOutput filesystem_output = _run_filesystem(args, mountDir);
|
FilesystemOutput filesystem_output = _run_filesystem(args, mountDir, []{});
|
||||||
|
|
||||||
EXPECT_EQ(0, filesystem_output.exit_code);
|
EXPECT_EQ(0, filesystem_output.exit_code);
|
||||||
EXPECT_TRUE(std::regex_search(filesystem_output.stdout_, std::regex("Mounting filesystem")));
|
EXPECT_TRUE(std::regex_search(filesystem_output.stdout_, std::regex("Mounting filesystem")));
|
||||||
@ -82,44 +83,64 @@ public:
|
|||||||
std::string stderr_;
|
std::string stderr_;
|
||||||
};
|
};
|
||||||
|
|
||||||
FilesystemOutput _run_filesystem(const std::vector<std::string>& args, const boost::optional<boost::filesystem::path>& mountDirForUnmounting) {
|
static void _unmount(const boost::filesystem::path &mountDir) {
|
||||||
testing::internal::CaptureStdout();
|
|
||||||
testing::internal::CaptureStderr();
|
|
||||||
std::future<int> exit_code = std::async(std::launch::async, [this, &args] {
|
|
||||||
return run(args);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (mountDirForUnmounting.is_initialized()) {
|
|
||||||
boost::filesystem::path mountDir = *mountDirForUnmounting;
|
|
||||||
std::future<bool> unmount_success = std::async(std::launch::async, [&mountDir] {
|
|
||||||
int returncode = -1;
|
int returncode = -1;
|
||||||
while (returncode != 0) {
|
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
returncode = cpputils::Subprocess::call(std::string("umount ") + mountDir.string().c_str() + " 2>/dev/null").exitcode;
|
returncode = cpputils::Subprocess::call(std::string("umount ") + mountDir.string().c_str() + " 2>/dev/null").exitcode;
|
||||||
#elif defined(_MSC_VER)
|
#elif defined(_MSC_VER)
|
||||||
// Somehow this sleeping is needed to not deadlock. Race condition in mounting/unmounting?
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
||||||
std::wstring mountDir_ = std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().from_bytes(mountDir.string());
|
std::wstring mountDir_ = std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().from_bytes(mountDir.string());
|
||||||
BOOL success = DokanRemoveMountPoint(mountDir_.c_str());
|
BOOL success = DokanRemoveMountPoint(mountDir_.c_str());
|
||||||
returncode = success ? 0 : -1;
|
returncode = success ? 0 : -1;
|
||||||
#else
|
#else
|
||||||
returncode = cpputils::Subprocess::call(std::string("fusermount -u ") + mountDir.string().c_str() + " 2>/dev/null").exitcode;
|
returncode = cpputils::Subprocess::call(
|
||||||
|
std::string("fusermount -u ") + mountDir.string().c_str() + " 2>/dev/null").exitcode;
|
||||||
#endif
|
#endif
|
||||||
|
ASSERT(returncode == 0, "Unmount failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
FilesystemOutput _run_filesystem(const std::vector<std::string>& args, boost::optional<boost::filesystem::path> mountDirForUnmounting, std::function<void()> onMounted) {
|
||||||
|
testing::internal::CaptureStdout();
|
||||||
|
testing::internal::CaptureStderr();
|
||||||
|
|
||||||
|
bool exited = false;
|
||||||
|
cpputils::ConditionBarrier isMountedOrFailedBarrier;
|
||||||
|
|
||||||
|
std::future<int> exit_code = std::async(std::launch::async, [&] {
|
||||||
|
int exit_code = run(args, [&] { isMountedOrFailedBarrier.release(); });
|
||||||
|
// just in case it fails, we also want to release the barrier.
|
||||||
|
// if it succeeds, this will release it a second time, which doesn't hurt.
|
||||||
|
exited = true;
|
||||||
|
isMountedOrFailedBarrier.release();
|
||||||
|
return exit_code;
|
||||||
|
});
|
||||||
|
|
||||||
|
std::future<bool> on_mounted_success = std::async(std::launch::async, [&] {
|
||||||
|
isMountedOrFailedBarrier.wait();
|
||||||
|
if (exited) {
|
||||||
|
// file system already exited on its own, this indicates an error. It should have stayed mounted.
|
||||||
|
// while the exit_code from run() will signal an error in this case, we didn't encounter another
|
||||||
|
// error in the onMounted future, so return true here.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// now we know the filesystem stayed online, so we can call the onMounted callback
|
||||||
|
onMounted();
|
||||||
|
// and unmount it afterwards
|
||||||
|
if (mountDirForUnmounting.is_initialized()) {
|
||||||
|
_unmount(*mountDirForUnmounting);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
if(std::future_status::ready != unmount_success.wait_for(std::chrono::seconds(10))) {
|
if(std::future_status::ready != on_mounted_success.wait_for(std::chrono::seconds(10))) {
|
||||||
testing::internal::GetCapturedStdout(); // stop capturing stdout
|
testing::internal::GetCapturedStdout(); // stop capturing stdout
|
||||||
testing::internal::GetCapturedStderr(); // stop capturing stderr
|
testing::internal::GetCapturedStderr(); // stop capturing stderr
|
||||||
|
|
||||||
std::cerr << "Unmount thread didn't finish";
|
std::cerr << "onMounted thread (e.g. used for unmount) didn't finish";
|
||||||
// The std::future destructor of a future created with std::async blocks until the future is ready.
|
// The std::future destructor of a future created with std::async blocks until the future is ready.
|
||||||
// so, instead of causing a deadlock, rather abort
|
// so, instead of causing a deadlock, rather abort
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(unmount_success.get()); // this also re-throws any potential exceptions
|
EXPECT_TRUE(on_mounted_success.get()); // this also re-throws any potential exceptions
|
||||||
}
|
|
||||||
|
|
||||||
if(std::future_status::ready != exit_code.wait_for(std::chrono::seconds(10))) {
|
if(std::future_status::ready != exit_code.wait_for(std::chrono::seconds(10))) {
|
||||||
testing::internal::GetCapturedStdout(); // stop capturing stdout
|
testing::internal::GetCapturedStdout(); // stop capturing stdout
|
||||||
|
@ -57,7 +57,7 @@ unique_ref<FuseTest::TempTestFS> FuseTest::TestFS() {
|
|||||||
|
|
||||||
FuseTest::TempTestFS::TempTestFS(shared_ptr<MockFilesystem> fsimpl)
|
FuseTest::TempTestFS::TempTestFS(shared_ptr<MockFilesystem> fsimpl)
|
||||||
:_mountDir(),
|
:_mountDir(),
|
||||||
_fuse([fsimpl] (Fuse*) {return fsimpl;}, "fusetest", boost::none), _fuse_thread(&_fuse) {
|
_fuse([fsimpl] (Fuse*) {return fsimpl;}, []{}, "fusetest", boost::none), _fuse_thread(&_fuse) {
|
||||||
|
|
||||||
_fuse_thread.start(_mountDir.path(), {"-f"});
|
_fuse_thread.start(_mountDir.path(), {"-f"});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user