Blame view
fs/fuse/passthrough.c
6.04 KB
314603f83 FROMLIST: fuse: D... |
1 2 3 4 5 |
// SPDX-License-Identifier: GPL-2.0 #include "fuse_i.h" #include <linux/fuse.h> |
89d676706 FROMLIST: fuse: P... |
6 |
#include <linux/idr.h> |
f32d3e5eb FROMLIST: fuse: I... |
7 |
#include <linux/uio.h> |
aa525f2e9 FROMLIST: fuse: H... |
8 9 10 11 |
struct fuse_aio_req { struct kiocb iocb; struct kiocb *iocb_fuse; }; |
f32d3e5eb FROMLIST: fuse: I... |
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
static void fuse_copyattr(struct file *dst_file, struct file *src_file) { struct inode *dst = file_inode(dst_file); struct inode *src = file_inode(src_file); i_size_write(dst, i_size_read(src)); } static inline rwf_t iocb_to_rw_flags(int ifl) { rwf_t flags = 0; if (ifl & IOCB_APPEND) flags |= RWF_APPEND; if (ifl & IOCB_DSYNC) flags |= RWF_DSYNC; if (ifl & IOCB_HIPRI) flags |= RWF_HIPRI; if (ifl & IOCB_NOWAIT) flags |= RWF_NOWAIT; if (ifl & IOCB_SYNC) flags |= RWF_SYNC; return flags; } |
aa525f2e9 FROMLIST: fuse: H... |
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
static void fuse_aio_cleanup_handler(struct fuse_aio_req *aio_req) { struct kiocb *iocb = &aio_req->iocb; struct kiocb *iocb_fuse = aio_req->iocb_fuse; if (iocb->ki_flags & IOCB_WRITE) { __sb_writers_acquired(file_inode(iocb->ki_filp)->i_sb, SB_FREEZE_WRITE); file_end_write(iocb->ki_filp); fuse_copyattr(iocb_fuse->ki_filp, iocb->ki_filp); } iocb_fuse->ki_pos = iocb->ki_pos; kfree(aio_req); } static void fuse_aio_rw_complete(struct kiocb *iocb, long res, long res2) { struct fuse_aio_req *aio_req = container_of(iocb, struct fuse_aio_req, iocb); struct kiocb *iocb_fuse = aio_req->iocb_fuse; fuse_aio_cleanup_handler(aio_req); iocb_fuse->ki_complete(iocb_fuse, res, res2); } |
f32d3e5eb FROMLIST: fuse: I... |
62 63 64 65 |
ssize_t fuse_passthrough_read_iter(struct kiocb *iocb_fuse, struct iov_iter *iter) { ssize_t ret; |
9ee350d7e FROMLIST: fuse: U... |
66 |
const struct cred *old_cred; |
f32d3e5eb FROMLIST: fuse: I... |
67 68 69 70 71 72 |
struct file *fuse_filp = iocb_fuse->ki_filp; struct fuse_file *ff = fuse_filp->private_data; struct file *passthrough_filp = ff->passthrough.filp; if (!iov_iter_count(iter)) return 0; |
9ee350d7e FROMLIST: fuse: U... |
73 |
old_cred = override_creds(ff->passthrough.cred); |
aa525f2e9 FROMLIST: fuse: H... |
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
if (is_sync_kiocb(iocb_fuse)) { ret = vfs_iter_read(passthrough_filp, iter, &iocb_fuse->ki_pos, iocb_to_rw_flags(iocb_fuse->ki_flags)); } else { struct fuse_aio_req *aio_req; aio_req = kmalloc(sizeof(struct fuse_aio_req), GFP_KERNEL); if (!aio_req) return -ENOMEM; aio_req->iocb_fuse = iocb_fuse; kiocb_clone(&aio_req->iocb, iocb_fuse, passthrough_filp); aio_req->iocb.ki_complete = fuse_aio_rw_complete; ret = call_read_iter(passthrough_filp, &aio_req->iocb, iter); if (ret != -EIOCBQUEUED) fuse_aio_cleanup_handler(aio_req); } |
9ee350d7e FROMLIST: fuse: U... |
91 |
revert_creds(old_cred); |
f32d3e5eb FROMLIST: fuse: I... |
92 93 94 95 96 97 98 99 |
return ret; } ssize_t fuse_passthrough_write_iter(struct kiocb *iocb_fuse, struct iov_iter *iter) { ssize_t ret; |
9ee350d7e FROMLIST: fuse: U... |
100 |
const struct cred *old_cred; |
f32d3e5eb FROMLIST: fuse: I... |
101 102 103 104 |
struct file *fuse_filp = iocb_fuse->ki_filp; struct fuse_file *ff = fuse_filp->private_data; struct inode *fuse_inode = file_inode(fuse_filp); struct file *passthrough_filp = ff->passthrough.filp; |
aa525f2e9 FROMLIST: fuse: H... |
105 |
struct inode *passthrough_inode = file_inode(passthrough_filp); |
f32d3e5eb FROMLIST: fuse: I... |
106 107 108 109 110 |
if (!iov_iter_count(iter)) return 0; inode_lock(fuse_inode); |
9ee350d7e FROMLIST: fuse: U... |
111 |
old_cred = override_creds(ff->passthrough.cred); |
aa525f2e9 FROMLIST: fuse: H... |
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
if (is_sync_kiocb(iocb_fuse)) { file_start_write(passthrough_filp); ret = vfs_iter_write(passthrough_filp, iter, &iocb_fuse->ki_pos, iocb_to_rw_flags(iocb_fuse->ki_flags)); file_end_write(passthrough_filp); if (ret > 0) fuse_copyattr(fuse_filp, passthrough_filp); } else { struct fuse_aio_req *aio_req; aio_req = kmalloc(sizeof(struct fuse_aio_req), GFP_KERNEL); if (!aio_req) { ret = -ENOMEM; goto out; } file_start_write(passthrough_filp); __sb_writers_release(passthrough_inode->i_sb, SB_FREEZE_WRITE); aio_req->iocb_fuse = iocb_fuse; kiocb_clone(&aio_req->iocb, iocb_fuse, passthrough_filp); aio_req->iocb.ki_complete = fuse_aio_rw_complete; ret = call_write_iter(passthrough_filp, &aio_req->iocb, iter); if (ret != -EIOCBQUEUED) fuse_aio_cleanup_handler(aio_req); } out: |
9ee350d7e FROMLIST: fuse: U... |
139 |
revert_creds(old_cred); |
f32d3e5eb FROMLIST: fuse: I... |
140 141 142 143 |
inode_unlock(fuse_inode); return ret; } |
314603f83 FROMLIST: fuse: D... |
144 145 146 147 |
int fuse_passthrough_open(struct fuse_dev *fud, struct fuse_passthrough_out *pto) { |
89d676706 FROMLIST: fuse: P... |
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
int res; struct file *passthrough_filp; struct fuse_conn *fc = fud->fc; struct fuse_passthrough *passthrough; if (!fc->passthrough) return -EPERM; /* This field is reserved for future implementation */ if (pto->len != 0) return -EINVAL; passthrough_filp = fget(pto->fd); if (!passthrough_filp) { pr_err("FUSE: invalid file descriptor for passthrough. "); return -EBADF; } if (!passthrough_filp->f_op->read_iter || !passthrough_filp->f_op->write_iter) { pr_err("FUSE: passthrough file misses file operations. "); return -EBADF; } passthrough = kmalloc(sizeof(struct fuse_passthrough), GFP_KERNEL); if (!passthrough) return -ENOMEM; passthrough->filp = passthrough_filp; |
9ee350d7e FROMLIST: fuse: U... |
179 |
passthrough->cred = prepare_creds(); |
89d676706 FROMLIST: fuse: P... |
180 181 182 183 184 185 186 187 188 189 190 191 |
idr_preload(GFP_KERNEL); spin_lock(&fc->passthrough_req_lock); res = idr_alloc(&fc->passthrough_req, passthrough, 1, 0, GFP_ATOMIC); spin_unlock(&fc->passthrough_req_lock); idr_preload_end(); if (res <= 0) { fuse_passthrough_release(passthrough); kfree(passthrough); } return res; |
314603f83 FROMLIST: fuse: D... |
192 193 194 195 196 |
} int fuse_passthrough_setup(struct fuse_conn *fc, struct fuse_file *ff, struct fuse_open_out *openarg) { |
89d676706 FROMLIST: fuse: P... |
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
struct inode *passthrough_inode; struct super_block *passthrough_sb; struct fuse_passthrough *passthrough; int passthrough_fh = openarg->passthrough_fh; if (!fc->passthrough) return -EPERM; /* Default case, passthrough is not requested */ if (passthrough_fh <= 0) return -EINVAL; spin_lock(&fc->passthrough_req_lock); passthrough = idr_remove(&fc->passthrough_req, passthrough_fh); spin_unlock(&fc->passthrough_req_lock); if (!passthrough) return -EINVAL; passthrough_inode = file_inode(passthrough->filp); passthrough_sb = passthrough_inode->i_sb; if (passthrough_sb->s_stack_depth >= FILESYSTEM_MAX_STACK_DEPTH) { pr_err("FUSE: fs stacking depth exceeded for passthrough "); fuse_passthrough_release(passthrough); kfree(passthrough); return -EINVAL; } ff->passthrough = *passthrough; kfree(passthrough); return 0; |
314603f83 FROMLIST: fuse: D... |
230 231 232 233 |
} void fuse_passthrough_release(struct fuse_passthrough *passthrough) { |
89d676706 FROMLIST: fuse: P... |
234 235 236 237 |
if (passthrough->filp) { fput(passthrough->filp); passthrough->filp = NULL; } |
9ee350d7e FROMLIST: fuse: U... |
238 239 240 241 |
if (passthrough->cred) { put_cred(passthrough->cred); passthrough->cred = NULL; } |
314603f83 FROMLIST: fuse: D... |
242 |
} |