Browse Source

Add getdents based readdir functions for Linux

Antonio SJ Musumeci 2 weeks ago
parent
commit
c2354e2203
  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. 4
      src/branch.hpp
  15. 2
      src/branches.cpp
  16. 8
      src/fs_getdents64.cpp
  17. 10
      src/fs_getdents64.hpp
  18. 12
      src/fs_statx.hpp
  19. 4
      src/fuse_readdir.cpp
  20. 148
      src/fuse_readdir_cor.cpp
  21. 79
      src/fuse_readdir_cor_getdents.icpp
  22. 87
      src/fuse_readdir_cor_readdir.icpp
  23. 162
      src/fuse_readdir_cosr.cpp
  24. 131
      src/fuse_readdir_cosr_getdents.icpp
  25. 124
      src/fuse_readdir_cosr_readdir.icpp
  26. 92
      src/fuse_readdir_seq.cpp
  27. 79
      src/fuse_readdir_seq_getdents.icpp
  28. 82
      src/fuse_readdir_seq_readdir.icpp
  29. 2
      src/fuse_statx.cpp
  30. 11
      src/supported_getdents64.hpp
  31. 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_);
}

4
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,8 @@ public:
public:
std::variant<u64,const u64*> _minfreespace;
Mode mode;
std::string path;
fs::path path;
// std::string 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;
}

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

79
src/fuse_readdir_cor_getdents.icpp

@ -0,0 +1,79 @@
#pragma message "using getdents"
#include "fs_close.hpp"
#include "fs_getdents64.hpp"
#include "fs_inode.hpp"
#include "fs_open.hpp"
#include "fs_path.hpp"
#include "hashset.hpp"
#include "scope_guard.hpp"
#include "fuse_msgbuf.hpp"
#include "fuse_dirents.hpp"
static
inline
int
_readdir(const fs::path &branch_path_,
const fs::path &rel_dirpath_,
HashSet &names_,
fuse_dirents_t *dirents_,
std::mutex &mutex_)
{
int rv;
int fd;
fuse_msgbuf_t *buf;
fs::path rel_filepath;
fs::path abs_dirpath;
abs_dirpath = branch_path_ / rel_dirpath_;
fd = fs::open_dir_ro(abs_dirpath);
if(fd < 0)
return -fd;
DEFER{ fs::close(fd); };
buf = msgbuf_alloc();
DEFER{ msgbuf_free(buf); };
rel_filepath = rel_dirpath_ / "dummy";
while(true)
{
std::lock_guard<std::mutex> lk(mutex_);
while(true)
{
ssize_t nread;
nread = fs::getdents64(fd,buf->mem,buf->size);
if(nread <= 0)
break;
for(ssize_t pos = 0; pos < nread;)
{
int namelen;
fs::dirent64 *d = reinterpret_cast<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);
rv = fuse_dirents_add(dirents_,d,namelen);
if(rv < 0)
return ENOMEM;
}
}
}
return 0;
}

87
src/fuse_readdir_cor_readdir.icpp

@ -0,0 +1,87 @@
#pragma message "using readdir"
#define MAX_ENTRIES_PER_LOOP 64
#include "fs_opendir.hpp"
#include "fs_closedir.hpp"
#include "hashset.hpp"
#include "scope_guard.hpp"
#include "fs_readdir.hpp"
#include "fs_inode.hpp"
#include "fuse_dirents.hpp"
#include <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_)
{
int rv;
DIR *dir;
fs::path rel_filepath;
fs::path abs_dirpath;
abs_dirpath = branch_path_ / rel_dirpath_;
errno = 0;
dir = fs::opendir(abs_dirpath);
if(dir == NULL)
return errno;
DEFER{ fs::closedir(dir); };
rel_filepath = rel_dirpath_ / "dummy";
while(true)
{
std::lock_guard<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(dirents_,de,namelen);
if(rv < 0)
return ENOMEM;
}
}
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);
}

131
src/fuse_readdir_cosr_getdents.icpp

@ -0,0 +1,131 @@
#pragma message "using getdents"
#include "fs_inode.hpp"
#include "fs_getdents64.hpp"
#include "hashset.hpp"
#include "branches.hpp"
#include "error.hpp"
#include "fs_close.hpp"
#include "fs_open.hpp"
#include "fs_path.hpp"
#include "ugid.hpp"
#include "scope_guard.hpp"
#include "fuse_msgbuf.hpp"
#include "fuse_dirents.hpp"
#include <dirent.h>
struct DirRV
{
const fs::path *branch_path;
int fd;
int err;
};
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_;
errno = 0;
fd = fs::open_dir_ro(abs_dirpath);
return DirRV{&branch.path,fd,errno};
};
auto rv = tp_.enqueue_task(std::move(func));
futures.emplace_back(std::move(rv));
}
return futures;
}
static
inline
int
_readdir(std::vector<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_)
{
int rv;
DirRV dirrv;
dirrv = dh_future.get();
err = dirrv.err;
if(dirrv.fd == -1)
continue;
DEFER { fs::close(dirrv.fd); };
rv = 0;
while(true)
{
ssize_t nread;
nread = fs::getdents64(dirrv.fd,buf->mem,buf->size);
if(nread <= 0)
break;
for(ssize_t pos = 0; pos < nread;)
{
int namelen;
fs::dirent64 *d = reinterpret_cast<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);
rv = fuse_dirents_add(dirents_,d,namelen);
if(rv == 0)
continue;
err = ENOMEM;
}
}
}
return -err;
}

