projects/linux·blob master · refs

180 lines · 3.6 KiB

1// SPDX-License-Identifier: GPL-2.0
2/*
3 * FUSE passthrough to backing file.
4 *
5 * Copyright (c) 2023 CTERA Networks.
6 */
7
8#include "dev.h"
9#include "fuse_i.h"
10
11#include <linux/file.h>
12
13struct fuse_backing *fuse_backing_get(struct fuse_backing *fb)
14{
15 if (fb && refcount_inc_not_zero(&fb->count))
16 return fb;
17 return NULL;
18}
19
20static void fuse_backing_free(struct fuse_backing *fb)
21{
22 pr_debug("%s: fb=0x%p\n", __func__, fb);
23
24 if (fb->file)
25 fput(fb->file);
26 put_cred(fb->cred);
27 kfree_rcu(fb, rcu);
28}
29
30void fuse_backing_put(struct fuse_backing *fb)
31{
32 if (fb && refcount_dec_and_test(&fb->count))
33 fuse_backing_free(fb);
34}
35
36void fuse_backing_files_init(struct fuse_conn *fc)
37{
38 idr_init(&fc->backing_files_map);
39}
40
41static int fuse_backing_id_alloc(struct fuse_conn *fc, struct fuse_backing *fb)
42{
43 int id;
44
45 idr_preload(GFP_KERNEL);
46 spin_lock(&fc->lock);
47 /* FIXME: xarray might be space inefficient */
48 id = idr_alloc_cyclic(&fc->backing_files_map, fb, 1, 0, GFP_ATOMIC);
49 spin_unlock(&fc->lock);
50 idr_preload_end();
51
52 WARN_ON_ONCE(id == 0);
53 return id;
54}
55
56static struct fuse_backing *fuse_backing_id_remove(struct fuse_conn *fc,
57 int id)
58{
59 struct fuse_backing *fb;
60
61 spin_lock(&fc->lock);
62 fb = idr_remove(&fc->backing_files_map, id);
63 spin_unlock(&fc->lock);
64
65 return fb;
66}
67
68static int fuse_backing_id_free(int id, void *p, void *data)
69{
70 struct fuse_backing *fb = p;
71
72 WARN_ON_ONCE(refcount_read(&fb->count) != 1);
73 fuse_backing_free(fb);
74 return 0;
75}
76
77void fuse_backing_files_free(struct fuse_conn *fc)
78{
79 idr_for_each(&fc->backing_files_map, fuse_backing_id_free, NULL);
80 idr_destroy(&fc->backing_files_map);
81}
82
83int fuse_backing_open(struct fuse_conn *fc, struct fuse_backing_map *map)
84{
85 struct file *file;
86 struct super_block *backing_sb;
87 struct fuse_backing *fb = NULL;
88 int res;
89
90 pr_debug("%s: fd=%d flags=0x%x\n", __func__, map->fd, map->flags);
91
92 /* TODO: relax CAP_SYS_ADMIN once backing files are visible to lsof */
93 res = -EPERM;
94 if (!fc->passthrough || !capable(CAP_SYS_ADMIN))
95 goto out;
96
97 res = -EINVAL;
98 if (map->flags || map->padding)
99 goto out;
100
101 file = fget_raw(map->fd);
102 res = -EBADF;
103 if (!file)
104 goto out;
105
106 /* read/write/splice/mmap passthrough only relevant for regular files */
107 res = d_is_dir(file->f_path.dentry) ? -EISDIR : -EINVAL;
108 if (!d_is_reg(file->f_path.dentry))
109 goto out_fput;
110
111 backing_sb = file_inode(file)->i_sb;
112 res = -ELOOP;
113 if (backing_sb->s_stack_depth >= fc->max_stack_depth)
114 goto out_fput;
115
116 fb = kmalloc_obj(struct fuse_backing);
117 res = -ENOMEM;
118 if (!fb)
119 goto out_fput;
120
121 fb->file = file;
122 fb->cred = get_current_cred();
123 refcount_set(&fb->count, 1);
124
125 res = fuse_backing_id_alloc(fc, fb);
126 if (res < 0) {
127 fuse_backing_free(fb);
128 fb = NULL;
129 }
130
131out:
132 pr_debug("%s: fb=0x%p, ret=%i\n", __func__, fb, res);
133
134 return res;
135
136out_fput:
137 fput(file);
138 goto out;
139}
140
141int fuse_backing_close(struct fuse_conn *fc, int backing_id)
142{
143 struct fuse_backing *fb = NULL;
144 int err;
145
146 pr_debug("%s: backing_id=%d\n", __func__, backing_id);
147
148 /* TODO: relax CAP_SYS_ADMIN once backing files are visible to lsof */
149 err = -EPERM;
150 if (!fc->passthrough || !capable(CAP_SYS_ADMIN))
151 goto out;
152
153 err = -EINVAL;
154 if (backing_id <= 0)
155 goto out;
156
157 err = -ENOENT;
158 fb = fuse_backing_id_remove(fc, backing_id);
159 if (!fb)
160 goto out;
161
162 fuse_backing_put(fb);
163 err = 0;
164out:
165 pr_debug("%s: fb=0x%p, err=%i\n", __func__, fb, err);
166
167 return err;
168}
169
170struct fuse_backing *fuse_backing_lookup(struct fuse_conn *fc, int backing_id)
171{
172 struct fuse_backing *fb;
173
174 rcu_read_lock();
175 fb = idr_find(&fc->backing_files_map, backing_id);
176 fb = fuse_backing_get(fb);
177 rcu_read_unlock();
178
179 return fb;
180}