From d119807adbaa36bcd033982511469f03d90245cd Mon Sep 17 00:00:00 2001 From: Antonio SJ Musumeci Date: Fri, 14 Jun 2019 08:59:04 -0400 Subject: [PATCH] restructure readdir, add readdir_plus Does not enable READDIR_AUTO. Might add in the future. --- LICENSE | 2 +- libfuse/Makefile | 1 + libfuse/include/fuse.h | 34 +-- libfuse/include/fuse_attr.h | 24 ++ libfuse/include/fuse_common.h | 37 +-- libfuse/include/fuse_compat.h | 9 +- libfuse/include/fuse_dirent.h | 31 +++ libfuse/include/fuse_direntplus.h | 31 +++ libfuse/include/fuse_dirents.h | 71 +++++ libfuse/include/fuse_entry.h | 14 + libfuse/include/fuse_lowlevel.h | 38 +-- libfuse/include/stat_utils.h | 17 ++ libfuse/lib/fuse.c | 420 ++++++++++++++---------------- libfuse/lib/fuse_dirents.c | 346 ++++++++++++++++++++++++ libfuse/lib/fuse_loop.c | 2 +- libfuse/lib/fuse_loop_mt.c | 2 +- libfuse/lib/fuse_lowlevel.c | 51 +++- src/config.cpp | 1 + src/config.hpp | 1 + src/fs_base_fstatat.hpp | 53 ++++ src/fs_inode.hpp | 18 +- src/fuse_init.cpp | 2 + src/fuse_readdir.cpp | 41 ++- src/fuse_readdir.hpp | 13 +- src/fuse_readdir_plus.cpp | 138 ++++++++++ src/fuse_readdir_plus.hpp | 30 +++ src/hashset.hpp | 5 +- src/mergerfs.cpp | 3 +- src/option_parser.cpp | 4 + 29 files changed, 1078 insertions(+), 361 deletions(-) create mode 100644 libfuse/include/fuse_attr.h create mode 100644 libfuse/include/fuse_dirent.h create mode 100644 libfuse/include/fuse_direntplus.h create mode 100644 libfuse/include/fuse_dirents.h create mode 100644 libfuse/include/fuse_entry.h create mode 100644 libfuse/include/stat_utils.h create mode 100644 libfuse/lib/fuse_dirents.c create mode 100644 src/fs_base_fstatat.hpp create mode 100644 src/fuse_readdir_plus.cpp create mode 100644 src/fuse_readdir_plus.hpp diff --git a/LICENSE b/LICENSE index 6ed51439..3718c1dc 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ /* ISC License - Copyright (c) 2016, Antonio SJ Musumeci + Copyright (c) 2019, Antonio SJ Musumeci Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above diff --git a/libfuse/Makefile b/libfuse/Makefile index f26be9a7..01c7e7a4 100644 --- a/libfuse/Makefile +++ b/libfuse/Makefile @@ -23,6 +23,7 @@ INSTALLMAN1DIR = $(DESTDIR)$(MAN1DIR) SRC = \ lib/buffer.c \ lib/cuse_lowlevel.c \ + lib/fuse_dirents.c \ lib/fuse.c \ lib/fuse_kern_chan.c \ lib/fuse_loop.c \ diff --git a/libfuse/include/fuse.h b/libfuse/include/fuse.h index c29effba..4931a168 100644 --- a/libfuse/include/fuse.h +++ b/libfuse/include/fuse.h @@ -24,6 +24,7 @@ #endif #include "fuse_common.h" +#include "fuse_dirents.h" #include #include @@ -47,22 +48,6 @@ struct fuse; /** Structure containing a raw command */ 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: * @@ -104,9 +89,6 @@ struct fuse_operations { */ 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 * * This is called for creation of all non-directory, non-symlink @@ -312,8 +294,12 @@ struct fuse_operations { * * 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 * @@ -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_opendir(struct fuse_fs *fs, const char *path, 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, struct fuse_file_info *fi); int fuse_fs_releasedir(struct fuse_fs *fs, const char *path, diff --git a/libfuse/include/fuse_attr.h b/libfuse/include/fuse_attr.h new file mode 100644 index 00000000..cdd9bd70 --- /dev/null +++ b/libfuse/include/fuse_attr.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +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; +}; diff --git a/libfuse/include/fuse_common.h b/libfuse/include/fuse_common.h index 42041981..0e62aaaf 100644 --- a/libfuse/include/fuse_common.h +++ b/libfuse/include/fuse_common.h @@ -109,24 +109,25 @@ fuse_file_info * FUSE_CAP_IOCTL_DIR: ioctl support on directories * 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 diff --git a/libfuse/include/fuse_compat.h b/libfuse/include/fuse_compat.h index e7497a9e..509f6896 100644 --- a/libfuse/include/fuse_compat.h +++ b/libfuse/include/fuse_compat.h @@ -12,7 +12,6 @@ struct fuse_operations_compat25 { int (*getattr) (const char *, struct stat *); 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 (*mkdir) (const char *, mode_t); int (*unlink) (const char *); @@ -38,7 +37,7 @@ struct fuse_operations_compat25 { int (*listxattr) (const char *, char *, size_t); int (*removexattr) (const char *, const char *); 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 *); int (*releasedir) (const char *, 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 { int (*getattr) (const char *, struct stat *); 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 (*mkdir) (const char *, mode_t); int (*unlink) (const char *); @@ -97,7 +95,7 @@ struct fuse_operations_compat22 { int (*listxattr) (const char *, char *, size_t); int (*removexattr) (const char *, const char *); 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 *); int (*releasedir) (const char *, 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, size_t op_size); -typedef int (*fuse_dirfil_t_compat) (fuse_dirh_t h, const char *name, int type); struct fuse_operations_compat2 { int (*getattr) (const char *, struct stat *); 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 (*mkdir) (const char *, mode_t); int (*unlink) (const char *); @@ -170,7 +166,6 @@ struct fuse_statfs_compat1 { struct fuse_operations_compat1 { int (*getattr) (const char *, struct stat *); 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 (*mkdir) (const char *, mode_t); int (*unlink) (const char *); diff --git a/libfuse/include/fuse_dirent.h b/libfuse/include/fuse_dirent.h new file mode 100644 index 00000000..a57291b4 --- /dev/null +++ b/libfuse/include/fuse_dirent.h @@ -0,0 +1,31 @@ +/* + ISC License + + Copyright (c) 2019, Antonio SJ Musumeci + + 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 + +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[]; +}; diff --git a/libfuse/include/fuse_direntplus.h b/libfuse/include/fuse_direntplus.h new file mode 100644 index 00000000..212496af --- /dev/null +++ b/libfuse/include/fuse_direntplus.h @@ -0,0 +1,31 @@ +/* + ISC License + + Copyright (c) 2019, Antonio SJ Musumeci + + 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; +}; diff --git a/libfuse/include/fuse_dirents.h b/libfuse/include/fuse_dirents.h new file mode 100644 index 00000000..501e1b9a --- /dev/null +++ b/libfuse/include/fuse_dirents.h @@ -0,0 +1,71 @@ +/* + ISC License + + Copyright (c) 2019, Antonio SJ Musumeci + + 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 +#include +#include +#include +#include +#include + +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, + 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); + +void *fuse_dirents_find(fuse_dirents_t *d, + const uint64_t ino); + +int fuse_dirents_convert_plus2normal(fuse_dirents_t *d); + +#ifdef __cplusplus +} +#endif diff --git a/libfuse/include/fuse_entry.h b/libfuse/include/fuse_entry.h new file mode 100644 index 00000000..92c866d0 --- /dev/null +++ b/libfuse/include/fuse_entry.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +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; +}; diff --git a/libfuse/include/fuse_lowlevel.h b/libfuse/include/fuse_lowlevel.h index c9e34d1f..7891e233 100644 --- a/libfuse/include/fuse_lowlevel.h +++ b/libfuse/include/fuse_lowlevel.h @@ -605,7 +605,11 @@ struct fuse_lowlevel_ops { * @param fi file information */ 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 @@ -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); -/* ----------------------------------------------------------- * - * 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 * will be retried with the specified input data fetched and output diff --git a/libfuse/include/stat_utils.h b/libfuse/include/stat_utils.h new file mode 100644 index 00000000..6c7b192f --- /dev/null +++ b/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 diff --git a/libfuse/lib/fuse.c b/libfuse/lib/fuse.c index aba3379c..7504b44f 100644 --- a/libfuse/lib/fuse.c +++ b/libfuse/lib/fuse.c @@ -18,6 +18,7 @@ #include "fuse_common_compat.h" #include "fuse_compat.h" #include "fuse_kernel.h" +#include "fuse_dirents.h" #include #include @@ -63,8 +64,7 @@ struct fuse_config { int remember; int nopath; int debug; - int use_ino; - int readdir_ino; + int use_ino; int set_mode; int set_uid; int set_gid; @@ -183,25 +183,11 @@ struct node_lru { 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 { @@ -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); } -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, @@ -1305,8 +1298,8 @@ out: 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) stbuf->st_mode = (stbuf->st_mode & S_IFMT) | (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, @@ -3370,18 +3348,13 @@ static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino, char *path; int err; - dh = (struct fuse_dh *) malloc(sizeof(struct fuse_dh)); + dh = (struct fuse_dh *) calloc(1,sizeof(struct fuse_dh)); if (dh == NULL) { reply_err(req, -ENOMEM); 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); 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); } -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, @@ -4273,6 +4251,7 @@ static struct fuse_lowlevel_ops fuse_path_ops = { .fsync = fuse_lib_fsync, .opendir = fuse_lib_opendir, .readdir = fuse_lib_readdir, + .readdir_plus = fuse_lib_readdir_plus, .releasedir = fuse_lib_releasedir, .fsyncdir = fuse_lib_fsyncdir, .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_LIB_OPT("debug", 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=%o", umask, 0), 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_signal=%d", intr_signal, 0), FUSE_LIB_OPT("threads=%d", threads, 0), + FUSE_LIB_OPT("use_ino", use_ino, 1), FUSE_OPT_END }; static void fuse_lib_help(void) { 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 uid=N set file owner\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) 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 (fuse_sync_compat_args(args) == -1) goto out_free_fs; diff --git a/libfuse/lib/fuse_dirents.c b/libfuse/lib/fuse_dirents.c new file mode 100644 index 00000000..e33ea3fc --- /dev/null +++ b/libfuse/lib/fuse_dirents.c @@ -0,0 +1,346 @@ +#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 +#include +#include +#include +#include +#include +#include + +#define DEFAULT_SIZE (1024 * 16) + +#ifndef _D_EXACT_NAMLEN +# define _D_EXACT_NAMLEN(d) ((d)->d_namlen) +#endif + +static +uint64_t +align_uint64_t(uint64_t v_) +{ + 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_, + 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_EXACT_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_EXACT_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; +} + +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); +} diff --git a/libfuse/lib/fuse_loop.c b/libfuse/lib/fuse_loop.c index b7b4ca4e..277080ef 100644 --- a/libfuse/lib/fuse_loop.c +++ b/libfuse/lib/fuse_loop.c @@ -17,7 +17,7 @@ int fuse_session_loop(struct fuse_session *se) int res = 0; struct fuse_chan *ch = fuse_session_next_chan(se, NULL); size_t bufsize = fuse_chan_bufsize(ch); - char *buf = (char *) malloc(bufsize); + char *buf = (char*)calloc(bufsize,1); if (!buf) { fprintf(stderr, "fuse: failed to allocate read buffer\n"); return -1; diff --git a/libfuse/lib/fuse_loop_mt.c b/libfuse/lib/fuse_loop_mt.c index ce29b490..6b1ed003 100644 --- a/libfuse/lib/fuse_loop_mt.c +++ b/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)); w->bufsize = fuse_chan_bufsize(mt->prevch); - w->buf = malloc(w->bufsize); + w->buf = calloc(w->bufsize,1); w->mt = mt; if (!w->buf) { fprintf(stderr, "fuse: failed to allocate read buffer\n"); diff --git a/libfuse/lib/fuse_lowlevel.c b/libfuse/lib/fuse_lowlevel.c index 745b956b..b36dd9f6 100644 --- a/libfuse/lib/fuse_lowlevel.c +++ b/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, size_t argsize) { - struct iovec iov[2]; + struct iovec iov[2]; int count = 1; if (argsize) { 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); } +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) { 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; if (arg->flags & FUSE_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 { f->conn.want &= ~FUSE_CAP_ASYNC_READ; 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; if (f->conn.want & FUSE_CAP_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_write = f->conn.max_write; if (f->conn.proto_minor >= 13) { @@ -2350,6 +2377,7 @@ static struct { [FUSE_INIT] = { do_init, "INIT" }, [FUSE_OPENDIR] = { do_opendir, "OPENDIR" }, [FUSE_READDIR] = { do_readdir, "READDIR" }, + [FUSE_READDIRPLUS] = { do_readdir_plus, "READDIR_PLUS" }, [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" }, [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" }, [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; 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; if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func) diff --git a/src/config.cpp b/src/config.cpp index f9d54709..b64cde4f 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -53,6 +53,7 @@ Config::Config() cache_readdir(false), async_read(true), writeback_cache(false), + readdirplus(false), cache_files(CacheFiles::LIBFUSE), fuse_msg_size(FUSE_MAX_MAX_PAGES), POLICYINIT(access), diff --git a/src/config.hpp b/src/config.hpp index f8ca93e1..a40fafe8 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -113,6 +113,7 @@ public: bool cache_readdir; bool async_read; bool writeback_cache; + bool readdirplus; CacheFiles cache_files; uint16_t fuse_msg_size; diff --git a/src/fs_base_fstatat.hpp b/src/fs_base_fstatat.hpp new file mode 100644 index 00000000..21ddd6ca --- /dev/null +++ b/src/fs_base_fstatat.hpp @@ -0,0 +1,53 @@ +/* + ISC License + + Copyright (c) 2019, Antonio SJ Musumeci + + 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 +#include +#include +#include +#include + +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); + } +} diff --git a/src/fs_inode.hpp b/src/fs_inode.hpp index 8209172d..d629b27c 100644 --- a/src/fs_inode.hpp +++ b/src/fs_inode.hpp @@ -30,18 +30,26 @@ namespace fs static const uint64_t MAGIC = 0x7472617065786974; inline - void - recompute(struct stat *st_) + uint64_t + recompute(ino_t ino_, + dev_t dev_) { 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[3] = 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); } } } diff --git a/src/fuse_init.cpp b/src/fuse_init.cpp index a5375fda..da875a97 100644 --- a/src/fuse_init.cpp +++ b/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_IOCTL_DIR); 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_WRITEBACK_CACHE,&c.writeback_cache); l::want_if_capable_max_pages(conn_,c); diff --git a/src/fuse_readdir.cpp b/src/fuse_readdir.cpp index da72964d..4360297b 100644 --- a/src/fuse_readdir.cpp +++ b/src/fuse_readdir.cpp @@ -32,27 +32,27 @@ #include "ugid.hpp" #include +#include #include #include +#include + 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_) + readdir(const Branches &branches_, + const char *dirname_, + fuse_dirents_t *buf_) { + dev_t dev; HashSet names; string basepath; - struct stat st = {0}; for(size_t i = 0, ei = branches_.size(); i != ei; i++) { @@ -66,24 +66,21 @@ namespace l if(!dh) continue; - dirfd = fs::dirfd(dh); - st.st_dev = fs::devid(dirfd); - if(st.st_dev == (dev_t)-1) - st.st_dev = i; + 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); + rv = names.put(de->d_name,_D_EXACT_NAMLEN(de)); if(rv == 0) continue; - st.st_ino = de->d_ino; - st.st_mode = DTTOIF(de->d_type); - - fs::inode::recompute(&st); + de->d_ino = fs::inode::recompute(de->d_ino,dev); - rv = filler_(buf_,de->d_name,&st,NO_OFFSET); + rv = fuse_dirents_add(buf_,de); if(rv) return (fs::closedir(dh),-ENOMEM); } @@ -98,11 +95,8 @@ namespace l namespace FUSE { 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_) { DirInfo *di = reinterpret_cast(ffi_->fh); const fuse_context *fc = fuse_get_context(); @@ -112,7 +106,6 @@ namespace FUSE return l::readdir(config.branches, di->fusepath.c_str(), - buf_, - filler_); + buf_); } } diff --git a/src/fuse_readdir.hpp b/src/fuse_readdir.hpp index ec820595..d9dbc593 100644 --- a/src/fuse_readdir.hpp +++ b/src/fuse_readdir.hpp @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Antonio SJ Musumeci + Copyright (c) 2019, Antonio SJ Musumeci Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -18,16 +18,9 @@ #include -#include -#include -#include - namespace FUSE { 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_); } diff --git a/src/fuse_readdir_plus.cpp b/src/fuse_readdir_plus.cpp new file mode 100644 index 00000000..53fbf522 --- /dev/null +++ b/src/fuse_readdir_plus.cpp @@ -0,0 +1,138 @@ +/* + Copyright (c) 2019, Antonio SJ Musumeci + + 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 +#include + +#include +#include + +#include + +using std::string; +using std::vector; + +#define NO_OFFSET 0 + + +static +int +dot_or_dotdot(const char *s_) +{ + return ((s_[0] == '.') && + ((s_[1] == '\0') || + ((s_[1] == '.') && (s_[2] == '\0')))); +} + + +namespace l +{ + static + int + readdir_plus(const Branches &branches_, + const char *dirname_, + fuse_dirents_t *buf_) + { + dev_t dev; + HashSet names; + string basepath; + struct stat st; + fuse_entry_t entry; + + entry.nodeid = 0; + entry.generation = 0; + entry.entry_valid = 1; + entry.attr_valid = 1; + entry.entry_valid_nsec = 0; + entry.attr_valid_nsec = 0; + for(size_t i = 0, ei = branches_.size(); i != ei; i++) + { + int rv; + int dirfd; + DIR *dh; + + basepath = fs::path::make(&branches_[i].path,dirname_); + + dh = fs::opendir(basepath); + if(!dh) + continue; + + dirfd = fs::dirfd(dh); + dev = fs::devid(dirfd); + if(dev == (dev_t)-1) + dev = i; + + rv = 0; + for(struct dirent *de = fs::readdir(dh); de && !rv; de = fs::readdir(dh)) + { + rv = names.put(de->d_name,_D_EXACT_NAMLEN(de)); + if(rv == 0) + continue; + + rv = fs::fstatat_nofollow(dirfd,de->d_name,&st); + if(rv == -1) + memset(&st,0,sizeof(st)); + + de->d_ino = fs::inode::recompute(de->d_ino,dev); + st.st_ino = fs::inode::recompute(st.st_ino,dev); + + rv = fuse_dirents_add_plus(buf_,de,&entry,&st); + if(rv) + return (fs::closedir(dh),-ENOMEM); + } + + fs::closedir(dh); + } + + return 0; + } +} + +namespace FUSE +{ + int + readdir_plus(fuse_file_info *ffi_, + fuse_dirents_t *buf_) + { + DirInfo *di = reinterpret_cast(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_); + } +} diff --git a/src/fuse_readdir_plus.hpp b/src/fuse_readdir_plus.hpp new file mode 100644 index 00000000..f24b8768 --- /dev/null +++ b/src/fuse_readdir_plus.hpp @@ -0,0 +1,30 @@ +/* + Copyright (c) 2016, Antonio SJ Musumeci + + 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 + +#include +#include +#include + +namespace FUSE +{ + int + readdir_plus(fuse_file_info *ffi_, + fuse_dirents_t *buf_); +} diff --git a/src/hashset.hpp b/src/hashset.hpp index 6f2370c4..fec6b3fd 100644 --- a/src/hashset.hpp +++ b/src/hashset.hpp @@ -38,13 +38,14 @@ public: inline int - put(const char *str_) + put(const char *str_, + const uint64_t len_) { int rv; uint64_t h; khint_t key; - h = fasthash64(str_,strlen(str_),0x7472617065786974); + h = fasthash64(str_,len_,0x7472617065786974); key = kh_put(hashset,_set,h,&rv); if(rv == 0) diff --git a/src/mergerfs.cpp b/src/mergerfs.cpp index 087655bf..e4903f35 100644 --- a/src/mergerfs.cpp +++ b/src/mergerfs.cpp @@ -50,6 +50,7 @@ #include "fuse_read.hpp" #include "fuse_read_buf.hpp" #include "fuse_readdir.hpp" +#include "fuse_readdir_plus.hpp" #include "fuse_readlink.hpp" #include "fuse_release.hpp" #include "fuse_releasedir.hpp" @@ -102,7 +103,6 @@ namespace l ops.ftruncate = FUSE::ftruncate; ops.futimens = FUSE::futimens; ops.getattr = FUSE::getattr; - ops.getdir = NULL; /* deprecated; use readdir */ ops.getxattr = FUSE::getxattr; ops.init = FUSE::init; ops.ioctl = FUSE::ioctl; @@ -118,6 +118,7 @@ namespace l ops.read = (nullrw ? FUSE::read_null : FUSE::read); ops.read_buf = (nullrw ? NULL : FUSE::read_buf); ops.readdir = FUSE::readdir; + ops.readdir_plus = FUSE::readdir_plus; ops.readlink = FUSE::readlink; ops.release = FUSE::release; ops.releasedir = FUSE::releasedir; diff --git a/src/option_parser.cpp b/src/option_parser.cpp index a1ce5f23..a6361124 100644 --- a/src/option_parser.cpp +++ b/src/option_parser.cpp @@ -310,6 +310,8 @@ parse_and_process_arg(Config &config, return 0; else if(arg == "big_writes") return 0; + else if(arg == "readdir_ino") + return 0; return 1; } @@ -373,6 +375,8 @@ parse_and_process_kv_arg(Config &config, rv = parse_and_process(value,config.auto_cache); else if(key == "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") rv = 0; else if(key == "fuse_msg_size")