Browse Source

Merge pull request #720 from trapexit/readdir_plus

Readdir plus
pull/722/head
trapexit 5 years ago
committed by GitHub
parent
commit
4cade5685c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      LICENSE
  2. 1
      libfuse/Makefile
  3. 34
      libfuse/include/fuse.h
  4. 24
      libfuse/include/fuse_attr.h
  5. 37
      libfuse/include/fuse_common.h
  6. 9
      libfuse/include/fuse_compat.h
  7. 31
      libfuse/include/fuse_dirent.h
  8. 31
      libfuse/include/fuse_direntplus.h
  9. 80
      libfuse/include/fuse_dirents.h
  10. 14
      libfuse/include/fuse_entry.h
  11. 38
      libfuse/include/fuse_lowlevel.h
  12. 17
      libfuse/include/stat_utils.h
  13. 420
      libfuse/lib/fuse.c
  14. 429
      libfuse/lib/fuse_dirents.c
  15. 2
      libfuse/lib/fuse_loop.c
  16. 2
      libfuse/lib/fuse_loop_mt.c
  17. 51
      libfuse/lib/fuse_lowlevel.c
  18. 1
      src/config.cpp
  19. 1
      src/config.hpp
  20. 87
      src/fixed_mem_pool.hpp
  21. 53
      src/fs_base_fstatat.hpp
  22. 8
      src/fs_base_open.hpp
  23. 18
      src/fs_inode.hpp
  24. 2
      src/fuse_getxattr.cpp
  25. 2
      src/fuse_init.cpp
  26. 1
      src/fuse_listxattr.cpp
  27. 111
      src/fuse_readdir.cpp
  28. 13
      src/fuse_readdir.hpp
  29. 161
      src/fuse_readdir_linux.icpp
  30. 23
      src/fuse_readdir_plus.cpp
  31. 30
      src/fuse_readdir_plus.hpp
  32. 172
      src/fuse_readdir_plus_linux.icpp
  33. 125
      src/fuse_readdir_plus_posix.icpp
  34. 111
      src/fuse_readdir_posix.icpp
  35. 5
      src/hashset.hpp
  36. 69
      src/locked_fixed_mem_pool.hpp
  37. 21
      src/mempools.cpp
  38. 23
      src/mempools.hpp
  39. 3
      src/mergerfs.cpp
  40. 4
      src/option_parser.cpp

2
LICENSE

