Add getdents based readdir functions for Linux (#1533)
Some checks failed
CodeQL / Analyze (push) Has been cancelled
mkdocs / deploy (push) Has been cancelled

This commit is contained in:
trapexit 2025-09-24 00:51:21 -04:00 • committed by GitHub
parent 95ab37fdd2
commit 2f3e807e9d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 886 additions and 904 deletions

View file

@ -16,29 +16,13 @@
#include "fuse_readdir_cor.hpp"
#include "supported_getdents64.hpp"
#include "config.hpp"
#include "dirinfo.hpp"
#include "errno.hpp"
#include "fs_close.hpp"
#include "fs_closedir.hpp"
#include "fs_getdents64.hpp"
#include "fs_inode.hpp"
#include "fs_opendir.hpp"
#include "fs_path.hpp"
#include "fs_readdir.hpp"
#include "hashset.hpp"
#include "scope_guard.hpp"
#include "error.hpp"
#include "ugid.hpp"
#include "fuse_dirents.h"
#include "linux_dirent64.h"
#include "int_types.h"
#include <filesystem>
#define MAX_ENTRIES_PER_LOOP 64
FUSE::ReadDirCOR::ReadDirCOR(unsigned concurrency_,
unsigned max_queue_depth_)
@ -52,113 +36,11 @@ FUSE::ReadDirCOR::~ReadDirCOR()
}
static
u64
_dirent_exact_namelen(const struct dirent *d_)
{
#ifdef _D_EXACT_NAMLEN
return _D_EXACT_NAMLEN(d_);
#elif defined _DIRENT_HAVE_D_NAMLEN
return d_->d_namlen;
#ifdef MERGERFS_SUPPORTED_GETDENTS64
#include "fuse_readdir_cor_getdents.icpp"
#else
return strlen(d_->d_name);
#include "fuse_readdir_cor_readdir.icpp"
#endif
}
struct Error
{
private:
int _err;
public:
Error()
: _err(ENOENT)
{
}
operator int()
{
return _err;
}
Error&
operator=(int v_)
{
if(_err != 0)
_err = v_;
return *this;
}
};
static
inline
int
_readdir(const fs::path &branch_path_,
const fs::path &rel_dirpath_,
HashSet &names_,
fuse_dirents_t *buf_,
std::mutex &mutex_)
{
int rv;
DIR *dir;
fs::path rel_filepath;
fs::path abs_dirpath;
abs_dirpath = branch_path_ / rel_dirpath_;
errno = 0;
dir = fs::opendir(abs_dirpath);
if(dir == NULL)
return errno;
DEFER{ fs::closedir(dir); };
// The default buffer size in glibc is 32KB. 64 is based on a
// guess of an average size of 512B per entry. This is to limit
// contention on the lock when needing to request more data from
// the kernel. This could be handled better by using getdents but
// due to the differences between Linux and FreeBSD a more
// complicated abstraction needs to be created to handle them to
// make it easier to use across the codebase.
// * https://man7.org/linux/man-pages/man2/getdents.2.html
// * https://man.freebsd.org/cgi/man.cgi?query=getdents
rel_filepath = rel_dirpath_ / "dummy";
while(true)
{
std::lock_guard<std::mutex> lk(mutex_);
for(int i = 0; i < MAX_ENTRIES_PER_LOOP; i++)
{
dirent *de;
u64 namelen;
de = fs::readdir(dir);
if(de == NULL)
return 0;
namelen = ::_dirent_exact_namelen(de);
rv = names_.put(de->d_name,namelen);
if(rv == 0)
continue;
rel_filepath.replace_filename(de->d_name);
de->d_ino = fs::inode::calc(branch_path_,
rel_filepath,
DTTOIF(de->d_type),
de->d_ino);
rv = fuse_dirents_add(buf_,de,namelen);
if(rv >= 0)
continue;
return ENOMEM;
}
}
return 0;
}
static
inline
@ -166,7 +48,7 @@ int
_concurrent_readdir(ThreadPool &tp_,
const Branches::Ptr &branches_,
const fs::path &rel_dirpath_,
fuse_dirents_t *buf_,
fuse_dirents_t *dirents_,
const uid_t uid_,
const gid_t gid_)
{
@ -174,20 +56,20 @@ _concurrent_readdir(ThreadPool &tp_,
std::mutex mutex;
std::vector<std::future<int>> futures;
fuse_dirents_reset(buf_);
fuse_dirents_reset(dirents_);
futures.reserve(branches_->size());
for(const auto &branch : *branches_)
{
auto func =
[&,buf_,uid_,gid_]()
[&,dirents_,uid_,gid_]()
{
const ugid::Set ugid(uid_,gid_);
return ::_readdir(branch.path,
rel_dirpath_,
names,
buf_,
dirents_,
mutex);
};
@ -196,16 +78,16 @@ _concurrent_readdir(ThreadPool &tp_,
futures.emplace_back(std::move(rv));
}
Error error;
Err err;
for(auto &future : futures)
error = future.get();
err = future.get();
return -error;
return err;
}
int
FUSE::ReadDirCOR::operator()(const fuse_file_info_t *ffi_,
fuse_dirents_t *buf_)
fuse_dirents_t *dirents_)
{
DirInfo *di = DirInfo::from_fh(ffi_->fh);
const fuse_context *fc = fuse_get_context();
@ -213,7 +95,7 @@ FUSE::ReadDirCOR::operator()(const fuse_file_info_t *ffi_,
return ::_concurrent_readdir(_tp,
cfg.branches,
di->fusepath,
buf_,
dirents_,
fc->uid,
fc->gid);
}