2.0.0-rc (#9)
Some checks failed
BlockStorage/repertory_osx/pipeline/head This commit looks good
BlockStorage/repertory_windows/pipeline/head This commit looks good
BlockStorage/repertory/pipeline/head There was a failure building this commit
BlockStorage/repertory_linux_builds/pipeline/head This commit looks good
BlockStorage/repertory_osx_builds/pipeline/head There was a failure building this commit
Some checks failed
BlockStorage/repertory_osx/pipeline/head This commit looks good
BlockStorage/repertory_windows/pipeline/head This commit looks good
BlockStorage/repertory/pipeline/head There was a failure building this commit
BlockStorage/repertory_linux_builds/pipeline/head This commit looks good
BlockStorage/repertory_osx_builds/pipeline/head There was a failure building this commit
### Issues * \#1 \[bug\] Unable to mount S3 due to 'item_not_found' exception * \#2 Require bucket name for S3 mounts * \#3 \[bug\] File size is not being updated in S3 mount * \#4 Upgrade to libfuse-3.x.x * \#5 Switch to renterd for Sia support * \#6 Switch to cpp-httplib to further reduce dependencies * \#7 Remove global_data and calculate used disk space per provider * \#8 Switch to libcurl for S3 mount support ### Changes from v1.x.x * Added read-only encrypt provider * Pass-through mount point that transparently encrypts source data using `XChaCha20-Poly1305` * Added S3 encryption support via `XChaCha20-Poly1305` * Added replay protection to remote mounts * Added support base64 writes in remote FUSE * Created static linked Linux binaries for `amd64` and `aarch64` using `musl-libc` * Removed legacy Sia renter support * Removed Skynet support * Fixed multiple remote mount WinFSP API issues on \*NIX servers * Implemented chunked read and write * Writes for non-cached files are performed in chunks of 8Mib * Removed `repertory-ui` support * Removed `FreeBSD` support * Switched to `libsodium` over `CryptoPP` * Switched to `XChaCha20-Poly1305` for remote mounts * Updated `GoogleTest` to v1.14.0 * Updated `JSON for Modern C++` to v3.11.2 * Updated `OpenSSL` to v1.1.1w * Updated `RocksDB` to v8.5.3 * Updated `WinFSP` to 2023 * Updated `boost` to v1.78.0 * Updated `cURL` to v8.3.0 * Updated `zlib` to v1.3 * Use `upload_manager` for all providers * Adds a delay to uploads to prevent excessive API calls * Supports re-upload after mount restart for incomplete uploads * NOTE: Uploads for all providers are full file (no resume support) * Multipart upload support is planned for S3 Reviewed-on: #9
This commit is contained in:
353
src/drives/fuse/fuse_drive_base.cpp
Normal file
353
src/drives/fuse/fuse_drive_base.cpp
Normal file
@ -0,0 +1,353 @@
|
||||
/*
|
||||
Copyright <2018-2023> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef _WIN32
|
||||
|
||||
#include "drives/fuse/fuse_drive_base.hpp"
|
||||
|
||||
#include "providers/i_provider.hpp"
|
||||
|
||||
namespace repertory {
|
||||
auto fuse_drive_base::access_impl(std::string api_path, int mask) -> api_error {
|
||||
return check_access(api_path, mask);
|
||||
}
|
||||
|
||||
auto fuse_drive_base::check_access(const std::string &api_path, int mask) const
|
||||
-> api_error {
|
||||
api_meta_map meta;
|
||||
const auto res = get_item_meta(api_path, meta);
|
||||
if (res != api_error::success) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// Always allow root
|
||||
auto current_uid = get_current_uid();
|
||||
if (current_uid != 0) {
|
||||
// Always allow forced user
|
||||
if (not forced_uid_.has_value() || (current_uid != get_effective_uid())) {
|
||||
// Always allow if checking file exists
|
||||
if (F_OK != mask) {
|
||||
const auto effective_uid =
|
||||
(forced_uid_.has_value() ? forced_uid_.value()
|
||||
: get_uid_from_meta(meta));
|
||||
const auto effective_gid =
|
||||
(forced_gid_.has_value() ? forced_gid_.value()
|
||||
: get_gid_from_meta(meta));
|
||||
|
||||
// Create file mode
|
||||
mode_t effective_mode =
|
||||
forced_umask_.has_value()
|
||||
? ((S_IRWXU | S_IRWXG | S_IRWXO) & (~forced_umask_.value()))
|
||||
: get_mode_from_meta(meta);
|
||||
|
||||
// Create access mask
|
||||
mode_t active_mask = S_IRWXO;
|
||||
if (current_uid == effective_uid) {
|
||||
active_mask |= S_IRWXU;
|
||||
}
|
||||
if (get_current_gid() == effective_gid) {
|
||||
active_mask |= S_IRWXG;
|
||||
}
|
||||
if (utils::is_uid_member_of_group(current_uid, effective_gid)) {
|
||||
active_mask |= S_IRWXG;
|
||||
}
|
||||
|
||||
// Calculate effective file mode
|
||||
effective_mode &= active_mask;
|
||||
|
||||
// Check allow execute
|
||||
if ((mask & X_OK) == X_OK) {
|
||||
if ((effective_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) {
|
||||
return api_error::permission_denied;
|
||||
}
|
||||
}
|
||||
|
||||
// Check allow write
|
||||
if ((mask & W_OK) == W_OK) {
|
||||
if ((effective_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0) {
|
||||
return api_error::access_denied;
|
||||
}
|
||||
}
|
||||
|
||||
// Check allow read
|
||||
if ((mask & R_OK) == R_OK) {
|
||||
if ((effective_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
|
||||
return api_error::access_denied;
|
||||
}
|
||||
}
|
||||
|
||||
if (effective_mode == 0) {
|
||||
// Deny access if effective mode is 0
|
||||
return api_error::access_denied;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
auto fuse_drive_base::check_and_perform(
|
||||
const std::string &api_path, int parent_mask,
|
||||
const std::function<api_error(api_meta_map &meta)> &action) -> api_error {
|
||||
api_meta_map meta;
|
||||
auto ret = get_item_meta(api_path, meta);
|
||||
if (ret != api_error::success) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = check_parent_access(api_path, parent_mask)) !=
|
||||
api_error::success) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = check_owner(meta)) != api_error::success) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return action(meta);
|
||||
}
|
||||
|
||||
auto fuse_drive_base::check_open_flags(int flags, int mask,
|
||||
const api_error &fail_error)
|
||||
-> api_error {
|
||||
return ((flags & mask) ? fail_error : api_error::success);
|
||||
}
|
||||
|
||||
auto fuse_drive_base::check_owner(const std::string &api_path) const
|
||||
-> api_error {
|
||||
api_meta_map meta{};
|
||||
auto ret = get_item_meta(api_path, meta);
|
||||
if (ret == api_error::success) {
|
||||
ret = check_owner(meta);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto fuse_drive_base::check_owner(const api_meta_map &meta) const -> api_error {
|
||||
// Always allow root
|
||||
auto current_uid = get_current_uid();
|
||||
if ((current_uid != 0) &&
|
||||
// Always allow forced UID
|
||||
(not forced_uid_.has_value() || (current_uid != get_effective_uid())) &&
|
||||
// Check if current uid matches file uid
|
||||
(get_uid_from_meta(meta) != get_effective_uid())) {
|
||||
return api_error::permission_denied;
|
||||
}
|
||||
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
auto fuse_drive_base::check_parent_access(const std::string &api_path,
|
||||
int mask) const -> api_error {
|
||||
auto ret = api_error::success;
|
||||
|
||||
// Ignore root
|
||||
if (api_path != "/") {
|
||||
if ((mask & X_OK) == X_OK) {
|
||||
for (auto parent = utils::path::get_parent_directory(api_path);
|
||||
(ret == api_error::success) && not parent.empty();
|
||||
parent = utils::path::get_parent_directory(parent)) {
|
||||
if (((ret = check_access(parent, X_OK)) == api_error::success) &&
|
||||
(parent == "/")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == api_error::success) {
|
||||
mask &= (~X_OK);
|
||||
if (mask != 0) {
|
||||
ret = check_access(utils::path::get_parent_directory(api_path), mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto fuse_drive_base::check_readable(int flags, const api_error &fail_error)
|
||||
-> api_error {
|
||||
const auto mode = (flags & O_ACCMODE);
|
||||
return ((mode == O_WRONLY) ? fail_error : api_error::success);
|
||||
}
|
||||
|
||||
auto fuse_drive_base::check_writeable(int flags, const api_error &fail_error)
|
||||
-> api_error {
|
||||
return ((flags & O_ACCMODE) ? api_error::success : fail_error);
|
||||
}
|
||||
|
||||
auto fuse_drive_base::get_current_gid() const -> gid_t {
|
||||
auto *ctx = fuse_get_context();
|
||||
return ctx ? ctx->gid : getgid();
|
||||
}
|
||||
|
||||
auto fuse_drive_base::get_current_uid() const -> uid_t {
|
||||
auto *ctx = fuse_get_context();
|
||||
return ctx ? ctx->uid : getuid();
|
||||
}
|
||||
|
||||
auto fuse_drive_base::get_effective_gid() const -> gid_t {
|
||||
return forced_gid_.has_value() ? forced_gid_.value() : get_current_gid();
|
||||
}
|
||||
|
||||
auto fuse_drive_base::get_effective_uid() const -> uid_t {
|
||||
return forced_uid_.has_value() ? forced_uid_.value() : get_current_uid();
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
auto fuse_drive_base::get_flags_from_meta(const api_meta_map &meta)
|
||||
-> __uint32_t {
|
||||
return utils::string::to_uint32(meta.at(META_OSXFLAGS));
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
||||
auto fuse_drive_base::get_gid_from_meta(const api_meta_map &meta) -> gid_t {
|
||||
return static_cast<gid_t>(utils::string::to_uint32(meta.at(META_GID)));
|
||||
}
|
||||
|
||||
auto fuse_drive_base::get_mode_from_meta(const api_meta_map &meta) -> mode_t {
|
||||
return static_cast<mode_t>(utils::string::to_uint32(meta.at(META_MODE)));
|
||||
}
|
||||
|
||||
void fuse_drive_base::get_timespec_from_meta(const api_meta_map &meta,
|
||||
const std::string &name,
|
||||
struct timespec &ts) {
|
||||
const auto t = utils::string::to_uint64(meta.at(name));
|
||||
ts.tv_nsec = t % NANOS_PER_SECOND;
|
||||
ts.tv_sec = t / NANOS_PER_SECOND;
|
||||
}
|
||||
|
||||
auto fuse_drive_base::get_uid_from_meta(const api_meta_map &meta) -> uid_t {
|
||||
return static_cast<uid_t>(utils::string::to_uint32(meta.at(META_UID)));
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
auto fuse_drive_base::parse_xattr_parameters(const char *name,
|
||||
const uint32_t &position,
|
||||
std::string &attribute_name,
|
||||
const std::string &api_path)
|
||||
-> api_error {
|
||||
#else
|
||||
auto fuse_drive_base::parse_xattr_parameters(const char *name,
|
||||
std::string &attribute_name,
|
||||
const std::string &api_path)
|
||||
-> api_error {
|
||||
#endif
|
||||
auto res = api_path.empty() ? api_error::bad_address : api_error::success;
|
||||
if (res != api_error::success) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (not name) {
|
||||
return api_error::bad_address;
|
||||
}
|
||||
|
||||
attribute_name = std::string(name);
|
||||
#ifdef __APPLE__
|
||||
if (attribute_name == A_KAUTH_FILESEC_XATTR) {
|
||||
char new_name[MAXPATHLEN] = {0};
|
||||
memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR));
|
||||
memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1);
|
||||
attribute_name = new_name;
|
||||
} else if (attribute_name.empty() ||
|
||||
((attribute_name != XATTR_RESOURCEFORK_NAME) && (position != 0))) {
|
||||
return api_error::invalid_operation;
|
||||
}
|
||||
#endif
|
||||
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
auto fuse_drive_base::parse_xattr_parameters(
|
||||
const char *name, const char *value, size_t size, const uint32_t &position,
|
||||
std::string &attribute_name, const std::string &api_path) -> api_error {
|
||||
auto res = parse_xattr_parameters(name, position, attribute_name, api_path);
|
||||
#else
|
||||
auto fuse_drive_base::parse_xattr_parameters(const char *name,
|
||||
const char *value, size_t size,
|
||||
std::string &attribute_name,
|
||||
const std::string &api_path)
|
||||
-> api_error {
|
||||
auto res = parse_xattr_parameters(name, attribute_name, api_path);
|
||||
#endif
|
||||
if (res != api_error::success) {
|
||||
return res;
|
||||
}
|
||||
|
||||
return (value ? api_error::success
|
||||
: (size ? api_error::bad_address : api_error::success));
|
||||
}
|
||||
|
||||
void fuse_drive_base::populate_stat(const std::string &api_path,
|
||||
std::uint64_t size_or_count,
|
||||
const api_meta_map &meta, bool directory,
|
||||
i_provider &provider, struct stat *st) {
|
||||
memset(st, 0, sizeof(struct stat));
|
||||
st->st_nlink =
|
||||
(directory
|
||||
? 2 + (size_or_count ? size_or_count
|
||||
: provider.get_directory_item_count(api_path))
|
||||
: 1);
|
||||
if (directory) {
|
||||
st->st_blocks = 0;
|
||||
} else {
|
||||
st->st_size = size_or_count;
|
||||
static const auto block_size_stat = static_cast<std::uint64_t>(512u);
|
||||
static const auto block_size = static_cast<std::uint64_t>(4096u);
|
||||
const auto size = utils::divide_with_ceiling(
|
||||
static_cast<std::uint64_t>(st->st_size), block_size) *
|
||||
block_size;
|
||||
st->st_blocks = std::max(block_size / block_size_stat,
|
||||
utils::divide_with_ceiling(size, block_size_stat));
|
||||
}
|
||||
st->st_gid = get_gid_from_meta(meta);
|
||||
st->st_mode = (directory ? S_IFDIR : S_IFREG) | get_mode_from_meta(meta);
|
||||
st->st_uid = get_uid_from_meta(meta);
|
||||
#ifdef __APPLE__
|
||||
st->st_blksize = 0;
|
||||
st->st_flags = get_flags_from_meta(meta);
|
||||
|
||||
set_timespec_from_meta(meta, META_MODIFIED, st->st_mtimespec);
|
||||
set_timespec_from_meta(meta, META_CREATION, st->st_birthtimespec);
|
||||
set_timespec_from_meta(meta, META_CHANGED, st->st_ctimespec);
|
||||
set_timespec_from_meta(meta, META_ACCESSED, st->st_atimespec);
|
||||
#else // __APPLE__
|
||||
st->st_blksize = 4096;
|
||||
|
||||
set_timespec_from_meta(meta, META_MODIFIED, st->st_mtim);
|
||||
set_timespec_from_meta(meta, META_CREATION, st->st_ctim);
|
||||
set_timespec_from_meta(meta, META_ACCESSED, st->st_atim);
|
||||
#endif // __APPLE__
|
||||
}
|
||||
|
||||
void fuse_drive_base::set_timespec_from_meta(const api_meta_map &meta,
|
||||
const std::string &name,
|
||||
struct timespec &ts) {
|
||||
const auto t = utils::string::to_uint64(meta.at(name));
|
||||
ts.tv_nsec = t % NANOS_PER_SECOND;
|
||||
ts.tv_sec = t / NANOS_PER_SECOND;
|
||||
}
|
||||
} // namespace repertory
|
||||
|
||||
#endif // _WIN32
|
Reference in New Issue
Block a user