@ -1,7 +1,7 @@
/* /*
ISC License ISC License
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above purpose with or without fee is hereby granted, provided that the above

1
libfuse/Makefile

@ -23,6 +23,7 @@ INSTALLMAN1DIR = $(DESTDIR)$(MAN1DIR)
SRC = \ SRC = \
lib/buffer.c \ lib/buffer.c \
lib/cuse_lowlevel.c \ lib/cuse_lowlevel.c \
lib/fuse_dirents.c \
lib/fuse.c \ lib/fuse.c \
lib/fuse_kern_chan.c \ lib/fuse_kern_chan.c \
lib/fuse_loop.c \ lib/fuse_loop.c \

34
libfuse/include/fuse.h

@ -24,6 +24,7 @@
#endif #endif
#include "fuse_common.h" #include "fuse_common.h"
#include "fuse_dirents.h"
#include <fcntl.h> #include <fcntl.h>
#include <time.h> #include <time.h>
@ -47,22 +48,6 @@ struct fuse;
/** Structure containing a raw command */ /** Structure containing a raw command */
struct fuse_cmd; struct fuse_cmd;
/** Function to add an entry in a readdir() operation
*
* @param buf the buffer passed to the readdir() operation
* @param name the file name of the directory entry
* @param stat file attributes, can be NULL
* @param off offset of the next entry or zero
* @return 1 if buffer is full, zero otherwise
*/
typedef int (*fuse_fill_dir_t) (void *buf, const char *name,
const struct stat *stbuf, off_t off);
/* Used by deprecated getdir() method */
typedef struct fuse_dirhandle *fuse_dirh_t;
typedef int (*fuse_dirfil_t) (fuse_dirh_t h, const char *name, int type,
ino_t ino);
/** /**
* The file system operations: * The file system operations:
* *
@ -104,9 +89,6 @@ struct fuse_operations {
*/ */
int (*readlink) (const char *, char *, size_t); int (*readlink) (const char *, char *, size_t);
/* Deprecated, use readdir() instead */
int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
/** Create a file node /** Create a file node
* *
* This is called for creation of all non-directory, non-symlink * This is called for creation of all non-directory, non-symlink
@ -312,8 +294,12 @@ struct fuse_operations {
* *
* Introduced in version 2.3 * Introduced in version 2.3
*/ */
int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t,
struct fuse_file_info *);
int (*readdir)(struct fuse_file_info *,
fuse_dirents_t *);
int (*readdir_plus)(struct fuse_file_info *,
fuse_dirents_t *);
/** Release directory /** Release directory
* *
@ -898,9 +884,9 @@ int fuse_fs_flush(struct fuse_fs *fs, const char *path,
int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf); int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf);
int fuse_fs_opendir(struct fuse_fs *fs, const char *path, int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
struct fuse_file_info *fi); struct fuse_file_info *fi);
int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
fuse_fill_dir_t filler, off_t off,
struct fuse_file_info *fi);
int fuse_fs_readdir(struct fuse_fs *fs,
struct fuse_file_info *fi,
fuse_dirents_t *buf);
int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync, int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync,
struct fuse_file_info *fi); struct fuse_file_info *fi);
int fuse_fs_releasedir(struct fuse_fs *fs, const char *path, int fuse_fs_releasedir(struct fuse_fs *fs, const char *path,

24
libfuse/include/fuse_attr.h

@ -0,0 +1,24 @@
#pragma once
#include <stdint.h>
typedef struct fuse_attr_s fuse_attr_t;
struct fuse_attr_s
{
uint64_t ino;
uint64_t size;
uint64_t blocks;
uint64_t atime;
uint64_t mtime;
uint64_t ctime;
uint32_t atimensec;
uint32_t mtimensec;
uint32_t ctimensec;
uint32_t mode;
uint32_t nlink;
uint32_t uid;
uint32_t gid;
uint32_t rdev;
uint32_t blksize;
uint32_t _padding;
};

37
libfuse/include/fuse_common.h

@ -109,24 +109,25 @@ fuse_file_info
* FUSE_CAP_IOCTL_DIR: ioctl support on directories * FUSE_CAP_IOCTL_DIR: ioctl support on directories
* FUSE_CAP_CACHE_SYMLINKS: cache READLINK responses * FUSE_CAP_CACHE_SYMLINKS: cache READLINK responses
*/ */
#define FUSE_CAP_ASYNC_READ (1 << 0)
#define FUSE_CAP_POSIX_LOCKS (1 << 1)
#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3)
#define FUSE_CAP_EXPORT_SUPPORT (1 << 4)
#define FUSE_CAP_BIG_WRITES (1 << 5)
#define FUSE_CAP_DONT_MASK (1 << 6)
#define FUSE_CAP_SPLICE_WRITE (1 << 7)
#define FUSE_CAP_SPLICE_MOVE (1 << 8)
#define FUSE_CAP_SPLICE_READ (1 << 9)
#define FUSE_CAP_FLOCK_LOCKS (1 << 10)
#define FUSE_CAP_IOCTL_DIR (1 << 11)
#define FUSE_CAP_ASYNC_DIO (1 << 15)
#define FUSE_CAP_WRITEBACK_CACHE (1 << 16)
#define FUSE_CAP_PARALLEL_DIROPS (1 << 18)
#define FUSE_CAP_POSIX_ACL (1 << 19)
#define FUSE_CAP_CACHE_SYMLINKS (1 << 20)
#define FUSE_CAP_MAX_PAGES (1 << 21)
#define FUSE_CAP_ASYNC_READ (1 << 0)
#define FUSE_CAP_POSIX_LOCKS (1 << 1)
#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3)
#define FUSE_CAP_EXPORT_SUPPORT (1 << 4)
#define FUSE_CAP_BIG_WRITES (1 << 5)
#define FUSE_CAP_DONT_MASK (1 << 6)
#define FUSE_CAP_SPLICE_WRITE (1 << 7)
#define FUSE_CAP_SPLICE_MOVE (1 << 8)
#define FUSE_CAP_SPLICE_READ (1 << 9)
#define FUSE_CAP_FLOCK_LOCKS (1 << 10)
#define FUSE_CAP_IOCTL_DIR (1 << 11)
#define FUSE_CAP_READDIR_PLUS (1 << 13)
#define FUSE_CAP_READDIR_PLUS_AUTO (1 << 14)
#define FUSE_CAP_ASYNC_DIO (1 << 15)
#define FUSE_CAP_WRITEBACK_CACHE (1 << 16)
#define FUSE_CAP_PARALLEL_DIROPS (1 << 18)
#define FUSE_CAP_POSIX_ACL (1 << 19)
#define FUSE_CAP_CACHE_SYMLINKS (1 << 20)
#define FUSE_CAP_MAX_PAGES (1 << 21)
/** /**
* Ioctl flags * Ioctl flags

9
libfuse/include/fuse_compat.h

@ -12,7 +12,6 @@
struct fuse_operations_compat25 { struct fuse_operations_compat25 {
int (*getattr) (const char *, struct stat *); int (*getattr) (const char *, struct stat *);
int (*readlink) (const char *, char *, size_t); int (*readlink) (const char *, char *, size_t);
int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
int (*mknod) (const char *, mode_t, dev_t); int (*mknod) (const char *, mode_t, dev_t);
int (*mkdir) (const char *, mode_t); int (*mkdir) (const char *, mode_t);
int (*unlink) (const char *); int (*unlink) (const char *);
@ -38,7 +37,7 @@ struct fuse_operations_compat25 {
int (*listxattr) (const char *, char *, size_t); int (*listxattr) (const char *, char *, size_t);
int (*removexattr) (const char *, const char *); int (*removexattr) (const char *, const char *);
int (*opendir) (const char *, struct fuse_file_info *); int (*opendir) (const char *, struct fuse_file_info *);
int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t,
int (*readdir) (const char *, void *, off_t,
struct fuse_file_info *); struct fuse_file_info *);
int (*releasedir) (const char *, struct fuse_file_info *); int (*releasedir) (const char *, struct fuse_file_info *);
int (*fsyncdir) (const char *, int, struct fuse_file_info *); int (*fsyncdir) (const char *, int, struct fuse_file_info *);
@ -71,7 +70,6 @@ void fuse_teardown_compat22(struct fuse *fuse, int fd, char *mountpoint);
struct fuse_operations_compat22 { struct fuse_operations_compat22 {
int (*getattr) (const char *, struct stat *); int (*getattr) (const char *, struct stat *);
int (*readlink) (const char *, char *, size_t); int (*readlink) (const char *, char *, size_t);
int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
int (*mknod) (const char *, mode_t, dev_t); int (*mknod) (const char *, mode_t, dev_t);
int (*mkdir) (const char *, mode_t); int (*mkdir) (const char *, mode_t);
int (*unlink) (const char *); int (*unlink) (const char *);
@ -97,7 +95,7 @@ struct fuse_operations_compat22 {
int (*listxattr) (const char *, char *, size_t); int (*listxattr) (const char *, char *, size_t);
int (*removexattr) (const char *, const char *); int (*removexattr) (const char *, const char *);
int (*opendir) (const char *, struct fuse_file_info_compat *); int (*opendir) (const char *, struct fuse_file_info_compat *);
int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t,
int (*readdir) (const char *, void *, off_t,
struct fuse_file_info_compat *); struct fuse_file_info_compat *);
int (*releasedir) (const char *, struct fuse_file_info_compat *); int (*releasedir) (const char *, struct fuse_file_info_compat *);
int (*fsyncdir) (const char *, int, struct fuse_file_info_compat *); int (*fsyncdir) (const char *, int, struct fuse_file_info_compat *);
@ -118,11 +116,9 @@ int fuse_main_real_compat22(int argc, char *argv[],
const struct fuse_operations_compat22 *op, const struct fuse_operations_compat22 *op,
size_t op_size); size_t op_size);
typedef int (*fuse_dirfil_t_compat) (fuse_dirh_t h, const char *name, int type);
struct fuse_operations_compat2 { struct fuse_operations_compat2 {
int (*getattr) (const char *, struct stat *); int (*getattr) (const char *, struct stat *);
int (*readlink) (const char *, char *, size_t); int (*readlink) (const char *, char *, size_t);
int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t_compat);
int (*mknod) (const char *, mode_t, dev_t); int (*mknod) (const char *, mode_t, dev_t);
int (*mkdir) (const char *, mode_t); int (*mkdir) (const char *, mode_t);
int (*unlink) (const char *); int (*unlink) (const char *);
@ -170,7 +166,6 @@ struct fuse_statfs_compat1 {
struct fuse_operations_compat1 { struct fuse_operations_compat1 {
int (*getattr) (const char *, struct stat *); int (*getattr) (const char *, struct stat *);
int (*readlink) (const char *, char *, size_t); int (*readlink) (const char *, char *, size_t);
int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t_compat);
int (*mknod) (const char *, mode_t, dev_t); int (*mknod) (const char *, mode_t, dev_t);
int (*mkdir) (const char *, mode_t); int (*mkdir) (const char *, mode_t);
int (*unlink) (const char *); int (*unlink) (const char *);

31
libfuse/include/fuse_dirent.h

@ -0,0 +1,31 @@
/*
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 <stdint.h>
typedef struct fuse_dirent_s fuse_dirent_t;
struct fuse_dirent_s
{
uint64_t ino;
uint64_t off;
uint32_t namelen;
uint32_t type;
char name[];
};

31
libfuse/include/fuse_direntplus.h

@ -0,0 +1,31 @@
/*
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 "fuse_attr.h"
#include "fuse_dirent.h"
#include "fuse_entry.h"
typedef struct fuse_direntplus_s fuse_direntplus_t;
struct fuse_direntplus_s
{
fuse_entry_t entry;
fuse_attr_t attr;
fuse_dirent_t dirent;
};

80
libfuse/include/fuse_dirents.h

@ -0,0 +1,80 @@
/*
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
#ifdef __cplusplus
extern "C" {
#endif
#include "fuse_dirent.h"
#include "fuse_direntplus.h"
#include "fuse_entry.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_s fuse_dirents_t;
struct fuse_dirents_s
{
char *buf;
uint64_t buf_len;
uint64_t data_len;
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);
int fuse_dirents_add_plus(fuse_dirents_t *d,
const struct dirent *de,
const fuse_entry_t *entry,
const struct stat *st);
#ifdef __linux__
struct linux_dirent64;
int fuse_dirents_add_linux(fuse_dirents_t *d,
const struct linux_dirent64 *de);
int fuse_dirents_add_linux_plus(fuse_dirents_t *d,
const struct linux_dirent64 *de,
const fuse_entry_t *entry,
const struct stat *st);
#endif
void *fuse_dirents_find(fuse_dirents_t *d,
const uint64_t ino);
int fuse_dirents_convert_plus2normal(fuse_dirents_t *d);
#ifdef __cplusplus
}
#endif

14
libfuse/include/fuse_entry.h

@ -0,0 +1,14 @@
#pragma once
#include <stdint.h>
typedef struct fuse_entry_s fuse_entry_t;
struct fuse_entry_s
{
uint64_t nodeid;
uint64_t generation;
uint64_t entry_valid;
uint64_t attr_valid;
uint32_t entry_valid_nsec;
uint32_t attr_valid_nsec;
};

38
libfuse/include/fuse_lowlevel.h

@ -605,7 +605,11 @@ struct fuse_lowlevel_ops {
* @param fi file information * @param fi file information
*/ */
void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
struct fuse_file_info *fi);
struct fuse_file_info *llffi);
void (*readdir_plus)(fuse_req_t req, fuse_ino_t ino,
size_t size, off_t off,
struct fuse_file_info *ffi);
/** /**
* Release an open directory * Release an open directory
@ -1262,38 +1266,6 @@ int fuse_reply_lock(fuse_req_t req, const struct flock *lock);
*/ */
int fuse_reply_bmap(fuse_req_t req, uint64_t idx); int fuse_reply_bmap(fuse_req_t req, uint64_t idx);
/* ----------------------------------------------------------- *
* Filling a buffer in readdir *
* ----------------------------------------------------------- */
/**
* Add a directory entry to the buffer
*
* Buffer needs to be large enough to hold the entry. If it's not,
* then the entry is not filled in but the size of the entry is still
* returned. The caller can check this by comparing the bufsize
* parameter with the returned entry size. If the entry size is
* larger than the buffer size, the operation failed.
*
* From the 'stbuf' argument the st_ino field and bits 12-15 of the
* st_mode field are used. The other fields are ignored.
*
* Note: offsets do not necessarily represent physical offsets, and
* could be any marker, that enables the implementation to find a
* specific point in the directory stream.
*
* @param req request handle
* @param buf the point where the new entry will be added to the buffer
* @param bufsize remaining size of the buffer
* @param name the name of the entry
* @param stbuf the file attributes
* @param off the offset of the next entry
* @return the space needed for the entry
*/
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
const char *name, const struct stat *stbuf,
off_t off);
/** /**
* Reply to ask for data fetch and output buffer preparation. ioctl * Reply to ask for data fetch and output buffer preparation. ioctl
* will be retried with the specified input data fetched and output * will be retried with the specified input data fetched and output

17
libfuse/include/stat_utils.h

@ -0,0 +1,17 @@
#pragma once
#include "config.h"
#ifdef HAVE_STRUCT_STAT_ST_ATIM
#define ST_ATIM_NSEC(ST) ((ST)->st_atim.tv_nsec)
#define ST_CTIM_NSEC(ST) ((ST)->st_ctim.tv_nsec)
#define ST_MTIM_NSEC(ST) ((ST)->st_mtim.tv_nsec)
#elif defined HAVE_STRUCT_STAT_ST_ATIMESPEC
#define ST_ATIM_NSEC(ST) ((ST)->st_atimespec.tv_nsec)
#define ST_CTIM_NSEC(ST) ((ST)->st_ctimespec.tv_nsec)
#define ST_MTIM_NSEC(ST) ((ST)->st_mtimespec.tv_nsec)
#else
#define ST_ATIM_NSEC(ST) 0
#define ST_CTIM_NSEC(ST) 0
#define ST_MTIM_NSEC(ST) 0
#endif

420
libfuse/lib/fuse.c

@ -18,6 +18,7 @@
#include "fuse_common_compat.h" #include "fuse_common_compat.h"
#include "fuse_compat.h" #include "fuse_compat.h"
#include "fuse_kernel.h" #include "fuse_kernel.h"
#include "fuse_dirents.h"
#include <assert.h> #include <assert.h>
#include <dlfcn.h> #include <dlfcn.h>
@ -63,8 +64,7 @@ struct fuse_config {
int remember; int remember;
int nopath; int nopath;
int debug; int debug;
int use_ino;
int readdir_ino;
int use_ino;
int set_mode; int set_mode;
int set_uid; int set_uid;
int set_gid; int set_gid;
@ -183,25 +183,11 @@ struct node_lru {
struct timespec forget_time; struct timespec forget_time;
}; };
struct fuse_dh {
pthread_mutex_t lock;
struct fuse *fuse;
fuse_req_t req;
char *contents;
int allocated;
unsigned len;
unsigned size;
unsigned needlen;
int filled;
uint64_t fh;
int error;
fuse_ino_t nodeid;
};
/* old dir handle */
struct fuse_dirhandle {
fuse_fill_dir_t filler;
void *buf;
struct fuse_dh
{
pthread_mutex_t lock;
uint64_t fh;
fuse_dirents_t d;
}; };
struct fuse_context_i { struct fuse_context_i {
@ -1097,19 +1083,26 @@ static int get_path(struct fuse *f, fuse_ino_t nodeid, char **path)
return get_path_common(f, nodeid, NULL, path, NULL); return get_path_common(f, nodeid, NULL, path, NULL);
} }
static int get_path_nullok(struct fuse *f, fuse_ino_t nodeid, char **path)
static
int
get_path_nullok(struct fuse *f,
fuse_ino_t nodeid,
char **path)
{ {
int err = 0;
int err = 0;
if (f->conf.nopath) {
*path = NULL;
} else {
err = get_path_common(f, nodeid, NULL, path, NULL);
if (err == -ENOENT && f->nullpath_ok)
err = 0;
}
if(f->conf.nopath)
{
*path = NULL;
}
else
{
err = get_path_common(f,nodeid,NULL,path,NULL);
if((err == -ENOENT) && f->nullpath_ok)
err = 0;
}
return err;
return err;
} }
static int get_path_name(struct fuse *f, fuse_ino_t nodeid, const char *name, static int get_path_name(struct fuse *f, fuse_ino_t nodeid, const char *name,
@ -1305,8 +1298,8 @@ out:
static void set_stat(struct fuse *f, fuse_ino_t nodeid, struct stat *stbuf) static void set_stat(struct fuse *f, fuse_ino_t nodeid, struct stat *stbuf)
{ {
if (!f->conf.use_ino)
stbuf->st_ino = nodeid;
if (!f->conf.use_ino)
stbuf->st_ino = nodeid;
if (f->conf.set_mode) if (f->conf.set_mode)
stbuf->st_mode = (stbuf->st_mode & S_IFMT) | stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
(0777 & ~f->conf.umask); (0777 & ~f->conf.umask);
@ -1929,45 +1922,30 @@ int fuse_fs_releasedir(struct fuse_fs *fs, const char *path,
} }
} }
static int fill_dir_old(struct fuse_dirhandle *dh, const char *name, int type,
ino_t ino)
int
fuse_fs_readdir(struct fuse_fs *fs,
struct fuse_file_info *fi,
fuse_dirents_t *buf)
{ {
int res;
struct stat stbuf;
if(fs->op.readdir == NULL)
return -ENOSYS;
memset(&stbuf, 0, sizeof(stbuf));
stbuf.st_mode = type << 12;
stbuf.st_ino = ino;
fuse_get_context()->private_data = fs->user_data;
res = dh->filler(dh->buf, name, &stbuf, 0);
return res ? -ENOMEM : 0;
return fs->op.readdir(fi,buf);
} }
int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
fuse_fill_dir_t filler, off_t off,
struct fuse_file_info *fi)
int
fuse_fs_readdir_plus(struct fuse_fs *fs_,
struct fuse_file_info *ffi_,
fuse_dirents_t *buf_)
{ {
fuse_get_context()->private_data = fs->user_data;
if (fs->op.readdir) {
if (fs->debug)
fprintf(stderr, "readdir[%llu] from %llu\n",
(unsigned long long) fi->fh,
(unsigned long long) off);
return fs->op.readdir(path, buf, filler, off, fi);
} else if (fs->op.getdir) {
struct fuse_dirhandle dh;
if(fs_->op.readdir_plus == NULL)
return -ENOSYS;
if (fs->debug)
fprintf(stderr, "getdir[%llu]\n",
(unsigned long long) fi->fh);
fuse_get_context()->private_data = fs_->user_data;
dh.filler = filler;
dh.buf = buf;
return fs->op.getdir(path, &dh, fill_dir_old);
} else {
return -ENOSYS;
}
return fs_->op.readdir_plus(ffi_,buf_);
} }
int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode, int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
@ -3370,18 +3348,13 @@ static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino,
char *path; char *path;
int err; int err;
dh = (struct fuse_dh *) malloc(sizeof(struct fuse_dh));
dh = (struct fuse_dh *) calloc(1,sizeof(struct fuse_dh));
if (dh == NULL) { if (dh == NULL) {
reply_err(req, -ENOMEM); reply_err(req, -ENOMEM);
return; return;
} }
memset(dh, 0, sizeof(struct fuse_dh));
dh->fuse = f;
dh->contents = NULL;
dh->len = 0;
dh->filled = 0;
dh->nodeid = ino;
fuse_dirents_init(&dh->d);
fuse_mutex_init(&dh->lock); fuse_mutex_init(&dh->lock);
llfi->fh = (uintptr_t) dh; llfi->fh = (uintptr_t) dh;
@ -3415,175 +3388,180 @@ static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino,
free_path(f, ino, path); free_path(f, ino, path);
} }
static int extend_contents(struct fuse_dh *dh, unsigned minsize)
static
int
readdir_fill(struct fuse *f_,
fuse_req_t req_,
fuse_dirents_t *d_,
struct fuse_file_info *fi_)
{ {
if (minsize > dh->size) {
char *newptr;
unsigned newsize = dh->size;
if (!newsize)
newsize = 1024;
while (newsize < minsize) {
if (newsize >= 0x80000000)
newsize = 0xffffffff;
else
newsize *= 2;
}
int rv;
struct fuse_intr_data intr_data;
newptr = (char *) realloc(dh->contents, newsize);
if (!newptr) {
dh->error = -ENOMEM;
return -1;
}
dh->contents = newptr;
dh->size = newsize;
}
return 0;
fuse_prepare_interrupt(f_,req_,&intr_data);
rv = fuse_fs_readdir(f_->fs,fi_,d_);
fuse_finish_interrupt(f_,req_,&intr_data);
return rv;
} }
static int fill_dir(void *dh_, const char *name, const struct stat *statp,
off_t off)
static
int
readdir_plus_fill(struct fuse *f_,
fuse_req_t req_,
fuse_dirents_t *d_,
struct fuse_file_info *fi_)
{ {
struct fuse_dh *dh = (struct fuse_dh *) dh_;
struct stat stbuf;
size_t newlen;
int rv;
struct fuse_intr_data intr_data;
if (statp)
stbuf = *statp;
else {
memset(&stbuf, 0, sizeof(stbuf));
stbuf.st_ino = FUSE_UNKNOWN_INO;
}
fuse_prepare_interrupt(f_,req_,&intr_data);
rv = fuse_fs_readdir_plus(f_->fs,fi_,d_);
fuse_finish_interrupt(f_,req_,&intr_data);
if (!dh->fuse->conf.use_ino) {
stbuf.st_ino = FUSE_UNKNOWN_INO;
if (dh->fuse->conf.readdir_ino) {
struct node *node;
pthread_mutex_lock(&dh->fuse->lock);
node = lookup_node(dh->fuse, dh->nodeid, name);
if (node)
stbuf.st_ino = (ino_t) node->nodeid;
pthread_mutex_unlock(&dh->fuse->lock);
}
}
return rv;
}
if (off) {
if (extend_contents(dh, dh->needlen) == -1)
return 1;
static
uint64_t
convert_plus2normal(fuse_dirents_t *d_,
uint64_t off_)
{
uint64_t ino;
fuse_dirent_t *d;
fuse_direntplus_t *dp;
dh->filled = 0;
newlen = dh->len +
fuse_add_direntry(dh->req, dh->contents + dh->len,
dh->needlen - dh->len, name,
&stbuf, off);
if (newlen > dh->needlen)
return 1;
} else {
newlen = dh->len +
fuse_add_direntry(dh->req, NULL, 0, name, NULL, 0);
if (extend_contents(dh, newlen) == -1)
return 1;
dp = (fuse_direntplus_t*)&d_->buf[off_];
ino = dp->dirent.ino;
fuse_dirents_convert_plus2normal(d_);
d = fuse_dirents_find(d_,ino);
fuse_add_direntry(dh->req, dh->contents + dh->len,
dh->size - dh->len, name, &stbuf, newlen);
}
dh->len = newlen;
return 0;
return d->off;
} }
static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
size_t size, off_t off, struct fuse_dh *dh,
struct fuse_file_info *fi)
static
void
fuse_lib_readdir(fuse_req_t req_,
fuse_ino_t ino_,
size_t size_,
off_t off_,
struct fuse_file_info *llffi_)
{ {
char *path;
int err;
int rv;
struct fuse *f;
fuse_dirents_t *d;
struct fuse_dh *dh;
struct fuse_file_info fi;
if (f->fs->op.readdir)
err = get_path_nullok(f, ino, &path);
else
err = get_path(f, ino, &path);
if (!err) {
struct fuse_intr_data d;
f = req_fuse_prepare(req_);
dh = get_dirhandle(llffi_,&fi);
d = &dh->d;
dh->len = 0;
dh->error = 0;
dh->needlen = size;
dh->filled = 1;
dh->req = req;
fuse_prepare_interrupt(f, req, &d);
err = fuse_fs_readdir(f->fs, path, dh, fill_dir, off, fi);
fuse_finish_interrupt(f, req, &d);
dh->req = NULL;
if (!err)
err = dh->error;
if (err)
dh->filled = 0;
free_path(f, ino, path);
}
return err;
pthread_mutex_lock(&dh->lock);
rv = 0;
if(off_ == 0)
rv = readdir_fill(f,req_,d,&fi);
if(rv)
{
reply_err(req_,rv);
goto out;
}
if(off_ >= d->data_len)
size_ = 0;
else if((off_ + size_) > d->data_len)
size_ = (d->data_len - off_);
/* if((size_ > 0) && (d->type == PLUS)) */
/* off_ = convert_plus2normal(d,off_); */
fuse_reply_buf(req_,
&d->buf[off_],
size_);
out:
pthread_mutex_unlock(&dh->lock);
} }
static void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *llfi)
static
void
fuse_lib_readdir_plus(fuse_req_t req_,
fuse_ino_t ino_,
size_t size_,
off_t off_,
struct fuse_file_info *llffi_)
{ {
struct fuse *f = req_fuse_prepare(req);
struct fuse_file_info fi;
struct fuse_dh *dh = get_dirhandle(llfi, &fi);
int rv;
struct fuse *f;
fuse_dirents_t *d;
struct fuse_dh *dh;
struct fuse_file_info fi;
pthread_mutex_lock(&dh->lock);
/* According to SUS, directory contents need to be refreshed on
rewinddir() */
if (!off)
dh->filled = 0;
f = req_fuse_prepare(req_);
dh = get_dirhandle(llffi_,&fi);
d = &dh->d;
if (!dh->filled) {
int err = readdir_fill(f, req, ino, size, off, dh, &fi);
if (err) {
reply_err(req, err);
goto out;
}
}
if (dh->filled) {
if (off < dh->len) {
if (off + size > dh->len)
size = dh->len - off;
} else
size = 0;
} else {
size = dh->len;
off = 0;
}
fuse_reply_buf(req, dh->contents + off, size);
out:
pthread_mutex_unlock(&dh->lock);
pthread_mutex_lock(&dh->lock);
rv = 0;
if(off_ == 0)
rv = readdir_plus_fill(f,req_,d,&fi);
if(rv)
{
reply_err(req_,rv);
goto out;
}
if(off_ >= d->data_len)
size_ = 0;
else if((off_ + size_) > d->data_len)
size_ = (d->data_len - off_);
fuse_reply_buf(req_,
&d->buf[off_],
size_);
out:
pthread_mutex_unlock(&dh->lock);
} }
static void fuse_lib_releasedir(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *llfi)
static
void
fuse_lib_releasedir(fuse_req_t req_,
fuse_ino_t ino_,
struct fuse_file_info *llfi_)
{ {
struct fuse *f = req_fuse_prepare(req);
struct fuse_intr_data d;
struct fuse_file_info fi;
struct fuse_dh *dh = get_dirhandle(llfi, &fi);
char *path;
const char *compatpath;
struct fuse *f;
struct fuse_dh *dh;
struct fuse_intr_data d;
struct fuse_file_info fi;
char *path;
const char *compatpath;
get_path_nullok(f, ino, &path);
if (path != NULL || f->nullpath_ok || f->conf.nopath)
compatpath = path;
else
compatpath = "-";
f = req_fuse_prepare(req_);
dh = get_dirhandle(llfi_,&fi);
fuse_prepare_interrupt(f, req, &d);
fuse_fs_releasedir(f->fs, compatpath, &fi);
fuse_finish_interrupt(f, req, &d);
free_path(f, ino, path);
get_path_nullok(f,ino_,&path);
if (path != NULL || f->nullpath_ok || f->conf.nopath)
compatpath = path;
else
compatpath = "-";
pthread_mutex_lock(&dh->lock);
pthread_mutex_unlock(&dh->lock);
pthread_mutex_destroy(&dh->lock);
free(dh->contents);
free(dh);
reply_err(req, 0);
fuse_prepare_interrupt(f,req_,&d);
fuse_fs_releasedir(f->fs,compatpath,&fi);
fuse_finish_interrupt(f,req_,&d);
free_path(f,ino_,path);
/* Done to keep race condition between last readdir reply and the unlock */
pthread_mutex_lock(&dh->lock);
pthread_mutex_unlock(&dh->lock);
pthread_mutex_destroy(&dh->lock);
fuse_dirents_free(&dh->d);
free(dh);
reply_err(req_,0);
} }
static void fuse_lib_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, static void fuse_lib_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
@ -4273,6 +4251,7 @@ static struct fuse_lowlevel_ops fuse_path_ops = {
.fsync = fuse_lib_fsync, .fsync = fuse_lib_fsync,
.opendir = fuse_lib_opendir, .opendir = fuse_lib_opendir,
.readdir = fuse_lib_readdir, .readdir = fuse_lib_readdir,
.readdir_plus = fuse_lib_readdir_plus,
.releasedir = fuse_lib_releasedir, .releasedir = fuse_lib_releasedir,
.fsyncdir = fuse_lib_fsyncdir, .fsyncdir = fuse_lib_fsyncdir,
.statfs = fuse_lib_statfs, .statfs = fuse_lib_statfs,
@ -4484,8 +4463,6 @@ static const struct fuse_opt fuse_lib_opts[] = {
FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP),
FUSE_LIB_OPT("debug", debug, 1), FUSE_LIB_OPT("debug", debug, 1),
FUSE_LIB_OPT("-d", debug, 1), FUSE_LIB_OPT("-d", debug, 1),
FUSE_LIB_OPT("use_ino", use_ino, 1),
FUSE_LIB_OPT("readdir_ino", readdir_ino, 1),
FUSE_LIB_OPT("umask=", set_mode, 1), FUSE_LIB_OPT("umask=", set_mode, 1),
FUSE_LIB_OPT("umask=%o", umask, 0), FUSE_LIB_OPT("umask=%o", umask, 0),
FUSE_LIB_OPT("uid=", set_uid, 1), FUSE_LIB_OPT("uid=", set_uid, 1),
@ -4501,14 +4478,13 @@ static const struct fuse_opt fuse_lib_opts[] = {
FUSE_LIB_OPT("intr", intr, 1), FUSE_LIB_OPT("intr", intr, 1),
FUSE_LIB_OPT("intr_signal=%d", intr_signal, 0), FUSE_LIB_OPT("intr_signal=%d", intr_signal, 0),
FUSE_LIB_OPT("threads=%d", threads, 0), FUSE_LIB_OPT("threads=%d", threads, 0),
FUSE_LIB_OPT("use_ino", use_ino, 1),
FUSE_OPT_END FUSE_OPT_END
}; };
static void fuse_lib_help(void) static void fuse_lib_help(void)
{ {
fprintf(stderr, fprintf(stderr,
" -o use_ino let filesystem set inode numbers\n"
" -o readdir_ino try to fill in d_ino in readdir\n"
" -o umask=M set file permissions (octal)\n" " -o umask=M set file permissions (octal)\n"
" -o uid=N set file owner\n" " -o uid=N set file owner\n"
" -o gid=N set file group\n" " -o gid=N set file group\n"
@ -4695,14 +4671,6 @@ struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args,
fuse_lib_opt_proc) == -1) fuse_lib_opt_proc) == -1)
goto out_free_fs; goto out_free_fs;
#if defined(__FreeBSD__) || defined(__NetBSD__)
/*
* In FreeBSD, we always use these settings as inode numbers
* are needed to make getcwd(3) work.
*/
f->conf.readdir_ino = 1;
#endif
if (compat && compat <= 25) { if (compat && compat <= 25) {
if (fuse_sync_compat_args(args) == -1) if (fuse_sync_compat_args(args) == -1)
goto out_free_fs; goto out_free_fs;

429
libfuse/lib/fuse_dirents.c

@ -0,0 +1,429 @@
#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 "stat_utils.h"
#include <dirent.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DEFAULT_SIZE (1024 * 16)
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_resize(fuse_dirents_t *d_,
uint64_t size_)
{
void *p;
if((d_->data_len + size_) >= d_->buf_len)
{
p = realloc(d_->buf,(d_->buf_len * 2));
if(p == NULL)
return -errno;
d_->buf = p;
d_->buf_len *= 2;
}
return 0;
}
static
void*
fuse_dirents_alloc(fuse_dirents_t *d_,
uint64_t size_)
{
int rv;
fuse_dirent_t *d;
rv = fuse_dirents_resize(d_,size_);
if(rv)
return NULL;
d = (fuse_dirent_t*)&d_->buf[d_->data_len];
d_->data_len += 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*)&d_->buf[0];
end = (fuse_dirent_t*)&d_->buf[d_->data_len];
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*)&d_->buf[0];
end = (fuse_direntplus_t*)&d_->buf[d_->data_len];
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_)
{
int rv;
uint64_t size;
fuse_dirent_t *d;
fuse_dirents_t normal;
fuse_direntplus_t *cur;
fuse_direntplus_t *end;
rv = fuse_dirents_init(&normal);
if(rv < 0)
return rv;
cur = (fuse_direntplus_t*)&d_->buf[0];
end = (fuse_direntplus_t*)&d_->buf[d_->data_len];
while(cur < end)
{
size = fuse_dirent_size(cur->dirent.namelen);
d = fuse_dirents_alloc(&normal,size);
if(d == NULL)
return -ENOMEM;
memcpy(d,&cur->dirent,size);
d->off = normal.data_len;;
cur = fuse_direntplus_next(cur);
}
fuse_dirents_free(d_);
normal.type = NORMAL;
*d_ = normal;
return 0;
}
int
fuse_dirents_add(fuse_dirents_t *d_,
const struct dirent *dirent_)
{
uint64_t size;
uint64_t namelen;
fuse_dirent_t *d;
switch(d_->type)
{
case UNSET:
d_->type = NORMAL;
break;
case NORMAL:
break;
case PLUS:
return -EINVAL;
}
namelen = _D_ALLOC_NAMLEN(dirent_);
size = fuse_dirent_size(namelen);
d = fuse_dirents_alloc(d_,size);
if(d == NULL)
return -ENOMEM;
d->ino = dirent_->d_ino;
d->off = d_->data_len;
d->namelen = namelen;
d->type = dirent_->d_type;
memcpy(d->name,dirent_->d_name,namelen);
return 0;
}
int
fuse_dirents_add_plus(fuse_dirents_t *d_,
const struct dirent *dirent_,
const fuse_entry_t *entry_,
const struct stat *st_)
{
uint64_t size;
uint64_t namelen;
fuse_direntplus_t *d;
switch(d_->type)
{
case UNSET:
d_->type = PLUS;
break;
case NORMAL:
return -EINVAL;
case PLUS:
break;
}
namelen = _D_ALLOC_NAMLEN(dirent_);
size = fuse_direntplus_size(namelen);
d = fuse_dirents_alloc(d_,size);
if(d == NULL)
return -ENOMEM;
d->dirent.ino = dirent_->d_ino;
d->dirent.off = d_->data_len;
d->dirent.namelen = namelen;
d->dirent.type = dirent_->d_type;
memcpy(d->dirent.name,dirent_->d_name,namelen);
d->entry = *entry_;
fuse_dirents_fill_attr(&d->attr,st_);
return 0;
}
#ifdef __linux__
struct linux_dirent64
{
uint64_t d_ino;
int64_t d_off;
uint16_t d_reclen;
uint8_t d_type;
char d_name[];
};
int
fuse_dirents_add_linux(fuse_dirents_t *d_,
const struct linux_dirent64 *dirent_)
{
uint64_t size;
uint64_t namelen;
fuse_dirent_t *d;
switch(d_->type)
{
case UNSET:
d_->type = NORMAL;
break;
case NORMAL:
break;
case PLUS:
return -EINVAL;
}
namelen = (dirent_->d_reclen - offsetof(struct linux_dirent64,d_name));
size = fuse_dirent_size(namelen);
d = fuse_dirents_alloc(d_,size);
if(d == NULL)
return -ENOMEM;
d->ino = dirent_->d_ino;
d->off = d_->data_len;
d->namelen = namelen;
d->type = dirent_->d_type;
memcpy(d->name,dirent_->d_name,namelen);
return 0;
}
int
fuse_dirents_add_linux_plus(fuse_dirents_t *d_,
const struct linux_dirent64 *dirent_,
const fuse_entry_t *entry_,
const struct stat *st_)
{
uint64_t size;
uint64_t namelen;
fuse_direntplus_t *d;
switch(d_->type)
{
case UNSET:
d_->type = PLUS;
break;
case NORMAL:
return -EINVAL;
case PLUS:
break;
}
namelen = (dirent_->d_reclen - offsetof(struct linux_dirent64,d_name));
size = fuse_direntplus_size(namelen);
d = fuse_dirents_alloc(d_,size);
if(d == NULL)
return -ENOMEM;
d->dirent.ino = dirent_->d_ino;
d->dirent.off = d_->data_len;
d->dirent.namelen = namelen;
d->dirent.type = dirent_->d_type;
memcpy(d->dirent.name,dirent_->d_name,namelen);
d->entry = *entry_;
fuse_dirents_fill_attr(&d->attr,st_);
return 0;
}
#endif
void
fuse_dirents_reset(fuse_dirents_t *d_)
{
d_->data_len = 0;
d_->type = UNSET;
}
int
fuse_dirents_init(fuse_dirents_t *d_)
{
void *buf;
buf = calloc(DEFAULT_SIZE,1);
if(buf == NULL)
return -ENOMEM;
d_->buf = buf;
d_->buf_len = DEFAULT_SIZE;
d_->data_len = 0;
d_->type = UNSET;
return 0;
}
void
fuse_dirents_free(fuse_dirents_t *d_)
{
d_->buf_len = 0;
d_->data_len = 0;
d_->type = UNSET;
free(d_->buf);
}

