Commit 4df51ca3 by Paweł Wegner

fuse: support fuse2.

parent 7e099ab4
......@@ -2,10 +2,15 @@ ACLOCAL_AMFLAGS = -I m4
if WITH_CURL
if WITH_MICROHTTPD
if WITH_FUSE
if WITH_FUSE
SUBDIRS = fuse
endif
if WITH_LEGACY_FUSE
SUBDIRS = fuse
endif
endif
endif
#include "FuseCommon.h"
#include "ICloudStorage.h"
#include "Utility/CurlHttp.h"
#include "Utility/MicroHttpdServer.h"
#include "Utility/Utility.h"
#ifdef WITH_FUSE
#include "FuseLowLevel.h"
#endif
#ifdef WITH_LEGACY_FUSE
#include "FuseHighLevel.h"
#endif
#include <cstring>
#include <fstream>
#include <sstream>
namespace cloudstorage {
namespace {
#define OPTION(t, p) \
{ t, offsetof(struct options, p), 1 }
template <class T>
using pointer = std::unique_ptr<T, std::function<void(T *)>>;
struct options {
~options() {
free(config_file);
free(provider_label);
}
char *config_file;
char *provider_label;
};
const struct fuse_opt option_spec[] = {OPTION("--config=%s", config_file),
OPTION("--add=%s", provider_label),
FUSE_OPT_END};
} // namespace
struct FUSE_STAT item_to_stat(IFileSystem::INode::Pointer i) {
struct FUSE_STAT ret = {};
#ifndef _WIN32
if (i->timestamp() != IItem::UnknownTimeStamp)
ret.st_mtime = std::chrono::system_clock::to_time_t(i->timestamp());
#endif
if (i->size() != IItem::UnknownSize)
ret.st_size = i->size();
else if (i->type() != IItem::FileType::Directory)
ret.st_size = 1LL << 32;
ret.st_mode =
(i->type() == IItem::FileType::Directory ? S_IFDIR : S_IFREG) | 0644;
ret.st_ino = i->inode();
return ret;
}
IHttpRequest::Pointer HttpWrapper::create(const std::string &url,
const std::string &method,
bool follow_redirect) const {
return http_->create(url, method, follow_redirect);
}
IHttpServer::IResponse::Pointer HttpServerCallback::handle(
const IHttpServer::IRequest &request) {
auto code = request.get("code");
auto state = request.get("state");
if (code && state) {
promise_.set_value({code, state});
return util::response_from_string(request, IHttpRequest::Ok, {},
"token received");
} else if (request.url() == "/login" && state) {
return util::response_from_string(request, IHttpRequest::Ok, {},
util::login_page(state));
} else {
return util::response_from_string(request, IHttpRequest::Bad, {}, "error");
}
}
ICloudProvider::Pointer create(std::shared_ptr<IHttp> http,
std::string temporary_directory,
Json::Value config) {
class AuthCallback : public ICloudProvider::IAuthCallback {
Status userConsentRequired(const ICloudProvider &) override {
return Status::None;
}
void done(const ICloudProvider &, EitherError<void>) override {}
};
ICloudProvider::InitData init_data;
init_data.callback_ = util::make_unique<AuthCallback>();
init_data.token_ = config["token"].asString();
init_data.http_engine_ = util::make_unique<HttpWrapper>(http);
init_data.hints_["access_token"] = config["access_token"].asString();
init_data.hints_["temporary_directory"] = temporary_directory;
return ICloudStorage::create()->provider(config["type"].asString(),
std::move(init_data));
}
std::vector<IFileSystem::ProviderEntry> providers(
const Json::Value &data, std::shared_ptr<IHttp> http,
const std::string &temporary_directory) {
std::vector<IFileSystem::ProviderEntry> providers;
for (auto &&p : data)
providers.push_back(
{p["label"].asString(), create(http, temporary_directory, p)});
return std::move(providers);
}
std::string default_temporary_directory() { return "/tmp/"; }
std::string default_home_directory() { return getenv("HOME"); }
template <class Backend>
int fuse_run(fuse_args *args, fuse_cmdline_opts *opts, Json::Value &json) {
auto ctx = new IFileSystem *;
Backend fuse(args, opts->mountpoint, ctx);
fuse_daemonize(opts->foreground);
auto http = std::make_shared<curl::CurlHttp>();
auto temporary_directory = json["temporary_directory"].asString();
if (temporary_directory.empty())
temporary_directory = default_temporary_directory();
auto p = providers(json["providers"], http, temporary_directory);
*ctx = IFileSystem::create(p, util::make_unique<HttpWrapper>(http),
temporary_directory)
.release();
int ret = fuse.run(opts->singlethread, opts->clone_fd);
for (size_t i = 0; i < p.size(); i++) {
json["providers"][int(i)]["token"] = p[i].provider_->token();
json["providers"][int(i)]["access_token"] =
p[i].provider_->hints()["access_token"];
}
delete *ctx;
delete ctx;
return ret;
}
int fuse_run(int argc, char **argv) {
pointer<fuse_args> args(new fuse_args(FUSE_ARGS_INIT(argc, argv)),
[](fuse_args *e) {
fuse_opt_free_args(e);
delete e;
});
struct options options {};
if (fuse_opt_parse(args.get(), &options, option_spec, nullptr) == -1)
return 1;
if (!options.config_file)
options.config_file = strdup(
(cloudstorage::default_home_directory() + "/.libcloudstorage-fuse.json")
.c_str());
pointer<fuse_cmdline_opts> opts(new fuse_cmdline_opts{},
[](fuse_cmdline_opts *opts) {
free(opts->mountpoint);
delete opts;
});
if (fuse_parse_cmdline(args.get(), opts.get()) != 0) return 1;
if (opts->show_help) {
std::cerr << util::libcloudstorage_ascii_art() << "\n\n";
std::cerr << " --add=provider_label add cloud provider with label\n";
std::cerr << " --config=config_path path to configuration file\n";
std::cerr << " (default: "
"~/.libcloudstorage-fuse.json)\n";
std::cerr << "\n";
fuse_cmdline_help();
#ifdef WITH_FUSE
fuse_lowlevel_help();
#endif
return 0;
} else if (opts->show_version) {
#ifdef WITH_FUSE
fuse_lowlevel_version();
#endif
return 0;
}
std::stringstream stream;
stream << std::ifstream(options.config_file).rdbuf();
Json::Value json;
Json::Reader().parse(stream.str(), json);
if (options.provider_label) {
std::cerr << cloudstorage::util::libcloudstorage_ascii_art() << "\n";
auto storage = ICloudStorage::create();
for (auto &&p : storage->providers()) {
ICloudProvider::InitData init_data;
init_data.hints_["state"] = p;
std::cerr << "\n";
std::cerr
<< p << ": "
<< storage->provider(p, std::move(init_data))->authorizeLibraryUrl()
<< "\n";
}
std::promise<cloudstorage::HttpServerData> result;
auto server = cloudstorage::MicroHttpdServerFactory().create(
std::make_shared<cloudstorage::HttpServerCallback>(result), "",
IHttpServer::Type::Authorization);
auto key = result.get_future().get();
auto provider = storage->provider(key.state_, {});
if (!provider) return 1;
auto ret = provider->exchangeCodeAsync(key.code_)->result();
if (auto token = ret.right()) {
Json::Value p;
p["label"] = options.provider_label;
p["type"] = provider->name();
p["token"] = token->token_;
p["access_token"] = token->access_token_;
json["providers"].append(p);
std::ofstream(options.config_file) << json;
} else
std::cerr << "error " << ret.left()->code_ << ": "
<< ret.left()->description_ << "\n";
return 0;
}
int ret = 0;
#ifdef WITH_FUSE
ret = fuse_run<FuseLowLevel>(args.get(), opts.get(), json);
#endif
#ifdef WITH_LEGACY_FUSE
ret = fuse_run<FuseHighLevel>(args.get(), opts.get(), json);
#endif
std::ofstream(options.config_file) << json;
return ret;
}
} // namespace cloudstorage
#ifndef FUSE_COMMON_H
#define FUSE_COMMON_H
#ifdef WITH_FUSE
#define FUSE_USE_VERSION 31
#include <fuse_lowlevel.h>
#elif WITH_LEGACY_FUSE
#define FUSE_USE_VERSION 27
#include <fuse.h>
#endif
#ifndef FUSE_STAT
#define FUSE_STAT stat
#endif
#include <json/json.h>
#include <future>
#include "IFileSystem.h"
namespace cloudstorage {
class HttpWrapper : public IHttp {
public:
HttpWrapper(std::shared_ptr<IHttp> http) : http_(http) {}
IHttpRequest::Pointer create(const std::string &url,
const std::string &method,
bool follow_redirect) const override;
private:
std::shared_ptr<IHttp> http_;
};
struct HttpServerData {
std::string code_;
std::string state_;
};
class HttpServerCallback : public IHttpServer::ICallback {
public:
HttpServerCallback(std::promise<HttpServerData> &p) : promise_(p) {}
IHttpServer::IResponse::Pointer handle(
const IHttpServer::IRequest &request) override;
private:
std::promise<HttpServerData> &promise_;
};
struct FUSE_STAT item_to_stat(IFileSystem::INode::Pointer i);
cloudstorage::ICloudProvider::Pointer create(std::shared_ptr<IHttp> http,
std::string temporary_directory,
Json::Value config);
std::vector<cloudstorage::IFileSystem::ProviderEntry> providers(
const Json::Value &data, std::shared_ptr<IHttp> http,
const std::string &temporary_directory);
std::string default_temporary_directory();
std::string default_home_directory();
int fuse_run(int argc, char **argv);
} // namespace cloudstorage
#endif // FUSE_COMMON_H
#ifndef FUSE_HIGH_LEVEL_H
#define FUSE_HIGH_LEVEL_H
#define FUSE_USE_VERSION 31
#define FUSE_USE_VERSION 27
#include <fuse.h>
#include "IFileSystem.h"
#include <json/json.h>
namespace cloudstorage {
struct stat item_to_stat(IFileSystem::INode::Pointer i);
struct fuse_cmdline_opts {
int clone_fd;
int singlethread;
int foreground;
int show_help;
int show_version;
char *mountpoint;
};
struct FuseHighLevel {
FuseHighLevel(fuse_args *args, const char *mountpoint, void *userdata);
~FuseHighLevel();
int run(bool singlethread, bool) const;
fuse *fuse_;
fuse_chan *channel_;
fuse_session *session_;
std::string mountpoint_;
};
int fuse_parse_cmdline(struct fuse_args *, fuse_cmdline_opts *);
void fuse_cmdline_help();
fuse_operations high_level_operations();
int fuse_highlevel(fuse_args *args, fuse_cmdline_opts *opts, Json::Value &json);
} // namespace cloudstorage
......
......@@ -6,7 +6,9 @@
#include <iostream>
#include <sstream>
#include "FuseHighLevel.h"
#include "FuseCommon.h"
#include "IFileSystem.h"
#include "Utility/CurlHttp.h"
#include "Utility/Utility.h"
namespace cloudstorage {
......@@ -196,4 +198,46 @@ fuse_lowlevel_ops low_level_operations() {
return operations;
}
FuseLowLevel::FuseLowLevel(fuse_args *args, const char *mountpoint,
void *userdata) {
auto operations = cloudstorage::low_level_operations();
session_ = fuse_session_new(args, &operations, sizeof(operations), userdata);
fuse_set_signal_handlers(session_);
fuse_session_mount(session_, mountpoint);
}
FuseLowLevel::~FuseLowLevel() {
fuse_session_unmount(session_);
fuse_remove_signal_handlers(session_);
fuse_session_destroy(session_);
}
int FuseLowLevel::run(bool singlethread, bool clone_fd) const {
return singlethread ? fuse_session_loop(session_)
: fuse_session_loop_mt(session_, clone_fd);
}
int fuse_lowlevel(fuse_args *args, fuse_cmdline_opts *opts, Json::Value &json) {
auto ctx = new IFileSystem *;
FuseLowLevel fuse(args, opts->mountpoint, ctx);
fuse_daemonize(opts->foreground);
auto http = std::make_shared<curl::CurlHttp>();
auto temporary_directory = json["temporary_directory"].asString();
if (temporary_directory.empty())
temporary_directory = default_temporary_directory();
auto p = providers(json["providers"], http, temporary_directory);
*ctx = IFileSystem::create(p, util::make_unique<HttpWrapper>(http),
temporary_directory)
.release();
int ret = fuse.run(opts->singlethread, opts->clone_fd);
for (size_t i = 0; i < p.size(); i++) {
json["providers"][int(i)]["token"] = p[i].provider_->token();
json["providers"][int(i)]["access_token"] =
p[i].provider_->hints()["access_token"];
}
delete *ctx;
delete ctx;
return ret;
}
} // namespace cloudstorage
......@@ -4,8 +4,22 @@
#define FUSE_USE_VERSION 31
#include <fuse_lowlevel.h>
#include <json/json.h>
namespace cloudstorage {
struct FuseLowLevel {
FuseLowLevel(fuse_args *args, const char *mountpoint, void *userdata);
~FuseLowLevel();
int run(bool singlethread, bool clone_fd) const;
fuse_session *session_;
};
fuse_lowlevel_ops low_level_operations();
int fuse_lowlevel(fuse_args *args, fuse_cmdline_opts *opts, Json::Value &json);
} // namespace cloudstorage
#endif // FUSE_LOW_LEVEL_H
......@@ -2,7 +2,6 @@ ACLOCAL_AMFLAGS = -I m4
AM_CXXFLAGS = \
-I$(top_srcdir)/src \
$(fuse_CFLAGS) \
$(libjsoncpp_CFLAGS)
AM_LDFLAGS = \
......@@ -12,12 +11,23 @@ AM_LDFLAGS = \
bin_PROGRAMS = libcloudstorage-fuse
libcloudstorage_fuse_SOURCES = \
main.cpp \
FuseLowLevel.h FuseLowLevel.cpp \
FuseHighLevel.h FuseHighLevel.cpp
FuseCommon.h FuseCommon.cpp \
main.cpp
libcloudstorage_fuse_LDADD = \
../../src/libcloudstorage.la \
$(fuse_LIBS) \
$(libjsoncpp_LIBS)
if WITH_FUSE
AM_CXXFLAGS += $(fuse_CFLAGS)
libcloudstorage_fuse_SOURCES += \
FuseLowLevel.h FuseLowLevel.cpp
libcloudstorage_fuse_LDADD += $(fuse_LIBS)
endif
if WITH_LEGACY_FUSE
AM_CXXFLAGS += $(fuse2_CFLAGS)
libcloudstorage_fuse_SOURCES += \
FuseHighLevel.h FuseHighLevel.cpp
libcloudstorage_fuse_LDADD += $(fuse2_LIBS)
endif
#include <json/json.h>
#include <cstddef>
#include <cstring>
#include <fstream>
#include <future>
#include <iostream>
#include <memory>
#include <mutex>
#include <sstream>
#include <string>
#include "FuseHighLevel.h"
#include "FuseLowLevel.h"
#include "ICloudStorage.h"
#include "IHttpServer.h"
#include "Utility/CurlHttp.h"
#include "Utility/MicroHttpdServer.h"
#include "Utility/Utility.h"
#include "FuseCommon.h"
using namespace std::placeholders;
using namespace cloudstorage;
#define OPTION(t, p) \
{ t, offsetof(struct options, p), 1 }
template <class T>
using pointer = std::unique_ptr<T, std::function<void(T *)>>;
struct options {
~options() {
free(config_file);
free(provider_label);
}
char *config_file;
char *provider_label;
};
struct HttpServerData {
std::string code_;
std::string state_;
};
struct FuseLowLevel {
FuseLowLevel(fuse_args *args, const char *mountpoint, void *userdata) {
auto operations = cloudstorage::low_level_operations();
session_ =
fuse_session_new(args, &operations, sizeof(operations), userdata);
fuse_set_signal_handlers(session_);
fuse_session_mount(session_, mountpoint);
}
~FuseLowLevel() {
fuse_session_unmount(session_);
fuse_remove_signal_handlers(session_);
fuse_session_destroy(session_);
}
int run(bool singlethread, bool clone_fd) const {
return singlethread ? fuse_session_loop(session_)
: fuse_session_loop_mt(session_, clone_fd);
}
fuse_session *session_;
};
class HttpServerCallback : public IHttpServer::ICallback {
public:
HttpServerCallback(std::promise<HttpServerData> &p) : promise_(p) {}
IHttpServer::IResponse::Pointer handle(const IHttpServer::IRequest &request) {
auto code = request.get("code");
auto state = request.get("state");
if (code && state) {
promise_.set_value({code, state});
return util::response_from_string(request, IHttpRequest::Ok, {},
"token received");
} else if (request.url() == "/login" && state) {
return util::response_from_string(request, IHttpRequest::Ok, {},
util::login_page(state));
} else {
return util::response_from_string(request, IHttpRequest::Bad, {},
"error");
}
}
private:
std::promise<HttpServerData> &promise_;
};
class HttpWrapper : public IHttp {
public:
HttpWrapper(std::shared_ptr<IHttp> http) : http_(http) {}
IHttpRequest::Pointer create(const std::string &url,
const std::string &method,
bool follow_redirect) const override {
return http_->create(url, method, follow_redirect);
}
private:
std::shared_ptr<IHttp> http_;
};
const struct fuse_opt option_spec[] = {OPTION("--config=%s", config_file),
OPTION("--add=%s", provider_label),
FUSE_OPT_END};
cloudstorage::ICloudProvider::Pointer create(std::shared_ptr<IHttp> http,
std::string temporary_directory,
Json::Value config) {
class AuthCallback : public ICloudProvider::IAuthCallback {
Status userConsentRequired(const ICloudProvider &) override {
return Status::None;
}
void done(const ICloudProvider &, EitherError<void>) override {}
};
ICloudProvider::InitData init_data;
init_data.callback_ = util::make_unique<AuthCallback>();
init_data.token_ = config["token"].asString();
init_data.http_engine_ = util::make_unique<HttpWrapper>(http);
init_data.hints_["access_token"] = config["access_token"].asString();
init_data.hints_["temporary_directory"] = temporary_directory;
return ICloudStorage::create()->provider(config["type"].asString(),
std::move(init_data));
}
std::vector<cloudstorage::IFileSystem::ProviderEntry> providers(
const Json::Value &data, std::shared_ptr<IHttp> http,
const std::string &temporary_directory) {
std::vector<cloudstorage::IFileSystem::ProviderEntry> providers;
for (auto &&p : data)
providers.push_back(
{p["label"].asString(), create(http, temporary_directory, p)});
return std::move(providers);
}
int fuse_lowlevel(fuse_args *args, fuse_cmdline_opts *opts, Json::Value &json) {
auto ctx = new IFileSystem *;
FuseLowLevel fuse(args, opts->mountpoint, ctx);
fuse_daemonize(opts->foreground);
auto http = std::make_shared<curl::CurlHttp>();
auto temporary_directory = json["temporary_directory"].asString();
if (temporary_directory.empty()) temporary_directory = "/tmp/";
auto p = providers(json["providers"], http, temporary_directory);
*ctx = IFileSystem::create(p, util::make_unique<HttpWrapper>(http),
temporary_directory)
.release();
int ret = fuse.run(opts->singlethread, opts->clone_fd);
for (size_t i = 0; i < p.size(); i++) {
json["providers"][int(i)]["token"] = p[i].provider_->token();
json["providers"][int(i)]["access_token"] =
p[i].provider_->hints()["access_token"];
}
delete *ctx;
delete ctx;
return ret;
}
int main(int argc, char **argv) {
pointer<fuse_args> args(new fuse_args(FUSE_ARGS_INIT(argc, argv)),
[](fuse_args *e) {
fuse_opt_free_args(e);
delete e;
});
struct options options {};
if (fuse_opt_parse(args.get(), &options, option_spec, nullptr) == -1)
return 1;
if (!options.config_file)
options.config_file = strdup(
(std::string(getenv("HOME")) + "/.libcloudstorage-fuse.json").c_str());
pointer<fuse_cmdline_opts> opts(new fuse_cmdline_opts{},
[](fuse_cmdline_opts *opts) {
free(opts->mountpoint);
delete opts;
});
if (fuse_parse_cmdline(args.get(), opts.get()) != 0) return 1;
if (opts->show_help) {
std::cerr << util::libcloudstorage_ascii_art() << "\n\n";
std::cerr << " --add=provider_label add cloud provider with label\n";
std::cerr << " --config=config_path path to configuration file\n";
std::cerr << " (default: "
"~/.libcloudstorage-fuse.json)\n";
std::cerr << "\n";
fuse_cmdline_help();
fuse_lowlevel_help();
return 0;
} else if (opts->show_version) {
fuse_lowlevel_version();
return 0;
}
std::stringstream stream;
stream << std::ifstream(options.config_file).rdbuf();
Json::Value json;
Json::Reader().parse(stream.str(), json);
if (options.provider_label) {
std::cerr << cloudstorage::util::libcloudstorage_ascii_art() << "\n";
auto storage = ICloudStorage::create();
for (auto &&p : storage->providers()) {
ICloudProvider::InitData init_data;
init_data.hints_["state"] = p;
std::cerr << "\n";
std::cerr
<< p << ": "
<< storage->provider(p, std::move(init_data))->authorizeLibraryUrl()
<< "\n";
}
std::promise<HttpServerData> result;
auto server = cloudstorage::MicroHttpdServerFactory().create(
std::make_shared<HttpServerCallback>(result), "",
IHttpServer::Type::Authorization);
auto key = result.get_future().get();
auto provider = storage->provider(key.state_, {});
if (!provider) return 1;
auto ret = provider->exchangeCodeAsync(key.code_)->result();
if (auto token = ret.right()) {
Json::Value p;
p["label"] = options.provider_label;
p["type"] = provider->name();
p["token"] = token->token_;
p["access_token"] = token->access_token_;
json["providers"].append(p);
std::ofstream(options.config_file) << json;
} else
std::cerr << "error " << ret.left()->code_ << ": "
<< ret.left()->description_ << "\n";
return 0;
}
int ret = fuse_lowlevel(args.get(), opts.get(), json);
std::ofstream(options.config_file) << json;
return ret;
}
int main(int argc, char **argv) { return cloudstorage::fuse_run(argc, argv); }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment