diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 1f64ae6d7a69..2dcd57f16c64 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -2040,6 +2041,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) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 54104dd48af7..d455b5d0b2f3 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -327,6 +327,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, @@ -335,6 +374,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 index e6cc3d552b13..2496b90c6910 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -312,6 +312,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 { diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index f1e99458e29e..ab9ee787ee90 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -638,6 +638,7 @@ enum fuse_opcode { FUSE_SYNCFS = 50, FUSE_TMPFILE = 51, FUSE_STATX = 52, + FUSE_CANONICAL_PATH = 2016, /* CUSE specific operations */ CUSE_INIT = 4096,