Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  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 "fuse_i.h"
  9
 10#include <linux/file.h>
 11#include <linux/backing-file.h>
 12#include <linux/splice.h>
 13
 14static void fuse_file_accessed(struct file *file)
 15{
 16	struct inode *inode = file_inode(file);
 17
 18	fuse_invalidate_atime(inode);
 19}
 20
 21static void fuse_passthrough_end_write(struct kiocb *iocb, ssize_t ret)
 22{
 23	struct inode *inode = file_inode(iocb->ki_filp);
 24
 25	fuse_write_update_attr(inode, iocb->ki_pos, ret);
 26}
 27
 28ssize_t fuse_passthrough_read_iter(struct kiocb *iocb, struct iov_iter *iter)
 29{
 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);
 34	ssize_t ret;
 35	struct backing_file_ctx ctx = {
 36		.cred = ff->cred,
 37		.accessed = fuse_file_accessed,
 38	};
 39
 40
 41	pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu\n", __func__,
 42		 backing_file, iocb->ki_pos, count);
 43
 44	if (!count)
 45		return 0;
 46
 47	ret = backing_file_read_iter(backing_file, iter, iocb, iocb->ki_flags,
 48				     &ctx);
 49
 50	return ret;
 51}
 52
 53ssize_t fuse_passthrough_write_iter(struct kiocb *iocb,
 54				    struct iov_iter *iter)
 55{
 56	struct file *file = iocb->ki_filp;
 57	struct inode *inode = file_inode(file);
 58	struct fuse_file *ff = file->private_data;
 59	struct file *backing_file = fuse_file_passthrough(ff);
 60	size_t count = iov_iter_count(iter);
 61	ssize_t ret;
 62	struct backing_file_ctx ctx = {
 63		.cred = ff->cred,
 64		.end_write = fuse_passthrough_end_write,
 65	};
 66
 67	pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu\n", __func__,
 68		 backing_file, iocb->ki_pos, count);
 69
 70	if (!count)
 71		return 0;
 72
 73	inode_lock(inode);
 74	ret = backing_file_write_iter(backing_file, iter, iocb, iocb->ki_flags,
 75				      &ctx);
 76	inode_unlock(inode);
 77
 78	return ret;
 79}
 80
 81ssize_t fuse_passthrough_splice_read(struct file *in, loff_t *ppos,
 82				     struct pipe_inode_info *pipe,
 83				     size_t len, unsigned int flags)
 84{
 85	struct fuse_file *ff = in->private_data;
 86	struct file *backing_file = fuse_file_passthrough(ff);
 87	struct backing_file_ctx ctx = {
 88		.cred = ff->cred,
 89		.accessed = fuse_file_accessed,
 90	};
 91	struct kiocb iocb;
 92	ssize_t ret;
 93
 94	pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu, flags=0x%x\n", __func__,
 95		 backing_file, *ppos, len, flags);
 96
 97	init_sync_kiocb(&iocb, in);
 98	iocb.ki_pos = *ppos;
 99	ret = backing_file_splice_read(backing_file, &iocb, pipe, len, flags, &ctx);
