xref: /OK3568_Linux_fs/kernel/fs/fuse/passthrough.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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