| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| From: Daniel Rosenberg <drosen@google.com> |
| Date: Fri, 22 Apr 2016 00:00:48 -0700 |
| Subject: ANDROID: fuse: Add support for d_canonical_path |
| |
| Allows FUSE to report to inotify that it is acting as a layered filesystem. |
| The userspace component returns a string representing the location of the |
| underlying file. If the string cannot be resolved into a path, the top |
| level path is returned instead. |
| |
| [CPNOTE: 20/07/21] Lee: Pinged Alessio for an update via the bug |
| |
| Bug: 23904372 |
| Bug: 171780975 |
| Test: FileObserverTest and FileObserverTestLegacyPath on cuttlefish |
| Change-Id: Iabdca0bbedfbff59e9c820c58636a68ef9683d9f |
| Signed-off-by: Daniel Rosenberg <drosen@google.com> |
| Signed-off-by: Alessio Balsini <balsini@google.com> |
| Signed-off-by: Lee Jones <joneslee@google.com> |
| --- |
| fs/fuse/dev.c | 10 +++++++++- |
| fs/fuse/dir.c | 40 +++++++++++++++++++++++++++++++++++++++ |
| fs/fuse/fuse_i.h | 6 ++++-- |
| fs/fuse/passthrough.c | 9 ++------- |
| include/uapi/linux/fuse.h | 14 ++++---------- |
| 5 files changed, 59 insertions(+), 20 deletions(-) |
| |
| diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c |
| --- a/fs/fuse/dev.c |
| +++ b/fs/fuse/dev.c |
| @@ -14,6 +14,7 @@ |
| #include <linux/sched/signal.h> |
| #include <linux/uio.h> |
| #include <linux/miscdevice.h> |
| +#include <linux/namei.h> |
| #include <linux/pagemap.h> |
| #include <linux/file.h> |
| #include <linux/slab.h> |
| @@ -1925,6 +1926,14 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud, |
| err = copy_out_args(cs, req->args, nbytes); |
| fuse_copy_finish(cs); |
| |
| + if (!err && req->in.h.opcode == FUSE_CANONICAL_PATH) { |
| + char *path = (char *)req->args->out_args[0].value; |
| + |
| + path[req->args->out_args[0].size - 1] = 0; |
| + req->out.h.error = |
| + kern_path(path, 0, req->args->canonical_path); |
| + } |
| + |
| spin_lock(&fpq->lock); |
| clear_bit(FR_LOCKED, &req->flags); |
| if (!fpq->connected) |
| @@ -2257,7 +2266,6 @@ static long fuse_dev_ioctl(struct file *file, unsigned int cmd, |
| int res; |
| int oldfd; |
| struct fuse_dev *fud = NULL; |
| - struct fuse_passthrough_out pto; |
| |
| switch (cmd) { |
| case FUSE_DEV_IOC_CLONE: |
| diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c |
| --- a/fs/fuse/dir.c |
| +++ b/fs/fuse/dir.c |
| @@ -337,6 +337,45 @@ static struct vfsmount *fuse_dentry_automount(struct path *path) |
| return mnt; |
| } |
| |
| +/* |
| + * Get the canonical path. Since we must translate to a path, this must be done |
| + * in the context of the userspace daemon, however, the userspace daemon cannot |
| + * look up paths on its own. Instead, we handle the lookup as a special case |
| + * inside of the write request. |
| + */ |
| +static void fuse_dentry_canonical_path(const struct path *path, |
| + struct path *canonical_path) |
| +{ |
| + struct inode *inode = d_inode(path->dentry); |
| + //struct fuse_conn *fc = get_fuse_conn(inode); |
| + struct fuse_mount *fm = get_fuse_mount_super(path->mnt->mnt_sb); |
| + FUSE_ARGS(args); |
| + char *path_name; |
| + int err; |
| + |
| + path_name = (char *)get_zeroed_page(GFP_KERNEL); |
| + if (!path_name) |
| + goto default_path; |
| + |
| + args.opcode = FUSE_CANONICAL_PATH; |
| + args.nodeid = get_node_id(inode); |
| + args.in_numargs = 0; |
| + args.out_numargs = 1; |
| + args.out_args[0].size = PATH_MAX; |
| + args.out_args[0].value = path_name; |
| + args.canonical_path = canonical_path; |
| + args.out_argvar = 1; |
| + |
| + err = fuse_simple_request(fm, &args); |
| + free_page((unsigned long)path_name); |
| + if (err > 0) |
| + return; |
| +default_path: |
| + canonical_path->dentry = path->dentry; |
| + canonical_path->mnt = path->mnt; |
| + path_get(canonical_path); |
| +} |
| + |
| const struct dentry_operations fuse_dentry_operations = { |
| .d_revalidate = fuse_dentry_revalidate, |
| .d_delete = fuse_dentry_delete, |
| @@ -345,6 +384,7 @@ const struct dentry_operations fuse_dentry_operations = { |
| .d_release = fuse_dentry_release, |
| #endif |
| .d_automount = fuse_dentry_automount, |
| + .d_canonical_path = fuse_dentry_canonical_path, |
| }; |
| |
| const struct dentry_operations fuse_root_dentry_operations = { |
| diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h |
| --- a/fs/fuse/fuse_i.h |
| +++ b/fs/fuse/fuse_i.h |
| @@ -278,6 +278,9 @@ struct fuse_args { |
| struct fuse_in_arg in_args[3]; |
| struct fuse_arg out_args[2]; |
| void (*end)(struct fuse_mount *fm, struct fuse_args *args, int error); |
| + |
| + /* Path used for completing d_canonical_path */ |
| + struct path *canonical_path; |
| }; |
| |
| struct fuse_args_pages { |
| @@ -1341,8 +1344,7 @@ void fuse_file_release(struct inode *inode, struct fuse_file *ff, |
| unsigned int open_flags, fl_owner_t id, bool isdir); |
| |
| /* passthrough.c */ |
| -int fuse_passthrough_open(struct fuse_dev *fud, |
| - struct fuse_passthrough_out *pto); |
| +int fuse_passthrough_open(struct fuse_dev *fud, u32 lower_fd); |
| int fuse_passthrough_setup(struct fuse_conn *fc, struct fuse_file *ff, |
| struct fuse_open_out *openarg); |
| void fuse_passthrough_release(struct fuse_passthrough *passthrough); |
| diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c |
| --- a/fs/fuse/passthrough.c |
| +++ b/fs/fuse/passthrough.c |
| @@ -180,8 +180,7 @@ ssize_t fuse_passthrough_mmap(struct file *file, struct vm_area_struct *vma) |
| return ret; |
| } |
| |
| -int fuse_passthrough_open(struct fuse_dev *fud, |
| - struct fuse_passthrough_out *pto) |
| +int fuse_passthrough_open(struct fuse_dev *fud, u32 lower_fd) |
| { |
| int res; |
| struct file *passthrough_filp; |
| @@ -193,11 +192,7 @@ int fuse_passthrough_open(struct fuse_dev *fud, |
| if (!fc->passthrough) |
| return -EPERM; |
| |
| - /* This field is reserved for future implementation */ |
| - if (pto->len != 0) |
| - return -EINVAL; |
| - |
| - passthrough_filp = fget(pto->fd); |
| + passthrough_filp = fget(lower_fd); |
| if (!passthrough_filp) { |
| pr_err("FUSE: invalid file descriptor for passthrough.\n"); |
| return -EBADF; |
| diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h |
| --- a/include/uapi/linux/fuse.h |
| +++ b/include/uapi/linux/fuse.h |
| @@ -553,6 +553,7 @@ enum fuse_opcode { |
| FUSE_REMOVEMAPPING = 49, |
| FUSE_SYNCFS = 50, |
| FUSE_TMPFILE = 51, |
| + FUSE_CANONICAL_PATH = 2016, |
| |
| /* CUSE specific operations */ |
| CUSE_INIT = 4096, |
| @@ -889,14 +890,6 @@ struct fuse_in_header { |
| uint32_t padding; |
| }; |
| |
| -/* fuse_passthrough_out for passthrough V1 */ |
| -struct fuse_passthrough_out { |
| - uint32_t fd; |
| - /* For future implementation */ |
| - uint32_t len; |
| - void *vec; |
| -}; |
| - |
| struct fuse_out_header { |
| uint32_t len; |
| int32_t error; |
| @@ -977,8 +970,9 @@ struct fuse_notify_retrieve_in { |
| /* Device ioctls: */ |
| #define FUSE_DEV_IOC_MAGIC 229 |
| #define FUSE_DEV_IOC_CLONE _IOR(FUSE_DEV_IOC_MAGIC, 0, uint32_t) |
| -/* 127 is reserved for the V1 interface implementation in Android */ |
| -#define FUSE_DEV_IOC_PASSTHROUGH_OPEN _IOW(FUSE_DEV_IOC_MAGIC, 127, struct fuse_passthrough_out) |
| +/* 127 is reserved for the V1 interface implementation in Android (deprecated) */ |
| +/* 126 is reserved for the V2 interface implementation in Android */ |
| +#define FUSE_DEV_IOC_PASSTHROUGH_OPEN _IOW(FUSE_DEV_IOC_MAGIC, 126, uint32_t) |
| |
| struct fuse_lseek_in { |
| uint64_t fh; |