ANDROID: fuse-bpf: Support FUSE_STATFS

Adds support for FUSE_STATFS, needed to run various filesystem tests

Bug: 217570523
Test: bpf_test_statfs
Change-Id: I5ee13e880118c5c79c4ca17bb2e902a3e17a7eb8
Signed-off-by: Daniel Rosenberg <drosen@google.com>
This commit is contained in:
Daniel Rosenberg
2022-02-02 17:36:24 -08:00
parent bea68245ef
commit de92205dac
8 changed files with 155 additions and 16 deletions

View File

@@ -1786,6 +1786,56 @@ void *fuse_setattr_finalize(struct fuse_args *fa,
return NULL;
}
int fuse_statfs_initialize(
struct fuse_args *fa, struct fuse_statfs_out *fso,
struct dentry *dentry, struct kstatfs *buf)
{
*fso = (struct fuse_statfs_out) {0};
*fa = (struct fuse_args) {
.nodeid = get_node_id(d_inode(dentry)),
.opcode = FUSE_STATFS,
.out_numargs = 1,
.out_numargs = 1,
.out_args[0].size = sizeof(fso),
.out_args[0].value = fso,
};
return 0;
}
int fuse_statfs_backing(
struct fuse_args *fa,
struct dentry *dentry, struct kstatfs *buf)
{
int err = 0;
struct path backing_path;
struct fuse_statfs_out *fso = fa->out_args[0].value;
get_fuse_backing_path(dentry, &backing_path);
if (!backing_path.dentry)
return -EBADF;
err = vfs_statfs(&backing_path, buf);
path_put(&backing_path);
buf->f_type = FUSE_SUPER_MAGIC;
//TODO Provide postfilter opportunity to modify
if (!err)
convert_statfs_to_fuse(&fso->st, buf);
return err;
}
void *fuse_statfs_finalize(
struct fuse_args *fa,
struct dentry *dentry, struct kstatfs *buf)
{
struct fuse_statfs_out *fso = fa->out_args[0].value;
if (!fa->error_in)
convert_fuse_statfs(buf, &fso->st);
return NULL;
}
int fuse_get_link_initialize(struct fuse_args *fa, struct fuse_dummy_io *unused,
struct inode *inode, struct dentry *dentry,
struct delayed_call *callback, const char **out)

View File

