mirror of
https://github.com/trapexit/mergerfs.git
synced 2025-04-24 22:14:05 +08:00
commit
1f3ebcf418
@ -549,6 +549,16 @@ struct fuse_operations
|
||||
int (*removemapping)();
|
||||
int (*syncfs)();
|
||||
int (*tmpfile)(const char *, mode_t, fuse_file_info_t *);
|
||||
int (*statx)(const char *fusepath,
|
||||
const uint32_t flags,
|
||||
const uint32_t mask,
|
||||
struct fuse_statx *st,
|
||||
fuse_timeouts_t *timeout);
|
||||
int (*statx_fh)(const uint64_t fh,
|
||||
const uint32_t flags,
|
||||
const uint32_t mask,
|
||||
struct fuse_statx *st,
|
||||
fuse_timeouts_t *timeout);
|
||||
};
|
||||
|
||||
/** Extra context that may be needed by some filesystems
|
||||
|
@ -257,6 +257,12 @@ int fuse_reply_attr(fuse_req_t req,
|
||||
const struct stat *attr,
|
||||
const uint64_t timeout);
|
||||
|
||||
|
||||
int fuse_reply_statx(fuse_req_t req,
|
||||
int flags,
|
||||
struct fuse_statx *st,
|
||||
const uint64_t timeout);
|
||||
|
||||
/**
|
||||
* Reply with the contents of a symbolic link
|
||||
*
|
||||
|
@ -1718,6 +1718,78 @@ fuse_lib_getattr(fuse_req_t req,
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
fuse_lib_statx_path(fuse_req_t req_,
|
||||
struct fuse_in_header *hdr_,
|
||||
struct fuse *f_,
|
||||
fuse_statx_in *inarg_)
|
||||
{
|
||||
int err;
|
||||
char *fusepath;
|
||||
struct fuse_statx st{};
|
||||
fuse_timeouts_t timeouts{};
|
||||
|
||||
err = get_path(f_,hdr_->nodeid,&fusepath);
|
||||
if(err)
|
||||
{
|
||||
fuse_reply_err(req_,err);
|
||||
return;
|
||||
}
|
||||
|
||||
err = f_->fs->op.statx(fusepath,
|
||||
inarg_->sx_flags,
|
||||
inarg_->sx_mask,
|
||||
&st,
|
||||
&timeouts);
|
||||
|
||||
free_path(f_,hdr_->nodeid,fusepath);
|
||||
|
||||
if(err)
|
||||
fuse_reply_err(req_,err);
|
||||
else
|
||||
fuse_reply_statx(req_,0,&st,timeouts.attr);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
fuse_lib_statx_fh(fuse_req_t req_,
|
||||
struct fuse_in_header *hdr_,
|
||||
struct fuse *f_,
|
||||
fuse_statx_in *inarg_)
|
||||
{
|
||||
int err;
|
||||
struct fuse_statx st{};
|
||||
fuse_timeouts_t timeouts{};
|
||||
|
||||
err = f_->fs->op.statx_fh(inarg_->fh,
|
||||
inarg_->sx_flags,
|
||||
inarg_->sx_mask,
|
||||
&st,
|
||||
&timeouts);
|
||||
|
||||
if(err)
|
||||
fuse_reply_err(req_,err);
|
||||
else
|
||||
fuse_reply_statx(req_,0,&st,timeouts.attr);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
fuse_lib_statx(fuse_req_t req_,
|
||||
struct fuse_in_header *hdr_)
|
||||
{
|
||||
struct fuse *f = req_fuse_prepare(req_);
|
||||
fuse_statx_in *inarg;
|
||||
|
||||
inarg = (fuse_statx_in*)fuse_hdr_arg(hdr_);
|
||||
|
||||
if(inarg->getattr_flags & FUSE_GETATTR_FH)
|
||||
fuse_lib_statx_fh(req_,hdr_,f,inarg);
|
||||
else
|
||||
fuse_lib_statx_path(req_,hdr_,f,inarg);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
fuse_lib_setattr(fuse_req_t req,
|
||||
@ -3643,6 +3715,7 @@ static struct fuse_lowlevel_ops fuse_path_ops =
|
||||
.setupmapping = fuse_lib_setupmapping,
|
||||
.setxattr = fuse_lib_setxattr,
|
||||
.statfs = fuse_lib_statfs,
|
||||
.statx = fuse_lib_statx,
|
||||
.symlink = fuse_lib_symlink,
|
||||
.syncfs = fuse_lib_syncfs,
|
||||
.tmpfile = fuse_lib_tmpfile,
|
||||
|
@ -343,6 +343,22 @@ fuse_reply_attr(fuse_req_t req,
|
||||
return send_reply_ok(req,&arg,size);
|
||||
}
|
||||
|
||||
int
|
||||
fuse_reply_statx(fuse_req_t req_,
|
||||
int flags_,
|
||||
struct fuse_statx *st_,
|
||||
uint64_t timeout_)
|
||||
{
|
||||
struct fuse_statx_out outarg{};
|
||||
|
||||
outarg.flags = flags_;
|
||||
outarg.attr_valid = timeout_;
|
||||
outarg.attr_valid_nsec = 0;
|
||||
outarg.stat = *(struct fuse_statx*)st_;
|
||||
|
||||
return send_reply_ok(req_,&outarg,sizeof(outarg));
|
||||
}
|
||||
|
||||
int
|
||||
fuse_reply_readlink(fuse_req_t req,
|
||||
const char *linkname)
|
||||
|
@ -16,9 +16,10 @@
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "fs_inode.hpp"
|
||||
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
#include "fs_inode.hpp"
|
||||
#include "rapidhash.h"
|
||||
|
||||
#include <cstdint>
|
||||
@ -39,7 +40,9 @@ static
|
||||
uint32_t
|
||||
h64_to_h32(uint64_t h_)
|
||||
{
|
||||
return (h_ - (h_ >> 32));
|
||||
h_ ^= (h_ >> 32);
|
||||
h_ *= 0x9E3779B9;
|
||||
return h_;
|
||||
}
|
||||
|
||||
static
|
||||
@ -227,6 +230,18 @@ namespace fs
|
||||
st_->st_ino);
|
||||
}
|
||||
|
||||
void
|
||||
calc(const char *fusepath_,
|
||||
const uint64_t fusepath_len_,
|
||||
struct fuse_statx *st_)
|
||||
{
|
||||
st_->ino = calc(fusepath_,
|
||||
fusepath_len_,
|
||||
st_->mode,
|
||||
st_->dev_major ^ st_->dev_minor,
|
||||
st_->ino);
|
||||
}
|
||||
|
||||
void
|
||||
calc(const char *fusepath_,
|
||||
struct stat *st_)
|
||||
@ -234,6 +249,13 @@ namespace fs
|
||||
calc(fusepath_,strlen(fusepath_),st_);
|
||||
}
|
||||
|
||||
void
|
||||
calc(const char *fusepath_,
|
||||
struct fuse_statx *st_)
|
||||
{
|
||||
calc(fusepath_,strlen(fusepath_),st_);
|
||||
}
|
||||
|
||||
void
|
||||
calc(const std::string &fusepath_,
|
||||
struct stat *st_)
|
||||
|
@ -18,6 +18,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fuse_kernel.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
@ -42,13 +44,20 @@ namespace fs
|
||||
mode_t const mode,
|
||||
dev_t const dev,
|
||||
ino_t ino);
|
||||
|
||||
void calc(const char *fusepath,
|
||||
const uint64_t fusepath_len,
|
||||
struct stat *st);
|
||||
void calc(const char *fusepath,
|
||||
const uint64_t fusepath_len,
|
||||
struct fuse_statx *st);
|
||||
|
||||
void calc(const char *fusepath,
|
||||
struct stat *st);
|
||||
void calc(const char *fusepath,
|
||||
struct fuse_statx *st);
|
||||
|
||||
void calc(const std::string &fusepath,
|
||||
struct stat *st);
|
||||
|
||||
}
|
||||
}
|
||||
|
51
src/fs_statx.hpp
Normal file
51
src/fs_statx.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include "fuse_kernel.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace fs
|
||||
{
|
||||
static
|
||||
inline
|
||||
int
|
||||
statx(const int dirfd_,
|
||||
const char *pathname_,
|
||||
const int flags_,
|
||||
const unsigned int mask_,
|
||||
struct fuse_statx *st_)
|
||||
{
|
||||
#ifdef STATX_TYPE
|
||||
int rv;
|
||||
|
||||
rv = ::statx(dirfd_,
|
||||
pathname_,
|
||||
flags_,
|
||||
mask_,
|
||||
(struct statx*)st_);
|
||||
|
||||
return ((rv == -1) ? -errno : 0);
|
||||
#else
|
||||
return -ENOSYS;
|
||||
#endif
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
statx(const int dirfd_,
|
||||
const std::string &pathname_,
|
||||
const int flags_,
|
||||
const unsigned int mask_,
|
||||
struct fuse_statx *st_)
|
||||
{
|
||||
return fs::statx(dirfd_,pathname_.c_str(),flags_,mask_,st_);
|
||||
}
|
||||
}
|
186
src/fuse_statx.cpp
Normal file
186
src/fuse_statx.cpp
Normal file
@ -0,0 +1,186 @@
|
||||
#include "fuse_statx.hpp"
|
||||
|
||||
#include "config.hpp"
|
||||
#include "errno.hpp"
|
||||
#include "fileinfo.hpp"
|
||||
#include "fs_inode.hpp"
|
||||
#include "fs_path.hpp"
|
||||
#include "fs_statx.hpp"
|
||||
#include "symlinkify.hpp"
|
||||
#include "ugid.hpp"
|
||||
|
||||
#include "fmt/core.h"
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
#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
|
||||
_statx_controlfile(struct fuse_statx *st_)
|
||||
{
|
||||
static const uid_t uid = ::getuid();
|
||||
static const gid_t gid = ::getgid();
|
||||
static const time_t now = ::time(NULL);
|
||||
|
||||
st_->ino = fs::inode::MAGIC;
|
||||
st_->mode = (S_IFREG|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
|
||||
st_->nlink = 1;
|
||||
st_->uid = uid;
|
||||
st_->gid = gid;
|
||||
st_->size = 0;
|
||||
st_->blocks = 0;
|
||||
st_->atime.tv_sec = now;
|
||||
st_->mtime.tv_sec = now;
|
||||
st_->ctime.tv_sec = now;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
_statx(const Policy::Search &searchFunc_,
|
||||
const Branches &branches_,
|
||||
const char *fusepath_,
|
||||
const uint32_t flags_,
|
||||
const uint32_t mask_,
|
||||
struct fuse_statx *st_,
|
||||
const bool symlinkify_,
|
||||
const time_t symlinkify_timeout_,
|
||||
FollowSymlinks followsymlinks_)
|
||||
{
|
||||
int rv;
|
||||
std::string fullpath;
|
||||
StrVec basepaths;
|
||||
|
||||
rv = searchFunc_(branches_,fusepath_,&basepaths);
|
||||
if(rv == -1)
|
||||
return -errno;
|
||||
|
||||
fullpath = fs::path::make(basepaths[0],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;
|
||||
}
|
||||
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
if(symlinkify_ && symlinkify::can_be_symlink(*st_,symlinkify_timeout_))
|
||||
symlinkify::convert(fullpath,st_);
|
||||
|
||||
fs::inode::calc(fusepath_,st_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
_statx(const char *fusepath_,
|
||||
const uint32_t flags_,
|
||||
const uint32_t mask_,
|
||||
struct fuse_statx *st_,
|
||||
fuse_timeouts_t *timeout_)
|
||||
{
|
||||
int rv;
|
||||
Config::Read cfg;
|
||||
const fuse_context *fc = fuse_get_context();
|
||||
const ugid::Set ugid(fc->uid,fc->gid);
|
||||
|
||||
rv = ::_statx(cfg->func.getattr.policy,
|
||||
cfg->branches,
|
||||
fusepath_,
|
||||
flags_,
|
||||
mask_,
|
||||
st_,
|
||||
cfg->symlinkify,
|
||||
cfg->symlinkify_timeout,
|
||||
cfg->follow_symlinks);
|
||||
|
||||
timeout_->entry = ((rv >= 0) ?
|
||||
cfg->cache_entry :
|
||||
cfg->cache_negative_entry);
|
||||
timeout_->attr = cfg->cache_attr;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
FUSE::statx(const char *fusepath_,
|
||||
const uint32_t flags_,
|
||||
const uint32_t mask_,
|
||||
struct fuse_statx *st_,
|
||||
fuse_timeouts_t *timeout_)
|
||||
{
|
||||
if(fusepath_ == CONTROLFILE)
|
||||
return ::_statx_controlfile(st_);
|
||||
|
||||
return ::_statx(fusepath_,flags_|AT_STATX_DONT_SYNC,mask_,st_,timeout_);
|
||||
}
|
||||
|
||||
int
|
||||
FUSE::statx_fh(const uint64_t fh_,
|
||||
const uint32_t flags_,
|
||||
const uint32_t mask_,
|
||||
struct fuse_statx *st_,
|
||||
fuse_timeouts_t *timeout_)
|
||||
{
|
||||
FileInfo *fi = reinterpret_cast<FileInfo*>(fh_);
|
||||
|
||||
return ::_statx(fi->fusepath.c_str(),flags_,mask_,st_,timeout_);
|
||||
}
|
38
src/fuse_statx.hpp
Normal file
38
src/fuse_statx.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fuse.h"
|
||||
#include "fuse_kernel.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace FUSE
|
||||
{
|
||||
int statx(const char *fusepath,
|
||||
const uint32_t flags,
|
||||
const uint32_t mask,
|
||||
struct fuse_statx *st,
|
||||
fuse_timeouts_t *timeout);
|
||||
int statx_fh(const uint64_t fh,
|
||||
const uint32_t flags,
|
||||
const uint32_t mask,
|
||||
struct fuse_statx *st,
|
||||
fuse_timeouts_t *timeout);
|
||||
}
|
@ -71,6 +71,7 @@
|
||||
#include "fuse_setupmapping.hpp"
|
||||
#include "fuse_setxattr.hpp"
|
||||
#include "fuse_statfs.hpp"
|
||||
#include "fuse_statx.hpp"
|
||||
#include "fuse_symlink.hpp"
|
||||
#include "fuse_syncfs.hpp"
|
||||
#include "fuse_tmpfile.hpp"
|
||||
@ -139,6 +140,8 @@ namespace l
|
||||
ops_.setupmapping = FUSE::setupmapping;
|
||||
ops_.setxattr = FUSE::setxattr;
|
||||
ops_.statfs = FUSE::statfs;
|
||||
ops_.statx = FUSE::statx;
|
||||
ops_.statx_fh = FUSE::statx_fh;
|
||||
ops_.symlink = FUSE::symlink;
|
||||
ops_.syncfs = FUSE::syncfs;
|
||||
ops_.tmpfile = FUSE::tmpfile;
|
||||
|
@ -18,6 +18,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fuse_kernel.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
|
||||
@ -42,6 +44,22 @@ namespace symlinkify
|
||||
((now - st_.st_ctime) > timeout_));
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
bool
|
||||
can_be_symlink(const struct fuse_statx &st_,
|
||||
const time_t timeout_)
|
||||
{
|
||||
if(S_ISDIR(st_.mode) ||
|
||||
(st_.mode & (S_IWUSR|S_IWGRP|S_IWOTH)))
|
||||
return false;
|
||||
|
||||
const time_t now = ::time(NULL);
|
||||
|
||||
return (((now - st_.mtime.tv_sec) > timeout_) &&
|
||||
((now - st_.ctime.tv_sec) > timeout_));
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
@ -52,4 +70,15 @@ namespace symlinkify
|
||||
st_->st_size = target_.size();
|
||||
st_->st_blocks = 0;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
convert(const std::string &target_,
|
||||
struct fuse_statx *st_)
|
||||
{
|
||||
st_->mode = (((st_->mode & ~S_IFMT) | S_IFLNK) | 0777);
|
||||
st_->size = target_.size();
|
||||
st_->blocks = 0;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user