summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Pärtel <martin.partel@gmail.com>2021-02-03 11:53:21 +0200
committerGitHub <noreply@github.com>2021-02-03 09:53:21 +0000
commit5012a05ac875c1988263faaa77177c27c62c52bb (patch)
tree0207e1ee07d56e020798b89b30bea75d10559f74
parentb9e3ea01dbbbba9518da216dd29c042af871ae31 (diff)
downloadfuse-5012a05ac875c1988263faaa77177c27c62c52bb.tar.gz
Fix returning inode numbers from readdir() in offset==0 mode. (#584)
- Test added for all passthrough examples. - passthrough.c uses offset==0 mode. The others don't. - passthrough.c changed to set FUSE_FILL_DIR_PLUS to make the test pass. - This fixes #583.
-rw-r--r--ChangeLog.rst1
-rw-r--r--example/passthrough.c2
-rwxr-xr-xlib/fuse.c2
-rw-r--r--test/meson.build3
-rw-r--r--test/readdir_inode.c45
-rwxr-xr-xtest/test_examples.py19
6 files changed, 69 insertions, 3 deletions
diff --git a/ChangeLog.rst b/ChangeLog.rst
index 47b993a..718033a 100644
--- a/ChangeLog.rst
+++ b/ChangeLog.rst
@@ -4,6 +4,7 @@ Unreleased Changes
* Allow "nonempty" as a mount option, for backwards compatibility with
fusermount 2. The option has no effect since mounting over non-empty
directories is allowed by default.
+* Fix returning inode numbers from readdir() in offset==0 mode.
libfuse 3.10.1 (2020-12-07)
===========================
diff --git a/example/passthrough.c b/example/passthrough.c
index 08273ff..86ac698 100644
--- a/example/passthrough.c
+++ b/example/passthrough.c
@@ -132,7 +132,7 @@ static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
memset(&st, 0, sizeof(st));
st.st_ino = de->d_ino;
st.st_mode = de->d_type << 12;
- if (filler(buf, de->d_name, &st, 0, 0))
+ if (filler(buf, de->d_name, &st, 0, FUSE_FILL_DIR_PLUS))
break;
}
diff --git a/lib/fuse.c b/lib/fuse.c
index a8c5915..737456e 100755
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -3566,7 +3566,7 @@ static int fill_dir_plus(void *dh_, const char *name, const struct stat *statp,
return 1;
}
- if (off && statp && (flags & FUSE_FILL_DIR_PLUS)) {
+ if (statp && (flags & FUSE_FILL_DIR_PLUS)) {
e.attr = *statp;
if (!is_dot_or_dotdot(name)) {
diff --git a/test/meson.build b/test/meson.build
index 5c5c2e6..12d3c41 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -10,6 +10,9 @@ endforeach
td += executable('test_syscalls', 'test_syscalls.c',
include_directories: include_dirs,
install: false)
+td += executable('readdir_inode', 'readdir_inode.c',
+ include_directories: include_dirs,
+ install: false)
test_scripts = [ 'conftest.py', 'pytest.ini', 'test_examples.py',
'util.py', 'test_ctests.py' ]
diff --git a/test/readdir_inode.c b/test/readdir_inode.c
new file mode 100644
index 0000000..7f46c0a
--- /dev/null
+++ b/test/readdir_inode.c
@@ -0,0 +1,45 @@
+/*
+ * Prints each directory entry and its inode as returned by 'readdir'.
+ * Skips '.' and '..' because readdir is not required to return them and
+ * some of our examples don't.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+
+int main(int argc, char* argv[])
+{
+ DIR* dirp;
+ struct dirent* dent;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: readdir_inode dir\n");
+ return 1;
+ }
+
+ dirp = opendir(argv[1]);
+ if (dirp == NULL) {
+ perror("failed to open directory");
+ return 2;
+ }
+
+ errno = 0;
+ dent = readdir(dirp);
+ while (dent != NULL) {
+ if (strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0) {
+ printf("%llu %s\n", (unsigned long long)dent->d_ino, dent->d_name);
+ }
+ dent = readdir(dirp);
+ }
+ if (errno != 0) {
+ perror("failed to read directory entry");
+ return 3;
+ }
+
+ closedir(dirp);
+
+ return 0;
+}
diff --git a/test/test_examples.py b/test/test_examples.py
index da50b2a..aab970f 100755
--- a/test/test_examples.py
+++ b/test/test_examples.py
@@ -72,6 +72,15 @@ class raii_tmpdir:
def short_tmpdir():
return raii_tmpdir()
+def readdir_inode(dir):
+ cmd = base_cmdline + [ pjoin(basename, 'test', 'readdir_inode'), dir ]
+ with subprocess.Popen(cmd, stdout=subprocess.PIPE,
+ universal_newlines=True) as proc:
+ lines = proc.communicate()[0].splitlines()
+ lines.sort()
+ return lines
+
+
@pytest.mark.parametrize("cmdline_builder", (invoke_directly, invoke_mount_fuse,
invoke_mount_fuse_drop_privileges))
@pytest.mark.parametrize("options", powerset(options))
@@ -602,6 +611,10 @@ def tst_readdir(src_dir, mnt_dir):
listdir_should.sort()
assert listdir_is == listdir_should
+ inodes_is = readdir_inode(mnt_newdir)
+ inodes_should = readdir_inode(src_newdir)
+ assert inodes_is == inodes_should
+
os.unlink(file_)
os.unlink(subfile)
os.rmdir(subdir)
@@ -623,6 +636,10 @@ def tst_readdir_big(src_dir, mnt_dir):
listdir_should = sorted(os.listdir(src_dir))
assert listdir_is == listdir_should
+ inodes_is = readdir_inode(mnt_dir)
+ inodes_should = readdir_inode(src_dir)
+ assert inodes_is == inodes_should
+
for fname in fnames:
stat_src = os.stat(pjoin(src_dir, fname))
stat_mnt = os.stat(pjoin(mnt_dir, fname))
@@ -631,7 +648,7 @@ def tst_readdir_big(src_dir, mnt_dir):
assert stat_src.st_ctime == stat_mnt.st_ctime
assert stat_src.st_size == stat_mnt.st_size
os.unlink(pjoin(src_dir, fname))
-
+
def tst_truncate_path(mnt_dir):
assert len(TEST_DATA) > 1024