aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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__