1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun
3*4882a593Smuzhiyun #include "fuse_i.h"
4*4882a593Smuzhiyun
5*4882a593Smuzhiyun #include <linux/fuse.h>
6*4882a593Smuzhiyun #include <linux/idr.h>
7*4882a593Smuzhiyun #include <linux/uio.h>
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #define PASSTHROUGH_IOCB_MASK \
10*4882a593Smuzhiyun (IOCB_APPEND | IOCB_DSYNC | IOCB_HIPRI | IOCB_NOWAIT | IOCB_SYNC)
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun struct fuse_aio_req {
13*4882a593Smuzhiyun struct kiocb iocb;
14*4882a593Smuzhiyun struct kiocb *iocb_fuse;
15*4882a593Smuzhiyun };
16*4882a593Smuzhiyun
fuse_file_accessed(struct file * dst_file,struct file * src_file)17*4882a593Smuzhiyun static void fuse_file_accessed(struct file *dst_file, struct file *src_file)
18*4882a593Smuzhiyun {
19*4882a593Smuzhiyun struct inode *dst_inode;
20*4882a593Smuzhiyun struct inode *src_inode;
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun if (dst_file->f_flags & O_NOATIME)
23*4882a593Smuzhiyun return;
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun dst_inode = file_inode(dst_file);
26*4882a593Smuzhiyun src_inode = file_inode(src_file);
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun if ((!timespec64_equal(&dst_inode->i_mtime, &src_inode->i_mtime) ||
29*4882a593Smuzhiyun !timespec64_equal(&dst_inode->i_ctime, &src_inode->i_ctime))) {
30*4882a593Smuzhiyun dst_inode->i_mtime = src_inode->i_mtime;
31*4882a593Smuzhiyun dst_inode->i_ctime = src_inode->i_ctime;
32*4882a593Smuzhiyun }
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun touch_atime(&dst_file->f_path);
35*4882a593Smuzhiyun }
36*4882a593Smuzhiyun
fuse_copyattr(struct file * dst_file,struct file * src_file)37*4882a593Smuzhiyun static void fuse_copyattr(struct file *dst_file, struct file *src_file)
38*4882a593Smuzhiyun {
39*4882a593Smuzhiyun struct inode *dst = file_inode(dst_file);
40*4882a593Smuzhiyun struct inode *src = file_inode(src_file);
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun dst->i_atime = src->i_atime;
43*4882a593Smuzhiyun dst->i_mtime = src->i_mtime;
44*4882a593Smuzhiyun dst->i_ctime = src->i_ctime;
45*4882a593Smuzhiyun i_size_write(dst, i_size_read(src));
46*4882a593Smuzhiyun }
47*4882a593Smuzhiyun
fuse_aio_cleanup_handler(struct fuse_aio_req * aio_req)48*4882a593Smuzhiyun static void fuse_aio_cleanup_handler(struct fuse_aio_req *aio_req)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun struct kiocb *iocb = &aio_req->iocb;
51*4882a593Smuzhiyun struct kiocb *iocb_fuse = aio_req->iocb_fuse;
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun if (iocb->ki_flags & IOCB_WRITE) {
54*4882a593Smuzhiyun __sb_writers_acquired(file_inode(iocb->ki_filp)->i_sb,
55*4882a593Smuzhiyun SB_FREEZE_WRITE);
56*4882a593Smuzhiyun file_end_write(iocb->ki_filp);
57*4882a593Smuzhiyun fuse_copyattr(iocb_fuse->ki_filp, iocb->ki_filp);
58*4882a593Smuzhiyun }
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun iocb_fuse->ki_pos = iocb->ki_pos;
61*4882a593Smuzhiyun kfree(aio_req);
62*4882a593Smuzhiyun }
63*4882a593Smuzhiyun
fuse_aio_rw_complete(struct kiocb * iocb,long res,long res2)64*4882a593Smuzhiyun static void fuse_aio_rw_complete(struct kiocb *iocb, long res, long res2)
65*4882a593Smuzhiyun {
66*4882a593Smuzhiyun struct fuse_aio_req *aio_req =
67*4882a593Smuzhiyun container_of(iocb, struct fuse_aio_req, iocb);
68*4882a593Smuzhiyun struct kiocb *iocb_fuse = aio_req->iocb_fuse;
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun fuse_aio_cleanup_handler(aio_req);
71*4882a593Smuzhiyun iocb_fuse->ki_complete(iocb_fuse, res, res2);
72*4882a593Smuzhiyun }
73*4882a593Smuzhiyun
fuse_passthrough_read_iter(struct kiocb * iocb_fuse,struct iov_iter * iter)74*4882a593Smuzhiyun ssize_t fuse_passthrough_read_iter(struct kiocb *iocb_fuse,
75*4882a593Smuzhiyun struct iov_iter *iter)
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun ssize_t ret;
78*4882a593Smuzhiyun const struct cred *old_cred;
79*4882a593Smuzhiyun struct file *fuse_filp = iocb_fuse->ki_filp;
80*4882a593Smuzhiyun struct fuse_file *ff = fuse_filp->private_data;
81*4882a593Smuzhiyun struct file *passthrough_filp = ff->passthrough.filp;
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun if (!iov_iter_count(iter))
84*4882a593Smuzhiyun return 0;
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun old_cred = override_creds(ff->passthrough.cred);
87*4882a593Smuzhiyun if (is_sync_kiocb(iocb_fuse)) {
88*4882a593Smuzhiyun ret = vfs_iter_read(passthrough_filp, iter, &iocb_fuse->ki_pos,
89*4882a593Smuzhiyun iocb_to_rw_flags(iocb_fuse->ki_flags,
90*4882a593Smuzhiyun PASSTHROUGH_IOCB_MASK));
91*4882a593Smuzhiyun } else {
92*4882a593Smuzhiyun struct fuse_aio_req *aio_req;
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun aio_req = kmalloc(sizeof(struct fuse_aio_req), GFP_KERNEL);
95*4882a593Smuzhiyun if (!aio_req) {
96*4882a593Smuzhiyun ret = -ENOMEM;
97*4882a593Smuzhiyun goto out;
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun aio_req->iocb_fuse = iocb_fuse;
101*4882a593Smuzhiyun kiocb_clone(&aio_req->iocb, iocb_fuse, passthrough_filp);
102*4882a593Smuzhiyun aio_req->iocb.ki_complete = fuse_aio_rw_complete;
103*4882a593Smuzhiyun ret = call_read_iter(passthrough_filp, &aio_req->iocb, iter);
104*4882a593Smuzhiyun if (ret != -EIOCBQUEUED)
105*4882a593Smuzhiyun fuse_aio_cleanup_handler(aio_req);
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun out:
108*4882a593Smuzhiyun revert_creds(old_cred);
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun fuse_file_accessed(fuse_filp, passthrough_filp);
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun return ret;
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun
fuse_passthrough_write_iter(struct kiocb * iocb_fuse,struct iov_iter * iter)115*4882a593Smuzhiyun ssize_t fuse_passthrough_write_iter(struct kiocb *iocb_fuse,
116*4882a593Smuzhiyun struct iov_iter *iter)
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun ssize_t ret;
119*4882a593Smuzhiyun const struct cred *old_cred;
120*4882a593Smuzhiyun struct file *fuse_filp = iocb_fuse->ki_filp;
121*4882a593Smuzhiyun struct fuse_file *ff = fuse_filp->private_data;
122*4882a593Smuzhiyun struct inode *fuse_inode = file_inode(fuse_filp);
123*4882a593Smuzhiyun struct file *passthrough_filp = ff->passthrough.filp;
124*4882a593Smuzhiyun struct inode *passthrough_inode = file_inode(passthrough_filp);
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun if (!iov_iter_count(iter))
127*4882a593Smuzhiyun return 0;
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun inode_lock(fuse_inode);
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun fuse_copyattr(fuse_filp, passthrough_filp);
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun old_cred = override_creds(ff->passthrough.cred);
134*4882a593Smuzhiyun if (is_sync_kiocb(iocb_fuse)) {
135*4882a593Smuzhiyun file_start_write(passthrough_filp);
136*4882a593Smuzhiyun ret = vfs_iter_write(passthrough_filp, iter, &iocb_fuse->ki_pos,
137*4882a593Smuzhiyun iocb_to_rw_flags(iocb_fuse->ki_flags,
138*4882a593Smuzhiyun PASSTHROUGH_IOCB_MASK));
139*4882a593Smuzhiyun file_end_write(passthrough_filp);
140*4882a593Smuzhiyun if (ret > 0)
141*4882a593Smuzhiyun fuse_copyattr(fuse_filp, passthrough_filp);
142*4882a593Smuzhiyun } else {
143*4882a593Smuzhiyun struct fuse_aio_req *aio_req;
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun aio_req = kmalloc(sizeof(struct fuse_aio_req), GFP_KERNEL);
146*4882a593Smuzhiyun if (!aio_req) {
147*4882a593Smuzhiyun ret = -ENOMEM;
148*4882a593Smuzhiyun goto out;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun file_start_write(passthrough_filp);
152*4882a593Smuzhiyun __sb_writers_release(passthrough_inode->i_sb, SB_FREEZE_WRITE);
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun aio_req->iocb_fuse = iocb_fuse;
155*4882a593Smuzhiyun kiocb_clone(&aio_req->iocb, iocb_fuse, passthrough_filp);
156*4882a593Smuzhiyun aio_req->iocb.ki_complete = fuse_aio_rw_complete;
157*4882a593Smuzhiyun ret = call_write_iter(passthrough_filp, &aio_req->iocb, iter);
158*4882a593Smuzhiyun if (ret != -EIOCBQUEUED)
159*4882a593Smuzhiyun fuse_aio_cleanup_handler(aio_req);
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun out:
162*4882a593Smuzhiyun revert_creds(old_cred);
163*4882a593Smuzhiyun inode_unlock(fuse_inode);
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun return ret;
166*4882a593Smuzhiyun }
167*4882a593Smuzhiyun
fuse_passthrough_mmap(struct file * file,struct vm_area_struct * vma)168*4882a593Smuzhiyun ssize_t fuse_passthrough_mmap(struct file *file, struct vm_area_struct *vma)
169*4882a593Smuzhiyun {
170*4882a593Smuzhiyun int ret;
171*4882a593Smuzhiyun const struct cred *old_cred;
172*4882a593Smuzhiyun struct fuse_file *ff = file->private_data;
173*4882a593Smuzhiyun struct file *passthrough_filp = ff->passthrough.filp;
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun if (!passthrough_filp->f_op->mmap)
176*4882a593Smuzhiyun return -ENODEV;
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun if (WARN_ON(file != vma->vm_file))
179*4882a593Smuzhiyun return -EIO;
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun vma->vm_file = get_file(passthrough_filp);
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun old_cred = override_creds(ff->passthrough.cred);
184*4882a593Smuzhiyun ret = call_mmap(vma->vm_file, vma);
185*4882a593Smuzhiyun revert_creds(old_cred);
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun if (ret)
188*4882a593Smuzhiyun fput(passthrough_filp);
189*4882a593Smuzhiyun else
190*4882a593Smuzhiyun fput(file);
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun fuse_file_accessed(file, passthrough_filp);
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun return ret;
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun
fuse_passthrough_open(struct fuse_dev * fud,u32 lower_fd)197*4882a593Smuzhiyun int fuse_passthrough_open(struct fuse_dev *fud, u32 lower_fd)
198*4882a593Smuzhiyun {
199*4882a593Smuzhiyun int res;
200*4882a593Smuzhiyun struct file *passthrough_filp;
201*4882a593Smuzhiyun struct fuse_conn *fc = fud->fc;
202*4882a593Smuzhiyun struct inode *passthrough_inode;
203*4882a593Smuzhiyun struct super_block *passthrough_sb;
204*4882a593Smuzhiyun struct fuse_passthrough *passthrough;
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun if (!fc->passthrough)
207*4882a593Smuzhiyun return -EPERM;
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun passthrough_filp = fget(lower_fd);
210*4882a593Smuzhiyun if (!passthrough_filp) {
211*4882a593Smuzhiyun pr_err("FUSE: invalid file descriptor for passthrough.\n");
212*4882a593Smuzhiyun return -EBADF;
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun if (!passthrough_filp->f_op->read_iter ||
216*4882a593Smuzhiyun !passthrough_filp->f_op->write_iter) {
217*4882a593Smuzhiyun pr_err("FUSE: passthrough file misses file operations.\n");
218*4882a593Smuzhiyun res = -EBADF;
219*4882a593Smuzhiyun goto err_free_file;
220*4882a593Smuzhiyun }
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun passthrough_inode = file_inode(passthrough_filp);
223*4882a593Smuzhiyun passthrough_sb = passthrough_inode->i_sb;
224*4882a593Smuzhiyun if (passthrough_sb->s_stack_depth >= FILESYSTEM_MAX_STACK_DEPTH) {
225*4882a593Smuzhiyun pr_err("FUSE: fs stacking depth exceeded for passthrough\n");
226*4882a593Smuzhiyun res = -EINVAL;
227*4882a593Smuzhiyun goto err_free_file;
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun passthrough = kmalloc(sizeof(struct fuse_passthrough), GFP_KERNEL);
231*4882a593Smuzhiyun if (!passthrough) {
232*4882a593Smuzhiyun res = -ENOMEM;
233*4882a593Smuzhiyun goto err_free_file;
234*4882a593Smuzhiyun }
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun passthrough->filp = passthrough_filp;
237*4882a593Smuzhiyun passthrough->cred = prepare_creds();
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun idr_preload(GFP_KERNEL);
240*4882a593Smuzhiyun spin_lock(&fc->passthrough_req_lock);
241*4882a593Smuzhiyun res = idr_alloc(&fc->passthrough_req, passthrough, 1, 0, GFP_ATOMIC);
242*4882a593Smuzhiyun spin_unlock(&fc->passthrough_req_lock);
243*4882a593Smuzhiyun idr_preload_end();
244*4882a593Smuzhiyun
245*4882a593Smuzhiyun if (res > 0)
246*4882a593Smuzhiyun return res;
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun fuse_passthrough_release(passthrough);
249*4882a593Smuzhiyun kfree(passthrough);
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun err_free_file:
252*4882a593Smuzhiyun fput(passthrough_filp);
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun return res;
255*4882a593Smuzhiyun }
256*4882a593Smuzhiyun
fuse_passthrough_setup(struct fuse_conn * fc,struct fuse_file * ff,struct fuse_open_out * openarg)257*4882a593Smuzhiyun int fuse_passthrough_setup(struct fuse_conn *fc, struct fuse_file *ff,
258*4882a593Smuzhiyun struct fuse_open_out *openarg)
259*4882a593Smuzhiyun {
260*4882a593Smuzhiyun struct fuse_passthrough *passthrough;
261*4882a593Smuzhiyun int passthrough_fh = openarg->passthrough_fh;
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun if (!fc->passthrough)
264*4882a593Smuzhiyun return -EPERM;
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun /* Default case, passthrough is not requested */
267*4882a593Smuzhiyun if (passthrough_fh <= 0)
268*4882a593Smuzhiyun return -EINVAL;
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun spin_lock(&fc->passthrough_req_lock);
271*4882a593Smuzhiyun passthrough = idr_remove(&fc->passthrough_req, passthrough_fh);
272*4882a593Smuzhiyun spin_unlock(&fc->passthrough_req_lock);
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun if (!passthrough)
275*4882a593Smuzhiyun return -EINVAL;
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun ff->passthrough = *passthrough;
278*4882a593Smuzhiyun kfree(passthrough);
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun return 0;
281*4882a593Smuzhiyun }
282*4882a593Smuzhiyun
fuse_passthrough_release(struct fuse_passthrough * passthrough)283*4882a593Smuzhiyun void fuse_passthrough_release(struct fuse_passthrough *passthrough)
284*4882a593Smuzhiyun {
285*4882a593Smuzhiyun if (passthrough->filp) {
286*4882a593Smuzhiyun fput(passthrough->filp);
287*4882a593Smuzhiyun passthrough->filp = NULL;
288*4882a593Smuzhiyun }
289*4882a593Smuzhiyun if (passthrough->cred) {
290*4882a593Smuzhiyun put_cred(passthrough->cred);
291*4882a593Smuzhiyun passthrough->cred = NULL;
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun }
294