1 // SPDX-License-Identifier: GPL-2.0
3 * FUSE passthrough to backing file.
5 * Copyright (c) 2023 CTERA Networks.
10 #include <linux/file.h>
11 #include <linux/backing-file.h>
12 #include <linux/splice.h>
14 static void fuse_file_accessed(struct file
*file
)
16 struct inode
*inode
= file_inode(file
);
18 fuse_invalidate_atime(inode
);
21 static void fuse_file_modified(struct file
*file
)
23 struct inode
*inode
= file_inode(file
);
25 fuse_invalidate_attr_mask(inode
, FUSE_STATX_MODSIZE
);
28 ssize_t
fuse_passthrough_read_iter(struct kiocb
*iocb
, struct iov_iter
*iter
)
30 struct file
*file
= iocb
->ki_filp
;
31 struct fuse_file
*ff
= file
->private_data
;
32 struct file
*backing_file
= fuse_file_passthrough(ff
);
33 size_t count
= iov_iter_count(iter
);
35 struct backing_file_ctx ctx
= {
38 .accessed
= fuse_file_accessed
,
42 pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu\n", __func__
,
43 backing_file
, iocb
->ki_pos
, count
);
48 ret
= backing_file_read_iter(backing_file
, iter
, iocb
, iocb
->ki_flags
,
54 ssize_t
fuse_passthrough_write_iter(struct kiocb
*iocb
,
55 struct iov_iter
*iter
)
57 struct file
*file
= iocb
->ki_filp
;
58 struct inode
*inode
= file_inode(file
);
59 struct fuse_file
*ff
= file
->private_data
;
60 struct file
*backing_file
= fuse_file_passthrough(ff
);
61 size_t count
= iov_iter_count(iter
);
63 struct backing_file_ctx ctx
= {
66 .end_write
= fuse_file_modified
,
69 pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu\n", __func__
,
70 backing_file
, iocb
->ki_pos
, count
);
76 ret
= backing_file_write_iter(backing_file
, iter
, iocb
, iocb
->ki_flags
,
83 ssize_t
fuse_passthrough_splice_read(struct file
*in
, loff_t
*ppos
,
84 struct pipe_inode_info
*pipe
,
85 size_t len
, unsigned int flags
)
87 struct fuse_file
*ff
= in
->private_data
;
88 struct file
*backing_file
= fuse_file_passthrough(ff
);
89 struct backing_file_ctx ctx
= {
92 .accessed
= fuse_file_accessed
,
95 pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu, flags=0x%x\n", __func__
,
96 backing_file
, ppos
? *ppos
: 0, len
, flags
);
98 return backing_file_splice_read(backing_file
, ppos
, pipe
, len
, flags
,
102 ssize_t
fuse_passthrough_splice_write(struct pipe_inode_info
*pipe
,
103 struct file
*out
, loff_t
*ppos
,
104 size_t len
, unsigned int flags
)
106 struct fuse_file
*ff
= out
->private_data
;
107 struct file
*backing_file
= fuse_file_passthrough(ff
);
108 struct inode
*inode
= file_inode(out
);
110 struct backing_file_ctx ctx
= {
113 .end_write
= fuse_file_modified
,
116 pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu, flags=0x%x\n", __func__
,
117 backing_file
, ppos
? *ppos
: 0, len
, flags
);
120 ret
= backing_file_splice_write(pipe
, backing_file
, ppos
, len
, flags
,
127 ssize_t
fuse_passthrough_mmap(struct file
*file
, struct vm_area_struct
*vma
)
129 struct fuse_file
*ff
= file
->private_data
;
130 struct file
*backing_file
= fuse_file_passthrough(ff
);
131 struct backing_file_ctx ctx
= {
134 .accessed
= fuse_file_accessed
,
137 pr_debug("%s: backing_file=0x%p, start=%lu, end=%lu\n", __func__
,
138 backing_file
, vma
->vm_start
, vma
->vm_end
);
140 return backing_file_mmap(backing_file
, vma
, &ctx
);
143 struct fuse_backing
*fuse_backing_get(struct fuse_backing
*fb
)
145 if (fb
&& refcount_inc_not_zero(&fb
->count
))
150 static void fuse_backing_free(struct fuse_backing
*fb
)
152 pr_debug("%s: fb=0x%p\n", __func__
, fb
);
160 void fuse_backing_put(struct fuse_backing
*fb
)
162 if (fb
&& refcount_dec_and_test(&fb
->count
))
163 fuse_backing_free(fb
);
166 void fuse_backing_files_init(struct fuse_conn
*fc
)
168 idr_init(&fc
->backing_files_map
);
171 static int fuse_backing_id_alloc(struct fuse_conn
*fc
, struct fuse_backing
*fb
)
175 idr_preload(GFP_KERNEL
);
176 spin_lock(&fc
->lock
);
177 /* FIXME: xarray might be space inefficient */
178 id
= idr_alloc_cyclic(&fc
->backing_files_map
, fb
, 1, 0, GFP_ATOMIC
);
179 spin_unlock(&fc
->lock
);
182 WARN_ON_ONCE(id
== 0);
186 static struct fuse_backing
*fuse_backing_id_remove(struct fuse_conn
*fc
,
189 struct fuse_backing
*fb
;
191 spin_lock(&fc
->lock
);
192 fb
= idr_remove(&fc
->backing_files_map
, id
);
193 spin_unlock(&fc
->lock
);
198 static int fuse_backing_id_free(int id
, void *p
, void *data
)
200 struct fuse_backing
*fb
= p
;
202 WARN_ON_ONCE(refcount_read(&fb
->count
) != 1);
203 fuse_backing_free(fb
);
207 void fuse_backing_files_free(struct fuse_conn
*fc
)
209 idr_for_each(&fc
->backing_files_map
, fuse_backing_id_free
, NULL
);
210 idr_destroy(&fc
->backing_files_map
);
213 int fuse_backing_open(struct fuse_conn
*fc
, struct fuse_backing_map
*map
)
216 struct super_block
*backing_sb
;
217 struct fuse_backing
*fb
= NULL
;
220 pr_debug("%s: fd=%d flags=0x%x\n", __func__
, map
->fd
, map
->flags
);
222 /* TODO: relax CAP_SYS_ADMIN once backing files are visible to lsof */
224 if (!fc
->passthrough
|| !capable(CAP_SYS_ADMIN
))
228 if (map
->flags
|| map
->padding
)
231 file
= fget_raw(map
->fd
);
236 backing_sb
= file_inode(file
)->i_sb
;
237 pr_info("%s: %x:%pD %i\n", __func__
, backing_sb
->s_dev
, file
, backing_sb
->s_stack_depth
);
239 if (backing_sb
->s_stack_depth
>= fc
->max_stack_depth
)
242 fb
= kmalloc(sizeof(struct fuse_backing
), GFP_KERNEL
);
248 fb
->cred
= prepare_creds();
249 refcount_set(&fb
->count
, 1);
251 res
= fuse_backing_id_alloc(fc
, fb
);
253 fuse_backing_free(fb
);
258 pr_debug("%s: fb=0x%p, ret=%i\n", __func__
, fb
, res
);
267 int fuse_backing_close(struct fuse_conn
*fc
, int backing_id
)
269 struct fuse_backing
*fb
= NULL
;
272 pr_debug("%s: backing_id=%d\n", __func__
, backing_id
);
274 /* TODO: relax CAP_SYS_ADMIN once backing files are visible to lsof */
276 if (!fc
->passthrough
|| !capable(CAP_SYS_ADMIN
))
284 fb
= fuse_backing_id_remove(fc
, backing_id
);
288 fuse_backing_put(fb
);
291 pr_debug("%s: fb=0x%p, err=%i\n", __func__
, fb
, err
);
297 * Setup passthrough to a backing file.
299 * Returns an fb object with elevated refcount to be stored in fuse inode.
301 struct fuse_backing
*fuse_passthrough_open(struct file
*file
,
305 struct fuse_file
*ff
= file
->private_data
;
306 struct fuse_conn
*fc
= ff
->fm
->fc
;
307 struct fuse_backing
*fb
= NULL
;
308 struct file
*backing_file
;
316 fb
= idr_find(&fc
->backing_files_map
, backing_id
);
317 fb
= fuse_backing_get(fb
);
324 /* Allocate backing file per fuse file to store fuse path */
325 backing_file
= backing_file_open(&file
->f_path
, file
->f_flags
,
326 &fb
->file
->f_path
, fb
->cred
);
327 err
= PTR_ERR(backing_file
);
328 if (IS_ERR(backing_file
)) {
329 fuse_backing_put(fb
);
334 ff
->passthrough
= backing_file
;
335 ff
->cred
= get_cred(fb
->cred
);
337 pr_debug("%s: backing_id=%d, fb=0x%p, backing_file=0x%p, err=%i\n", __func__
,
338 backing_id
, fb
, ff
->passthrough
, err
);
340 return err
? ERR_PTR(err
) : fb
;
343 void fuse_passthrough_release(struct fuse_file
*ff
, struct fuse_backing
*fb
)
345 pr_debug("%s: fb=0x%p, backing_file=0x%p\n", __func__
,
346 fb
, ff
->passthrough
);
348 fput(ff
->passthrough
);
349 ff
->passthrough
= NULL
;