diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | src/bindfs.c | 64 | ||||
-rw-r--r-- | tests/Makefile.am | 6 | ||||
-rwxr-xr-x | tests/test_bindfs.rb | 12 | ||||
-rw-r--r-- | tests/test_dir_rewind.c | 80 |
5 files changed, 114 insertions, 49 deletions
@@ -36,6 +36,7 @@ Makefile src/bindfs tests/fcntl_locker tests/readdir_inode +tests/test_dir_rewind tests/utimens_nofollow tests/*.log tests/*.trs diff --git a/src/bindfs.c b/src/bindfs.c index 460f362..28c9dfe 100644 --- a/src/bindfs.c +++ b/src/bindfs.c @@ -215,11 +215,8 @@ static int bindfs_getattr(const char *path, struct stat *stbuf); static int bindfs_fgetattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi); static int bindfs_readlink(const char *path, char *buf, size_t size); -static int bindfs_opendir(const char *path, struct fuse_file_info *fi); -static inline DIR *get_dirp(struct fuse_file_info *fi); static int bindfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi); -static int bindfs_releasedir(const char *path, struct fuse_file_info *fi); static int bindfs_mknod(const char *path, mode_t mode, dev_t rdev); static int bindfs_mkdir(const char *path, mode_t mode); static int bindfs_unlink(const char *path); @@ -627,53 +624,32 @@ static int bindfs_readlink(const char *path, char *buf, size_t size) return 0; } -static int bindfs_opendir(const char *path, struct fuse_file_info *fi) -{ - DIR *dp; - char *real_path; - - real_path = process_path(path, true); - if (real_path == NULL) - return -errno; - - dp = opendir(real_path); - free(real_path); - if (dp == NULL) - return -errno; - - fi->fh = (unsigned long) dp; - return 0; -} - -static inline DIR *get_dirp(struct fuse_file_info *fi) -{ - return (DIR *) (uintptr_t) fi->fh; -} - static int bindfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { - DIR *dp = get_dirp(fi); - struct dirent *de_buf; - struct dirent *de; - struct stat st; - int result = 0; - long pc_ret; - char *real_path; + char *real_path = process_path(path, true); + if (real_path == NULL) { + return -errno; + } - real_path = process_path(path, true); - if (real_path == NULL) + DIR *dp = opendir(real_path); + if (dp == NULL) { + free(real_path); return -errno; - pc_ret = pathconf(real_path, _PC_NAME_MAX); - free(real_path); + } + long pc_ret = pathconf(real_path, _PC_NAME_MAX); + free(real_path); if (pc_ret < 0) { DPRINTF("pathconf failed: %s (%d)", strerror(errno), errno); pc_ret = NAME_MAX; } - de_buf = malloc(offsetof(struct dirent, d_name) + pc_ret + 1); + struct dirent *de_buf = + malloc(offsetof(struct dirent, d_name) + pc_ret + 1); + int result = 0; while (1) { + struct dirent *de; result = readdir_r(dp, de_buf, &de); if (result != 0) { result = -result; @@ -683,6 +659,7 @@ static int bindfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, break; } + struct stat st; memset(&st, 0, sizeof(st)); st.st_ino = de->d_ino; st.st_mode = de->d_type << 12; @@ -702,15 +679,8 @@ static int bindfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, } free(de_buf); - return result; -} - -static int bindfs_releasedir(const char *path, struct fuse_file_info *fi) -{ - DIR *dp = get_dirp(fi); - (void) path; closedir(dp); - return 0; + return result; } static int bindfs_mknod(const char *path, mode_t mode, dev_t rdev) @@ -1361,9 +1331,7 @@ static struct fuse_operations bindfs_oper = { .fgetattr = bindfs_fgetattr, /* no access() since we always use -o default_permissions */ .readlink = bindfs_readlink, - .opendir = bindfs_opendir, .readdir = bindfs_readdir, - .releasedir = bindfs_releasedir, .mknod = bindfs_mknod, .mkdir = bindfs_mkdir, .symlink = bindfs_symlink, diff --git a/tests/Makefile.am b/tests/Makefile.am index 66713e2..0a52108 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,10 +1,14 @@ UNAME_S := $(shell uname -s) -noinst_PROGRAMS = readdir_inode utimens_nofollow fcntl_locker +AM_CPPFLAGS = ${my_CPPFLAGS} +AM_CFLAGS = ${my_CFLAGS} + +noinst_PROGRAMS = readdir_inode utimens_nofollow fcntl_locker test_dir_rewind readdir_inode_SOURCES = readdir_inode.c utimens_nofollow_SOURCES = utimens_nofollow.c fcntl_locker_SOURCES = fcntl_locker.c +test_dir_rewind_SOURCES = test_dir_rewind.c TESTS = test_bindfs.rb SUBDIRS = internals diff --git a/tests/test_bindfs.rb b/tests/test_bindfs.rb index c0ae5dd..e355e2a 100755 --- a/tests/test_bindfs.rb +++ b/tests/test_bindfs.rb @@ -678,6 +678,18 @@ root_testenv("", :title => "ioctl not enabled by default") do assert { `chattr +a mnt/file 2>&1`; !$?.success? } end +# Issue #41 +testenv("", :title => "reading directory with rewind") do + touch('mnt/file1') + touch('mnt/file2') + mkdir('mnt/subdir') + + Dir.chdir 'mnt' do + system("#{$tests_dir}/test_dir_rewind") + assert { $?.success? } + end +end + # FIXME: this stuff around testenv is a hax, and testenv may also exit(), which defeats the 'ensure' below. # the test setup ought to be refactored. It might well use MiniTest or something. if Process.uid == 0 diff --git a/tests/test_dir_rewind.c b/tests/test_dir_rewind.c new file mode 100644 index 0000000..d4050f9 --- /dev/null +++ b/tests/test_dir_rewind.c @@ -0,0 +1,80 @@ +// Tests that opening the current directory, reading its entries +// rewinding and reading its entries again gives the same entries both times. +// +// https://github.com/mpartel/bindfs/issues/41 + +#ifdef __linux__ + +#define _GNU_SOURCE +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/syscall.h> +#include <unistd.h> + +#define BUF_SIZE 4096 + +int main() +{ + int fd = open(".", O_RDONLY | O_DIRECTORY); + if (fd == -1) { + perror("failed to open '.'"); + return 1; + } + + char buf1[BUF_SIZE]; + char buf2[BUF_SIZE]; + memset(buf1, 0, BUF_SIZE); + memset(buf2, 0, BUF_SIZE); + + int amt_read1 = syscall(SYS_getdents, fd, buf1, BUF_SIZE); + if (amt_read1 <= 0) { + fprintf(stderr, "amt_read1=%d\n", amt_read1); + close(fd); + return 1; + } + + off_t seek_res = lseek(fd, 0, SEEK_SET); + if (seek_res == (off_t)-1) { + perror("failed to lseek to 0"); + close(fd); + return 1; + } + + int amt_read2 = syscall(SYS_getdents, fd, buf2, BUF_SIZE); + if (amt_read2 <= 0) { + fprintf(stderr, "amt_read2=%d\n", amt_read2); + close(fd); + return 1; + } + + if (amt_read1 != amt_read2) { + fprintf(stderr, + "First read gave %d bytes, second read gave %d bytes.\n", + amt_read1, amt_read2); + close(fd); + return 1; + } + if (memcmp(buf1, buf2, BUF_SIZE) != 0) { + fprintf(stderr, "First and second read results differ.\n"); + close(fd); + return 1; + } + + close(fd); + return 0; +} + +#else // #ifdef __linux__ + +int main() +{ + printf("This test (probably) only compiles on Linux.\n"); + printf("Skipping by just returning successfully.\n"); + return 0; +} + +#endif // #ifdef __linux__ |