mirror of
https://github.com/trapexit/mergerfs.git
synced 2026-10-19 16:16:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a5e94be74 |
@@ -74,6 +74,7 @@ CXXFLAGS ?= ${OPT_FLAGS}
|
||||
CXXFLAGS := \
|
||||
${CXXFLAGS} \
|
||||
-std=c++17 \
|
||||
-fno-rtti \
|
||||
$(STATIC_FLAGS) \
|
||||
$(LTO_FLAGS) \
|
||||
-Isrc \
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
struct ffi
|
||||
{
|
||||
uint64_t nodeid;
|
||||
uint64_t gen;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
pid_t pid;
|
||||
uint64_t fh;
|
||||
char *name;
|
||||
char path[0];
|
||||
};
|
||||
@@ -1567,6 +1567,9 @@ fuse_lib_lookup(fuse_req_t req,
|
||||
nodeid = hdr_->nodeid;
|
||||
f = req_fuse_prepare(req);
|
||||
|
||||
fmt::print("lookup: {}\n",
|
||||
nodeid);
|
||||
|
||||
if(name[0] == '.')
|
||||
{
|
||||
if(name[1] == '\0')
|
||||
@@ -1685,6 +1688,9 @@ fuse_lib_getattr(fuse_req_t req,
|
||||
arg = (fuse_getattr_in*)fuse_hdr_arg(hdr_);
|
||||
f = req_fuse_prepare(req);
|
||||
|
||||
fmt::print("getattr: {}\n",
|
||||
hdr_->nodeid);
|
||||
|
||||
fh = 0;
|
||||
if(arg->getattr_flags & FUSE_GETATTR_FH)
|
||||
fh = arg->fh;
|
||||
@@ -1789,6 +1795,9 @@ fuse_lib_statx(fuse_req_t req_,
|
||||
struct fuse *f = req_fuse_prepare(req_);
|
||||
fuse_statx_in *inarg;
|
||||
|
||||
fmt::print("statx: {}\n",
|
||||
hdr_->nodeid);
|
||||
|
||||
inarg = (fuse_statx_in*)fuse_hdr_arg(hdr_);
|
||||
|
||||
if(inarg->getattr_flags & FUSE_GETATTR_FH)
|
||||
|
||||
@@ -80,6 +80,13 @@ public:
|
||||
operator Ptr() const { std::lock_guard<std::mutex> lg(_mutex); return _impl; }
|
||||
Ptr operator->() const { std::lock_guard<std::mutex> lg(_mutex); return _impl; }
|
||||
|
||||
public:
|
||||
Impl::iterator begin() { return _impl->begin(); }
|
||||
Impl::iterator end() { return _impl->end(); }
|
||||
Impl::const_iterator begin() const { return _impl->begin(); }
|
||||
Impl::const_iterator end() const { return _impl->end(); }
|
||||
|
||||
|
||||
public:
|
||||
void find_and_set_mode_ro();
|
||||
};
|
||||
|
||||
@@ -223,6 +223,8 @@ Config::Config()
|
||||
_map["process-thread-queue-depth"] = &fuse_process_thread_queue_depth;
|
||||
_map["version"] = &version;
|
||||
_map["xattr"] = &xattr;
|
||||
|
||||
_map["func2.getattr"] = &getattr;
|
||||
}
|
||||
|
||||
Config&
|
||||
|
||||
+6
-1
@@ -16,6 +16,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "func_getattr.hpp"
|
||||
|
||||
#include "branches.hpp"
|
||||
#include "category.hpp"
|
||||
#include "config_cachefiles.hpp"
|
||||
@@ -56,6 +58,7 @@
|
||||
|
||||
typedef ToFromWrapper<bool> ConfigBOOL;
|
||||
typedef ToFromWrapper<uint64_t> ConfigUINT64;
|
||||
typedef ToFromWrapper<int64_t> ConfigS64;
|
||||
typedef ToFromWrapper<int> ConfigINT;
|
||||
typedef ToFromWrapper<std::string> ConfigSTR;
|
||||
typedef ToFromWrapper<std::filesystem::path> ConfigPath;
|
||||
@@ -161,7 +164,7 @@ public:
|
||||
StatFS statfs;
|
||||
StatFSIgnore statfs_ignore;
|
||||
ConfigBOOL symlinkify;
|
||||
ConfigUINT64 symlinkify_timeout;
|
||||
ConfigS64 symlinkify_timeout;
|
||||
ConfigINT fuse_read_thread_count;
|
||||
ConfigINT fuse_process_thread_count;
|
||||
ConfigINT fuse_process_thread_queue_depth;
|
||||
@@ -170,6 +173,8 @@ public:
|
||||
ConfigBOOL writeback_cache;
|
||||
XAttr xattr;
|
||||
|
||||
Func2::GetAttr getattr{"combine"};
|
||||
|
||||
private:
|
||||
bool _initialized;
|
||||
|
||||
|
||||
@@ -18,13 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "follow_symlinks_enum.hpp"
|
||||
#include "enum.hpp"
|
||||
|
||||
enum class FollowSymlinksEnum
|
||||
{
|
||||
NEVER,
|
||||
DIRECTORY,
|
||||
REGULAR,
|
||||
ALL
|
||||
};
|
||||
typedef Enum<FollowSymlinksEnum> FollowSymlinks;
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
enum class FollowSymlinksEnum
|
||||
{
|
||||
NEVER,
|
||||
DIRECTORY,
|
||||
REGULAR,
|
||||
ALL
|
||||
};
|
||||
@@ -65,6 +65,53 @@ namespace str
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
from(const std::string &value_,
|
||||
int64_t *int64_)
|
||||
{
|
||||
char *endptr;
|
||||
int64_t tmp;
|
||||
|
||||
tmp = ::strtoll(value_.c_str(),&endptr,10);
|
||||
switch(*endptr)
|
||||
{
|
||||
case 'b':
|
||||
case 'B':
|
||||
tmp *= 1ULL;
|
||||
break;
|
||||
|
||||
case 'k':
|
||||
case 'K':
|
||||
tmp *= 1024ULL;
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
case 'M':
|
||||
tmp *= (1024ULL * 1024ULL);
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
case 'G':
|
||||
tmp *= (1024ULL * 1024ULL * 1024ULL);
|
||||
break;
|
||||
|
||||
case 't':
|
||||
case 'T':
|
||||
tmp *= (1024ULL * 1024ULL * 1024ULL * 1024ULL);
|
||||
break;
|
||||
|
||||
case '\0':
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*int64_ = tmp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
from(const std::string &value_,
|
||||
uint64_t *uint64_)
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace str
|
||||
int from(const std::string &, bool *);
|
||||
int from(const std::string &, int *);
|
||||
int from(const std::string &, uint64_t *);
|
||||
int from(const std::string &, int64_t *);
|
||||
int from(const std::string &, std::string *);
|
||||
int from(const std::string &, const std::string *);
|
||||
int from(const std::string &, fs::Path *);
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
#include "fs_stat.hpp"
|
||||
#include "fs_lstat.hpp"
|
||||
|
||||
static
|
||||
void
|
||||
_set_stat_if_leads_to_dir(const char *path_,
|
||||
struct stat *st_)
|
||||
{
|
||||
int rv;
|
||||
struct stat st;
|
||||
|
||||
rv = fs::stat(path_,&st);
|
||||
if(rv < 0)
|
||||
return;
|
||||
|
||||
if(S_ISDIR(st.st_mode))
|
||||
*st_ = st;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_set_stat_if_leads_to_reg(const char *path_,
|
||||
struct stat *st_)
|
||||
{
|
||||
int rv;
|
||||
struct stat st;
|
||||
|
||||
rv = fs::stat(path_,&st);
|
||||
if(rv < 0)
|
||||
return;
|
||||
|
||||
if(S_ISREG(st.st_mode))
|
||||
*st_ = st;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
fs::stat(const char *path_,
|
||||
struct stat *st_,
|
||||
FollowSymlinksEnum follow_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
switch(follow_)
|
||||
{
|
||||
case FollowSymlinksEnum::NEVER:
|
||||
rv = fs::lstat(path_,st_);
|
||||
break;
|
||||
case FollowSymlinksEnum::DIRECTORY:
|
||||
rv = fs::lstat(path_,st_);
|
||||
if(S_ISLNK(st_->st_mode))
|
||||
_set_stat_if_leads_to_dir(path_,st_);
|
||||
break;
|
||||
case FollowSymlinksEnum::REGULAR:
|
||||
rv = fs::lstat(path_,st_);
|
||||
if(S_ISLNK(st_->st_mode))
|
||||
_set_stat_if_leads_to_reg(path_,st_);
|
||||
break;
|
||||
case FollowSymlinksEnum::ALL:
|
||||
rv = fs::stat(path_,st_);
|
||||
if(rv != 0)
|
||||
rv = fs::lstat(path_,st_);
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "follow_symlinks_enum.hpp"
|
||||
#include "to_neg_errno.hpp"
|
||||
|
||||
#include <string>
|
||||
@@ -50,4 +51,21 @@ namespace fs
|
||||
{
|
||||
return fs::stat(path_.c_str(),st_);
|
||||
}
|
||||
|
||||
int
|
||||
stat(const char *path,
|
||||
struct stat *st,
|
||||
FollowSymlinksEnum follow);
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
stat(const std::string &path_,
|
||||
struct stat *st_,
|
||||
FollowSymlinksEnum follow_)
|
||||
{
|
||||
return fs::stat(path_.c_str(),
|
||||
st_,
|
||||
follow_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2025, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "fs_statx.hpp"
|
||||
|
||||
static
|
||||
void
|
||||
_set_stat_if_leads_to_dir(const std::string &path_,
|
||||
struct fuse_statx *st_)
|
||||
{
|
||||
int rv;
|
||||
struct fuse_statx st;
|
||||
|
||||
rv = fs::statx(AT_FDCWD,path_,AT_SYMLINK_FOLLOW,STATX_TYPE,&st);
|
||||
if(rv < 0)
|
||||
return;
|
||||
|
||||
if(S_ISDIR(st.mode))
|
||||
*st_ = st;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_set_stat_if_leads_to_reg(const std::string &path_,
|
||||
struct fuse_statx *st_)
|
||||
{
|
||||
int rv;
|
||||
struct fuse_statx st;
|
||||
|
||||
rv = fs::statx(AT_FDCWD,path_,AT_SYMLINK_FOLLOW,STATX_TYPE,&st);
|
||||
if(rv < 0)
|
||||
return;
|
||||
|
||||
if(S_ISREG(st.mode))
|
||||
*st_ = st;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
fs::statx(const int dirfd_,
|
||||
const char *pathname_,
|
||||
const int flags_,
|
||||
const unsigned int mask_,
|
||||
struct fuse_statx *st_,
|
||||
FollowSymlinksEnum follow_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
switch(follow_)
|
||||
{
|
||||
case FollowSymlinksEnum::NEVER:
|
||||
rv = fs::statx(AT_FDCWD,pathname_,flags_|AT_SYMLINK_NOFOLLOW,mask_,st_);
|
||||
break;
|
||||
case FollowSymlinksEnum::DIRECTORY:
|
||||
rv = fs::statx(AT_FDCWD,pathname_,flags_|AT_SYMLINK_NOFOLLOW,mask_,st_);
|
||||
if((rv >= 0) && S_ISLNK(st_->mode))
|
||||
::_set_stat_if_leads_to_dir(pathname_,st_);
|
||||
break;
|
||||
case FollowSymlinksEnum::REGULAR:
|
||||
rv = fs::statx(AT_FDCWD,pathname_,flags_|AT_SYMLINK_NOFOLLOW,mask_,st_);
|
||||
if((rv >= 0) && S_ISLNK(st_->mode))
|
||||
::_set_stat_if_leads_to_reg(pathname_,st_);
|
||||
break;
|
||||
case FollowSymlinksEnum::ALL:
|
||||
rv = fs::statx(AT_FDCWD,pathname_,flags_|AT_SYMLINK_FOLLOW,mask_,st_);
|
||||
if(rv < 0)
|
||||
rv = fs::statx(AT_FDCWD,pathname_,flags_|AT_SYMLINK_NOFOLLOW,mask_,st_);
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
+61
-1
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "to_neg_errno.hpp"
|
||||
#include "follow_symlinks_enum.hpp"
|
||||
|
||||
#include "fuse_kernel.h"
|
||||
|
||||
@@ -14,7 +15,7 @@
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef STATX_TYPE
|
||||
#define MERGERFS_STATX_SUPPORTED
|
||||
//#define MERGERFS_STATX_SUPPORTED
|
||||
#endif
|
||||
|
||||
namespace fs
|
||||
@@ -54,4 +55,63 @@ namespace fs
|
||||
{
|
||||
return fs::statx(dirfd_,pathname_.c_str(),flags_,mask_,st_);
|
||||
}
|
||||
|
||||
int
|
||||
statx(const int dirfd,
|
||||
const char *pathname,
|
||||
const int flags,
|
||||
const unsigned int mask,
|
||||
struct fuse_statx *st,
|
||||
FollowSymlinksEnum follow);
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
statx(const int dirfd_,
|
||||
const std::string &pathname_,
|
||||
const int flags_,
|
||||
const unsigned int mask_,
|
||||
struct fuse_statx *st_,
|
||||
FollowSymlinksEnum follow_)
|
||||
{
|
||||
return fs::statx(dirfd_,
|
||||
pathname_.c_str(),
|
||||
flags_,
|
||||
mask_,
|
||||
st_,
|
||||
follow_);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
statx(const char *pathname_,
|
||||
const int flags_,
|
||||
const unsigned int mask_,
|
||||
struct fuse_statx *st_,
|
||||
FollowSymlinksEnum follow_)
|
||||
{
|
||||
return fs::statx(AT_FDCWD,
|
||||
pathname_,
|
||||
flags_,
|
||||
mask_,
|
||||
st_,
|
||||
follow_);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
statx(const std::string &pathname_,
|
||||
const int flags_,
|
||||
const unsigned int mask_,
|
||||
struct fuse_statx *st_,
|
||||
FollowSymlinksEnum follow_)
|
||||
{
|
||||
return fs::statx(pathname_.c_str(),
|
||||
flags_,
|
||||
mask_,
|
||||
st_,
|
||||
follow_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include "func_getattr_base.hpp"
|
||||
#include "func_getattr_factory.hpp"
|
||||
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
#include "fmt/core.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace Func2
|
||||
{
|
||||
class GetAttr : public ToFromString
|
||||
{
|
||||
private:
|
||||
std::shared_ptr<Func2::GetAttrBase> _impl;
|
||||
|
||||
public:
|
||||
GetAttr()
|
||||
{
|
||||
}
|
||||
|
||||
GetAttr(const std::string &name_)
|
||||
{
|
||||
_impl = Func2::GetAttrFactory::make(name_);
|
||||
assert(_impl);
|
||||
}
|
||||
|
||||
public:
|
||||
int
|
||||
operator()(const Branches &branches_,
|
||||
const fs::Path &fusepath_,
|
||||
struct stat *st_,
|
||||
const FollowSymlinksEnum follow_symlinks_,
|
||||
const s64 symlinkify_timeout_)
|
||||
{
|
||||
std::shared_ptr<Func2::GetAttrBase> p;
|
||||
|
||||
p = std::atomic_load(&_impl);
|
||||
|
||||
return (*p)(branches_,
|
||||
fusepath_,
|
||||
st_,
|
||||
follow_symlinks_,
|
||||
symlinkify_timeout_);
|
||||
}
|
||||
|
||||
public:
|
||||
std::string
|
||||
to_string() const
|
||||
{
|
||||
std::shared_ptr<Func2::GetAttrBase> p;
|
||||
|
||||
p = std::atomic_load(&_impl);
|
||||
|
||||
return std::string(p->name());
|
||||
}
|
||||
|
||||
int
|
||||
from_string(const std::string &str_)
|
||||
{
|
||||
std::shared_ptr<Func2::GetAttrBase> p;
|
||||
|
||||
p = Func2::GetAttrFactory::make(str_);
|
||||
if(!p)
|
||||
return -EINVAL;
|
||||
|
||||
_impl = std::atomic_load(&p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "follow_symlinks_enum.hpp"
|
||||
|
||||
#include "fs_path.hpp"
|
||||
#include "branches.hpp"
|
||||
#include "int_types.h"
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace Func2
|
||||
{
|
||||
class GetAttrBase
|
||||
{
|
||||
public:
|
||||
GetAttrBase() {}
|
||||
~GetAttrBase() {}
|
||||
|
||||
public:
|
||||
virtual std::string_view name() const = 0;
|
||||
|
||||
public:
|
||||
virtual int operator()(const Branches &branches,
|
||||
const fs::Path &fusepath,
|
||||
struct stat *st,
|
||||
const FollowSymlinksEnum follow_symlinks,
|
||||
const s64 symlinkify_timeout) = 0;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#include "func_getattr_combine.hpp"
|
||||
|
||||
#include "fs_inode.hpp"
|
||||
#include "fs_stat.hpp"
|
||||
#include "symlinkify.hpp"
|
||||
#include "timespec_utils.hpp"
|
||||
|
||||
|
||||
std::string_view
|
||||
Func2::GetAttrCombine::name() const
|
||||
{
|
||||
return "combine";
|
||||
}
|
||||
|
||||
int
|
||||
Func2::GetAttrCombine::operator()(const Branches &branches_,
|
||||
const fs::Path &fusepath_,
|
||||
struct stat *st_,
|
||||
const FollowSymlinksEnum follow_symlinks_,
|
||||
const s64 symlinkify_timeout_)
|
||||
{
|
||||
int rv;
|
||||
fs::Path fullpath;
|
||||
const Branch *first_branch;
|
||||
|
||||
first_branch = nullptr;
|
||||
for(const auto &branch : branches_)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
fullpath = branch.path;
|
||||
fullpath += fusepath_;
|
||||
rv = fs::stat(fullpath.c_str(),&st,follow_symlinks_);
|
||||
if(rv == -1)
|
||||
continue;
|
||||
|
||||
if(!first_branch)
|
||||
{
|
||||
*st_ = st;
|
||||
first_branch = &branch;
|
||||
continue;
|
||||
}
|
||||
|
||||
st_->st_atim = TimeSpec::newest(st_->st_atim,st.st_atim);
|
||||
st_->st_ctim = TimeSpec::newest(st_->st_ctim,st.st_ctim);
|
||||
st_->st_mtim = TimeSpec::newest(st_->st_mtim,st.st_mtim);
|
||||
st_->st_nlink += st.st_nlink;
|
||||
}
|
||||
|
||||
if(!first_branch)
|
||||
return -ENOENT;
|
||||
|
||||
if(symlinkify_timeout_ >= 0)
|
||||
{
|
||||
fullpath = fs::path::make(first_branch->path,fusepath_);
|
||||
symlinkify::convert_if_can_be_symlink(fullpath,
|
||||
st_,
|
||||
symlinkify_timeout_);
|
||||
}
|
||||
|
||||
fs::inode::calc(first_branch->path,fusepath_.string(),st_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "func_getattr_base.hpp"
|
||||
|
||||
namespace Func2
|
||||
{
|
||||
class GetAttrCombine : public GetAttrBase
|
||||
{
|
||||
public:
|
||||
GetAttrCombine() {}
|
||||
~GetAttrCombine() {}
|
||||
|
||||
public:
|
||||
std::string_view name() const;
|
||||
|
||||
public:
|
||||
int operator()(const Branches &branches,
|
||||
const fs::Path &fusepath,
|
||||
struct stat *st,
|
||||
const FollowSymlinksEnum follow_symlinks,
|
||||
const s64 symlinkify_timeout);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "func_getattr_factory.hpp"
|
||||
|
||||
#include "func_getattr_combine.hpp"
|
||||
#include "func_getattr_ff.hpp"
|
||||
#include "func_getattr_newest.hpp"
|
||||
|
||||
#include "policies.hpp"
|
||||
|
||||
bool
|
||||
Func2::GetAttrFactory::valid(const std::string str_)
|
||||
{
|
||||
if(str_ == "combined")
|
||||
return true;
|
||||
if(str_ == "ff")
|
||||
return true;
|
||||
if(str_ == "newest")
|
||||
return true;
|
||||
|
||||
if(Policies::Search::find(str_))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<Func2::GetAttrBase>
|
||||
Func2::GetAttrFactory::make(const std::string str_)
|
||||
{
|
||||
if(str_ == "combine")
|
||||
return std::make_shared<Func2::GetAttrCombine>();
|
||||
if(str_ == "ff")
|
||||
return std::make_shared<Func2::GetAttrFF>();
|
||||
if(str_ == "newest")
|
||||
return std::make_shared<Func2::GetAttrNewest>();
|
||||
|
||||
if(Policies::Search::find(str_))
|
||||
return std::make_shared<Func2::GetAttrCombine>();
|
||||
|
||||
return {};
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "func_getattr_base.hpp"
|
||||
|
||||
namespace Func2
|
||||
{
|
||||
class GetAttrFactory
|
||||
{
|
||||
public:
|
||||
static bool valid(const std::string str);
|
||||
static std::shared_ptr<GetAttrBase> make(const std::string str);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#include "func_getattr_ff.hpp"
|
||||
|
||||
#include "fs_stat.hpp"
|
||||
#include "fs_inode.hpp"
|
||||
#include "symlinkify.hpp"
|
||||
|
||||
|
||||
std::string_view
|
||||
Func2::GetAttrFF::name() const
|
||||
{
|
||||
return "ff";
|
||||
}
|
||||
|
||||
int
|
||||
Func2::GetAttrFF::operator()(const Branches &branches_,
|
||||
const fs::Path &fusepath_,
|
||||
struct stat *st_,
|
||||
const FollowSymlinksEnum follow_symlinks_,
|
||||
const s64 symlinkify_timeout_)
|
||||
{
|
||||
int rv;
|
||||
fs::Path fullpath;
|
||||
|
||||
for(const auto &branch : branches_)
|
||||
{
|
||||
fullpath = branch.path;
|
||||
fullpath += fusepath_;
|
||||
rv = fs::stat(fullpath.string(),st_,follow_symlinks_);
|
||||
if(rv != 0)
|
||||
continue;
|
||||
|
||||
symlinkify::convert_if_can_be_symlink(fullpath,
|
||||
st_,
|
||||
symlinkify_timeout_);
|
||||
|
||||
fs::inode::calc(branch.path,fusepath_.string(),st_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "func_getattr_base.hpp"
|
||||
|
||||
namespace Func2
|
||||
{
|
||||
class GetAttrFF : public GetAttrBase
|
||||
{
|
||||
public:
|
||||
GetAttrFF() {}
|
||||
~GetAttrFF() {}
|
||||
|
||||
public:
|
||||
std::string_view name() const;
|
||||
|
||||
public:
|
||||
int operator()(const Branches &branches,
|
||||
const fs::Path &fusepath,
|
||||
struct stat *st,
|
||||
const FollowSymlinksEnum follow_symlinks,
|
||||
const s64 symlinkify_timeout);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#include "func_getattr_newest.hpp"
|
||||
|
||||
#include "fs_stat.hpp"
|
||||
#include "fs_inode.hpp"
|
||||
#include "timespec_utils.hpp"
|
||||
#include "symlinkify.hpp"
|
||||
|
||||
|
||||
std::string_view
|
||||
Func2::GetAttrNewest::name() const
|
||||
{
|
||||
return "newest";
|
||||
}
|
||||
|
||||
int
|
||||
Func2::GetAttrNewest::operator()(const Branches &branches_,
|
||||
const fs::Path &fusepath_,
|
||||
struct stat *st_,
|
||||
const FollowSymlinksEnum follow_symlinks_,
|
||||
const s64 symlinkify_timeout_)
|
||||
{
|
||||
int rv;
|
||||
fs::Path fullpath;
|
||||
const Branch *newest_branch;
|
||||
|
||||
newest_branch = nullptr;
|
||||
for(const auto &branch : branches_)
|
||||
{
|
||||
struct stat tmp_st;
|
||||
|
||||
fullpath = branch.path;
|
||||
fullpath += fusepath_;
|
||||
rv = fs::stat(fullpath,&tmp_st,follow_symlinks_);
|
||||
if(rv == -1)
|
||||
continue;
|
||||
|
||||
if(!TimeSpec::is_newer(tmp_st.st_mtim,st_->st_mtim))
|
||||
continue;
|
||||
|
||||
*st_ = tmp_st;
|
||||
newest_branch = &branch;
|
||||
}
|
||||
|
||||
if(!newest_branch)
|
||||
return -ENOENT;
|
||||
|
||||
if(symlinkify_timeout_ >= 0)
|
||||
{
|
||||
fullpath = fs::path::make(newest_branch->path,fusepath_);
|
||||
symlinkify::convert_if_can_be_symlink(fullpath,
|
||||
st_,
|
||||
symlinkify_timeout_);
|
||||
}
|
||||
|
||||
fs::inode::calc(newest_branch->path,fusepath_.string(),st_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "func_getattr_base.hpp"
|
||||
|
||||
namespace Func2
|
||||
{
|
||||
class GetAttrNewest : public GetAttrBase
|
||||
{
|
||||
public:
|
||||
GetAttrNewest() {}
|
||||
~GetAttrNewest() {}
|
||||
|
||||
public:
|
||||
std::string_view name() const;
|
||||
|
||||
public:
|
||||
int operator()(const Branches &branches,
|
||||
const fs::Path &fusepath,
|
||||
struct stat *st,
|
||||
const FollowSymlinksEnum follow_symlinks,
|
||||
const s64 symlinkify_timeout);
|
||||
};
|
||||
}
|
||||
@@ -36,41 +36,6 @@
|
||||
#include <string>
|
||||
|
||||
|
||||
static
|
||||
void
|
||||
_set_stat_if_leads_to_dir(const std::string &path_,
|
||||
struct fuse_statx *st_)
|
||||
{
|
||||
int rv;
|
||||
struct fuse_statx st;
|
||||
|
||||
rv = fs::statx(AT_FDCWD,path_,AT_SYMLINK_FOLLOW,STATX_TYPE,&st);
|
||||
if(rv < 0)
|
||||
return;
|
||||
|
||||
if(S_ISDIR(st.mode))
|
||||
*st_ = st;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_set_stat_if_leads_to_reg(const std::string &path_,
|
||||
struct fuse_statx *st_)
|
||||
{
|
||||
int rv;
|
||||
struct fuse_statx st;
|
||||
|
||||
rv = fs::statx(AT_FDCWD,path_,AT_SYMLINK_FOLLOW,STATX_TYPE,&st);
|
||||
if(rv < 0)
|
||||
return;
|
||||
|
||||
if(S_ISREG(st.mode))
|
||||
*st_ = st;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
@@ -134,28 +99,7 @@ _statx(const Policy::Search &searchFunc_,
|
||||
|
||||
fullpath = fs::path::make(branches[0]->path,fusepath_);
|
||||
|
||||
switch(followsymlinks_)
|
||||
{
|
||||
case FollowSymlinks::ENUM::NEVER:
|
||||
rv = fs::statx(AT_FDCWD,fullpath,flags_|AT_SYMLINK_NOFOLLOW,mask_,st_);
|
||||
break;
|
||||
case FollowSymlinks::ENUM::DIRECTORY:
|
||||
rv = fs::statx(AT_FDCWD,fullpath,flags_|AT_SYMLINK_NOFOLLOW,mask_,st_);
|
||||
if((rv >= 0) && S_ISLNK(st_->mode))
|
||||
::_set_stat_if_leads_to_dir(fullpath,st_);
|
||||
break;
|
||||
case FollowSymlinks::ENUM::REGULAR:
|
||||
rv = fs::statx(AT_FDCWD,fullpath,flags_|AT_SYMLINK_NOFOLLOW,mask_,st_);
|
||||
if((rv >= 0) && S_ISLNK(st_->mode))
|
||||
::_set_stat_if_leads_to_reg(fullpath,st_);
|
||||
break;
|
||||
case FollowSymlinks::ENUM::ALL:
|
||||
rv = fs::statx(AT_FDCWD,fullpath,flags_|AT_SYMLINK_FOLLOW,mask_,st_);
|
||||
if(rv < 0)
|
||||
rv = fs::statx(AT_FDCWD,fullpath,flags_|AT_SYMLINK_NOFOLLOW,mask_,st_);
|
||||
break;
|
||||
}
|
||||
|
||||
rv = fs::statx(fullpath,flags_,mask_,st_,followsymlinks_);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
@@ -210,7 +154,11 @@ FUSE::statx(const char *fusepath_,
|
||||
if(Config::is_ctrl_file(fusepath_))
|
||||
return ::_statx_controlfile(st_);
|
||||
|
||||
return ::_statx(fusepath_,flags_|AT_STATX_DONT_SYNC,mask_,st_,timeout_);
|
||||
return ::_statx(fusepath_,
|
||||
flags_|AT_STATX_DONT_SYNC,
|
||||
mask_,
|
||||
st_,
|
||||
timeout_);
|
||||
}
|
||||
|
||||
int
|
||||
@@ -222,5 +170,9 @@ FUSE::statx_fh(const uint64_t fh_,
|
||||
{
|
||||
FileInfo *fi = reinterpret_cast<FileInfo*>(fh_);
|
||||
|
||||
return ::_statx(fi->fusepath.c_str(),flags_,mask_,st_,timeout_);
|
||||
return ::_statx(fi->fusepath.c_str(),
|
||||
flags_|AT_STATX_DONT_SYNC,
|
||||
mask_,
|
||||
st_,
|
||||
timeout_);
|
||||
}
|
||||
|
||||
+48
-12
@@ -56,7 +56,6 @@ enum
|
||||
MERGERFS_OPT_VERSION
|
||||
};
|
||||
|
||||
|
||||
static
|
||||
void
|
||||
_set_option(const std::string &option_,
|
||||
@@ -204,7 +203,7 @@ _parse_and_process_kv_arg(Config::Write &cfg_,
|
||||
|
||||
static
|
||||
int
|
||||
process_opt(Config::Write &cfg_,
|
||||
_process_opt(Config::Write &cfg_,
|
||||
Config::ErrVec *errs_,
|
||||
const std::string &arg_)
|
||||
{
|
||||
@@ -220,7 +219,7 @@ process_opt(Config::Write &cfg_,
|
||||
|
||||
static
|
||||
int
|
||||
process_branches(Config::Write &cfg_,
|
||||
_process_branches(Config::Write &cfg_,
|
||||
Config::ErrVec *errs_,
|
||||
const char *arg_)
|
||||
{
|
||||
@@ -237,7 +236,7 @@ process_branches(Config::Write &cfg_,
|
||||
|
||||
static
|
||||
int
|
||||
process_mount(Config::Write &cfg_,
|
||||
_process_mount(Config::Write &cfg_,
|
||||
Config::ErrVec *errs_,
|
||||
const char *arg_)
|
||||
{
|
||||
@@ -290,10 +289,10 @@ _usage(void)
|
||||
|
||||
static
|
||||
int
|
||||
option_processor(void *data_,
|
||||
const char *arg_,
|
||||
int key_,
|
||||
fuse_args *outargs_)
|
||||
_option_processor(void *data_,
|
||||
const char *arg_,
|
||||
int key_,
|
||||
fuse_args *outargs_)
|
||||
{
|
||||
Config::Write cfg;
|
||||
Config::ErrVec *errs = (Config::ErrVec*)data_;
|
||||
@@ -301,13 +300,13 @@ option_processor(void *data_,
|
||||
switch(key_)
|
||||
{
|
||||
case FUSE_OPT_KEY_OPT:
|
||||
return process_opt(cfg,errs,arg_);
|
||||
return ::_process_opt(cfg,errs,arg_);
|
||||
|
||||
case FUSE_OPT_KEY_NONOPT:
|
||||
if(cfg->branches->empty())
|
||||
return process_branches(cfg,errs,arg_);
|
||||
return ::_process_branches(cfg,errs,arg_);
|
||||
else
|
||||
return process_mount(cfg,errs,arg_);
|
||||
return ::_process_mount(cfg,errs,arg_);
|
||||
|
||||
case MERGERFS_OPT_HELP:
|
||||
::_usage();
|
||||
@@ -367,6 +366,41 @@ _check_for_mount_loop(Config::Write &cfg_,
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_print_warnings(Config::Write &cfg_)
|
||||
{
|
||||
if(cfg_->passthrough != Passthrough::ENUM::OFF)
|
||||
{
|
||||
if(cfg_->cache_files == CacheFiles::ENUM::OFF)
|
||||
{
|
||||
SysLog::warning("'cache.files' can not be 'off' when using 'passthrough'."
|
||||
" Setting 'cache.files=auto-full'");
|
||||
cfg_->cache_files = CacheFiles::ENUM::AUTO_FULL;
|
||||
}
|
||||
|
||||
if(cfg_->writeback_cache == true)
|
||||
{
|
||||
SysLog::warning("'cache.writeback' can not be enabled when using 'passthrough'."
|
||||
" Setting 'cache.writeback=false'");
|
||||
cfg_->writeback_cache = false;
|
||||
}
|
||||
|
||||
if(cfg_->moveonenospc.enabled == true)
|
||||
{
|
||||
SysLog::warning("`moveonenospc` will not function when `passthrough` is enabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_cleanup_options(Config::Write &cfg_)
|
||||
{
|
||||
if(!cfg_->symlinkify)
|
||||
cfg_->symlinkify_timeout = -1;
|
||||
}
|
||||
|
||||
namespace options
|
||||
{
|
||||
void
|
||||
@@ -387,7 +421,7 @@ namespace options
|
||||
fuse_opt_parse(args_,
|
||||
errs_,
|
||||
opts,
|
||||
::option_processor);
|
||||
::_option_processor);
|
||||
|
||||
if(cfg->branches->empty())
|
||||
errs_->push_back({0,"branches not set"});
|
||||
@@ -400,6 +434,8 @@ namespace options
|
||||
::_set_fsname(cfg,args_);
|
||||
::_set_subtype(args_);
|
||||
::_set_fuse_threads(cfg);
|
||||
::_print_warnings(cfg);
|
||||
::_cleanup_options(cfg);
|
||||
|
||||
cfg->finish_initializing();
|
||||
}
|
||||
|
||||
+35
-7
@@ -19,9 +19,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fuse_kernel.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include "int_types.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -32,13 +30,13 @@ namespace symlinkify
|
||||
inline
|
||||
bool
|
||||
can_be_symlink(const struct stat &st_,
|
||||
const time_t timeout_)
|
||||
const s64 timeout_)
|
||||
{
|
||||
if(S_ISDIR(st_.st_mode) ||
|
||||
(st_.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)))
|
||||
return false;
|
||||
|
||||
const time_t now = ::time(NULL);
|
||||
const s64 now = ::time(NULL);
|
||||
|
||||
return (((now - st_.st_mtime) > timeout_) &&
|
||||
((now - st_.st_ctime) > timeout_));
|
||||
@@ -48,13 +46,13 @@ namespace symlinkify
|
||||
inline
|
||||
bool
|
||||
can_be_symlink(const struct fuse_statx &st_,
|
||||
const time_t timeout_)
|
||||
const s64 timeout_)
|
||||
{
|
||||
if(S_ISDIR(st_.mode) ||
|
||||
(st_.mode & (S_IWUSR|S_IWGRP|S_IWOTH)))
|
||||
return false;
|
||||
|
||||
const time_t now = ::time(NULL);
|
||||
const s64 now = ::time(NULL);
|
||||
|
||||
return (((now - st_.mtime.tv_sec) > timeout_) &&
|
||||
((now - st_.ctime.tv_sec) > timeout_));
|
||||
@@ -81,4 +79,34 @@ namespace symlinkify
|
||||
st_->size = target_.size();
|
||||
st_->blocks = 0;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
convert_if_can_be_symlink(const std::string &target_,
|
||||
struct stat *st_,
|
||||
const s64 timeout_)
|
||||
{
|
||||
if(timeout_ < 0)
|
||||
return;
|
||||
if(!symlinkify::can_be_symlink(*st_,timeout_))
|
||||
return;
|
||||
|
||||
symlinkify::convert(target_,st_);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
convert_if_can_be_symlink(const std::string &target_,
|
||||
fuse_statx *st_,
|
||||
const s64 timeout_)
|
||||
{
|
||||
if(timeout_ < 0)
|
||||
return;
|
||||
if(!symlinkify::can_be_symlink(*st_,timeout_))
|
||||
return;
|
||||
|
||||
symlinkify::convert(target_,st_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
namespace TimeSpec
|
||||
{
|
||||
static
|
||||
inline
|
||||
bool
|
||||
is_newer(const timespec &t0_,
|
||||
const timespec &t1_)
|
||||
{
|
||||
if(t0_.tv_sec > t1_.tv_sec)
|
||||
return true;
|
||||
if(t0_.tv_sec == t1_.tv_sec)
|
||||
{
|
||||
if(t0_.tv_nsec > t1_.tv_nsec)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
timespec
|
||||
newest(const timespec &t0_,
|
||||
const timespec &t1_)
|
||||
{
|
||||
if(t0_.tv_sec > t1_.tv_sec)
|
||||
return t0_;
|
||||
if(t0_.tv_sec == t1_.tv_sec)
|
||||
{
|
||||
if(t0_.tv_nsec > t1_.tv_nsec)
|
||||
return t0_;
|
||||
}
|
||||
|
||||
return t1_;
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,12 @@ namespace str
|
||||
return fmt::format("{}",uint64_);
|
||||
}
|
||||
|
||||
std::string
|
||||
to(const int64_t int64_)
|
||||
{
|
||||
return fmt::format("{}",int64_);
|
||||
}
|
||||
|
||||
std::string
|
||||
to(const std::string &s_)
|
||||
{
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace str
|
||||
std::string to(const bool);
|
||||
std::string to(const int);
|
||||
std::string to(const uint64_t);
|
||||
std::string to(const int64_t);
|
||||
std::string to(const std::string&);
|
||||
std::string to(const fs::Path&);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user