Browse Source

Add getdents based readdir functions for Linux (#1533)

pull/1534/head
trapexit 1 month ago
committed by GitHub
parent
commit
2f3e807e9d
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      libfuse/Makefile
  2. 23
      libfuse/include/fs_dirent64.hpp
  3. 80
      libfuse/include/fuse_dirents.h
  4. 48
      libfuse/include/fuse_dirents.hpp
  5. 2
      libfuse/include/fuse_msgbuf.hpp
  6. 0
      libfuse/include/fuse_msgbuf_t.h
  7. 16
      libfuse/include/linux_dirent64.h
  8. 2
      libfuse/lib/fuse.cpp
  9. 406
      libfuse/lib/fuse_dirents.c
  10. 160
      libfuse/lib/fuse_dirents.cpp
  11. 2
      libfuse/lib/fuse_i.h
  12. 2
      libfuse/lib/fuse_ll.hpp
  13. 5
      libfuse/lib/fuse_msgbuf.cpp
  14. 3
      src/branch.hpp
  15. 2
      src/branches.cpp
  16. 2
      src/error.hpp
  17. 6
      src/fileinfo.hpp
  18. 8
      src/fs_getdents64.cpp
  19. 10
      src/fs_getdents64.hpp
  20. 12
      src/fs_statx.hpp
  21. 4
      src/fuse_readdir.cpp
  22. 148
      src/fuse_readdir_cor.cpp
  23. 75
      src/fuse_readdir_cor_getdents.icpp
  24. 86
      src/fuse_readdir_cor_readdir.icpp
  25. 162
      src/fuse_readdir_cosr.cpp
  26. 125
      src/fuse_readdir_cosr_getdents.icpp
  27. 121
      src/fuse_readdir_cosr_readdir.icpp
  28. 92
      src/fuse_readdir_seq.cpp
  29. 81
      src/fuse_readdir_seq_getdents.icpp
  30. 81
      src/fuse_readdir_seq_readdir.icpp
  31. 2
      src/fuse_statx.cpp
  32. 11
      src/supported_getdents64.hpp
  33. 12
      src/supported_statx.hpp

1
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 \

23
libfuse/include/fs_dirent64.hpp

@ -0,0 +1,23 @@
#pragma once
#include <cstddef>
#include <cstdint>
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));
}
};
}

80
libfuse/include/fuse_dirents.h

@ -1,80 +0,0 @@
/*
ISC License
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.
*/
#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 <dirent.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
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

48
libfuse/include/fuse_dirents.hpp

@ -0,0 +1,48 @@
/*
ISC License
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.
*/
#pragma once
#include "kvec.h"
#include "fuse_dirent.h"
#include "fuse_entry.h"
#include "fs_dirent64.hpp"
#include <dirent.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
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);

2
libfuse/lib/fuse_msgbuf.hpp → 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

0
libfuse/include/fuse_msgbuf.h → libfuse/include/fuse_msgbuf_t.h

16
libfuse/include/linux_dirent64.h

@ -1,16 +0,0 @@
#pragma once
#include <stdint.h>
#include <string.h>
#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[];
};

2
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"

406
libfuse/lib/fuse_dirents.c

@ -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 <dirent.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* 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);
}

160
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 <dirent.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* 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);
}

2
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"

2
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);

5
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_);
}

3
src/branch.hpp

@ -21,6 +21,7 @@
#include "int_types.h"
#include "strvec.hpp"
#include "tofrom_string.hpp"
#include "fs_path.hpp"
#include <cstdint>
#include <memory>
@ -44,7 +45,7 @@ public:
public:
std::variant<u64,const u64*> _minfreespace;
Mode mode;
std::string path;
fs::path path;
public:
Branch();

2
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;
}

2
src/error.hpp

@ -26,7 +26,7 @@ public:
if(!_err.has_value())
_err = v_;
else if(v_ >= 0)
_err = v_;
_err = 0;
return *this;
}

6
src/fileinfo.hpp

@ -28,7 +28,7 @@
class FileInfo : public FH
{
public:
static FileInfo *from_fh(const uint64_t fh);
static FileInfo *from_fh(const uintptr_t fh);
public:
FileInfo(const int fd_,
@ -72,10 +72,10 @@ public:
};
inline
uint64_t
uintptr_t
FileInfo::to_fh() const
{
return reinterpret_cast<uint64_t>(this);
return reinterpret_cast<uintptr_t>(this);
}
inline

8
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_);

10
src/fs_getdents64.hpp

