]>
git.ipfire.org Git - thirdparty/kernel/linux.git/blob - fs/fuse/passthrough.c
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>
13 struct fuse_backing
*fuse_backing_get(struct fuse_backing
*fb
)
15 if (fb
&& refcount_inc_not_zero(&fb
->count
))
20 static void fuse_backing_free(struct fuse_backing
*fb
)
22 pr_debug("%s: fb=0x%p\n", __func__
, fb
);
30 void fuse_backing_put(struct fuse_backing
*fb
)
32 if (fb
&& refcount_dec_and_test(&fb
->count
))
33 fuse_backing_free(fb
);
36 void fuse_backing_files_init(struct fuse_conn
*fc
)
38 idr_init(&fc
->backing_files_map
);
41 static int fuse_backing_id_alloc(struct fuse_conn
*fc
, struct fuse_backing
*fb
)
45 idr_preload(GFP_KERNEL
);
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
);
52 WARN_ON_ONCE(id
== 0);
56 static struct fuse_backing
*fuse_backing_id_remove(struct fuse_conn
*fc
,
59 struct fuse_backing
*fb
;
62 fb
= idr_remove(&fc
->backing_files_map
, id
);
63 spin_unlock(&fc
->lock
);
68 static int fuse_backing_id_free(int id
, void *p
, void *data
)
70 struct fuse_backing
*fb
= p
;
72 WARN_ON_ONCE(refcount_read(&fb
->count
) != 1);
73 fuse_backing_free(fb
);
77 void fuse_backing_files_free(struct fuse_conn
*fc
)
79 idr_for_each(&fc
->backing_files_map
, fuse_backing_id_free
, NULL
);
80 idr_destroy(&fc
->backing_files_map
);
83 int fuse_backing_open(struct fuse_conn
*fc
, struct fuse_backing_map
*map
)
86 struct super_block
*backing_sb
;
87 struct fuse_backing
*fb
= NULL
;
90 pr_debug("%s: fd=%d flags=0x%x\n", __func__
, map
->fd
, map
->flags
);
92 /* TODO: relax CAP_SYS_ADMIN once backing files are visible to lsof */
94 if (!fc
->passthrough
|| !capable(CAP_SYS_ADMIN
))
101 file
= fget(map
->fd
);
107 if (!file
->f_op
->read_iter
|| !file
->f_op
->write_iter
)
110 backing_sb
= file_inode(file
)->i_sb
;
112 if (backing_sb
->s_stack_depth
>= fc
->max_stack_depth
)
115 fb
= kmalloc(sizeof(struct fuse_backing
), GFP_KERNEL
);
121 fb
->cred
= prepare_creds();
122 refcount_set(&fb
->count
, 1);
124 res
= fuse_backing_id_alloc(fc
, fb
);
126 fuse_backing_free(fb
);
131 pr_debug("%s: fb=0x%p, ret=%i\n", __func__
, fb
, res
);
140 int fuse_backing_close(struct fuse_conn
*fc
, int backing_id
)
142 struct fuse_backing
*fb
= NULL
;
145 pr_debug("%s: backing_id=%d\n", __func__
, backing_id
);
147 /* TODO: relax CAP_SYS_ADMIN once backing files are visible to lsof */
149 if (!fc
->passthrough
|| !capable(CAP_SYS_ADMIN
))
157 fb
= fuse_backing_id_remove(fc
, backing_id
);
161 fuse_backing_put(fb
);
164 pr_debug("%s: fb=0x%p, err=%i\n", __func__
, fb
, err
);
170 * Setup passthrough to a backing file.
172 * Returns an fb object with elevated refcount to be stored in fuse inode.
174 struct fuse_backing
*fuse_passthrough_open(struct file
*file
,
178 struct fuse_file
*ff
= file
->private_data
;
179 struct fuse_conn
*fc
= ff
->fm
->fc
;
180 struct fuse_backing
*fb
= NULL
;
181 struct file
*backing_file
;
189 fb
= idr_find(&fc
->backing_files_map
, backing_id
);
190 fb
= fuse_backing_get(fb
);
197 /* Allocate backing file per fuse file to store fuse path */
198 backing_file
= backing_file_open(&file
->f_path
, file
->f_flags
,
199 &fb
->file
->f_path
, fb
->cred
);
200 err
= PTR_ERR(backing_file
);
201 if (IS_ERR(backing_file
)) {
202 fuse_backing_put(fb
);
207 ff
->passthrough
= backing_file
;
208 ff
->cred
= get_cred(fb
->cred
);
210 pr_debug("%s: backing_id=%d, fb=0x%p, backing_file=0x%p, err=%i\n", __func__
,
211 backing_id
, fb
, ff
->passthrough
, err
);
213 return err
? ERR_PTR(err
) : fb
;
216 void fuse_passthrough_release(struct fuse_file
*ff
, struct fuse_backing
*fb
)
218 pr_debug("%s: fb=0x%p, backing_file=0x%p\n", __func__
,
219 fb
, ff
->passthrough
);
221 fput(ff
->passthrough
);
222 ff
->passthrough
= NULL
;