From c2354e22037c4323285014493ba7fbc3e1ba55b2 Mon Sep 17 00:00:00 2001 From: Antonio SJ Musumeci Date: Fri, 19 Sep 2025 22:48:48 -0500 Subject: [PATCH] Add getdents based readdir functions for Linux --- libfuse/Makefile | 1 - libfuse/include/fs_dirent64.hpp | 23 + libfuse/include/fuse_dirents.h | 80 ---- libfuse/include/fuse_dirents.hpp | 48 +++ libfuse/{lib => include}/fuse_msgbuf.hpp | 2 +- .../{fuse_msgbuf.h => fuse_msgbuf_t.h} | 0 libfuse/include/linux_dirent64.h | 16 - libfuse/lib/fuse.cpp | 2 +- libfuse/lib/fuse_dirents.c | 406 ------------------ libfuse/lib/fuse_dirents.cpp | 160 +++++++ libfuse/lib/fuse_i.h | 2 +- libfuse/lib/fuse_ll.hpp | 2 +- libfuse/lib/fuse_msgbuf.cpp | 5 +- src/branch.hpp | 4 +- src/branches.cpp | 2 +- src/fs_getdents64.cpp | 8 +- src/fs_getdents64.hpp | 10 +- src/fs_statx.hpp | 12 +- src/fuse_readdir.cpp | 4 +- src/fuse_readdir_cor.cpp | 148 +------ src/fuse_readdir_cor_getdents.icpp | 79 ++++ src/fuse_readdir_cor_readdir.icpp | 87 ++++ src/fuse_readdir_cosr.cpp | 162 +------ src/fuse_readdir_cosr_getdents.icpp | 131 ++++++ src/fuse_readdir_cosr_readdir.icpp | 124 ++++++ src/fuse_readdir_seq.cpp | 92 +--- src/fuse_readdir_seq_getdents.icpp | 79 ++++ src/fuse_readdir_seq_readdir.icpp | 82 ++++ src/fuse_statx.cpp | 2 +- src/supported_getdents64.hpp | 11 + src/supported_statx.hpp | 12 + 31 files changed, 896 insertions(+), 900 deletions(-) create mode 100644 libfuse/include/fs_dirent64.hpp delete mode 100644 libfuse/include/fuse_dirents.h create mode 100644 libfuse/include/fuse_dirents.hpp rename libfuse/{lib => include}/fuse_msgbuf.hpp (98%) rename libfuse/include/{fuse_msgbuf.h => fuse_msgbuf_t.h} (100%) delete mode 100644 libfuse/include/linux_dirent64.h delete mode 100644 libfuse/lib/fuse_dirents.c create mode 100644 libfuse/lib/fuse_dirents.cpp create mode 100644 src/fuse_readdir_cor_getdents.icpp create mode 100644 src/fuse_readdir_cor_readdir.icpp create mode 100644 src/fuse_readdir_cosr_getdents.icpp create mode 100644 src/fuse_readdir_cosr_readdir.icpp create mode 100644 src/fuse_readdir_seq_getdents.icpp create mode 100644 src/fuse_readdir_seq_readdir.icpp create mode 100644 src/supported_getdents64.hpp create mode 100644 src/supported_statx.hpp diff --git a/libfuse/Makefile b/libfuse/Makefile index 444cee5a..45e198b1 100644 --- a/libfuse/Makefile +++ b/libfuse/Makefile @@ -62,7 +62,6 @@ BUILDDIR := build SRC_C = \ lib/buffer.c \ lib/crc32b.c \ - lib/fuse_dirents.c \ lib/fuse_opt.c \ lib/fuse_session.c \ lib/fuse_signals.c \ diff --git a/libfuse/include/fs_dirent64.hpp b/libfuse/include/fs_dirent64.hpp new file mode 100644 index 00000000..6e660fad --- /dev/null +++ b/libfuse/include/fs_dirent64.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +namespace fs +{ + struct dirent64 + { + public: + uint64_t d_ino; + int64_t d_off; + uint16_t d_reclen; + uint8_t d_type; + char d_name[]; + + public: + int d_namelen() const + { + return (d_reclen - offsetof(dirent64,d_name)); + } + }; +} diff --git a/libfuse/include/fuse_dirents.h b/libfuse/include/fuse_dirents.h deleted file mode 100644 index e5b14095..00000000 --- a/libfuse/include/fuse_dirents.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - ISC License - - Copyright (c) 2019, Antonio SJ Musumeci - - 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 "extern_c.h" - -EXTERN_C_BEGIN - -#include "kvec.h" -#include "fuse_dirent.h" -#include "fuse_direntplus.h" -#include "fuse_entry.h" -#include "linux_dirent64.h" - -#include -#include -#include -#include -#include -#include - -enum fuse_dirents_type_e -{ - UNSET = 0, - NORMAL, - PLUS -}; -typedef enum fuse_dirents_type_e fuse_dirents_type_t; - -typedef struct fuse_dirents_t fuse_dirents_t; -struct fuse_dirents_t -{ - kvec_t(char) data; - kvec_t(uint32_t) offs; - fuse_dirents_type_t type; -}; - -int fuse_dirents_init(fuse_dirents_t *d); -void fuse_dirents_free(fuse_dirents_t *d); -void fuse_dirents_reset(fuse_dirents_t *d); - -int fuse_dirents_add(fuse_dirents_t *d, - const struct dirent *de, - const uint64_t namelen); -int fuse_dirents_add_plus(fuse_dirents_t *d, - const struct dirent *de, - const uint64_t namelen, - const fuse_entry_t *entry, - const struct stat *st); -int fuse_dirents_add_linux(fuse_dirents_t *d, - const linux_dirent64_t *de, - const uint64_t namelen); -int fuse_dirents_add_linux_plus(fuse_dirents_t *d, - const linux_dirent64_t *de, - const uint64_t namelen, - const fuse_entry_t *entry, - const struct stat *st); - -void *fuse_dirents_find(fuse_dirents_t *d, - const uint64_t ino); - -int fuse_dirents_convert_plus2normal(fuse_dirents_t *d); - -EXTERN_C_END diff --git a/libfuse/include/fuse_dirents.hpp b/libfuse/include/fuse_dirents.hpp new file mode 100644 index 00000000..13caa91c --- /dev/null +++ b/libfuse/include/fuse_dirents.hpp @@ -0,0 +1,48 @@ +/* + ISC License + + Copyright (c) 2019, Antonio SJ Musumeci + + 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 "kvec.h" +#include "fuse_dirent.h" +#include "fuse_entry.h" +#include "fs_dirent64.hpp" + +#include +#include +#include +#include +#include +#include + +struct fuse_dirents_t +{ + kvec_t(char) data; + kvec_t(uint32_t) offs; +}; + +int fuse_dirents_init(fuse_dirents_t *d); +void fuse_dirents_free(fuse_dirents_t *d); +void fuse_dirents_reset(fuse_dirents_t *d); + +int fuse_dirents_add(fuse_dirents_t *d, + const dirent *de, + const uint64_t namelen); +int fuse_dirents_add(fuse_dirents_t *d, + const fs::dirent64 *de, + const uint64_t namelen); diff --git a/libfuse/lib/fuse_msgbuf.hpp b/libfuse/include/fuse_msgbuf.hpp similarity index 98% rename from libfuse/lib/fuse_msgbuf.hpp rename to libfuse/include/fuse_msgbuf.hpp index 0e3bc6f1..07215e57 100644 --- a/libfuse/lib/fuse_msgbuf.hpp +++ b/libfuse/include/fuse_msgbuf.hpp @@ -18,7 +18,7 @@ #pragma once -#include "fuse_msgbuf.h" +#include "fuse_msgbuf_t.h" #include "extern_c.h" EXTERN_C_BEGIN diff --git a/libfuse/include/fuse_msgbuf.h b/libfuse/include/fuse_msgbuf_t.h similarity index 100% rename from libfuse/include/fuse_msgbuf.h rename to libfuse/include/fuse_msgbuf_t.h diff --git a/libfuse/include/linux_dirent64.h b/libfuse/include/linux_dirent64.h deleted file mode 100644 index efeba0d2..00000000 --- a/libfuse/include/linux_dirent64.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include -#include - -#define DIRENT_NAMELEN(X) (strlen((X)->name)) - -typedef struct linux_dirent64_t linux_dirent64_t; -struct linux_dirent64_t -{ - uint64_t ino; - int64_t off; - uint16_t reclen; - uint8_t type; - char name[]; -}; diff --git a/libfuse/lib/fuse.cpp b/libfuse/lib/fuse.cpp index f94e0845..ad7aee60 100644 --- a/libfuse/lib/fuse.cpp +++ b/libfuse/lib/fuse.cpp @@ -19,7 +19,7 @@ #include "mutex.hpp" #include "node.hpp" -#include "fuse_dirents.h" +#include "fuse_dirents.hpp" #include "fuse_i.h" #include "fuse_kernel.h" #include "fuse_lowlevel.h" diff --git a/libfuse/lib/fuse_dirents.c b/libfuse/lib/fuse_dirents.c deleted file mode 100644 index 8281459a..00000000 --- a/libfuse/lib/fuse_dirents.c +++ /dev/null @@ -1,406 +0,0 @@ -#define _FILE_OFFSET_BITS 64 - -#include "fuse_attr.h" -#include "fuse_dirent.h" -#include "fuse_direntplus.h" -#include "fuse_dirents.h" -#include "fuse_entry.h" -#include "linux_dirent64.h" -#include "stat_utils.h" - -#include -#include -#include -#include -#include -#include -#include - -/* 32KB - same as glibc getdents buffer size */ -#define DEFAULT_SIZE (1024 * 32) - -static -uint64_t -round_up(const uint64_t number_, - const uint64_t multiple_) -{ - return (((number_ + multiple_ - 1) / multiple_) * multiple_); -} - -static -uint64_t -align_uint64_t(uint64_t v_) -{ - return ((v_ + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1)); -} - -static -uint64_t -fuse_dirent_size(const uint64_t namelen_) -{ - uint64_t rv; - - rv = offsetof(fuse_dirent_t,name); - rv += namelen_; - rv = align_uint64_t(rv); - - return rv; -} - -static -uint64_t -fuse_direntplus_size(const uint64_t namelen_) -{ - uint64_t rv; - - rv = offsetof(fuse_direntplus_t,dirent.name); - rv += namelen_; - rv = align_uint64_t(rv); - - return rv; -} - -static -int -fuse_dirents_buf_resize(fuse_dirents_t *d_, - uint64_t size_) -{ - if((kv_size(d_->data) + size_) >= kv_max(d_->data)) - { - uint64_t new_size; - - new_size = round_up((kv_size(d_->data) + size_),DEFAULT_SIZE); - - kv_resize(char,d_->data,new_size); - if(d_->data.a == NULL) - return -ENOMEM; - } - - return 0; -} - -static -void* -fuse_dirents_dirent_alloc(fuse_dirents_t *d_, - uint64_t namelen_) -{ - int rv; - uint64_t size; - fuse_dirent_t *d; - - size = fuse_dirent_size(namelen_); - - rv = fuse_dirents_buf_resize(d_,size); - if(rv) - return NULL; - - d = (fuse_dirent_t*)&kv_end(d_->data); - kv_size(d_->data) += size; - - return d; -} - -static -void* -fuse_dirents_direntplus_alloc(fuse_dirents_t *d_, - uint64_t namelen_) -{ - int rv; - uint64_t size; - fuse_dirent_t *d; - - size = fuse_direntplus_size(namelen_); - - rv = fuse_dirents_buf_resize(d_,size); - if(rv) - return NULL; - - d = (fuse_dirent_t*)&kv_end(d_->data); - kv_size(d_->data) += size; - - return d; -} - -static -void -fuse_dirents_fill_attr(fuse_attr_t *attr_, - const struct stat *st_) -{ - attr_->ino = st_->st_ino; - attr_->size = st_->st_size; - attr_->blocks = st_->st_blocks; - attr_->atime = st_->st_atime; - attr_->mtime = st_->st_mtime; - attr_->ctime = st_->st_ctime; - attr_->atimensec = ST_ATIM_NSEC(st_); - attr_->mtimensec = ST_MTIM_NSEC(st_); - attr_->ctimensec = ST_CTIM_NSEC(st_); - attr_->mode = st_->st_mode; - attr_->nlink = st_->st_nlink; - attr_->uid = st_->st_uid; - attr_->gid = st_->st_gid; - attr_->rdev = st_->st_rdev; - attr_->blksize = st_->st_blksize; -} - -fuse_dirent_t* -fuse_dirent_next(fuse_dirent_t *cur_) -{ - char *buf; - - buf = (char*)cur_; - buf += fuse_dirent_size(cur_->namelen); - - return (fuse_dirent_t*)buf; -} - -fuse_direntplus_t* -fuse_direntplus_next(fuse_direntplus_t *cur_) -{ - char *buf; - - buf = (char*)cur_; - buf += fuse_direntplus_size(cur_->dirent.namelen); - - return (fuse_direntplus_t*)buf; -} - -fuse_dirent_t* -fuse_dirent_find(fuse_dirents_t *d_, - const uint64_t ino_) -{ - fuse_dirent_t *cur; - fuse_dirent_t *end; - - if(d_->type != NORMAL) - return NULL; - - cur = (fuse_dirent_t*)&kv_first(d_->data); - end = (fuse_dirent_t*)&kv_end(d_->data); - while(cur < end) - { - if(cur->ino == ino_) - return cur; - - cur = fuse_dirent_next(cur); - } - - return NULL; -} - -fuse_direntplus_t* -fuse_direntplus_find(fuse_dirents_t *d_, - const uint64_t ino_) -{ - fuse_direntplus_t *cur; - fuse_direntplus_t *end; - - if(d_->type != PLUS) - return NULL; - - cur = (fuse_direntplus_t*)&kv_first(d_->data); - end = (fuse_direntplus_t*)&kv_end(d_->data); - while(cur < end) - { - if(cur->dirent.ino == ino_) - return cur; - - cur = fuse_direntplus_next(cur); - } - - return NULL; -} - -void* -fuse_dirents_find(fuse_dirents_t *d_, - const uint64_t ino_) -{ - switch(d_->type) - { - default: - case UNSET: - return NULL; - case NORMAL: - return fuse_dirent_find(d_,ino_); - case PLUS: - return fuse_direntplus_find(d_,ino_); - } -} - -int -fuse_dirents_convert_plus2normal(fuse_dirents_t *d_) -{ - return -ENOSYS; -} - -int -fuse_dirents_add(fuse_dirents_t *d_, - const struct dirent *dirent_, - const uint64_t namelen_) -{ - fuse_dirent_t *d; - - switch(d_->type) - { - case UNSET: - d_->type = NORMAL; - break; - case NORMAL: - break; - case PLUS: - return -EINVAL; - } - - d = fuse_dirents_dirent_alloc(d_,namelen_); - if(d == NULL) - return -ENOMEM; - - d->off = kv_size(d_->offs); - kv_push(uint32_t,d_->offs,kv_size(d_->data)); - d->ino = dirent_->d_ino; - d->namelen = namelen_; - d->type = dirent_->d_type; - memcpy(d->name,dirent_->d_name,namelen_); - - return 0; -} - -int -fuse_dirents_add_plus(fuse_dirents_t *d_, - const struct dirent *dirent_, - const uint64_t namelen_, - const fuse_entry_t *entry_, - const struct stat *st_) -{ - fuse_direntplus_t *d; - - switch(d_->type) - { - case UNSET: - d_->type = PLUS; - break; - case NORMAL: - return -EINVAL; - case PLUS: - break; - } - - d = fuse_dirents_direntplus_alloc(d_,namelen_); - if(d == NULL) - return -ENOMEM; - - d->dirent.off = kv_size(d_->offs); - kv_push(uint32_t,d_->offs,kv_size(d_->data)); - d->dirent.ino = dirent_->d_ino; - d->dirent.namelen = namelen_; - d->dirent.type = dirent_->d_type; - memcpy(d->dirent.name,dirent_->d_name,namelen_); - - d->entry = *entry_; - - fuse_dirents_fill_attr(&d->attr,st_); - - return 0; -} - -int -fuse_dirents_add_linux(fuse_dirents_t *d_, - const linux_dirent64_t *dirent_, - const uint64_t namelen_) -{ - fuse_dirent_t *d; - - switch(d_->type) - { - case UNSET: - d_->type = NORMAL; - break; - case NORMAL: - break; - case PLUS: - return -EINVAL; - } - - d = fuse_dirents_dirent_alloc(d_,namelen_); - if(d == NULL) - return -ENOMEM; - - d->off = kv_size(d_->offs); - kv_push(uint32_t,d_->offs,kv_size(d_->data)); - d->ino = dirent_->ino; - d->namelen = namelen_; - d->type = dirent_->type; - memcpy(d->name,dirent_->name,namelen_); - - return 0; -} - -int -fuse_dirents_add_linux_plus(fuse_dirents_t *d_, - const linux_dirent64_t *dirent_, - const uint64_t namelen_, - const fuse_entry_t *entry_, - const struct stat *st_) -{ - fuse_direntplus_t *d; - - switch(d_->type) - { - case UNSET: - d_->type = PLUS; - break; - case NORMAL: - return -EINVAL; - case PLUS: - break; - } - - d = fuse_dirents_direntplus_alloc(d_,namelen_); - if(d == NULL) - return -ENOMEM; - - d->dirent.off = kv_size(d_->offs); - kv_push(uint32_t,d_->offs,kv_size(d_->data)); - d->dirent.ino = dirent_->ino; - d->dirent.namelen = namelen_; - d->dirent.type = dirent_->type; - memcpy(d->dirent.name,dirent_->name,namelen_); - - d->entry = *entry_; - - fuse_dirents_fill_attr(&d->attr,st_); - - return 0; -} - -void -fuse_dirents_reset(fuse_dirents_t *d_) -{ - d_->type = UNSET; - kv_size(d_->data) = 0; - kv_size(d_->offs) = 1; -} - -int -fuse_dirents_init(fuse_dirents_t *d_) -{ - d_->type = UNSET; - - kv_init(d_->data); - kv_resize(char,d_->data,DEFAULT_SIZE); - if(d_->data.a == NULL) - return -ENOMEM; - - kv_init(d_->offs); - kv_resize(uint32_t,d_->offs,64); - kv_push(uint32_t,d_->offs,0); - - return 0; -} - -void -fuse_dirents_free(fuse_dirents_t *d_) -{ - kv_destroy(d_->data); - kv_destroy(d_->offs); -} diff --git a/libfuse/lib/fuse_dirents.cpp b/libfuse/lib/fuse_dirents.cpp new file mode 100644 index 00000000..14409cc1 --- /dev/null +++ b/libfuse/lib/fuse_dirents.cpp @@ -0,0 +1,160 @@ +#define _FILE_OFFSET_BITS 64 + +#include "fs_dirent64.hpp" +#include "fuse_attr.h" +#include "fuse_dirent.h" +#include "fuse_direntplus.h" +#include "fuse_dirents.hpp" +#include "fuse_entry.h" +#include "stat_utils.h" + +#include +#include +#include +#include +#include +#include +#include + + +/* 32KB - same as glibc getdents buffer size */ +#define DENTS_BUF_EXPAND_SIZE (1024 * 32) + +static +uint64_t +_round_up(const uint64_t number_, + const uint64_t multiple_) +{ + return (((number_ + multiple_ - 1) / multiple_) * multiple_); +} + +static +uint64_t +_align_uint64_t(uint64_t v_) +{ + return ((v_ + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1)); +} + +static +uint64_t +_dirent_size(const uint64_t namelen_) +{ + uint64_t rv; + + rv = offsetof(fuse_dirent_t,name); + rv += namelen_; + rv = _align_uint64_t(rv); + + return rv; +} + +static +int +_dirents_buf_resize(fuse_dirents_t *d_, + const uint64_t size_) +{ + if((kv_size(d_->data) + size_) >= kv_max(d_->data)) + { + uint64_t new_size; + + new_size = _round_up((kv_size(d_->data) + size_),DENTS_BUF_EXPAND_SIZE); + + kv_resize(char,d_->data,new_size); + if(d_->data.a == NULL) + return -ENOMEM; + } + + return 0; +} + +static +fuse_dirent_t* +_dirents_dirent_alloc(fuse_dirents_t *d_, + const uint64_t namelen_) +{ + int rv; + uint64_t size; + fuse_dirent_t *d; + + size = _dirent_size(namelen_); + + rv = _dirents_buf_resize(d_,size); + if(rv) + return NULL; + + d = (fuse_dirent_t*)&kv_end(d_->data); + kv_size(d_->data) += size; + + return d; +} + +int +fuse_dirents_add(fuse_dirents_t *d_, + const dirent *de_, + const uint64_t namelen_) +{ + fuse_dirent_t *d; + + d = _dirents_dirent_alloc(d_,namelen_); + if(d == NULL) + return -ENOMEM; + + d->off = kv_size(d_->offs); + kv_push(uint32_t,d_->offs,kv_size(d_->data)); + d->ino = de_->d_ino; + d->namelen = namelen_; + d->type = de_->d_type; + memcpy(d->name,de_->d_name,namelen_); + + return 0; +} + +int +fuse_dirents_add(fuse_dirents_t *d_, + const fs::dirent64 *de_, + const uint64_t namelen_) +{ + fuse_dirent_t *d; + + d = _dirents_dirent_alloc(d_,namelen_); + if(d == NULL) + return -ENOMEM; + + d->off = kv_size(d_->offs); + kv_push(uint32_t,d_->offs,kv_size(d_->data)); + d->ino = de_->d_ino; + d->namelen = namelen_; + d->type = de_->d_type; + memcpy(d->name,de_->d_name,namelen_); + + return 0; +} + +void +fuse_dirents_reset(fuse_dirents_t *d_) +{ + kv_size(d_->data) = 0; + kv_size(d_->offs) = 1; +} + +int +fuse_dirents_init(fuse_dirents_t *d_) +{ + kv_init(d_->data); + kv_resize(char,d_->data,DENTS_BUF_EXPAND_SIZE); + if(d_->data.a == NULL) + return -ENOMEM; + + kv_init(d_->offs); + kv_resize(uint32_t,d_->offs,64); + kv_push(uint32_t,d_->offs,0); + + return 0; +} + +void +fuse_dirents_free(fuse_dirents_t *d_) +{ + kv_destroy(d_->data); + kv_destroy(d_->offs); +} diff --git a/libfuse/lib/fuse_i.h b/libfuse/lib/fuse_i.h index abcf4cc7..e702971d 100644 --- a/libfuse/lib/fuse_i.h +++ b/libfuse/lib/fuse_i.h @@ -10,7 +10,7 @@ #include "fuse.h" #include "fuse_lowlevel.h" -#include "fuse_msgbuf.h" +#include "fuse_msgbuf_t.h" #include "extern_c.h" diff --git a/libfuse/lib/fuse_ll.hpp b/libfuse/lib/fuse_ll.hpp index 8f7bf45f..66a07b5e 100644 --- a/libfuse/lib/fuse_ll.hpp +++ b/libfuse/lib/fuse_ll.hpp @@ -1,6 +1,6 @@ #pragma once -#include "fuse_msgbuf.h" +#include "fuse_msgbuf_t.h" int fuse_receive_buf(struct fuse_session *se, fuse_msgbuf_t *msgbuf); diff --git a/libfuse/lib/fuse_msgbuf.cpp b/libfuse/lib/fuse_msgbuf.cpp index ed44bed8..5803fd36 100644 --- a/libfuse/lib/fuse_msgbuf.cpp +++ b/libfuse/lib/fuse_msgbuf.cpp @@ -69,7 +69,7 @@ msgbuf_write_align(fuse_msgbuf_t *msgbuf_) static __attribute__((constructor)) void -msgbuf_constructor() +_msgbuf_constructor() { g_PAGESIZE = sysconf(_SC_PAGESIZE); // FUSE_MAX_MAX_PAGES for payload + 1 for message header @@ -79,7 +79,7 @@ msgbuf_constructor() static __attribute__((destructor)) void -msgbuf_destroy() +_msgbuf_destructor() { } @@ -148,7 +148,6 @@ static void msgbuf_destroy(fuse_msgbuf_t *msgbuf_) { - // free(msgbuf_->mem); free(msgbuf_); } diff --git a/src/branch.hpp b/src/branch.hpp index b6b7d1fe..5ad73535 100644 --- a/src/branch.hpp +++ b/src/branch.hpp @@ -21,6 +21,7 @@ #include "int_types.h" #include "strvec.hpp" #include "tofrom_string.hpp" +#include "fs_path.hpp" #include #include @@ -44,7 +45,8 @@ public: public: std::variant _minfreespace; Mode mode; - std::string path; + fs::path path; + // std::string path; public: Branch(); diff --git a/src/branches.cpp b/src/branches.cpp index c93aae71..43080c70 100644 --- a/src/branches.cpp +++ b/src/branches.cpp @@ -449,7 +449,7 @@ Branches::find_and_set_mode_ro() continue; SysLog::warning("branch `{}` found to be readonly - setting its mode=RO", - branch.path); + branch.path.string()); branch.mode = Branch::Mode::RO; } diff --git a/src/fs_getdents64.cpp b/src/fs_getdents64.cpp index dda24de3..6dffbd16 100644 --- a/src/fs_getdents64.cpp +++ b/src/fs_getdents64.cpp @@ -26,13 +26,13 @@ namespace fs { - int - getdents64(unsigned int fd_, + ssize_t + getdents64(const int fd_, void *dirp_, - unsigned int count_) + const size_t count_) { #if defined SYS_getdents64 - int rv; + ssize_t rv; rv = ::syscall(SYS_getdents64,fd_,dirp_,count_); diff --git a/src/fs_getdents64.hpp b/src/fs_getdents64.hpp index e608bf73..6c76c185 100644 --- a/src/fs_getdents64.hpp +++ b/src/fs_getdents64.hpp @@ -1,7 +1,7 @@ /* ISC License - Copyright (c) 2020, Antonio SJ Musumeci + Copyright (c) 2025, Antonio SJ Musumeci Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -18,11 +18,13 @@ #pragma once +#include + namespace fs { - int - getdents64(unsigned int fd, + ssize_t + getdents64(const int fd, void *dirp, - unsigned int count); + const size_t count); } diff --git a/src/fs_statx.hpp b/src/fs_statx.hpp index 9fb48eae..6c0365b0 100644 --- a/src/fs_statx.hpp +++ b/src/fs_statx.hpp @@ -13,9 +13,7 @@ #include #include -#ifdef STATX_TYPE -#define MERGERFS_STATX_SUPPORTED -#endif +#include "supported_statx.hpp" namespace fs { @@ -28,7 +26,7 @@ namespace fs const unsigned int mask_, struct fuse_statx *st_) { -#ifdef MERGERFS_STATX_SUPPORTED +#ifdef MERGERFS_SUPPORTED_STATX int rv; rv = ::statx(dirfd_, @@ -52,6 +50,10 @@ namespace fs const unsigned int mask_, struct fuse_statx *st_) { - return fs::statx(dirfd_,pathname_.c_str(),flags_,mask_,st_); + return fs::statx(dirfd_, + pathname_.c_str(), + flags_, + mask_, + st_); } } diff --git a/src/fuse_readdir.cpp b/src/fuse_readdir.cpp index 16fc2045..3b02f1cd 100644 --- a/src/fuse_readdir.cpp +++ b/src/fuse_readdir.cpp @@ -21,7 +21,7 @@ #include "fuse_readdir_factory.hpp" #include "dirinfo.hpp" -#include "fuse_dirents.h" +#include "fuse_dirents.hpp" #include "config.hpp" @@ -82,7 +82,7 @@ _handle_ENOENT(const fuse_file_info_t *ffi_, dirent de; DirInfo *di = DirInfo::from_fh(ffi_->fh); - if(di->fusepath != "/") + if(!di->fusepath.empty()) return -ENOENT; de = {0}; diff --git a/src/fuse_readdir_cor.cpp b/src/fuse_readdir_cor.cpp index b21a729e..fc02a441 100644 --- a/src/fuse_readdir_cor.cpp +++ b/src/fuse_readdir_cor.cpp @@ -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 - -#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 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> 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); } diff --git a/src/fuse_readdir_cor_getdents.icpp b/src/fuse_readdir_cor_getdents.icpp new file mode 100644 index 00000000..b84972b7 --- /dev/null +++ b/src/fuse_readdir_cor_getdents.icpp @@ -0,0 +1,79 @@ +#pragma message "using getdents" + +#include "fs_close.hpp" +#include "fs_getdents64.hpp" +#include "fs_inode.hpp" +#include "fs_open.hpp" +#include "fs_path.hpp" +#include "hashset.hpp" +#include "scope_guard.hpp" + +#include "fuse_msgbuf.hpp" +#include "fuse_dirents.hpp" + + +static +inline +int +_readdir(const fs::path &branch_path_, + const fs::path &rel_dirpath_, + HashSet &names_, + fuse_dirents_t *dirents_, + std::mutex &mutex_) +{ + int rv; + int fd; + fuse_msgbuf_t *buf; + fs::path rel_filepath; + fs::path abs_dirpath; + + abs_dirpath = branch_path_ / rel_dirpath_; + + fd = fs::open_dir_ro(abs_dirpath); + if(fd < 0) + return -fd; + DEFER{ fs::close(fd); }; + + buf = msgbuf_alloc(); + DEFER{ msgbuf_free(buf); }; + + rel_filepath = rel_dirpath_ / "dummy"; + while(true) + { + std::lock_guard lk(mutex_); + + while(true) + { + ssize_t nread; + + nread = fs::getdents64(fd,buf->mem,buf->size); + if(nread <= 0) + break; + + for(ssize_t pos = 0; pos < nread;) + { + int namelen; + fs::dirent64 *d = reinterpret_cast(&buf->mem[pos]); + + pos += d->d_reclen; + + namelen = d->d_namelen(); + rv = names_.put(d->d_name,namelen); + if(rv == 0) + continue; + + rel_filepath.replace_filename(d->d_name); + d->d_ino = fs::inode::calc(branch_path_, + rel_filepath, + DTTOIF(d->d_type), + d->d_ino); + + rv = fuse_dirents_add(dirents_,d,namelen); + if(rv < 0) + return ENOMEM; + } + } + } + + return 0; +} diff --git a/src/fuse_readdir_cor_readdir.icpp b/src/fuse_readdir_cor_readdir.icpp new file mode 100644 index 00000000..3022966f --- /dev/null +++ b/src/fuse_readdir_cor_readdir.icpp @@ -0,0 +1,87 @@ +#pragma message "using readdir" + +#define MAX_ENTRIES_PER_LOOP 64 + +#include "fs_opendir.hpp" +#include "fs_closedir.hpp" +#include "hashset.hpp" +#include "scope_guard.hpp" +#include "fs_readdir.hpp" +#include "fs_inode.hpp" + +#include "fuse_dirents.hpp" + +#include + +#include + + +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; +#else + return strlen(d_->d_name); +#endif +} + +static +inline +int +_readdir(const fs::path &branch_path_, + const fs::path &rel_dirpath_, + HashSet &names_, + fuse_dirents_t *dirents_, + 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); }; + + rel_filepath = rel_dirpath_ / "dummy"; + while(true) + { + std::lock_guard 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(dirents_,de,namelen); + if(rv < 0) + return ENOMEM; + } + } + + return 0; +} diff --git a/src/fuse_readdir_cosr.cpp b/src/fuse_readdir_cosr.cpp index d25ff244..ebd905f9 100644 --- a/src/fuse_readdir_cosr.cpp +++ b/src/fuse_readdir_cosr.cpp @@ -18,19 +18,7 @@ #include "config.hpp" #include "dirinfo.hpp" -#include "errno.hpp" -#include "fs_closedir.hpp" -#include "fs_dirfd.hpp" -#include "fs_inode.hpp" -#include "fs_opendir.hpp" -#include "fs_path.hpp" -#include "fs_readdir.hpp" -#include "fs_stat.hpp" -#include "hashset.hpp" -#include "scope_guard.hpp" -#include "ugid.hpp" - -#include "fuse_dirents.h" +#include "supported_getdents64.hpp" FUSE::ReadDirCOSR::ReadDirCOSR(unsigned concurrency_, @@ -45,143 +33,11 @@ FUSE::ReadDirCOSR::~ReadDirCOSR() } -struct DirRV -{ - const std::string *branch_path; - DIR *dir; - int err; -}; - -struct Error -{ -private: - int _err; - -public: - Error() - : _err(ENOENT) - { - - } - - operator int() - { - return _err; - } - - Error& - operator=(int const v_) - { - if(_err != 0) - _err = v_; - - return *this; - } -}; - -static -uint64_t -_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_cosr_getdents.icpp" #else - return strlen(d_->d_name); +#include "fuse_readdir_cosr_readdir.icpp" #endif -} - -static -inline -std::vector> -_opendir(ThreadPool &tp_, - const Branches::Ptr &branches_, - const fs::path &rel_dirpath_, - uid_t const uid_, - gid_t const gid_) -{ - std::vector> futures; - - futures.reserve(branches_->size()); - - for(const auto &branch : *branches_) - { - auto func = - [&branch,&rel_dirpath_,uid_,gid_]() - { - DIR *dir; - fs::path abs_dirpath; - const ugid::Set ugid(uid_,gid_); - - abs_dirpath = branch.path / rel_dirpath_; - - errno = 0; - dir = fs::opendir(abs_dirpath); - - return DirRV{&branch.path,dir,errno}; - }; - - auto rv = tp_.enqueue_task(std::move(func)); - - futures.emplace_back(std::move(rv)); - } - - return futures; -} - -static -inline -int -_readdir(std::vector> &dh_futures_, - const fs::path &rel_dirpath_, - fuse_dirents_t *buf_) -{ - Error error; - HashSet names; - fs::path rel_filepath; - - rel_filepath = rel_dirpath_ / "dummy"; - for(auto &dh_future : dh_futures_) - { - int rv; - DirRV dirrv; - - dirrv = dh_future.get(); - - error = dirrv.err; - if(dirrv.dir == NULL) - continue; - - DEFER { fs::closedir(dirrv.dir); }; - - rv = 0; - for(dirent *de = fs::readdir(dirrv.dir); de && !rv; de = fs::readdir(dirrv.dir)) - { - std::uint64_t namelen; - - 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(*dirrv.branch_path, - rel_filepath, - DTTOIF(de->d_type), - de->d_ino); - - rv = fuse_dirents_add(buf_,de,namelen); - if(rv == 0) - continue; - - error = ENOMEM; - } - } - - return -error; -} static inline @@ -189,24 +45,24 @@ int _readdir(ThreadPool &tp_, const Branches::Ptr &branches_, const fs::path &rel_dirpath_, - fuse_dirents_t *buf_, + fuse_dirents_t *dirents_, uid_t const uid_, gid_t const gid_) { int rv; std::vector> futures; - fuse_dirents_reset(buf_); + fuse_dirents_reset(dirents_); futures = ::_opendir(tp_,branches_,rel_dirpath_,uid_,gid_); - rv = ::_readdir(futures,rel_dirpath_,buf_); + rv = ::_readdir(futures,rel_dirpath_,dirents_); return rv; } int FUSE::ReadDirCOSR::operator()(fuse_file_info_t const *ffi_, - fuse_dirents_t *buf_) + fuse_dirents_t *dirents_) { DirInfo *di = DirInfo::from_fh(ffi_->fh); const fuse_context *fc = fuse_get_context(); @@ -214,7 +70,7 @@ FUSE::ReadDirCOSR::operator()(fuse_file_info_t const *ffi_, return ::_readdir(_tp, cfg.branches, di->fusepath, - buf_, + dirents_, fc->uid, fc->gid); } diff --git a/src/fuse_readdir_cosr_getdents.icpp b/src/fuse_readdir_cosr_getdents.icpp new file mode 100644 index 00000000..b2479c16 --- /dev/null +++ b/src/fuse_readdir_cosr_getdents.icpp @@ -0,0 +1,131 @@ +#pragma message "using getdents" + +#include "fs_inode.hpp" +#include "fs_getdents64.hpp" +#include "hashset.hpp" +#include "branches.hpp" +#include "error.hpp" +#include "fs_close.hpp" +#include "fs_open.hpp" +#include "fs_path.hpp" +#include "ugid.hpp" +#include "scope_guard.hpp" + +#include "fuse_msgbuf.hpp" +#include "fuse_dirents.hpp" + +#include + + +struct DirRV +{ + const fs::path *branch_path; + int fd; + int err; +}; + +static +inline +std::vector> +_opendir(ThreadPool &tp_, + const Branches::Ptr &branches_, + const fs::path &rel_dirpath_, + uid_t const uid_, + gid_t const gid_) +{ + std::vector> futures; + + futures.reserve(branches_->size()); + + for(const auto &branch : *branches_) + { + auto func = + [&branch,&rel_dirpath_,uid_,gid_]() + { + int fd; + fs::path abs_dirpath; + const ugid::Set ugid(uid_,gid_); + + abs_dirpath = branch.path / rel_dirpath_; + + errno = 0; + fd = fs::open_dir_ro(abs_dirpath); + + return DirRV{&branch.path,fd,errno}; + }; + + auto rv = tp_.enqueue_task(std::move(func)); + + futures.emplace_back(std::move(rv)); + } + + return futures; +} + +static +inline +int +_readdir(std::vector> &dh_futures_, + const fs::path &rel_dirpath_, + fuse_dirents_t *dirents_) +{ + Err err; + HashSet names; + fs::path rel_filepath; + fuse_msgbuf_t *buf; + + buf = msgbuf_alloc(); + DEFER { msgbuf_free(buf); }; + + rel_filepath = rel_dirpath_ / "dummy"; + for(auto &dh_future : dh_futures_) + { + int rv; + DirRV dirrv; + + dirrv = dh_future.get(); + + err = dirrv.err; + if(dirrv.fd == -1) + continue; + + DEFER { fs::close(dirrv.fd); }; + + rv = 0; + while(true) + { + ssize_t nread; + + nread = fs::getdents64(dirrv.fd,buf->mem,buf->size); + if(nread <= 0) + break; + + for(ssize_t pos = 0; pos < nread;) + { + int namelen; + fs::dirent64 *d = reinterpret_cast(&buf->mem[pos]); + + pos += d->d_reclen; + + namelen = d->d_namelen(); + rv = names.put(d->d_name,namelen); + if(rv == 0) + continue; + + rel_filepath.replace_filename(d->d_name); + d->d_ino = fs::inode::calc(*dirrv.branch_path, + rel_filepath, + DTTOIF(d->d_type), + d->d_ino); + + rv = fuse_dirents_add(dirents_,d,namelen); + if(rv == 0) + continue; + + err = ENOMEM; + } + } + } + + return -err; +} diff --git a/src/fuse_readdir_cosr_readdir.icpp b/src/fuse_readdir_cosr_readdir.icpp new file mode 100644 index 00000000..d40d7140 --- /dev/null +++ b/src/fuse_readdir_cosr_readdir.icpp @@ -0,0 +1,124 @@ +#pragma message "using readdir" + +#include "error.hpp" +#include "fs_closedir.hpp" +#include "fs_inode.hpp" +#include "fs_opendir.hpp" +#include "fs_readdir.hpp" +#include "hashset.hpp" +#include "scope_guard.hpp" +#include "ugid.hpp" + +#include "fuse_dirents.hpp" + + +struct DirRV +{ + const fs::path *branch_path; + DIR *dir; + int err; +}; + +static +uint64_t +_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; +#else + return strlen(d_->d_name); +#endif +} + +static +inline +std::vector> +_opendir(ThreadPool &tp_, + const Branches::Ptr &branches_, + const fs::path &rel_dirpath_, + uid_t const uid_, + gid_t const gid_) +{ + std::vector> futures; + + futures.reserve(branches_->size()); + + for(const auto &branch : *branches_) + { + auto func = + [&branch,&rel_dirpath_,uid_,gid_]() + { + DIR *dir; + fs::path abs_dirpath; + const ugid::Set ugid(uid_,gid_); + + abs_dirpath = branch.path / rel_dirpath_; + + errno = 0; + dir = fs::opendir(abs_dirpath); + + return DirRV{&branch.path,dir,errno}; + }; + + auto rv = tp_.enqueue_task(std::move(func)); + + futures.emplace_back(std::move(rv)); + } + + return futures; +} + +static +inline +int +_readdir(std::vector> &dh_futures_, + const fs::path &rel_dirpath_, + fuse_dirents_t *dirents_) +{ + Err err; + HashSet names; + fs::path rel_filepath; + + rel_filepath = rel_dirpath_ / "dummy"; + for(auto &dh_future : dh_futures_) + { + int rv; + DirRV dirrv; + + dirrv = dh_future.get(); + + err = dirrv.err; + if(dirrv.dir == NULL) + continue; + + DEFER { fs::closedir(dirrv.dir); }; + + rv = 0; + for(dirent *de = fs::readdir(dirrv.dir); de && !rv; de = fs::readdir(dirrv.dir)) + { + std::uint64_t namelen; + + 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(*dirrv.branch_path, + rel_filepath, + DTTOIF(de->d_type), + de->d_ino); + + rv = fuse_dirents_add(dirents_,de,namelen); + if(rv == 0) + continue; + + err = ENOMEM; + } + } + + return -err; +} diff --git a/src/fuse_readdir_seq.cpp b/src/fuse_readdir_seq.cpp index 7bb55c70..699f1f3c 100644 --- a/src/fuse_readdir_seq.cpp +++ b/src/fuse_readdir_seq.cpp @@ -18,99 +18,19 @@ #include "fuse_readdir_seq.hpp" -#include "error.hpp" -#include "branches.hpp" #include "config.hpp" -#include "dirinfo.hpp" -#include "errno.hpp" -#include "fs_closedir.hpp" -#include "fs_dirfd.hpp" -#include "fs_inode.hpp" -#include "fs_opendir.hpp" -#include "fs_path.hpp" -#include "fs_readdir.hpp" -#include "fs_stat.hpp" -#include "hashset.hpp" -#include "scope_guard.hpp" +#include "supported_getdents64.hpp" #include "ugid.hpp" -#include "fuse.h" -#include "fuse_dirents.h" - -#include - - -static -uint64_t -_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_seq_getdents.icpp" #else - return strlen(d_->d_name); +#include "fuse_readdir_seq_readdir.icpp" #endif -} - -static -int -_readdir(const Branches::Ptr &branches_, - const fs::path &rel_dirpath_, - fuse_dirents_t *buf_) -{ - Err err; - HashSet names; - fs::path rel_filepath; - fs::path abs_dirpath; - - fuse_dirents_reset(buf_); - - rel_filepath = rel_dirpath_ / "dummy"; - for(const auto &branch : *branches_) - { - int rv; - DIR *dh; - - abs_dirpath = branch.path / rel_dirpath_; - - errno = 0; - dh = fs::opendir(abs_dirpath); - err = -errno; - if(!dh) - continue; - - DEFER{ fs::closedir(dh); }; - - rv = 0; - for(dirent *de = fs::readdir(dh); de; de = fs::readdir(dh)) - { - std::uint64_t namelen; - - 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) - return -ENOMEM; - } - } - - return err; -} int FUSE::ReadDirSeq::operator()(fuse_file_info_t const *ffi_, - fuse_dirents_t *buf_) + fuse_dirents_t *dirents_) { DirInfo *di = DirInfo::from_fh(ffi_->fh); const fuse_context *fc = fuse_get_context(); @@ -118,5 +38,5 @@ FUSE::ReadDirSeq::operator()(fuse_file_info_t const *ffi_, return ::_readdir(cfg.branches, di->fusepath, - buf_); + dirents_); } diff --git a/src/fuse_readdir_seq_getdents.icpp b/src/fuse_readdir_seq_getdents.icpp new file mode 100644 index 00000000..6f61ef7b --- /dev/null +++ b/src/fuse_readdir_seq_getdents.icpp @@ -0,0 +1,79 @@ +#pragma message "using getdents" + +#include "dirinfo.hpp" +#include "fs_close.hpp" +#include "fs_getdents64.hpp" +#include "fs_inode.hpp" +#include "fs_open.hpp" +#include "hashset.hpp" +#include "scope_guard.hpp" + +#include "fuse_dirents.hpp" +#include "fuse_msgbuf.hpp" + + +static +int +_readdir(const Branches::Ptr &branches_, + const fs::path &rel_dirpath_, + fuse_dirents_t *dirents_) +{ + HashSet names; + fs::path rel_filepath; + fs::path abs_dirpath; + fuse_msgbuf_t *buf; + + fuse_dirents_reset(dirents_); + + buf = msgbuf_alloc(); + DEFER { msgbuf_free(buf); }; + + rel_filepath = rel_dirpath_ / "dummy"; + for(const auto &branch : *branches_) + { + int rv; + int fd; + + abs_dirpath = branch.path / rel_dirpath_; + + fd = fs::open_dir_ro(abs_dirpath); + if(fd < 0) + continue; + DEFER{ fs::close(fd); }; + + rv = 0; + while(true) + { + ssize_t nread; + + nread = fs::getdents64(fd,buf->mem,buf->size); + if(nread <= 0) + break; + + for(ssize_t pos = 0; pos < nread;) + { + int namelen; + fs::dirent64 *d = reinterpret_cast(&buf->mem[pos]); + + pos += d->d_reclen; + + namelen = d->d_namelen(); + rv = names.put(d->d_name,namelen); + if(rv == 0) + continue; + + rel_filepath.replace_filename(d->d_name); + d->d_ino = fs::inode::calc(branch.path, + rel_filepath, + DTTOIF(d->d_type), + d->d_ino); + + rv = fuse_dirents_add(dirents_,d,namelen); + if(rv) + return -ENOMEM; + } + } + } + + return 0; +} diff --git a/src/fuse_readdir_seq_readdir.icpp b/src/fuse_readdir_seq_readdir.icpp new file mode 100644 index 00000000..8f608788 --- /dev/null +++ b/src/fuse_readdir_seq_readdir.icpp @@ -0,0 +1,82 @@ +#pragma message "using readdir" + +#include "fs_opendir.hpp" +#include "fs_closedir.hpp" +#include "fs_readdir.hpp" +#include "error.hpp" +#include "hashset.hpp" +#include "scope_guard.hpp" +#include "fs_inode.hpp" +#include "dirinfo.hpp" + +#include "fuse_dirents.hpp" + +#include + +static +uint64_t +_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; +#else + return strlen(d_->d_name); +#endif +} + +static +int +_readdir(const Branches::Ptr &branches_, + const fs::path &rel_dirpath_, + fuse_dirents_t *buf_) +{ + Err err; + HashSet names; + fs::path rel_filepath; + fs::path abs_dirpath; + + fuse_dirents_reset(buf_); + + rel_filepath = rel_dirpath_ / "dummy"; + for(const auto &branch : *branches_) + { + int rv; + DIR *dh; + + abs_dirpath = branch.path / rel_dirpath_; + + errno = 0; + dh = fs::opendir(abs_dirpath); + err = -errno; + if(!dh) + continue; + + DEFER{ fs::closedir(dh); }; + + rv = 0; + for(dirent *de = fs::readdir(dh); de; de = fs::readdir(dh)) + { + std::uint64_t namelen; + + 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) + return -ENOMEM; + } + } + + return err; +} diff --git a/src/fuse_statx.cpp b/src/fuse_statx.cpp index bd8af7c8..1893a3cc 100644 --- a/src/fuse_statx.cpp +++ b/src/fuse_statx.cpp @@ -1,6 +1,6 @@ #include "fs_statx.hpp" -#ifdef MERGERFS_STATX_SUPPORTED +#ifdef MERGERFS_SUPPORTED_STATX #pragma message "statx supported" # include "fuse_statx_supported.icpp" #else diff --git a/src/supported_getdents64.hpp b/src/supported_getdents64.hpp new file mode 100644 index 00000000..bb15bfc4 --- /dev/null +++ b/src/supported_getdents64.hpp @@ -0,0 +1,11 @@ +#pragma once + +#ifdef __linux__ + +#include + +#ifdef SYS_getdents64 +#define MERGERFS_SUPPORTED_GETDENTS64 +#endif + +#endif diff --git a/src/supported_statx.hpp b/src/supported_statx.hpp new file mode 100644 index 00000000..83d7ac51 --- /dev/null +++ b/src/supported_statx.hpp @@ -0,0 +1,12 @@ +#pragma once + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include + +#ifdef STATX_TYPE +#define MERGERFS_SUPPORTED_STATX +#endif