From 62873d2d3b0e174138cc915ce662d232302abe89 Mon Sep 17 00:00:00 2001 From: Antonio SJ Musumeci Date: Mon, 24 Feb 2020 18:14:00 -0500 Subject: [PATCH] use getdents64 on linux --- libfuse/include/fuse_dirents.h | 13 ++- libfuse/lib/fuse_dirents.c | 99 ++++++++++++++++-- src/fixed_mem_pool.hpp | 87 ++++++++++++++++ src/fs_base_open.hpp | 8 ++ src/fuse_getxattr.cpp | 2 + src/fuse_listxattr.cpp | 1 + src/fuse_readdir.cpp | 104 ++----------------- src/fuse_readdir_linux.icpp | 161 +++++++++++++++++++++++++++++ src/fuse_readdir_plus.cpp | 131 ++--------------------- src/fuse_readdir_plus_linux.icpp | 172 +++++++++++++++++++++++++++++++ src/fuse_readdir_plus_posix.icpp | 125 ++++++++++++++++++++++ src/fuse_readdir_posix.icpp | 111 ++++++++++++++++++++ src/locked_fixed_mem_pool.hpp | 69 +++++++++++++ src/mempools.cpp | 21 ++++ src/mempools.hpp | 23 +++++ 15 files changed, 898 insertions(+), 229 deletions(-) create mode 100644 src/fixed_mem_pool.hpp create mode 100644 src/fuse_readdir_linux.icpp create mode 100644 src/fuse_readdir_plus_linux.icpp create mode 100644 src/fuse_readdir_plus_posix.icpp create mode 100644 src/fuse_readdir_posix.icpp create mode 100644 src/locked_fixed_mem_pool.hpp create mode 100644 src/mempools.cpp create mode 100644 src/mempools.hpp diff --git a/libfuse/include/fuse_dirents.h b/libfuse/include/fuse_dirents.h index 501e1b9a..963960a3 100644 --- a/libfuse/include/fuse_dirents.h +++ b/libfuse/include/fuse_dirents.h @@ -54,12 +54,21 @@ 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, - struct dirent *de); +int fuse_dirents_add(fuse_dirents_t *d, + const struct dirent *de); int fuse_dirents_add_plus(fuse_dirents_t *d, const struct dirent *de, const fuse_entry_t *entry, const struct stat *st); +#ifdef __linux__ +struct linux_dirent64; +int fuse_dirents_add_linux(fuse_dirents_t *d, + const struct linux_dirent64 *de); +int fuse_dirents_add_linux_plus(fuse_dirents_t *d, + const struct linux_dirent64 *de, + const fuse_entry_t *entry, + const struct stat *st); +#endif void *fuse_dirents_find(fuse_dirents_t *d, const uint64_t ino); diff --git a/libfuse/lib/fuse_dirents.c b/libfuse/lib/fuse_dirents.c index e33ea3fc..629c6909 100644 --- a/libfuse/lib/fuse_dirents.c +++ b/libfuse/lib/fuse_dirents.c @@ -17,10 +17,6 @@ #define DEFAULT_SIZE (1024 * 16) -#ifndef _D_EXACT_NAMLEN -# define _D_EXACT_NAMLEN(d) ((d)->d_namlen) -#endif - static uint64_t align_uint64_t(uint64_t v_) @@ -237,8 +233,8 @@ fuse_dirents_convert_plus2normal(fuse_dirents_t *d_) } int -fuse_dirents_add(fuse_dirents_t *d_, - struct dirent *dirent_) +fuse_dirents_add(fuse_dirents_t *d_, + const struct dirent *dirent_) { uint64_t size; uint64_t namelen; @@ -255,7 +251,7 @@ fuse_dirents_add(fuse_dirents_t *d_, return -EINVAL; } - namelen = _D_EXACT_NAMLEN(dirent_); + namelen = _D_ALLOC_NAMLEN(dirent_); size = fuse_dirent_size(namelen); d = fuse_dirents_alloc(d_,size); @@ -292,7 +288,7 @@ fuse_dirents_add_plus(fuse_dirents_t *d_, break; } - namelen = _D_EXACT_NAMLEN(dirent_); + namelen = _D_ALLOC_NAMLEN(dirent_); size = fuse_direntplus_size(namelen); d = fuse_dirents_alloc(d_,size); @@ -312,6 +308,93 @@ fuse_dirents_add_plus(fuse_dirents_t *d_, return 0; } +#ifdef __linux__ +struct linux_dirent64 +{ + uint64_t d_ino; + int64_t d_off; + uint16_t d_reclen; + uint8_t d_type; + char d_name[]; +}; + +int +fuse_dirents_add_linux(fuse_dirents_t *d_, + const struct linux_dirent64 *dirent_) +{ + uint64_t size; + uint64_t namelen; + fuse_dirent_t *d; + + switch(d_->type) + { + case UNSET: + d_->type = NORMAL; + break; + case NORMAL: + break; + case PLUS: + return -EINVAL; + } + + namelen = (dirent_->d_reclen - offsetof(struct linux_dirent64,d_name)); + size = fuse_dirent_size(namelen); + + d = fuse_dirents_alloc(d_,size); + if(d == NULL) + return -ENOMEM; + + d->ino = dirent_->d_ino; + d->off = d_->data_len; + d->namelen = namelen; + d->type = dirent_->d_type; + memcpy(d->name,dirent_->d_name,namelen); + + return 0; +} + +int +fuse_dirents_add_linux_plus(fuse_dirents_t *d_, + const struct linux_dirent64 *dirent_, + const fuse_entry_t *entry_, + const struct stat *st_) +{ + uint64_t size; + uint64_t namelen; + fuse_direntplus_t *d; + + switch(d_->type) + { + case UNSET: + d_->type = PLUS; + break; + case NORMAL: + return -EINVAL; + case PLUS: + break; + } + + namelen = (dirent_->d_reclen - offsetof(struct linux_dirent64,d_name)); + size = fuse_direntplus_size(namelen); + + d = fuse_dirents_alloc(d_,size); + if(d == NULL) + return -ENOMEM; + + d->dirent.ino = dirent_->d_ino; + d->dirent.off = d_->data_len; + 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; +} +#endif + void fuse_dirents_reset(fuse_dirents_t *d_) { diff --git a/src/fixed_mem_pool.hpp b/src/fixed_mem_pool.hpp new file mode 100644 index 00000000..e81f1343 --- /dev/null +++ b/src/fixed_mem_pool.hpp @@ -0,0 +1,87 @@ +/* + ISC License + + Copyright (c) 2020, 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 +#include + +typedef struct fixed_mem_pool_t fixed_mem_pool_t; +struct fixed_mem_pool_t +{ + fixed_mem_pool_t *next; +}; + +template +class FixedMemPool +{ +public: + FixedMemPool() + { + list.next = NULL; + } + + ~FixedMemPool() + { + void *mem; + while(!empty()) + { + mem = alloc(); + ::free(mem); + } + } + + bool + empty(void) + { + return (list.next == NULL); + } + + uint64_t + size(void) + { + return SIZE; + } + + void* + alloc(void) + { + void *rv; + + if(list.next == NULL) + return malloc(SIZE); + + rv = (void*)list.next; + list.next = list.next->next; + + return rv; + } + + void + free(void *mem_) + { + fixed_mem_pool_t *next; + + next = (fixed_mem_pool_t*)mem_; + next->next = list.next; + list.next = next; + } + +private: + fixed_mem_pool_t list; +}; diff --git a/src/fs_base_open.hpp b/src/fs_base_open.hpp index 6346095e..2e4a7279 100644 --- a/src/fs_base_open.hpp +++ b/src/fs_base_open.hpp @@ -63,4 +63,12 @@ namespace fs { return fs::open(path_.c_str(),flags_,mode_); } + + static + inline + int + open_dir_ro(const std::string &path_) + { + return fs::open(path_,O_RDONLY|O_DIRECTORY); + } } diff --git a/src/fuse_getxattr.cpp b/src/fuse_getxattr.cpp index 503fe8d3..89a1485e 100644 --- a/src/fuse_getxattr.cpp +++ b/src/fuse_getxattr.cpp @@ -372,6 +372,8 @@ namespace l l::getxattr_controlfile_bool(config.async_read,attrvalue); else if(attr[2] == "fuse_msg_size") l::getxattr_controlfile(config.fuse_msg_size,attrvalue); + else if(attr[2] == "readdirplus") + l::getxattr_controlfile_bool(config.readdirplus,attrvalue); break; case 4: diff --git a/src/fuse_listxattr.cpp b/src/fuse_listxattr.cpp index 242bdc5c..98728716 100644 --- a/src/fuse_listxattr.cpp +++ b/src/fuse_listxattr.cpp @@ -66,6 +66,7 @@ namespace l ("user.mergerfs.pid") ("user.mergerfs.policies") ("user.mergerfs.posix_acl") + ("user.mergerfs.readdirplus") ("user.mergerfs.security_capability") ("user.mergerfs.srcmounts") ("user.mergerfs.statfs") diff --git a/src/fuse_readdir.cpp b/src/fuse_readdir.cpp index 4360297b..d2e9580b 100644 --- a/src/fuse_readdir.cpp +++ b/src/fuse_readdir.cpp @@ -1,5 +1,7 @@ /* - Copyright (c) 2019, Antonio SJ Musumeci + ISC License + + Copyright (c) 2020, 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 @@ -14,98 +16,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define _DEFAULT_SOURCE - -#include "config.hpp" -#include "dirinfo.hpp" -#include "errno.hpp" -#include "fs_base_closedir.hpp" -#include "fs_base_dirfd.hpp" -#include "fs_base_opendir.hpp" -#include "fs_base_readdir.hpp" -#include "fs_base_stat.hpp" -#include "fs_devid.hpp" -#include "fs_inode.hpp" -#include "fs_path.hpp" -#include "hashset.hpp" -#include "rwlock.hpp" -#include "ugid.hpp" - -#include -#include - -#include -#include - -#include - -using std::string; -using std::vector; - -namespace l -{ - static - int - readdir(const Branches &branches_, - const char *dirname_, - fuse_dirents_t *buf_) - { - dev_t dev; - HashSet names; - string basepath; - - for(size_t i = 0, ei = branches_.size(); i != ei; i++) - { - int rv; - int dirfd; - DIR *dh; - - basepath = fs::path::make(&branches_[i].path,dirname_); - - dh = fs::opendir(basepath); - if(!dh) - continue; - - dirfd = fs::dirfd(dh); - dev = fs::devid(dirfd); - if(dev == (dev_t)-1) - dev = i; - - rv = 0; - for(struct dirent *de = fs::readdir(dh); de && !rv; de = fs::readdir(dh)) - { - rv = names.put(de->d_name,_D_EXACT_NAMLEN(de)); - if(rv == 0) - continue; - - de->d_ino = fs::inode::recompute(de->d_ino,dev); - - rv = fuse_dirents_add(buf_,de); - if(rv) - return (fs::closedir(dh),-ENOMEM); - } - - fs::closedir(dh); - } - - return 0; - } -} - -namespace FUSE -{ - int - readdir(fuse_file_info *ffi_, - fuse_dirents_t *buf_) - { - DirInfo *di = reinterpret_cast(ffi_->fh); - const fuse_context *fc = fuse_get_context(); - const Config &config = Config::get(fc); - const ugid::Set ugid(fc->uid,fc->gid); - const rwlock::ReadGuard readlock(&config.branches_lock); - - return l::readdir(config.branches, - di->fusepath.c_str(), - buf_); - } -} +#ifdef __linux__ +# include "fuse_readdir_linux.icpp" +#else +# include "fuse_readdir_posix.icpp" +#endif diff --git a/src/fuse_readdir_linux.icpp b/src/fuse_readdir_linux.icpp new file mode 100644 index 00000000..a03a1fa6 --- /dev/null +++ b/src/fuse_readdir_linux.icpp @@ -0,0 +1,161 @@ +/* + 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. +*/ + +#define _DEFAULT_SOURCE + +#include "config.hpp" +#include "dirinfo.hpp" +#include "errno.hpp" +#include "fs_base_close.hpp" +#include "fs_base_open.hpp" +#include "fs_base_stat.hpp" +#include "fs_devid.hpp" +#include "fs_inode.hpp" +#include "fs_path.hpp" +#include "hashset.hpp" +#include "mempools.hpp" +#include "rwlock.hpp" +#include "ugid.hpp" + +#include +#include + +#include +#include + +#include +#include + +using std::string; +using std::vector; + +struct linux_dirent64 +{ + uint64_t d_ino; + int64_t d_off; + uint16_t d_reclen; + uint8_t d_type; + char d_name[]; +}; + +namespace l +{ + static + inline + int + getdents64(unsigned int fd_, + char *dirp_, + unsigned int count_) + { + return syscall(SYS_getdents64,fd_,dirp_,count_); + } + + static + int + close_free_ret_enomem(int fd_, + void *buf_) + { + fs::close(fd_); + g_DENTS_BUF_POOL.free(buf_); + return -ENOMEM; + } + + static + int + readdir(const Branches &branches_, + const char *dirname_, + fuse_dirents_t *buf_) + { + int rv; + dev_t dev; + char *buf; + HashSet names; + string basepath; + struct linux_dirent64 *d; + + buf = (char*)g_DENTS_BUF_POOL.alloc(); + if(buf == NULL) + return -ENOMEM; + + for(size_t i = 0, ei = branches_.size(); i != ei; i++) + { + int dirfd; + int64_t nread; + uint64_t namelen; + + basepath = fs::path::make(&branches_[i].path,dirname_); + + dirfd = fs::open_dir_ro(basepath); + if(dirfd == -1) + continue; + + dev = fs::devid(dirfd); + if(dev == (dev_t)-1) + dev = i; + + for(;;) + { + nread = l::getdents64(dirfd,buf,g_DENTS_BUF_POOL.size()); + if(nread == -1) + break; + if(nread == 0) + break; + + for(int64_t pos = 0; pos < nread;) + { + d = (struct linux_dirent64*)(buf + pos); + namelen = (d->d_reclen - offsetof(struct linux_dirent64,d_name)); + + rv = names.put(d->d_name,namelen); + if(rv == 0) + continue; + + d->d_ino = fs::inode::recompute(d->d_ino,dev); + + rv = fuse_dirents_add_linux(buf_,d); + if(rv) + return close_free_ret_enomem(dirfd,buf); + + pos += d->d_reclen; + } + } + + fs::close(dirfd); + } + + g_DENTS_BUF_POOL.free(buf); + + return 0; + } +} + +namespace FUSE +{ + int + readdir(fuse_file_info *ffi_, + fuse_dirents_t *buf_) + { + DirInfo *di = reinterpret_cast(ffi_->fh); + const fuse_context *fc = fuse_get_context(); + const Config &config = Config::get(fc); + const ugid::Set ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.branches_lock); + + return l::readdir(config.branches, + di->fusepath.c_str(), + buf_); + } +} diff --git a/src/fuse_readdir_plus.cpp b/src/fuse_readdir_plus.cpp index 53fbf522..bb46542f 100644 --- a/src/fuse_readdir_plus.cpp +++ b/src/fuse_readdir_plus.cpp @@ -1,5 +1,7 @@ /* - Copyright (c) 2019, Antonio SJ Musumeci + ISC License + + Copyright (c) 2020, 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 @@ -14,125 +16,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define _DEFAULT_SOURCE - -#include "config.hpp" -#include "dirinfo.hpp" -#include "errno.hpp" -#include "fs_base_closedir.hpp" -#include "fs_base_dirfd.hpp" -#include "fs_base_opendir.hpp" -#include "fs_base_readdir.hpp" -#include "fs_base_fstatat.hpp" -#include "fs_base_stat.hpp" -#include "fs_devid.hpp" -#include "fs_inode.hpp" -#include "fs_path.hpp" -#include "hashset.hpp" -#include "rwlock.hpp" -#include "ugid.hpp" - -#include -#include - -#include -#include - -#include - -using std::string; -using std::vector; - -#define NO_OFFSET 0 - - -static -int -dot_or_dotdot(const char *s_) -{ - return ((s_[0] == '.') && - ((s_[1] == '\0') || - ((s_[1] == '.') && (s_[2] == '\0')))); -} - - -namespace l -{ - static - int - readdir_plus(const Branches &branches_, - const char *dirname_, - fuse_dirents_t *buf_) - { - dev_t dev; - HashSet names; - string basepath; - struct stat st; - fuse_entry_t entry; - - entry.nodeid = 0; - entry.generation = 0; - entry.entry_valid = 1; - entry.attr_valid = 1; - entry.entry_valid_nsec = 0; - entry.attr_valid_nsec = 0; - for(size_t i = 0, ei = branches_.size(); i != ei; i++) - { - int rv; - int dirfd; - DIR *dh; - - basepath = fs::path::make(&branches_[i].path,dirname_); - - dh = fs::opendir(basepath); - if(!dh) - continue; - - dirfd = fs::dirfd(dh); - dev = fs::devid(dirfd); - if(dev == (dev_t)-1) - dev = i; - - rv = 0; - for(struct dirent *de = fs::readdir(dh); de && !rv; de = fs::readdir(dh)) - { - rv = names.put(de->d_name,_D_EXACT_NAMLEN(de)); - if(rv == 0) - continue; - - rv = fs::fstatat_nofollow(dirfd,de->d_name,&st); - if(rv == -1) - memset(&st,0,sizeof(st)); - - de->d_ino = fs::inode::recompute(de->d_ino,dev); - st.st_ino = fs::inode::recompute(st.st_ino,dev); - - rv = fuse_dirents_add_plus(buf_,de,&entry,&st); - if(rv) - return (fs::closedir(dh),-ENOMEM); - } - - fs::closedir(dh); - } - - return 0; - } -} - -namespace FUSE -{ - int - readdir_plus(fuse_file_info *ffi_, - fuse_dirents_t *buf_) - { - DirInfo *di = reinterpret_cast(ffi_->fh); - const fuse_context *fc = fuse_get_context(); - const Config &config = Config::get(fc); - const ugid::Set ugid(fc->uid,fc->gid); - const rwlock::ReadGuard readlock(&config.branches_lock); - - return l::readdir_plus(config.branches, - di->fusepath.c_str(), - buf_); - } -} +#ifdef __linux__ +# include "fuse_readdir_plus_linux.icpp" +#else +# include "fuse_readdir_plus_posix.icpp" +#endif diff --git a/src/fuse_readdir_plus_linux.icpp b/src/fuse_readdir_plus_linux.icpp new file mode 100644 index 00000000..81844fed --- /dev/null +++ b/src/fuse_readdir_plus_linux.icpp @@ -0,0 +1,172 @@ +/* + 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. +*/ + +#define _DEFAULT_SOURCE + +#include "config.hpp" +#include "dirinfo.hpp" +#include "errno.hpp" +#include "fs_base_close.hpp" +#include "fs_base_fstatat.hpp" +#include "fs_base_open.hpp" +#include "fs_base_stat.hpp" +#include "fs_devid.hpp" +#include "fs_inode.hpp" +#include "fs_path.hpp" +#include "hashset.hpp" +#include "mempools.hpp" +#include "rwlock.hpp" +#include "ugid.hpp" + +#include +#include + +#include +#include + +#include +#include + +using std::string; +using std::vector; + +struct linux_dirent64 +{ + uint64_t d_ino; + int64_t d_off; + uint16_t d_reclen; + uint8_t d_type; + char d_name[]; +}; + +namespace l +{ + static + inline + int + getdents64(unsigned int fd_, + char *dirp_, + unsigned int count_) + { + return syscall(SYS_getdents64,fd_,dirp_,count_); + } + + int + close_free_ret_enomem(int fd_, + void *buf_) + { + fs::close(fd_); + g_DENTS_BUF_POOL.free(buf_); + return -ENOMEM; + } + + static + int + readdir_plus(const Branches &branches_, + const char *dirname_, + fuse_dirents_t *buf_) + { + int rv; + dev_t dev; + char *buf; + HashSet names; + string basepath; + struct stat st; + fuse_entry_t entry; + struct linux_dirent64 *d; + + buf = (char*)g_DENTS_BUF_POOL.alloc(); + + entry.nodeid = 0; + entry.generation = 0; + entry.entry_valid = 1; + entry.attr_valid = 1; + entry.entry_valid_nsec = 0; + entry.attr_valid_nsec = 0; + for(size_t i = 0, ei = branches_.size(); i != ei; i++) + { + int dirfd; + int64_t nread; + uint64_t namelen; + + basepath = fs::path::make(&branches_[i].path,dirname_); + + dirfd = fs::open_dir_ro(basepath); + if(dirfd == -1) + continue; + + dev = fs::devid(dirfd); + if(dev == (dev_t)-1) + dev = i; + + for(;;) + { + nread = l::getdents64(dirfd,buf,g_DENTS_BUF_POOL.size()); + if(nread == -1) + break; + if(nread == 0) + break; + + for(int64_t pos = 0; pos < nread;) + { + d = (struct linux_dirent64*)(buf + pos); + namelen = (d->d_reclen - offsetof(struct linux_dirent64,d_name)); + + rv = names.put(d->d_name,namelen); + if(rv == 0) + continue; + + rv = fs::fstatat_nofollow(dirfd,d->d_name,&st); + if(rv == -1) + memset(&st,0,sizeof(st)); + + d->d_ino = fs::inode::recompute(d->d_ino,dev); + st.st_ino = d->d_ino; + + rv = fuse_dirents_add_linux_plus(buf_,d,&entry,&st); + if(rv) + return close_free_ret_enomem(dirfd,buf); + + pos += d->d_reclen; + } + } + + fs::close(dirfd); + } + + g_DENTS_BUF_POOL.free(buf); + + return 0; + } +} + +namespace FUSE +{ + int + readdir_plus(fuse_file_info *ffi_, + fuse_dirents_t *buf_) + { + DirInfo *di = reinterpret_cast(ffi_->fh); + const fuse_context *fc = fuse_get_context(); + const Config &config = Config::get(fc); + const ugid::Set ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.branches_lock); + + return l::readdir_plus(config.branches, + di->fusepath.c_str(), + buf_); + } +} diff --git a/src/fuse_readdir_plus_posix.icpp b/src/fuse_readdir_plus_posix.icpp new file mode 100644 index 00000000..966dec4d --- /dev/null +++ b/src/fuse_readdir_plus_posix.icpp @@ -0,0 +1,125 @@ +/* + 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. +*/ + +#define _DEFAULT_SOURCE + +#include "config.hpp" +#include "dirinfo.hpp" +#include "errno.hpp" +#include "fs_base_closedir.hpp" +#include "fs_base_dirfd.hpp" +#include "fs_base_opendir.hpp" +#include "fs_base_readdir.hpp" +#include "fs_base_fstatat.hpp" +#include "fs_base_stat.hpp" +#include "fs_devid.hpp" +#include "fs_inode.hpp" +#include "fs_path.hpp" +#include "hashset.hpp" +#include "rwlock.hpp" +#include "ugid.hpp" + +#include +#include + +#include +#include + +#include + +using std::string; +using std::vector; + +namespace l +{ + static + int + readdir_plus(const Branches &branches_, + const char *dirname_, + fuse_dirents_t *buf_) + { + dev_t dev; + HashSet names; + string basepath; + struct stat st; + fuse_entry_t entry; + + entry.nodeid = 0; + entry.generation = 0; + entry.entry_valid = 1; + entry.attr_valid = 1; + entry.entry_valid_nsec = 0; + entry.attr_valid_nsec = 0; + for(size_t i = 0, ei = branches_.size(); i != ei; i++) + { + int rv; + int dirfd; + DIR *dh; + + basepath = fs::path::make(&branches_[i].path,dirname_); + + dh = fs::opendir(basepath); + if(!dh) + continue; + + dirfd = fs::dirfd(dh); + dev = fs::devid(dirfd); + if(dev == (dev_t)-1) + dev = i; + + rv = 0; + for(struct dirent *de = fs::readdir(dh); de && !rv; de = fs::readdir(dh)) + { + rv = names.put(de->d_name,_D_EXACT_NAMLEN(de)); + if(rv == 0) + continue; + + rv = fs::fstatat_nofollow(dirfd,de->d_name,&st); + if(rv == -1) + memset(&st,0,sizeof(st)); + + de->d_ino = fs::inode::recompute(de->d_ino,dev); + st.st_ino = de->d_ino; + + rv = fuse_dirents_add_plus(buf_,de,&entry,&st); + if(rv) + return (fs::closedir(dh),-ENOMEM); + } + + fs::closedir(dh); + } + + return 0; + } +} + +namespace FUSE +{ + int + readdir_plus(fuse_file_info *ffi_, + fuse_dirents_t *buf_) + { + DirInfo *di = reinterpret_cast(ffi_->fh); + const fuse_context *fc = fuse_get_context(); + const Config &config = Config::get(fc); + const ugid::Set ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.branches_lock); + + return l::readdir_plus(config.branches, + di->fusepath.c_str(), + buf_); + } +} diff --git a/src/fuse_readdir_posix.icpp b/src/fuse_readdir_posix.icpp new file mode 100644 index 00000000..4360297b --- /dev/null +++ b/src/fuse_readdir_posix.icpp @@ -0,0 +1,111 @@ +/* + 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. +*/ + +#define _DEFAULT_SOURCE + +#include "config.hpp" +#include "dirinfo.hpp" +#include "errno.hpp" +#include "fs_base_closedir.hpp" +#include "fs_base_dirfd.hpp" +#include "fs_base_opendir.hpp" +#include "fs_base_readdir.hpp" +#include "fs_base_stat.hpp" +#include "fs_devid.hpp" +#include "fs_inode.hpp" +#include "fs_path.hpp" +#include "hashset.hpp" +#include "rwlock.hpp" +#include "ugid.hpp" + +#include +#include + +#include +#include + +#include + +using std::string; +using std::vector; + +namespace l +{ + static + int + readdir(const Branches &branches_, + const char *dirname_, + fuse_dirents_t *buf_) + { + dev_t dev; + HashSet names; + string basepath; + + for(size_t i = 0, ei = branches_.size(); i != ei; i++) + { + int rv; + int dirfd; + DIR *dh; + + basepath = fs::path::make(&branches_[i].path,dirname_); + + dh = fs::opendir(basepath); + if(!dh) + continue; + + dirfd = fs::dirfd(dh); + dev = fs::devid(dirfd); + if(dev == (dev_t)-1) + dev = i; + + rv = 0; + for(struct dirent *de = fs::readdir(dh); de && !rv; de = fs::readdir(dh)) + { + rv = names.put(de->d_name,_D_EXACT_NAMLEN(de)); + if(rv == 0) + continue; + + de->d_ino = fs::inode::recompute(de->d_ino,dev); + + rv = fuse_dirents_add(buf_,de); + if(rv) + return (fs::closedir(dh),-ENOMEM); + } + + fs::closedir(dh); + } + + return 0; + } +} + +namespace FUSE +{ + int + readdir(fuse_file_info *ffi_, + fuse_dirents_t *buf_) + { + DirInfo *di = reinterpret_cast(ffi_->fh); + const fuse_context *fc = fuse_get_context(); + const Config &config = Config::get(fc); + const ugid::Set ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.branches_lock); + + return l::readdir(config.branches, + di->fusepath.c_str(), + buf_); + } +} diff --git a/src/locked_fixed_mem_pool.hpp b/src/locked_fixed_mem_pool.hpp new file mode 100644 index 00000000..3654a44b --- /dev/null +++ b/src/locked_fixed_mem_pool.hpp @@ -0,0 +1,69 @@ +/* + ISC License + + Copyright (c) 2020, 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 "fixed_mem_pool.hpp" + +#include + +template +class LockedFixedMemPool +{ +public: + LockedFixedMemPool() + { + pthread_mutex_init(&_mutex,NULL); + } + + ~LockedFixedMemPool() + { + pthread_mutex_destroy(&_mutex); + } + +public: + void* + alloc(void) + { + void *mem; + + pthread_mutex_lock(&_mutex); + mem = _fmp.alloc(); + pthread_mutex_unlock(&_mutex); + + return mem; + } + + void + free(void *mem_) + { + pthread_mutex_lock(&_mutex); + _fmp.free(mem_); + pthread_mutex_unlock(&_mutex); + } + + uint64_t + size(void) + { + return _fmp.size(); + } + +private: + FixedMemPool _fmp; + pthread_mutex_t _mutex; +}; diff --git a/src/mempools.cpp b/src/mempools.cpp new file mode 100644 index 00000000..c02e4bee --- /dev/null +++ b/src/mempools.cpp @@ -0,0 +1,21 @@ +/* + ISC License + + Copyright (c) 2020, 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. +*/ + +#include "locked_fixed_mem_pool.hpp" + +LockedFixedMemPool<128 * 1024> g_DENTS_BUF_POOL; diff --git a/src/mempools.hpp b/src/mempools.hpp new file mode 100644 index 00000000..06df7501 --- /dev/null +++ b/src/mempools.hpp @@ -0,0 +1,23 @@ +/* + ISC License + + Copyright (c) 2020, 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 "locked_fixed_mem_pool.hpp" + +extern LockedFixedMemPool<128 * 1024> g_DENTS_BUF_POOL;