aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Pärtel <martin.partel@gmail.com>2016-10-15 01:44:45 +0100
committerMartin Pärtel <martin.partel@gmail.com>2016-10-15 01:44:45 +0100
commit9b37e64eb3e625535a1a20f315d7932f2e74b399 (patch)
treeec9792c2b77478442b18a969043c9967f1be90c8
parent3285e0aee6ea92da2eef119cb66c54cf74c255dc (diff)
downloadbindfs-9b37e64eb3e625535a1a20f315d7932f2e74b399.tar.gz
Fix #41 (seeking in directory).
Since version 1.31.1, bindfs has implemented readdir by always reading the entire directory and passing 0 offset to filler, but this does not work if the same directory pointer is rewound and reused. We remove the opendir and closedir implementations (which FUSE permits) and now always opendir() and closedir() in readdir. Alternatively we could have added a rewinddir() to our readdir.
-rw-r--r--.gitignore1
-rw-r--r--src/bindfs.c64
-rw-r--r--tests/Makefile.am6
-rwxr-xr-xtests/test_bindfs.rb12
-rw-r--r--tests/test_dir_rewind.c80
5 files changed, 114 insertions, 49 deletions
diff --git a/.gitignore b/.gitignore
index c7ff7e0..76cad8c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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__