diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 1ff5a6b21db0..9861204da06f 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -573,6 +574,7 @@ ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args) req->out.numargs = args->out.numargs; memcpy(req->out.args, args->out.args, args->out.numargs * sizeof(struct fuse_arg)); + req->out.canonical_path = args->out.canonical_path; fuse_request_send(fc, req); ret = req->out.h.error; if (!ret && args->out.argvar) { @@ -1932,6 +1934,13 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud, err = copy_out_args(cs, &req->out, nbytes); fuse_copy_finish(cs); + if (!err && req->in.h.opcode == FUSE_CANONICAL_PATH) { + char *path = (char *)req->out.args[0].value; + + path[req->out.args[0].size - 1] = 0; + req->out.h.error = kern_path(path, 0, req->out.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 6244345a5745..90bd6c0d18cc 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -281,10 +281,49 @@ static void fuse_dentry_release(struct dentry *dentry) kfree_rcu(fd, rcu); } +/* + * 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.in.h.opcode = FUSE_CANONICAL_PATH; + args.in.h.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.out.argvar = 1; + args.out.canonical_path = canonical_path; + + 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_init = fuse_dentry_init, .d_release = fuse_dentry_release, + .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 dbfc35efbefb..123f87804f51 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -213,6 +213,9 @@ struct fuse_out { /** Array of arguments */ struct fuse_arg args[2]; + + /* Path used for completing d_canonical_path */ + struct path *canonical_path; }; /** FUSE page descriptor */ @@ -235,6 +238,9 @@ struct fuse_args { unsigned argvar:1; unsigned numargs; struct fuse_arg args[2]; + + /* Path used for completing d_canonical_path */ + struct path *canonical_path; } out; }; diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 2170e58a2a97..24af4edfc98c 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -383,6 +383,7 @@ enum fuse_opcode { FUSE_READDIRPLUS = 44, FUSE_RENAME2 = 45, FUSE_LSEEK = 46, + FUSE_CANONICAL_PATH= 2016, /* CUSE specific operations */ CUSE_INIT = 4096,