ANDROID: fuse: Don't use readdirplus w/ nodeid 0

If we have a nodeid of 0, we've probably got a backing inode, and a
regular getattr will be fast. Otherwise, userspace is likely ill suited
to properly handle a readdirplus anyways.

Test: fuse_test
Bug: 219958836
Signed-off-by: Daniel Rosenberg <drosen@google.com>
Change-Id: I02f031d87dcc5fcbe1e080e4f8ec92187b00fe2d
This commit is contained in:
Daniel Rosenberg
2022-07-06 14:57:14 -07:00
parent 55f267ee04
commit 329650e3b9
3 changed files with 115 additions and 1 deletions

View File

@@ -20,6 +20,8 @@ static bool fuse_use_readdirplus(struct inode *dir, struct dir_context *ctx)
if (!fc->do_readdirplus)
return false;
if (fi->nodeid == 0)
return false;
if (!fc->readdirplus_auto)
return true;
if (test_and_clear_bit(FUSE_I_ADVISE_RDPLUS, &fi->state))

View File

@@ -1677,6 +1677,107 @@ out:
return result;
}
static int bpf_test_no_readdirplus_without_nodeid(const char *mount_dir)
{
const char *folder1 = "folder1";
const char *folder2 = "folder2";
int result = TEST_FAILURE;
int fuse_dev = -1;
int src_fd = -1;
int content_fd = -1;
int bpf_fd = -1;
int pid = -1;
int status;
TESTEQUAL(install_elf_bpf("test_bpf.bpf", "test_readdirplus",
&bpf_fd, NULL, NULL), 0);
TESTSYSCALL(s_mkdir(s_path(s(ft_src), s(folder1)), 0777));
TESTSYSCALL(s_mkdir(s_path(s(ft_src), s(folder2)), 0777));
TESTEQUAL(mount_fuse_no_init(mount_dir, -1, -1, &fuse_dev), 0);
FUSE_ACTION
DIR *open_dir = NULL;
struct dirent *dirent;
// Folder 1: Readdir with no nodeid
TEST(open_dir = s_opendir(s_path(s(ft_dst), s(folder1))),
open_dir != NULL);
TEST(dirent = readdir(open_dir), dirent == NULL);
TESTCOND(errno == EINVAL);
TESTSYSCALL(closedir(open_dir));
open_dir = NULL;
// Folder 2: Readdir with a nodeid
TEST(open_dir = s_opendir(s_path(s(ft_dst), s(folder2))),
open_dir != NULL);
TEST(dirent = readdir(open_dir), dirent == NULL);
TESTCOND(errno == EINVAL);
TESTSYSCALL(closedir(open_dir));
open_dir = NULL;
FUSE_DAEMON
size_t read_size;
struct fuse_in_header *in_header = (struct fuse_in_header *)bytes_in;
struct fuse_attr attr = {};
int backing_fd = -1;
TESTFUSEINITFLAGS(FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO);
// folder 1: Set 0 as nodeid, Expect READDIR
TESTFUSELOOKUP(folder1, 0);
TEST(backing_fd = s_open(s_path(s(ft_src), s(folder1)),
O_DIRECTORY | O_RDONLY | O_CLOEXEC),
backing_fd != -1);
TESTFUSEOUT2(fuse_entry_out, ((struct fuse_entry_out) {
.nodeid = 0,
.generation = 0,
.entry_valid = UINT64_MAX,
.attr_valid = UINT64_MAX,
.entry_valid_nsec = UINT32_MAX,
.attr_valid_nsec = UINT32_MAX,
.attr = attr,
}), fuse_entry_bpf_out, ((struct fuse_entry_bpf_out) {
.backing_action = FUSE_ACTION_REPLACE,
.backing_fd = backing_fd,
.bpf_action = FUSE_ACTION_REPLACE,
.bpf_fd = bpf_fd,
}));
TESTSYSCALL(close(backing_fd));
TEST(read_size = read(fuse_dev, bytes_in, sizeof(bytes_in)), read_size > 0);
TESTEQUAL(in_header->opcode, FUSE_READDIR);
TESTFUSEOUTERROR(-EINVAL);
// folder 2: Set 10 as nodeid, Expect READDIRPLUS
TESTFUSELOOKUP(folder2, 0);
TEST(backing_fd = s_open(s_path(s(ft_src), s(folder2)),
O_DIRECTORY | O_RDONLY | O_CLOEXEC),
backing_fd != -1);
TESTFUSEOUT2(fuse_entry_out, ((struct fuse_entry_out) {
.nodeid = 10,
.generation = 0,
.entry_valid = UINT64_MAX,
.attr_valid = UINT64_MAX,
.entry_valid_nsec = UINT32_MAX,
.attr_valid_nsec = UINT32_MAX,
.attr = attr,
}), fuse_entry_bpf_out, ((struct fuse_entry_bpf_out) {
.backing_action = FUSE_ACTION_REPLACE,
.backing_fd = backing_fd,
.bpf_action = FUSE_ACTION_REPLACE,
.bpf_fd = bpf_fd,
}));
TESTSYSCALL(close(backing_fd));
TEST(read_size = read(fuse_dev, bytes_in, sizeof(bytes_in)), read_size > 0);
TESTEQUAL(in_header->opcode, FUSE_READDIRPLUS);
TESTFUSEOUTERROR(-EINVAL);
FUSE_DONE
result = TEST_SUCCESS;
out:
close(fuse_dev);
close(content_fd);
close(src_fd);
close(bpf_fd);
umount(mount_dir);
return result;
}
static int parse_options(int argc, char *const *argv)
{
@@ -1782,7 +1883,8 @@ int main(int argc, char *argv[])
MAKE_TEST(inotify_test),
MAKE_TEST(bpf_test_statfs),
MAKE_TEST(bpf_test_lseek),
MAKE_TEST(bpf_test_readdirplus_not_overriding_backing)
MAKE_TEST(bpf_test_readdirplus_not_overriding_backing),
MAKE_TEST(bpf_test_no_readdirplus_without_nodeid)
};
#undef MAKE_TEST

View File

@@ -514,3 +514,13 @@ int error_test(struct fuse_bpf_args *fa)
}
}
SEC("test_readdirplus")
int readdirplus_test(struct fuse_bpf_args *fa)
{
switch (fa->opcode) {
case FUSE_READDIR | FUSE_PREFILTER: {
return 0;
}
}
return FUSE_BPF_BACKING;
}