File Index
Files and directories
Sign in
FILE

backing.c

File preview with syntax highlighting.

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