mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 03:15:31 +09:00
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:
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user