@@ -33,6 +33,9 @@
#include <linux/pid_namespace.h>
#include <linux/refcount.h>
#include <linux/user_namespace.h>
#include <linux/statfs.h>
#define FUSE_SUPER_MAGIC 0x65735546
/** Default max number of pages that can be used in a single read request */
#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
@@ -1567,6 +1570,13 @@ int fuse_setattr_backing(struct fuse_args *fa,
void *fuse_setattr_finalize(struct fuse_args *fa,
struct dentry *dentry, struct iattr *attr, struct file *file);
int fuse_statfs_initialize(struct fuse_args *fa, struct fuse_statfs_out *fso,
struct dentry *dentry, struct kstatfs *buf);
int fuse_statfs_backing(struct fuse_args *fa,
struct dentry *dentry, struct kstatfs *buf);
void *fuse_statfs_finalize(struct fuse_args *fa,
struct dentry *dentry, struct kstatfs *buf);
int fuse_get_link_initialize(struct fuse_args *fa, struct fuse_dummy_io *dummy,
struct inode *inode, struct dentry *dentry,
struct delayed_call *callback, const char **out);
@@ -1708,6 +1718,33 @@ static inline int finalize_attr(struct inode *inode, struct fuse_attr_out *outar
return err;
}
static inline void convert_statfs_to_fuse(struct fuse_kstatfs *attr, struct kstatfs *stbuf)
{
attr->bsize = stbuf->f_bsize;
attr->frsize = stbuf->f_frsize;
attr->blocks = stbuf->f_blocks;
attr->bfree = stbuf->f_bfree;
attr->bavail = stbuf->f_bavail;
attr->files = stbuf->f_files;
attr->ffree = stbuf->f_ffree;
attr->namelen = stbuf->f_namelen;
/* fsid is left zero */
}
static inline void convert_fuse_statfs(struct kstatfs *stbuf, struct fuse_kstatfs *attr)
{
stbuf->f_type = FUSE_SUPER_MAGIC;
stbuf->f_bsize = attr->bsize;
stbuf->f_frsize = attr->frsize;
stbuf->f_blocks = attr->blocks;
stbuf->f_bfree = attr->bfree;
stbuf->f_bavail = attr->bavail;
stbuf->f_files = attr->files;
stbuf->f_ffree = attr->ffree;
stbuf->f_namelen = attr->namelen;
/* fsid is left zero */
}
#ifdef CONFIG_FUSE_BPF
struct fuse_err_ret {
void *result;

View File

@@ -50,8 +50,6 @@ MODULE_PARM_DESC(max_user_congthresh,
"Global limit for the maximum congestion threshold an "
"unprivileged user can set");
#define FUSE_SUPER_MAGIC 0x65735546
#define FUSE_DEFAULT_BLKSIZE 512
/** Maximum number of outstanding background requests */
@@ -549,20 +547,6 @@ static void fuse_put_super(struct super_block *sb)
fuse_mount_put(fm);
}
static void convert_fuse_statfs(struct kstatfs *stbuf, struct fuse_kstatfs *attr)
{
stbuf->f_type = FUSE_SUPER_MAGIC;
stbuf->f_bsize = attr->bsize;
stbuf->f_frsize = attr->frsize;
stbuf->f_blocks = attr->blocks;
stbuf->f_bfree = attr->bfree;
stbuf->f_bavail = attr->bavail;
stbuf->f_files = attr->files;
stbuf->f_ffree = attr->ffree;
stbuf->f_namelen = attr->namelen;
/* fsid is left zero */
}
static int fuse_statfs(struct dentry *dentry, struct kstatfs *buf)
{
struct super_block *sb = dentry->d_sb;
@@ -570,12 +554,24 @@ static int fuse_statfs(struct dentry *dentry, struct kstatfs *buf)
FUSE_ARGS(args);
struct fuse_statfs_out outarg;
int err;
#ifdef CONFIG_FUSE_BPF
struct fuse_err_ret fer;
#endif
if (!fuse_allow_current_process(fm->fc)) {
buf->f_type = FUSE_SUPER_MAGIC;
return 0;
}
#ifdef CONFIG_FUSE_BPF
fer = fuse_bpf_backing(dentry->d_inode, struct fuse_statfs_out,
fuse_statfs_initialize, fuse_statfs_backing,
fuse_statfs_finalize,
dentry, buf);
if (fer.ret)
return PTR_ERR(fer.result);
#endif
memset(&outarg, 0, sizeof(outarg));
args.in_numargs = 0;
args.opcode = FUSE_STATFS;

View File

@@ -16,6 +16,7 @@
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/xattr.h>
#include <linux/unistd.h>
@@ -302,6 +303,20 @@ int s_stat(struct s pathname, struct stat *st)
return res;
}
int s_statfs(struct s pathname, struct statfs *st)
{
int res;
if (!pathname.s) {
errno = ENOMEM;
return -1;
}
res = statfs(pathname.s, st);
free(pathname.s);
return res;
}
DIR *s_opendir(struct s pathname)
{
DIR *res;

View File

@@ -284,6 +284,11 @@ int trace_daemon(struct fuse_args *fa)
return FUSE_BPF_BACKING;
}
case FUSE_STATFS | FUSE_PREFILTER: {
bpf_printk("statfs %d", fa->nodeid);
return FUSE_BPF_BACKING;
}
default:
if (fa->opcode & FUSE_PREFILTER)
bpf_printk("prefilter *** UNKNOWN *** opcode: %d",

View File

@@ -1422,6 +1422,34 @@ out:
return result;
}
static int bpf_test_statfs(const char *mount_dir)
{
int result = TEST_FAILURE;
int src_fd = -1;
int bpf_fd = -1;
int fuse_dev = -1;
int fd = -1;
struct statfs st;
TEST(src_fd = open(ft_src, O_DIRECTORY | O_RDONLY | O_CLOEXEC),
src_fd != -1);
TESTEQUAL(install_elf_bpf("test_bpf.bpf", "test_trace",
&bpf_fd, NULL, NULL), 0);
TESTEQUAL(mount_fuse(mount_dir, bpf_fd, src_fd, &fuse_dev), 0);
TESTSYSCALL(s_statfs(s(mount_dir), &st));
TESTEQUAL(bpf_test_trace("statfs"), 0);
TESTEQUAL(st.f_type, 0x65735546);
result = TEST_SUCCESS;
out:
close(fd);
umount(mount_dir);
close(fuse_dev);
close(bpf_fd);
close(src_fd);
return result;
}
static int parse_options(int argc, char *const *argv)
{
signed char c;
@@ -1524,6 +1552,7 @@ int main(int argc, char *argv[])
MAKE_TEST(mmap_test),
MAKE_TEST(readdir_perms_test),
MAKE_TEST(inotify_test),
MAKE_TEST(bpf_test_statfs),
};
#undef MAKE_TEST

View File

@@ -341,6 +341,11 @@ int trace_test(struct fuse_args *fa)
return FUSE_BPF_BACKING;
}
case FUSE_STATFS | FUSE_PREFILTER: {
bpf_printk("statfs");
return FUSE_BPF_BACKING;
}
default:
bpf_printk("Unknown opcode %d", fa->opcode);
return 0;

View File

@@ -12,6 +12,7 @@
#include <dirent.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/types.h>
#define PAGE_SIZE 4096
@@ -50,6 +51,7 @@ int s_openat(int dirfd, struct s pathname, int flags, ...);
int s_creat(struct s pathname, mode_t mode);
int s_mkfifo(struct s pathname, mode_t mode);
int s_stat(struct s pathname, struct stat *st);
int s_statfs(struct s pathname, struct statfs *st);
DIR *s_opendir(struct s pathname);
int s_getxattr(struct s pathname, const char name[], void *value, size_t size,
ssize_t *ret_size);