#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 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 = 0; 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(basepaths[0],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(fh_); return ::_statx(fi->fusepath.c_str(),flags_,mask_,st_,timeout_); }