100	*ppos = iocb.ki_pos;
101
102	return ret;
103}
104
105ssize_t fuse_passthrough_splice_write(struct pipe_inode_info *pipe,
106				      struct file *out, loff_t *ppos,
107				      size_t len, unsigned int flags)
108{
109	struct fuse_file *ff = out->private_data;
110	struct file *backing_file = fuse_file_passthrough(ff);
111	struct inode *inode = file_inode(out);
112	ssize_t ret;
113	struct backing_file_ctx ctx = {
114		.cred = ff->cred,
115		.end_write = fuse_passthrough_end_write,
116	};
117	struct kiocb iocb;
118
119	pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu, flags=0x%x\n", __func__,
120		 backing_file, *ppos, len, flags);
121
122	inode_lock(inode);
123	init_sync_kiocb(&iocb, out);
124	iocb.ki_pos = *ppos;
125	ret = backing_file_splice_write(pipe, backing_file, &iocb, len, flags, &ctx);
126	*ppos = iocb.ki_pos;
127	inode_unlock(inode);
128
129	return ret;
130}
131
132ssize_t fuse_passthrough_mmap(struct file *file, struct vm_area_struct *vma)
133{
134	struct fuse_file *ff = file->private_data;
135	struct file *backing_file = fuse_file_passthrough(ff);
136	struct backing_file_ctx ctx = {
137		.cred = ff->cred,
138		.accessed = fuse_file_accessed,
139	};
140
141	pr_debug("%s: backing_file=0x%p, start=%lu, end=%lu\n", __func__,
142		 backing_file, vma->vm_start, vma->vm_end);
143
144	return backing_file_mmap(backing_file, vma, &ctx);
145}
146
147struct fuse_backing *fuse_backing_get(struct fuse_backing *fb)
148{
149	if (fb && refcount_inc_not_zero(&fb->count))
150		return fb;
151	return NULL;
152}
153
154static void fuse_backing_free(struct fuse_backing *fb)
155{
156	pr_debug("%s: fb=0x%p\n", __func__, fb);
157
158	if (fb->file)
159		fput(fb->file);
160	put_cred(fb->cred);
161	kfree_rcu(fb, rcu);
162}
163
164void fuse_backing_put(struct fuse_backing *fb)
165{
166	if (fb && refcount_dec_and_test(&fb->count))
167		fuse_backing_free(fb);
168}
169
170void fuse_backing_files_init(struct fuse_conn *fc)
171{
172	idr_init(&fc->backing_files_map);
173}
174
175static int fuse_backing_id_alloc(struct fuse_conn *fc, struct fuse_backing *fb)
176{
177	int id;
178
179	idr_preload(GFP_KERNEL);
180	spin_lock(&fc->lock);
181	/* FIXME: xarray might be space inefficient */
182	id = idr_alloc_cyclic(&fc->backing_files_map, fb, 1, 0, GFP_ATOMIC);
183	spin_unlock(&fc->lock);
184	idr_preload_end();
185
186	WARN_ON_ONCE(id == 0);
187	return id;
188}
189
190static struct fuse_backing *fuse_backing_id_remove(struct fuse_conn *fc,
191						   int id)
192{
193	struct fuse_backing *fb;
194
195	spin_lock(&fc->lock);
196	fb = idr_remove(&fc->backing_files_map, id);
197	spin_unlock(&fc->lock);
198
199	return fb;
200}
201
202static int fuse_backing_id_free(int id, void *p, void *data)
203{
204	struct fuse_backing *fb = p;
205
206	WARN_ON_ONCE(refcount_read(&fb->count) != 1);
207	fuse_backing_free(fb);
208	return 0;
209}
210
211void fuse_backing_files_free(struct fuse_conn *fc)
212{
213	idr_for_each(&fc->backing_files_map, fuse_backing_id_free, NULL);
214	idr_destroy(&fc->backing_files_map);
215}
216
217int fuse_backing_open(struct fuse_conn *fc, struct fuse_backing_map *map)
218{
219	struct file *file;
220	struct super_block *backing_sb;
221	struct fuse_backing *fb = NULL;
222	int res;
223
224	pr_debug("%s: fd=%d flags=0x%x\n", __func__, map->fd, map->flags);
225
226	/* TODO: relax CAP_SYS_ADMIN once backing files are visible to lsof */
227	res = -EPERM;
228	if (!fc->passthrough || !capable(CAP_SYS_ADMIN))
229		goto out;
230
231	res = -EINVAL;
232	if (map->flags || map->padding)
233		goto out;
234
235	file = fget_raw(map->fd);
236	res = -EBADF;
237	if (!file)
238		goto out;
239
240	backing_sb = file_inode(file)->i_sb;
241	res = -ELOOP;
242	if (backing_sb->s_stack_depth >= fc->max_stack_depth)
243		goto out_fput;
244
245	fb = kmalloc(sizeof(struct fuse_backing), GFP_KERNEL);
246	res = -ENOMEM;
247	if (!fb)
248		goto out_fput;
249
250	fb->file = file;
251	fb->cred = prepare_creds();
252	refcount_set(&fb->count, 1);
253
254	res = fuse_backing_id_alloc(fc, fb);
255	if (res < 0) {
256		fuse_backing_free(fb);
257		fb = NULL;
258	}
259
260out:
261	pr_debug("%s: fb=0x%p, ret=%i\n", __func__, fb, res);
262
263	return res;
264
265out_fput:
266	fput(file);
267	goto out;
268}
269
270int fuse_backing_close(struct fuse_conn *fc, int backing_id)
271{
272	struct fuse_backing *fb = NULL;
273	int err;
274
275	pr_debug("%s: backing_id=%d\n", __func__, backing_id);
276
277	/* TODO: relax CAP_SYS_ADMIN once backing files are visible to lsof */
278	err = -EPERM;
279	if (!fc->passthrough || !capable(CAP_SYS_ADMIN))
280		goto out;
281
282	err = -EINVAL;
283	if (backing_id <= 0)
284		goto out;
285
286	err = -ENOENT;
287	fb = fuse_backing_id_remove(fc, backing_id);
288	if (!fb)
289		goto out;
290
291	fuse_backing_put(fb);
292	err = 0;
293out:
294	pr_debug("%s: fb=0x%p, err=%i\n", __func__, fb, err);
295
296	return err;
297}
298
299/*
300 * Setup passthrough to a backing file.
301 *
302 * Returns an fb object with elevated refcount to be stored in fuse inode.
303 */
304struct fuse_backing *fuse_passthrough_open(struct file *file,
305					   struct inode *inode,
306					   int backing_id)
307{
308	struct fuse_file *ff = file->private_data;
309	struct fuse_conn *fc = ff->fm->fc;
310	struct fuse_backing *fb = NULL;
311	struct file *backing_file;
312	int err;
313
314	err = -EINVAL;
315	if (backing_id <= 0)
316		goto out;
317
318	rcu_read_lock();
319	fb = idr_find(&fc->backing_files_map, backing_id);
320	fb = fuse_backing_get(fb);
321	rcu_read_unlock();
322
323	err = -ENOENT;
324	if (!fb)
325		goto out;
326
327	/* Allocate backing file per fuse file to store fuse path */
328	backing_file = backing_file_open(&file->f_path, file->f_flags,
329					 &fb->file->f_path, fb->cred);
330	err = PTR_ERR(backing_file);
331	if (IS_ERR(backing_file)) {
332		fuse_backing_put(fb);
333		goto out;
334	}
335
336	err = 0;
337	ff->passthrough = backing_file;
338	ff->cred = get_cred(fb->cred);
339out:
340	pr_debug("%s: backing_id=%d, fb=0x%p, backing_file=0x%p, err=%i\n", __func__,
341		 backing_id, fb, ff->passthrough, err);
342
343	return err ? ERR_PTR(err) : fb;
344}
345
346void fuse_passthrough_release(struct fuse_file *ff, struct fuse_backing *fb)
347{
348	pr_debug("%s: fb=0x%p, backing_file=0x%p\n", __func__,
349		 fb, ff->passthrough);
350
351	fput(ff->passthrough);
352	ff->passthrough = NULL;
353	put_cred(ff->cred);
354	ff->cred = NULL;
355}