2
libfuse/lib/fuse_loop.c

@ -17,7 +17,7 @@ int fuse_session_loop(struct fuse_session *se)
int res = 0; int res = 0;
struct fuse_chan *ch = fuse_session_next_chan(se, NULL); struct fuse_chan *ch = fuse_session_next_chan(se, NULL);
size_t bufsize = fuse_chan_bufsize(ch); size_t bufsize = fuse_chan_bufsize(ch);
char *buf = (char *) malloc(bufsize);
char *buf = (char*)calloc(bufsize,1);
if (!buf) { if (!buf) {
fprintf(stderr, "fuse: failed to allocate read buffer\n"); fprintf(stderr, "fuse: failed to allocate read buffer\n");
return -1; return -1;

2
libfuse/lib/fuse_loop_mt.c

@ -141,7 +141,7 @@ static int fuse_loop_start_thread(struct fuse_mt *mt)
} }
memset(w, 0, sizeof(struct fuse_worker)); memset(w, 0, sizeof(struct fuse_worker));
w->bufsize = fuse_chan_bufsize(mt->prevch); w->bufsize = fuse_chan_bufsize(mt->prevch);
w->buf = malloc(w->bufsize);
w->buf = calloc(w->bufsize,1);
w->mt = mt; w->mt = mt;
if (!w->buf) { if (!w->buf) {
fprintf(stderr, "fuse: failed to allocate read buffer\n"); fprintf(stderr, "fuse: failed to allocate read buffer\n");

51
libfuse/lib/fuse_lowlevel.c

@ -222,7 +222,7 @@ static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov,
static int send_reply(fuse_req_t req, int error, const void *arg, static int send_reply(fuse_req_t req, int error, const void *arg,
size_t argsize) size_t argsize)
{ {
struct iovec iov[2];
struct iovec iov[2];
int count = 1; int count = 1;
if (argsize) { if (argsize) {
iov[1].iov_base = (void *) arg; iov[1].iov_base = (void *) arg;
@ -1411,6 +1411,24 @@ static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
fuse_reply_err(req, ENOSYS); fuse_reply_err(req, ENOSYS);
} }
static
void
do_readdir_plus(fuse_req_t req_,
fuse_ino_t nodeid_,
const void *inarg_)
{
const struct fuse_read_in *arg;
struct fuse_file_info ffi = {0};
arg = (struct fuse_read_in*)inarg_;
ffi.fh = arg->fh;
if(req_->f->op.readdir_plus)
req_->f->op.readdir_plus(req_,nodeid_,arg->size,arg->offset,&ffi);
else
fuse_reply_err(req_,ENOSYS);
}
static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
{ {
struct fuse_release_in *arg = (struct fuse_release_in *) inarg; struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
@ -1817,6 +1835,10 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
f->conn.capable |= FUSE_CAP_MAX_PAGES; f->conn.capable |= FUSE_CAP_MAX_PAGES;
if (arg->flags & FUSE_WRITEBACK_CACHE) if (arg->flags & FUSE_WRITEBACK_CACHE)
f->conn.capable |= FUSE_CAP_WRITEBACK_CACHE; f->conn.capable |= FUSE_CAP_WRITEBACK_CACHE;
if (arg->flags & FUSE_DO_READDIRPLUS)
f->conn.capable |= FUSE_CAP_READDIR_PLUS;
if (arg->flags & FUSE_READDIRPLUS_AUTO)
f->conn.capable |= FUSE_CAP_READDIR_PLUS_AUTO;
} else { } else {
f->conn.want &= ~FUSE_CAP_ASYNC_READ; f->conn.want &= ~FUSE_CAP_ASYNC_READ;
f->conn.max_readahead = 0; f->conn.max_readahead = 0;
@ -1895,6 +1917,11 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
outarg.flags |= FUSE_PARALLEL_DIROPS; outarg.flags |= FUSE_PARALLEL_DIROPS;
if (f->conn.want & FUSE_CAP_WRITEBACK_CACHE) if (f->conn.want & FUSE_CAP_WRITEBACK_CACHE)
outarg.flags |= FUSE_WRITEBACK_CACHE; outarg.flags |= FUSE_WRITEBACK_CACHE;
if (f->conn.want & FUSE_CAP_READDIR_PLUS)
outarg.flags |= FUSE_DO_READDIRPLUS;
if (f->conn.want & FUSE_CAP_READDIR_PLUS_AUTO)
outarg.flags |= FUSE_READDIRPLUS_AUTO;
outarg.max_readahead = f->conn.max_readahead; outarg.max_readahead = f->conn.max_readahead;
outarg.max_write = f->conn.max_write; outarg.max_write = f->conn.max_write;
if (f->conn.proto_minor >= 13) { if (f->conn.proto_minor >= 13) {
@ -2350,6 +2377,7 @@ static struct {
[FUSE_INIT] = { do_init, "INIT" }, [FUSE_INIT] = { do_init, "INIT" },
[FUSE_OPENDIR] = { do_opendir, "OPENDIR" }, [FUSE_OPENDIR] = { do_opendir, "OPENDIR" },
[FUSE_READDIR] = { do_readdir, "READDIR" }, [FUSE_READDIR] = { do_readdir, "READDIR" },
[FUSE_READDIRPLUS] = { do_readdir_plus, "READDIR_PLUS" },
[FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" }, [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" },
[FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" }, [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" },
[FUSE_GETLK] = { do_getlk, "GETLK" }, [FUSE_GETLK] = { do_getlk, "GETLK" },
@ -2469,13 +2497,20 @@ static void fuse_ll_process_buf(void *data, const struct fuse_buf *buf,
goto reply_err; goto reply_err;
err = EACCES; err = EACCES;
if (f->allow_root && in->uid != f->owner && in->uid != 0 &&
in->opcode != FUSE_INIT && in->opcode != FUSE_READ &&
in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC &&
in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR &&
in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR &&
in->opcode != FUSE_NOTIFY_REPLY)
goto reply_err;
if (f->allow_root &&
in->uid != f->owner &&
in->uid != 0 &&
in->opcode != FUSE_INIT &&
in->opcode != FUSE_READ &&
in->opcode != FUSE_WRITE &&
in->opcode != FUSE_FSYNC &&
in->opcode != FUSE_RELEASE &&
in->opcode != FUSE_READDIR &&
in->opcode != FUSE_READDIRPLUS &&
in->opcode != FUSE_FSYNCDIR &&
in->opcode != FUSE_RELEASEDIR &&
in->opcode != FUSE_NOTIFY_REPLY)
goto reply_err;
err = ENOSYS; err = ENOSYS;
if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func) if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)

1
src/config.cpp

@ -53,6 +53,7 @@ Config::Config()
cache_readdir(false), cache_readdir(false),
async_read(true), async_read(true),
writeback_cache(false), writeback_cache(false),
readdirplus(false),
cache_files(CacheFiles::LIBFUSE), cache_files(CacheFiles::LIBFUSE),
fuse_msg_size(FUSE_MAX_MAX_PAGES), fuse_msg_size(FUSE_MAX_MAX_PAGES),
POLICYINIT(access), POLICYINIT(access),

1
src/config.hpp

@ -113,6 +113,7 @@ public:
bool cache_readdir; bool cache_readdir;
bool async_read; bool async_read;
bool writeback_cache; bool writeback_cache;
bool readdirplus;
CacheFiles cache_files; CacheFiles cache_files;
uint16_t fuse_msg_size; uint16_t fuse_msg_size;

87
src/fixed_mem_pool.hpp

@ -0,0 +1,87 @@
/*
ISC License
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <stdlib.h>
#include <stdint.h>
typedef struct fixed_mem_pool_t fixed_mem_pool_t;
struct fixed_mem_pool_t
{
fixed_mem_pool_t *next;
};
template<uint64_t SIZE>
class FixedMemPool
{
public:
FixedMemPool()
{
list.next = NULL;
}
~FixedMemPool()
{
void *mem;
while(!empty())
{
mem = alloc();
::free(mem);
}
}
bool
empty(void)
{
return (list.next == NULL);
}
uint64_t
size(void)
{
return SIZE;
}
void*
alloc(void)
{
void *rv;
if(list.next == NULL)
return malloc(SIZE);
rv = (void*)list.next;
list.next = list.next->next;
return rv;
}
void
free(void *mem_)
{
fixed_mem_pool_t *next;
next = (fixed_mem_pool_t*)mem_;
next->next = list.next;
list.next = next;
}
private:
fixed_mem_pool_t list;
};

53
src/fs_base_fstatat.hpp

@ -0,0 +1,53 @@
/*
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.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
namespace fs
{
static
inline
int
fstatat(const int dirfd,
const char *pathname,
struct stat *statbuf,
const int flags)
{
return ::fstatat(dirfd,
pathname,
statbuf,
flags);
}
static
inline
int
fstatat_nofollow(const int dirfd,
const char *pathname,
struct stat *statbuf)
{
return fs::fstatat(dirfd,
pathname,
statbuf,
AT_SYMLINK_NOFOLLOW);
}
}

8
src/fs_base_open.hpp

@ -63,4 +63,12 @@ namespace fs
{ {
return fs::open(path_.c_str(),flags_,mode_); return fs::open(path_.c_str(),flags_,mode_);
} }
static
inline
int
open_dir_ro(const std::string &path_)
{
return fs::open(path_,O_RDONLY|O_DIRECTORY);
}
} }

18
src/fs_inode.hpp

@ -30,18 +30,26 @@ namespace fs
static const uint64_t MAGIC = 0x7472617065786974; static const uint64_t MAGIC = 0x7472617065786974;
inline inline
void
recompute(struct stat *st_)
uint64_t
recompute(ino_t ino_,
dev_t dev_)
{ {
uint64_t buf[5]; uint64_t buf[5];
buf[0] = st_->st_ino;
buf[1] = st_->st_dev;
buf[0] = ino_;
buf[1] = dev_;
buf[2] = buf[0] ^ buf[1]; buf[2] = buf[0] ^ buf[1];
buf[3] = buf[0] & buf[1]; buf[3] = buf[0] & buf[1];
buf[4] = buf[0] | buf[1]; buf[4] = buf[0] | buf[1];
st_->st_ino = fasthash64(&buf[0],sizeof(buf),MAGIC);
return fasthash64(&buf[0],sizeof(buf),MAGIC);
}
inline
void
recompute(struct stat *st_)
{
st_->st_ino = recompute(st_->st_ino,st_->st_dev);
} }
} }
} }

2
src/fuse_getxattr.cpp

@ -372,6 +372,8 @@ namespace l
l::getxattr_controlfile_bool(config.async_read,attrvalue); l::getxattr_controlfile_bool(config.async_read,attrvalue);
else if(attr[2] == "fuse_msg_size") else if(attr[2] == "fuse_msg_size")
l::getxattr_controlfile(config.fuse_msg_size,attrvalue); l::getxattr_controlfile(config.fuse_msg_size,attrvalue);
else if(attr[2] == "readdirplus")
l::getxattr_controlfile_bool(config.readdirplus,attrvalue);
break; break;
case 4: case 4:

2
src/fuse_init.cpp

@ -95,6 +95,8 @@ namespace FUSE
l::want_if_capable(conn_,FUSE_CAP_DONT_MASK); l::want_if_capable(conn_,FUSE_CAP_DONT_MASK);
l::want_if_capable(conn_,FUSE_CAP_IOCTL_DIR); l::want_if_capable(conn_,FUSE_CAP_IOCTL_DIR);
l::want_if_capable(conn_,FUSE_CAP_PARALLEL_DIROPS); l::want_if_capable(conn_,FUSE_CAP_PARALLEL_DIROPS);
l::want_if_capable(conn_,FUSE_CAP_READDIR_PLUS,&c.readdirplus);
//l::want_if_capable(conn_,FUSE_CAP_READDIR_PLUS_AUTO);
l::want_if_capable(conn_,FUSE_CAP_POSIX_ACL,&c.posix_acl); l::want_if_capable(conn_,FUSE_CAP_POSIX_ACL,&c.posix_acl);
l::want_if_capable(conn_,FUSE_CAP_WRITEBACK_CACHE,&c.writeback_cache); l::want_if_capable(conn_,FUSE_CAP_WRITEBACK_CACHE,&c.writeback_cache);
l::want_if_capable_max_pages(conn_,c); l::want_if_capable_max_pages(conn_,c);

1
src/fuse_listxattr.cpp

@ -66,6 +66,7 @@ namespace l
("user.mergerfs.pid") ("user.mergerfs.pid")
("user.mergerfs.policies") ("user.mergerfs.policies")
("user.mergerfs.posix_acl") ("user.mergerfs.posix_acl")
("user.mergerfs.readdirplus")
("user.mergerfs.security_capability") ("user.mergerfs.security_capability")
("user.mergerfs.srcmounts") ("user.mergerfs.srcmounts")
("user.mergerfs.statfs") ("user.mergerfs.statfs")

111
src/fuse_readdir.cpp

@ -1,5 +1,7 @@
/* /*
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
ISC License
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above purpose with or without fee is hereby granted, provided that the above
@ -14,105 +16,8 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#define _DEFAULT_SOURCE
#include "config.hpp"
#include "dirinfo.hpp"
#include "errno.hpp"
#include "fs_base_closedir.hpp"
#include "fs_base_dirfd.hpp"
#include "fs_base_opendir.hpp"
#include "fs_base_readdir.hpp"
#include "fs_base_stat.hpp"
#include "fs_devid.hpp"
#include "fs_inode.hpp"
#include "fs_path.hpp"
#include "hashset.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <fuse.h>
#include <string>
#include <vector>
using std::string;
using std::vector;
#define NO_OFFSET 0
namespace l
{
static
int
readdir(const Branches &branches_,
const char *dirname_,
void *buf_,
const fuse_fill_dir_t filler_)
{
HashSet names;
string basepath;
struct stat st = {0};
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
int rv;
int dirfd;
DIR *dh;
basepath = fs::path::make(&branches_[i].path,dirname_);
dh = fs::opendir(basepath);
if(!dh)
continue;
dirfd = fs::dirfd(dh);
st.st_dev = fs::devid(dirfd);
if(st.st_dev == (dev_t)-1)
st.st_dev = i;
rv = 0;
for(struct dirent *de = fs::readdir(dh); de && !rv; de = fs::readdir(dh))
{
rv = names.put(de->d_name);
if(rv == 0)
continue;
st.st_ino = de->d_ino;
st.st_mode = DTTOIF(de->d_type);
fs::inode::recompute(&st);
rv = filler_(buf_,de->d_name,&st,NO_OFFSET);
if(rv)
return (fs::closedir(dh),-ENOMEM);
}
fs::closedir(dh);
}
return 0;
}
}
namespace FUSE
{
int
readdir(const char *fusepath_,
void *buf_,
fuse_fill_dir_t filler_,
off_t offset_,
fuse_file_info *ffi_)
{
DirInfo *di = reinterpret_cast<DirInfo*>(ffi_->fh);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
return l::readdir(config.branches,
di->fusepath.c_str(),
buf_,
filler_);
}
}
#ifdef __linux__
# include "fuse_readdir_linux.icpp"
#else
# include "fuse_readdir_posix.icpp"
#endif

13
src/fuse_readdir.hpp

@ -1,5 +1,5 @@
/* /*
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above purpose with or without fee is hereby granted, provided that the above
@ -18,16 +18,9 @@
#include <fuse.h> #include <fuse.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
namespace FUSE namespace FUSE
{ {
int int
readdir(const char *fusepath_,
void *buf_,
fuse_fill_dir_t filler_,
off_t offset_,
fuse_file_info *ffi_);
readdir(fuse_file_info *ffi_,
fuse_dirents_t *buf_);
} }

161
src/fuse_readdir_linux.icpp

@ -0,0 +1,161 @@
/*
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define _DEFAULT_SOURCE
#include "config.hpp"
#include "dirinfo.hpp"
#include "errno.hpp"
#include "fs_base_close.hpp"
#include "fs_base_open.hpp"
#include "fs_base_stat.hpp"
#include "fs_devid.hpp"
#include "fs_inode.hpp"
#include "fs_path.hpp"
#include "hashset.hpp"
#include "mempools.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <fuse.h>
#include <fuse_dirents.h>
#include <string>
#include <vector>
#include <stddef.h>
#include <sys/syscall.h>
using std::string;
using std::vector;
struct linux_dirent64
{
uint64_t d_ino;
int64_t d_off;
uint16_t d_reclen;
uint8_t d_type;
char d_name[];
};
namespace l
{
static
inline
int
getdents64(unsigned int fd_,
char *dirp_,
unsigned int count_)
{
return syscall(SYS_getdents64,fd_,dirp_,count_);
}
static
int
close_free_ret_enomem(int fd_,
void *buf_)
{
fs::close(fd_);
g_DENTS_BUF_POOL.free(buf_);
return -ENOMEM;
}
static
int
readdir(const Branches &branches_,
const char *dirname_,
fuse_dirents_t *buf_)
{
int rv;
dev_t dev;
char *buf;
HashSet names;
string basepath;
struct linux_dirent64 *d;
buf = (char*)g_DENTS_BUF_POOL.alloc();
if(buf == NULL)
return -ENOMEM;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
int dirfd;
int64_t nread;
uint64_t namelen;
basepath = fs::path::make(&branches_[i].path,dirname_);
dirfd = fs::open_dir_ro(basepath);
if(dirfd == -1)
continue;
dev = fs::devid(dirfd);
if(dev == (dev_t)-1)
dev = i;
for(;;)
{
nread = l::getdents64(dirfd,buf,g_DENTS_BUF_POOL.size());
if(nread == -1)
break;
if(nread == 0)
break;
for(int64_t pos = 0; pos < nread;)
{
d = (struct linux_dirent64*)(buf + pos);
namelen = (d->d_reclen - offsetof(struct linux_dirent64,d_name));
rv = names.put(d->d_name,namelen);
if(rv == 0)
continue;
d->d_ino = fs::inode::recompute(d->d_ino,dev);
rv = fuse_dirents_add_linux(buf_,d);
if(rv)
return close_free_ret_enomem(dirfd,buf);
pos += d->d_reclen;
}
}
fs::close(dirfd);
}
g_DENTS_BUF_POOL.free(buf);
return 0;
}
}
namespace FUSE
{
int
readdir(fuse_file_info *ffi_,
fuse_dirents_t *buf_)
{
DirInfo *di = reinterpret_cast<DirInfo*>(ffi_->fh);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
return l::readdir(config.branches,
di->fusepath.c_str(),
buf_);
}
}

23
src/fuse_readdir_plus.cpp

@ -0,0 +1,23 @@
/*
ISC License
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef __linux__
# include "fuse_readdir_plus_linux.icpp"
#else
# include "fuse_readdir_plus_posix.icpp"
#endif

30
src/fuse_readdir_plus.hpp

@ -0,0 +1,30 @@
/*
Copyright (c) 2016, 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 <fuse.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
namespace FUSE
{
int
readdir_plus(fuse_file_info *ffi_,
fuse_dirents_t *buf_);
}

172
src/fuse_readdir_plus_linux.icpp

@ -0,0 +1,172 @@
/*
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define _DEFAULT_SOURCE
#include "config.hpp"
#include "dirinfo.hpp"
#include "errno.hpp"
#include "fs_base_close.hpp"
#include "fs_base_fstatat.hpp"
#include "fs_base_open.hpp"
#include "fs_base_stat.hpp"
#include "fs_devid.hpp"
#include "fs_inode.hpp"
#include "fs_path.hpp"
#include "hashset.hpp"
#include "mempools.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <fuse.h>
#include <fuse_dirents.h>
#include <string>
#include <vector>
#include <stddef.h>
#include <sys/syscall.h>
using std::string;
using std::vector;
struct linux_dirent64
{
uint64_t d_ino;
int64_t d_off;
uint16_t d_reclen;
uint8_t d_type;
char d_name[];
};
namespace l
{
static
inline
int
getdents64(unsigned int fd_,
char *dirp_,
unsigned int count_)
{
return syscall(SYS_getdents64,fd_,dirp_,count_);
}
int
close_free_ret_enomem(int fd_,
void *buf_)
{
fs::close(fd_);
g_DENTS_BUF_POOL.free(buf_);
return -ENOMEM;
}
static
int
readdir_plus(const Branches &branches_,
const char *dirname_,
fuse_dirents_t *buf_)
{
int rv;
dev_t dev;
char *buf;
HashSet names;
string basepath;
struct stat st;
fuse_entry_t entry;
struct linux_dirent64 *d;
buf = (char*)g_DENTS_BUF_POOL.alloc();
entry.nodeid = 0;
entry.generation = 0;
entry.entry_valid = 1;
entry.attr_valid = 1;
entry.entry_valid_nsec = 0;
entry.attr_valid_nsec = 0;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
int dirfd;
int64_t nread;
uint64_t namelen;
basepath = fs::path::make(&branches_[i].path,dirname_);
dirfd = fs::open_dir_ro(basepath);
if(dirfd == -1)
continue;
dev = fs::devid(dirfd);
if(dev == (dev_t)-1)
dev = i;
for(;;)
{
nread = l::getdents64(dirfd,buf,g_DENTS_BUF_POOL.size());
if(nread == -1)
break;
if(nread == 0)
break;
for(int64_t pos = 0; pos < nread;)
{
d = (struct linux_dirent64*)(buf + pos);
namelen = (d->d_reclen - offsetof(struct linux_dirent64,d_name));
rv = names.put(d->d_name,namelen);
if(rv == 0)
continue;
rv = fs::fstatat_nofollow(dirfd,d->d_name,&st);
if(rv == -1)
memset(&st,0,sizeof(st));
d->d_ino = fs::inode::recompute(d->d_ino,dev);
st.st_ino = d->d_ino;
rv = fuse_dirents_add_linux_plus(buf_,d,&entry,&st);
if(rv)
return close_free_ret_enomem(dirfd,buf);
pos += d->d_reclen;
}
}
fs::close(dirfd);
}
g_DENTS_BUF_POOL.free(buf);
return 0;
}
}
namespace FUSE
{
int
readdir_plus(fuse_file_info *ffi_,
fuse_dirents_t *buf_)
{
DirInfo *di = reinterpret_cast<DirInfo*>(ffi_->fh);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
return l::readdir_plus(config.branches,
di->fusepath.c_str(),
buf_);
}
}

125
src/fuse_readdir_plus_posix.icpp

@ -0,0 +1,125 @@
/*
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define _DEFAULT_SOURCE
#include "config.hpp"
#include "dirinfo.hpp"
#include "errno.hpp"
#include "fs_base_closedir.hpp"
#include "fs_base_dirfd.hpp"
#include "fs_base_opendir.hpp"
#include "fs_base_readdir.hpp"
#include "fs_base_fstatat.hpp"
#include "fs_base_stat.hpp"
#include "fs_devid.hpp"
#include "fs_inode.hpp"
#include "fs_path.hpp"
#include "hashset.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <fuse.h>
#include <fuse_dirents.h>
#include <string>
#include <vector>
#include <dirent.h>
using std::string;
using std::vector;
namespace l
{
static
int
readdir_plus(const Branches &branches_,
const char *dirname_,
fuse_dirents_t *buf_)
{
dev_t dev;
HashSet names;
string basepath;
struct stat st;
fuse_entry_t entry;
entry.nodeid = 0;
entry.generation = 0;
entry.entry_valid = 1;
entry.attr_valid = 1;
entry.entry_valid_nsec = 0;
entry.attr_valid_nsec = 0;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
int rv;
int dirfd;
DIR *dh;
basepath = fs::path::make(&branches_[i].path,dirname_);
dh = fs::opendir(basepath);
if(!dh)
continue;
dirfd = fs::dirfd(dh);
dev = fs::devid(dirfd);
if(dev == (dev_t)-1)
dev = i;
rv = 0;
for(struct dirent *de = fs::readdir(dh); de && !rv; de = fs::readdir(dh))
{
rv = names.put(de->d_name,_D_EXACT_NAMLEN(de));
if(rv == 0)
continue;
rv = fs::fstatat_nofollow(dirfd,de->d_name,&st);
if(rv == -1)
memset(&st,0,sizeof(st));
de->d_ino = fs::inode::recompute(de->d_ino,dev);
st.st_ino = de->d_ino;
rv = fuse_dirents_add_plus(buf_,de,&entry,&st);
if(rv)
return (fs::closedir(dh),-ENOMEM);
}
fs::closedir(dh);
}
return 0;
}
}
namespace FUSE
{
int
readdir_plus(fuse_file_info *ffi_,
fuse_dirents_t *buf_)
{
DirInfo *di = reinterpret_cast<DirInfo*>(ffi_->fh);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
return l::readdir_plus(config.branches,
di->fusepath.c_str(),
buf_);
}
}

111
src/fuse_readdir_posix.icpp

@ -0,0 +1,111 @@
/*
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define _DEFAULT_SOURCE
#include "config.hpp"
#include "dirinfo.hpp"
#include "errno.hpp"
#include "fs_base_closedir.hpp"
#include "fs_base_dirfd.hpp"
#include "fs_base_opendir.hpp"
#include "fs_base_readdir.hpp"
#include "fs_base_stat.hpp"
#include "fs_devid.hpp"
#include "fs_inode.hpp"
#include "fs_path.hpp"
#include "hashset.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <fuse.h>
#include <fuse_dirents.h>
#include <string>
#include <vector>
#include <dirent.h>
using std::string;
using std::vector;
namespace l
{
static
int
readdir(const Branches &branches_,
const char *dirname_,
fuse_dirents_t *buf_)
{
dev_t dev;
HashSet names;
string basepath;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
int rv;
int dirfd;
DIR *dh;
basepath = fs::path::make(&branches_[i].path,dirname_);
dh = fs::opendir(basepath);
if(!dh)
continue;
dirfd = fs::dirfd(dh);
dev = fs::devid(dirfd);
if(dev == (dev_t)-1)
dev = i;
rv = 0;
for(struct dirent *de = fs::readdir(dh); de && !rv; de = fs::readdir(dh))
{
rv = names.put(de->d_name,_D_EXACT_NAMLEN(de));
if(rv == 0)
continue;
de->d_ino = fs::inode::recompute(de->d_ino,dev);
rv = fuse_dirents_add(buf_,de);
if(rv)
return (fs::closedir(dh),-ENOMEM);
}
fs::closedir(dh);
}
return 0;
}
}
namespace FUSE
{
int
readdir(fuse_file_info *ffi_,
fuse_dirents_t *buf_)
{
DirInfo *di = reinterpret_cast<DirInfo*>(ffi_->fh);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
return l::readdir(config.branches,
di->fusepath.c_str(),
buf_);
}
}

5
src/hashset.hpp

@ -38,13 +38,14 @@ public:
inline inline
int int
put(const char *str_)
put(const char *str_,
const uint64_t len_)
{ {
int rv; int rv;
uint64_t h; uint64_t h;
khint_t key; khint_t key;
h = fasthash64(str_,strlen(str_),0x7472617065786974);
h = fasthash64(str_,len_,0x7472617065786974);
key = kh_put(hashset,_set,h,&rv); key = kh_put(hashset,_set,h,&rv);
if(rv == 0) if(rv == 0)

69
src/locked_fixed_mem_pool.hpp

@ -0,0 +1,69 @@
/*
ISC License
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include "fixed_mem_pool.hpp"
#include <pthread.h>
template<size_t SIZE>
class LockedFixedMemPool
{
public:
LockedFixedMemPool()
{
pthread_mutex_init(&_mutex,NULL);
}
~LockedFixedMemPool()
{
pthread_mutex_destroy(&_mutex);
}
public:
void*
alloc(void)
{
void *mem;
pthread_mutex_lock(&_mutex);
mem = _fmp.alloc();
pthread_mutex_unlock(&_mutex);
return mem;
}
void
free(void *mem_)
{
pthread_mutex_lock(&_mutex);
_fmp.free(mem_);
pthread_mutex_unlock(&_mutex);
}
uint64_t
size(void)
{
return _fmp.size();
}
private:
FixedMemPool<SIZE> _fmp;
pthread_mutex_t _mutex;
};

21
src/mempools.cpp

@ -0,0 +1,21 @@
/*
ISC License
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "locked_fixed_mem_pool.hpp"
LockedFixedMemPool<128 * 1024> g_DENTS_BUF_POOL;

23
src/mempools.hpp

@ -0,0 +1,23 @@
/*
ISC License
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include "locked_fixed_mem_pool.hpp"
extern LockedFixedMemPool<128 * 1024> g_DENTS_BUF_POOL;

3
src/mergerfs.cpp

@ -50,6 +50,7 @@
#include "fuse_read.hpp" #include "fuse_read.hpp"
#include "fuse_read_buf.hpp" #include "fuse_read_buf.hpp"
#include "fuse_readdir.hpp" #include "fuse_readdir.hpp"
#include "fuse_readdir_plus.hpp"
#include "fuse_readlink.hpp" #include "fuse_readlink.hpp"
#include "fuse_release.hpp" #include "fuse_release.hpp"
#include "fuse_releasedir.hpp" #include "fuse_releasedir.hpp"
@ -102,7 +103,6 @@ namespace l
ops.ftruncate = FUSE::ftruncate; ops.ftruncate = FUSE::ftruncate;
ops.futimens = FUSE::futimens; ops.futimens = FUSE::futimens;
ops.getattr = FUSE::getattr; ops.getattr = FUSE::getattr;
ops.getdir = NULL; /* deprecated; use readdir */
ops.getxattr = FUSE::getxattr; ops.getxattr = FUSE::getxattr;
ops.init = FUSE::init; ops.init = FUSE::init;
ops.ioctl = FUSE::ioctl; ops.ioctl = FUSE::ioctl;
@ -118,6 +118,7 @@ namespace l
ops.read = (nullrw ? FUSE::read_null : FUSE::read); ops.read = (nullrw ? FUSE::read_null : FUSE::read);
ops.read_buf = (nullrw ? NULL : FUSE::read_buf); ops.read_buf = (nullrw ? NULL : FUSE::read_buf);
ops.readdir = FUSE::readdir; ops.readdir = FUSE::readdir;
ops.readdir_plus = FUSE::readdir_plus;
ops.readlink = FUSE::readlink; ops.readlink = FUSE::readlink;
ops.release = FUSE::release; ops.release = FUSE::release;
ops.releasedir = FUSE::releasedir; ops.releasedir = FUSE::releasedir;

4
src/option_parser.cpp

@ -310,6 +310,8 @@ parse_and_process_arg(Config &config,
return 0; return 0;
else if(arg == "big_writes") else if(arg == "big_writes")
return 0; return 0;
else if(arg == "readdir_ino")
return 0;
return 1; return 1;
} }
@ -373,6 +375,8 @@ parse_and_process_kv_arg(Config &config,
rv = parse_and_process(value,config.auto_cache); rv = parse_and_process(value,config.auto_cache);
else if(key == "async_read") else if(key == "async_read")
rv = parse_and_process(value,config.async_read); rv = parse_and_process(value,config.async_read);
else if(key == "readdirplus")
rv = parse_and_process(value,config.readdirplus);
else if(key == "max_write") else if(key == "max_write")
rv = 0; rv = 0;
else if(key == "fuse_msg_size") else if(key == "fuse_msg_size")

Loading…
Cancel
Save