@ -1,7 +1,7 @@
/*
ISC License
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
Copyright (c) 2025, 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
@ -18,11 +18,13 @@
#pragma once
#include <sys/types.h>
namespace fs
{
int
getdents64(unsigned int fd,
ssize_t
getdents64(const int fd,
void *dirp,
unsigned int count);
const size_t count);
}

12
src/fs_statx.hpp

@ -13,9 +13,7 @@
#include <fcntl.h>
#include <sys/stat.h>
#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_);
}
}

4
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};

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

75
src/fuse_readdir_cor_getdents.icpp

@ -0,0 +1,75 @@
#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 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)
{
ssize_t nread;
nread = fs::getdents64(fd,buf->mem,buf->size);
if(nread <= 0)
return nread;
std::lock_guard<std::mutex> lk(mutex_);
for(ssize_t pos = 0; pos < nread;)
{
int rv;
int namelen;
fs::dirent64 *d = reinterpret_cast<fs::dirent64*>(&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);
fuse_dirents_add(dirents_,d,namelen);
}
}
return 0;
}

86
src/fuse_readdir_cor_readdir.icpp

@ -0,0 +1,86 @@
#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 <cstring>
#include <dirent.h>
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_)
{
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<std::mutex> lk(mutex_);
for(int i = 0; i < MAX_ENTRIES_PER_LOOP; i++)
{
int rv;
int namelen;
dirent *de;
errno = 0;
de = fs::readdir(dir);
if(de == NULL)
return -errno;
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);
fuse_dirents_add(dirents_,de,namelen);
}
}
return 0;
}

162
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<std::future<DirRV>>
_opendir(ThreadPool &tp_,
const Branches::Ptr &branches_,
const fs::path &rel_dirpath_,
uid_t const uid_,
gid_t const gid_)
{
std::vector<std::future<DirRV>> 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<std::future<DirRV>> &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<std::future<DirRV>> 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);
}

125
src/fuse_readdir_cosr_getdents.icpp

@ -0,0 +1,125 @@
#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 <dirent.h>
struct DirRV
{
const fs::path *branch_path;
int fd;
};
static
inline
std::vector<std::future<DirRV>>
_opendir(ThreadPool &tp_,
const Branches::Ptr &branches_,
const fs::path &rel_dirpath_,
uid_t const uid_,
gid_t const gid_)
{
std::vector<std::future<DirRV>> 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_;
fd = fs::open_dir_ro(abs_dirpath);
return DirRV{&branch.path,fd};
};
auto rv = tp_.enqueue_task(std::move(func));
futures.emplace_back(std::move(rv));
}
return futures;
}
static
inline
int
_readdir(std::vector<std::future<DirRV>> &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_)
{
DirRV dirrv;
dirrv = dh_future.get();
err = dirrv.fd;
if(dirrv.fd < 0)
continue;
DEFER { fs::close(dirrv.fd); };
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 rv;
int namelen;
fs::dirent64 *d = reinterpret_cast<fs::dirent64*>(&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);
fuse_dirents_add(dirents_,d,namelen);
}
}
}
return err;
}

121
src/fuse_readdir_cosr_readdir.icpp

@ -0,0 +1,121 @@
#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<std::future<DirRV>>
_opendir(ThreadPool &tp_,
const Branches::Ptr &branches_,
const fs::path &rel_dirpath_,
uid_t const uid_,
gid_t const gid_)
{
std::vector<std::future<DirRV>> 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(const std::vector<std::future<DirRV>> &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_)
{
DirRV dirrv;
dirrv = dh_future.get();
err = dirrv.err;
if(dirrv.dir == NULL)
continue;
DEFER { fs::closedir(dirrv.dir); };
for(dirent *de = fs::readdir(dirrv.dir);
de;
de = fs::readdir(dirrv.dir))
{
int rv;
int 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);
fuse_dirents_add(dirents_,de,namelen);
}
}
return err;
}

92
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 <string>
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_);
}

81
src/fuse_readdir_seq_getdents.icpp

@ -0,0 +1,81 @@
#pragma message "using getdents"
#include "dirinfo.hpp"
#include "error.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_)
{
Err err;
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 fd;
abs_dirpath = branch.path / rel_dirpath_;
fd = fs::open_dir_ro(abs_dirpath);
err = fd;
if(fd < 0)
continue;
DEFER{ fs::close(fd); };
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 rv;
int namelen;
fs::dirent64 *d = reinterpret_cast<fs::dirent64*>(&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);
fuse_dirents_add(dirents_,d,namelen);
}
}
}
return err;
}

81
src/fuse_readdir_seq_readdir.icpp

@ -0,0 +1,81 @@
#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 <cstring>
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 *dirents_)
{
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_)
{
DIR *dh;
abs_dirpath = branch.path / rel_dirpath_;
errno = 0;
dh = fs::opendir(abs_dirpath);
err = -errno;
if(!dh)
continue;
DEFER{ fs::closedir(dh); };
for(dirent *de = fs::readdir(dh);
de;
de = fs::readdir(dh))
{
int rv;
int 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);
fuse_dirents_add(buf_,de,namelen);
}
}
return err;
}

2
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

11
src/supported_getdents64.hpp

@ -0,0 +1,11 @@
#pragma once
#ifdef __linux__
#include <sys/syscall.h>
#ifdef SYS_getdents64
#define MERGERFS_SUPPORTED_GETDENTS64
#endif
#endif

12
src/supported_statx.hpp

@ -0,0 +1,12 @@
#pragma once
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <fcntl.h>
#include <sys/stat.h>
#ifdef STATX_TYPE
#define MERGERFS_SUPPORTED_STATX
#endif
Loading…
Cancel
Save