mirror of
https://github.com/trapexit/mergerfs.git
synced 2025-03-14 09:55:12 +08:00
use getdents64 on linux
This commit is contained in:
parent
d119807adb
commit
62873d2d3b
@ -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);
|
||||
|
@ -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_)
|
||||
{
|
||||
|
87
src/fixed_mem_pool.hpp
Normal file
87
src/fixed_mem_pool.hpp
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2020, 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 <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct fixed_mem_pool_t fixed_mem_pool_t;
|
||||
struct fixed_mem_pool_t
|
||||
{
|
||||
fixed_mem_pool_t *next;
|
||||
};
|
||||
|
||||
template<uint64_t SIZE>
|
||||
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;
|
||||
};
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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")
|
||||
|
@ -1,5 +1,7 @@
|
||||
/*
|
||||
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2020, 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
|
||||
@ -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 <fuse.h>
|
||||
#include <fuse_dirents.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
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<DirInfo*>(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
|
||||
|
161
src/fuse_readdir_linux.icpp
Normal file
161
src/fuse_readdir_linux.icpp
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
Copyright (c) 2019, 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.
|
||||
*/
|
||||
|
||||
#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 <fuse.h>
|
||||
#include <fuse_dirents.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
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<DirInfo*>(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_);
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
/*
|
||||
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2020, 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
|
||||
@ -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 <fuse.h>
|
||||
#include <fuse_dirents.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
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<DirInfo*>(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
|
||||
|
172
src/fuse_readdir_plus_linux.icpp
Normal file
172
src/fuse_readdir_plus_linux.icpp
Normal file
@ -0,0 +1,172 @@
|
||||
/*
|
||||
Copyright (c) 2019, 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.
|
||||
*/
|
||||
|
||||
#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 <fuse.h>
|
||||
#include <fuse_dirents.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
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<DirInfo*>(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_);
|
||||
}
|
||||
}
|
125
src/fuse_readdir_plus_posix.icpp
Normal file
125
src/fuse_readdir_plus_posix.icpp
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
Copyright (c) 2019, 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.
|
||||
*/
|
||||
|
||||
#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 <fuse.h>
|
||||
#include <fuse_dirents.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
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<DirInfo*>(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_);
|
||||
}
|
||||
}
|
111
src/fuse_readdir_posix.icpp
Normal file
111
src/fuse_readdir_posix.icpp
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
Copyright (c) 2019, 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.
|
||||
*/
|
||||
|
||||
#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 <fuse.h>
|
||||
#include <fuse_dirents.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
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<DirInfo*>(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_);
|
||||
}
|
||||
}
|
69
src/locked_fixed_mem_pool.hpp
Normal file
69
src/locked_fixed_mem_pool.hpp
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2020, 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 "fixed_mem_pool.hpp"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
template<size_t SIZE>
|
||||
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<SIZE> _fmp;
|
||||
pthread_mutex_t _mutex;
|
||||
};
|
21
src/mempools.cpp
Normal file
21
src/mempools.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2020, 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.
|
||||
*/
|
||||
|
||||
#include "locked_fixed_mem_pool.hpp"
|
||||
|
||||
LockedFixedMemPool<128 * 1024> g_DENTS_BUF_POOL;
|
23
src/mempools.hpp
Normal file
23
src/mempools.hpp
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2020, 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 "locked_fixed_mem_pool.hpp"
|
||||
|
||||
extern LockedFixedMemPool<128 * 1024> g_DENTS_BUF_POOL;
|
Loading…
x
Reference in New Issue
Block a user