/* * CUSE servers compiled on 32bit broke on 64bit kernels because the * ABI was defined to be 'struct iovec' which is different on 32bit * and 64bit. Fortunately we can determine which structure the server * used from the size of the reply.
*/ staticint fuse_copy_ioctl_iovec_old(struct iovec *dst, void *src,
size_t transferred, unsigned count, bool is_compat)
{ #ifdef CONFIG_COMPAT if (count * sizeof(struct compat_iovec) == transferred) { struct compat_iovec *ciov = src; unsigned i;
/* * With this interface a 32bit server cannot support * non-compat (i.e. ones coming from 64bit apps) ioctl * requests
*/ if (!is_compat) return -EINVAL;
for (i = 0; i < count; i++) {
dst[i].iov_base = compat_ptr(ciov[i].iov_base);
dst[i].iov_len = ciov[i].iov_len;
} return 0;
} #endif
if (count * sizeof(struct iovec) != transferred) return -EIO;
if (count * sizeof(struct fuse_ioctl_iovec) != transferred) return -EIO;
for (i = 0; i < count; i++) { /* Did the server supply an inappropriate value? */ if (fiov[i].base != (unsignedlong) fiov[i].base ||
fiov[i].len != (unsignedlong) fiov[i].len) return -EIO;
/* * For ioctls, there is no generic way to determine how much memory * needs to be read and/or written. Furthermore, ioctls are allowed * to dereference the passed pointer, so the parameter requires deep * copying but FUSE has no idea whatsoever about what to copy in or * out. * * This is solved by allowing FUSE server to retry ioctl with * necessary in/out iovecs. Let's assume the ioctl implementation * needs to read in the following structure. * * struct a { * char *buf; * size_t buflen; * } * * On the first callout to FUSE server, inarg->in_size and * inarg->out_size will be NULL; then, the server completes the ioctl * with FUSE_IOCTL_RETRY set in out->flags, out->in_iovs set to 1 and * the actual iov array to * * { { .iov_base = inarg.arg, .iov_len = sizeof(struct a) } } * * which tells FUSE to copy in the requested area and retry the ioctl. * On the second round, the server has access to the structure and * from that it can tell what to look for next, so on the invocation, * it sets FUSE_IOCTL_RETRY, out->in_iovs to 2 and iov array to * * { { .iov_base = inarg.arg, .iov_len = sizeof(struct a) }, * { .iov_base = a.buf, .iov_len = a.buflen } } * * FUSE will copy both struct a and the pointed buffer from the * process doing the ioctl and retry ioctl with both struct a and the * buffer. * * This time, FUSE server has everything it needs and completes ioctl * without FUSE_IOCTL_RETRY which finishes the ioctl call. * * Copying data out works the same way. * * Note that if FUSE_IOCTL_UNRESTRICTED is clear, the kernel * automatically initializes in and out iovs by decoding @cmd with * _IOC_* macros and the server is not allowed to request RETRY. This * limits ioctl data transfers to well-formed ioctls and is the forced * behavior for all FUSE servers.
*/ long fuse_do_ioctl(struct file *file, unsignedint cmd, unsignedlong arg, unsignedint flags)
{ struct fuse_file *ff = file->private_data; struct fuse_mount *fm = ff->fm; struct fuse_ioctl_in inarg = {
.fh = ff->fh,
.cmd = cmd,
.arg = arg,
.flags = flags
}; struct fuse_ioctl_out outarg; struct iovec *iov_page = NULL; struct iovec *in_iov = NULL, *out_iov = NULL; unsignedint in_iovs = 0, out_iovs = 0, max_pages;
size_t in_size, out_size, c;
ssize_t transferred; int err, i; struct iov_iter ii; struct fuse_args_pages ap = {};
/* * If restricted, initialize IO parameters as encoded in @cmd. * RETRY from server is not allowed.
*/ if (!(flags & FUSE_IOCTL_UNRESTRICTED)) { struct iovec *iov = iov_page;
/* * Out data can be used either for actual out data or iovs, * make sure there always is at least one page.
*/
out_size = max_t(size_t, out_size, PAGE_SIZE);
max_pages = DIV_ROUND_UP(max(in_size, out_size), PAGE_SIZE);
/* make sure there are enough buffer pages and init request with them */
err = -ENOMEM; if (max_pages > fm->fc->max_pages) goto out; while (ap.num_folios < max_pages) {
ap.folios[ap.num_folios] = folio_alloc(GFP_KERNEL | __GFP_HIGHMEM, 0); if (!ap.folios[ap.num_folios]) goto out;
ap.num_folios++;
}
/* okay, let's send it to the client */
ap.args.opcode = FUSE_IOCTL;
ap.args.nodeid = ff->nodeid;
ap.args.in_numargs = 1;
ap.args.in_args[0].size = sizeof(inarg);
ap.args.in_args[0].value = &inarg; if (in_size) {
ap.args.in_numargs++;
ap.args.in_args[1].size = in_size;
ap.args.in_pages = true;
err = -EFAULT;
iov_iter_init(&ii, ITER_SOURCE, in_iov, in_iovs, in_size); for (i = 0; iov_iter_count(&ii) && !WARN_ON(i >= ap.num_folios); i++) {
c = copy_folio_from_iter(ap.folios[i], 0, PAGE_SIZE, &ii); if (c != PAGE_SIZE && iov_iter_count(&ii)) goto out;
}
}
/* * Make sure things are in boundary, separate checks * are to protect against overflow.
*/
err = -ENOMEM; if (in_iovs > FUSE_IOCTL_MAX_IOV ||
out_iovs > FUSE_IOCTL_MAX_IOV ||
in_iovs + out_iovs > FUSE_IOCTL_MAX_IOV) goto out;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.