From 24ab59f6bb42d609543b913e2402137c35b39f35 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 22 Apr 2016 00:00:48 -0700 Subject: [PATCH] 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. Bug: 23904372 Bug: 171780975 Test: inotify on cuttlefish Change-Id: Iabdca0bbedfbff59e9c820c58636a68ef9683d9f Signed-off-by: Daniel Rosenberg Signed-off-by: Alessio Balsini --- fs/fuse/dev.c | 7 +++++++ fs/fuse/dir.c | 37 +++++++++++++++++++++++++++++++++++++ fs/fuse/fuse_i.h | 3 +++ include/uapi/linux/fuse.h | 1 + 4 files changed, 48 insertions(+) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 06dd38e76c62..18232eab80ed 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -1899,6 +1900,12 @@ 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) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index ee190119f45c..ebd1927b2f61 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -298,6 +298,42 @@ static int fuse_dentry_delete(const struct dentry *dentry) return time_before64(fuse_dentry_time(dentry), get_jiffies_64()); } +/* + * 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); + FUSE_ARGS(args); + char *path_name; + int err; + + path_name = (char*)__get_free_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(fc, &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, @@ -305,6 +341,7 @@ const struct dentry_operations fuse_dentry_operations = { .d_init = fuse_dentry_init, .d_release = fuse_dentry_release, #endif + .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 index d7cde216fc87..bf040533bed3 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -253,6 +253,9 @@ struct fuse_args { struct fuse_in_arg in_args[3]; struct fuse_arg out_args[2]; void (*end)(struct fuse_conn *fc, struct fuse_args *args, int error); + + /* Path used for completing d_canonical_path */ + struct path *canonical_path; }; struct fuse_args_pages { diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 373cada89815..0a88b2179fda 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -465,6 +465,7 @@ enum fuse_opcode { FUSE_COPY_FILE_RANGE = 47, FUSE_SETUPMAPPING = 48, FUSE_REMOVEMAPPING = 49, + FUSE_CANONICAL_PATH = 2016, /* CUSE specific operations */ CUSE_INIT = 4096,