124
src/fuse_readdir_cosr_readdir.icpp

@ -0,0 +1,124 @@
#pragma message "using readdir"
#include "error.hpp"
#include "fs_closedir.hpp"
#include "fs_inode.hpp"
#include "fs_opendir.hpp"
#include "fs_readdir.hpp"
#include "hashset.hpp"
#include "scope_guard.hpp"
#include "ugid.hpp"
#include "fuse_dirents.hpp"
struct DirRV
{
const fs::path *branch_path;
DIR *dir;
int err;
};
static
uint64_t
_dirent_exact_namelen(const struct dirent *d_)
{
#ifdef _D_EXACT_NAMLEN
return _D_EXACT_NAMLEN(d_);
#elif defined _DIRENT_HAVE_D_NAMLEN
return d_->d_namlen;
#else
return strlen(d_->d_name);
#endif
}
static
inline
std::vector<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 *dirents_)
{
Err err;
HashSet names;
fs::path rel_filepath;
rel_filepath = rel_dirpath_ / "dummy";
for(auto &dh_future : dh_futures_)
{
int rv;
DirRV dirrv;
dirrv = dh_future.get();
err = dirrv.err;
if(dirrv.dir == NULL)
continue;
DEFER { fs::closedir(dirrv.dir); };
rv = 0;
for(dirent *de = fs::readdir(dirrv.dir); de && !rv; de = fs::readdir(dirrv.dir))
{
std::uint64_t namelen;
namelen = ::_dirent_exact_namelen(de);
rv = names.put(de->d_name,namelen);
if(rv == 0)
continue;
rel_filepath.replace_filename(de->d_name);
de->d_ino = fs::inode::calc(*dirrv.branch_path,
rel_filepath,
DTTOIF(de->d_type),
de->d_ino);
rv = fuse_dirents_add(dirents_,de,namelen);
if(rv == 0)
continue;
err = ENOMEM;
}
}
return -err;
}

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

79
src/fuse_readdir_seq_getdents.icpp

@ -0,0 +1,79 @@
#pragma message "using getdents"
#include "dirinfo.hpp"
#include "fs_close.hpp"
#include "fs_getdents64.hpp"
#include "fs_inode.hpp"
#include "fs_open.hpp"
#include "hashset.hpp"
#include "scope_guard.hpp"
#include "fuse_dirents.hpp"
#include "fuse_msgbuf.hpp"
static
int
_readdir(const Branches::Ptr &branches_,
const fs::path &rel_dirpath_,
fuse_dirents_t *dirents_)
{
HashSet names;
fs::path rel_filepath;
fs::path abs_dirpath;
fuse_msgbuf_t *buf;
fuse_dirents_reset(dirents_);
buf = msgbuf_alloc();
DEFER { msgbuf_free(buf); };
rel_filepath = rel_dirpath_ / "dummy";
for(const auto &branch : *branches_)
{
int rv;
int fd;
abs_dirpath = branch.path / rel_dirpath_;
fd = fs::open_dir_ro(abs_dirpath);
if(fd < 0)
continue;
DEFER{ fs::close(fd); };
rv = 0;
while(true)
{
ssize_t nread;
nread = fs::getdents64(fd,buf->mem,buf->size);
if(nread <= 0)
break;
for(ssize_t pos = 0; pos < nread;)
{
int namelen;
fs::dirent64 *d = reinterpret_cast<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);
rv = fuse_dirents_add(dirents_,d,namelen);
if(rv)
return -ENOMEM;
}
}
}
return 0;
}

82
src/fuse_readdir_seq_readdir.icpp

@ -0,0 +1,82 @@
#pragma message "using readdir"
#include "fs_opendir.hpp"
#include "fs_closedir.hpp"
#include "fs_readdir.hpp"
#include "error.hpp"
#include "hashset.hpp"
#include "scope_guard.hpp"
#include "fs_inode.hpp"
#include "dirinfo.hpp"
#include "fuse_dirents.hpp"
#include <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 *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;
}

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