mirror of https://github.com/trapexit/mergerfs.git
Browse Source
Add getdents based readdir functions for Linux
Add getdents based readdir functions for Linux
31 changed files with 896 additions and 900 deletions
-
1libfuse/Makefile
-
23libfuse/include/fs_dirent64.hpp
-
80libfuse/include/fuse_dirents.h
-
48libfuse/include/fuse_dirents.hpp
-
2libfuse/include/fuse_msgbuf.hpp
-
0libfuse/include/fuse_msgbuf_t.h
-
16libfuse/include/linux_dirent64.h
-
2libfuse/lib/fuse.cpp
-
406libfuse/lib/fuse_dirents.c
-
160libfuse/lib/fuse_dirents.cpp
-
2libfuse/lib/fuse_i.h
-
2libfuse/lib/fuse_ll.hpp
-
5libfuse/lib/fuse_msgbuf.cpp
-
4src/branch.hpp
-
2src/branches.cpp
-
8src/fs_getdents64.cpp
-
10src/fs_getdents64.hpp
-
12src/fs_statx.hpp
-
4src/fuse_readdir.cpp
-
148src/fuse_readdir_cor.cpp
-
79src/fuse_readdir_cor_getdents.icpp
-
87src/fuse_readdir_cor_readdir.icpp
-
162src/fuse_readdir_cosr.cpp
-
131src/fuse_readdir_cosr_getdents.icpp
-
124src/fuse_readdir_cosr_readdir.icpp
-
92src/fuse_readdir_seq.cpp
-
79src/fuse_readdir_seq_getdents.icpp
-
82src/fuse_readdir_seq_readdir.icpp
-
2src/fuse_statx.cpp
-
11src/supported_getdents64.hpp
-
12src/supported_statx.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)); |
|||
} |
|||
}; |
|||
} |
@ -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 |
@ -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); |
@ -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[]; |
|||
}; |
@ -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); |
|||
} |
@ -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); |
|||
} |
@ -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; |
|||
} |
@ -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; |
|||
} |
@ -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; |
|||
} |
@ -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; |
|||
} |
@ -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; |
|||
} |
@ -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; |
|||
} |
@ -0,0 +1,11 @@ |
|||
#pragma once
|
|||
|
|||
#ifdef __linux__
|
|||
|
|||
#include <sys/syscall.h>
|
|||
|
|||
#ifdef SYS_getdents64
|
|||
#define MERGERFS_SUPPORTED_GETDENTS64
|
|||
#endif
|
|||
|
|||
#endif
|
@ -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
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue