16881f68fSopenharmony_ci/* 26881f68fSopenharmony_ci CUSE: Character device in Userspace 36881f68fSopenharmony_ci Copyright (C) 2008 SUSE Linux Products GmbH 46881f68fSopenharmony_ci Copyright (C) 2008 Tejun Heo <teheo@suse.de> 56881f68fSopenharmony_ci 66881f68fSopenharmony_ci This program can be distributed under the terms of the GNU LGPLv2. 76881f68fSopenharmony_ci See the file COPYING.LIB. 86881f68fSopenharmony_ci*/ 96881f68fSopenharmony_ci 106881f68fSopenharmony_ci#include "fuse_config.h" 116881f68fSopenharmony_ci#include "cuse_lowlevel.h" 126881f68fSopenharmony_ci#include "fuse_kernel.h" 136881f68fSopenharmony_ci#include "fuse_i.h" 146881f68fSopenharmony_ci#include "fuse_opt.h" 156881f68fSopenharmony_ci 166881f68fSopenharmony_ci#include <stdio.h> 176881f68fSopenharmony_ci#include <string.h> 186881f68fSopenharmony_ci#include <stdlib.h> 196881f68fSopenharmony_ci#include <stddef.h> 206881f68fSopenharmony_ci#include <errno.h> 216881f68fSopenharmony_ci#include <unistd.h> 226881f68fSopenharmony_ci 236881f68fSopenharmony_cistruct cuse_data { 246881f68fSopenharmony_ci struct cuse_lowlevel_ops clop; 256881f68fSopenharmony_ci unsigned max_read; 266881f68fSopenharmony_ci unsigned dev_major; 276881f68fSopenharmony_ci unsigned dev_minor; 286881f68fSopenharmony_ci unsigned flags; 296881f68fSopenharmony_ci unsigned dev_info_len; 306881f68fSopenharmony_ci char dev_info[]; 316881f68fSopenharmony_ci}; 326881f68fSopenharmony_ci 336881f68fSopenharmony_cistatic struct cuse_lowlevel_ops *req_clop(fuse_req_t req) 346881f68fSopenharmony_ci{ 356881f68fSopenharmony_ci return &req->se->cuse_data->clop; 366881f68fSopenharmony_ci} 376881f68fSopenharmony_ci 386881f68fSopenharmony_cistatic void cuse_fll_open(fuse_req_t req, fuse_ino_t ino, 396881f68fSopenharmony_ci struct fuse_file_info *fi) 406881f68fSopenharmony_ci{ 416881f68fSopenharmony_ci (void)ino; 426881f68fSopenharmony_ci req_clop(req)->open(req, fi); 436881f68fSopenharmony_ci} 446881f68fSopenharmony_ci 456881f68fSopenharmony_cistatic void cuse_fll_read(fuse_req_t req, fuse_ino_t ino, size_t size, 466881f68fSopenharmony_ci off_t off, struct fuse_file_info *fi) 476881f68fSopenharmony_ci{ 486881f68fSopenharmony_ci (void)ino; 496881f68fSopenharmony_ci req_clop(req)->read(req, size, off, fi); 506881f68fSopenharmony_ci} 516881f68fSopenharmony_ci 526881f68fSopenharmony_cistatic void cuse_fll_write(fuse_req_t req, fuse_ino_t ino, const char *buf, 536881f68fSopenharmony_ci size_t size, off_t off, struct fuse_file_info *fi) 546881f68fSopenharmony_ci{ 556881f68fSopenharmony_ci (void)ino; 566881f68fSopenharmony_ci req_clop(req)->write(req, buf, size, off, fi); 576881f68fSopenharmony_ci} 586881f68fSopenharmony_ci 596881f68fSopenharmony_cistatic void cuse_fll_flush(fuse_req_t req, fuse_ino_t ino, 606881f68fSopenharmony_ci struct fuse_file_info *fi) 616881f68fSopenharmony_ci{ 626881f68fSopenharmony_ci (void)ino; 636881f68fSopenharmony_ci req_clop(req)->flush(req, fi); 646881f68fSopenharmony_ci} 656881f68fSopenharmony_ci 666881f68fSopenharmony_cistatic void cuse_fll_release(fuse_req_t req, fuse_ino_t ino, 676881f68fSopenharmony_ci struct fuse_file_info *fi) 686881f68fSopenharmony_ci{ 696881f68fSopenharmony_ci (void)ino; 706881f68fSopenharmony_ci req_clop(req)->release(req, fi); 716881f68fSopenharmony_ci} 726881f68fSopenharmony_ci 736881f68fSopenharmony_cistatic void cuse_fll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, 746881f68fSopenharmony_ci struct fuse_file_info *fi) 756881f68fSopenharmony_ci{ 766881f68fSopenharmony_ci (void)ino; 776881f68fSopenharmony_ci req_clop(req)->fsync(req, datasync, fi); 786881f68fSopenharmony_ci} 796881f68fSopenharmony_ci 806881f68fSopenharmony_cistatic void cuse_fll_ioctl(fuse_req_t req, fuse_ino_t ino, unsigned int cmd, void *arg, 816881f68fSopenharmony_ci struct fuse_file_info *fi, unsigned int flags, 826881f68fSopenharmony_ci const void *in_buf, size_t in_bufsz, size_t out_bufsz) 836881f68fSopenharmony_ci{ 846881f68fSopenharmony_ci (void)ino; 856881f68fSopenharmony_ci req_clop(req)->ioctl(req, cmd, arg, fi, flags, in_buf, in_bufsz, 866881f68fSopenharmony_ci out_bufsz); 876881f68fSopenharmony_ci} 886881f68fSopenharmony_ci 896881f68fSopenharmony_cistatic void cuse_fll_poll(fuse_req_t req, fuse_ino_t ino, 906881f68fSopenharmony_ci struct fuse_file_info *fi, struct fuse_pollhandle *ph) 916881f68fSopenharmony_ci{ 926881f68fSopenharmony_ci (void)ino; 936881f68fSopenharmony_ci req_clop(req)->poll(req, fi, ph); 946881f68fSopenharmony_ci} 956881f68fSopenharmony_ci 966881f68fSopenharmony_cistatic size_t cuse_pack_info(int argc, const char **argv, char *buf) 976881f68fSopenharmony_ci{ 986881f68fSopenharmony_ci size_t size = 0; 996881f68fSopenharmony_ci int i; 1006881f68fSopenharmony_ci 1016881f68fSopenharmony_ci for (i = 0; i < argc; i++) { 1026881f68fSopenharmony_ci size_t len; 1036881f68fSopenharmony_ci 1046881f68fSopenharmony_ci len = strlen(argv[i]) + 1; 1056881f68fSopenharmony_ci size += len; 1066881f68fSopenharmony_ci if (buf) { 1076881f68fSopenharmony_ci memcpy(buf, argv[i], len); 1086881f68fSopenharmony_ci buf += len; 1096881f68fSopenharmony_ci } 1106881f68fSopenharmony_ci } 1116881f68fSopenharmony_ci 1126881f68fSopenharmony_ci return size; 1136881f68fSopenharmony_ci} 1146881f68fSopenharmony_ci 1156881f68fSopenharmony_cistatic struct cuse_data *cuse_prep_data(const struct cuse_info *ci, 1166881f68fSopenharmony_ci const struct cuse_lowlevel_ops *clop) 1176881f68fSopenharmony_ci{ 1186881f68fSopenharmony_ci struct cuse_data *cd; 1196881f68fSopenharmony_ci size_t dev_info_len; 1206881f68fSopenharmony_ci 1216881f68fSopenharmony_ci dev_info_len = cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv, 1226881f68fSopenharmony_ci NULL); 1236881f68fSopenharmony_ci 1246881f68fSopenharmony_ci if (dev_info_len > CUSE_INIT_INFO_MAX) { 1256881f68fSopenharmony_ci fuse_log(FUSE_LOG_ERR, "cuse: dev_info (%zu) too large, limit=%u\n", 1266881f68fSopenharmony_ci dev_info_len, CUSE_INIT_INFO_MAX); 1276881f68fSopenharmony_ci return NULL; 1286881f68fSopenharmony_ci } 1296881f68fSopenharmony_ci 1306881f68fSopenharmony_ci cd = calloc(1, sizeof(*cd) + dev_info_len); 1316881f68fSopenharmony_ci if (!cd) { 1326881f68fSopenharmony_ci fuse_log(FUSE_LOG_ERR, "cuse: failed to allocate cuse_data\n"); 1336881f68fSopenharmony_ci return NULL; 1346881f68fSopenharmony_ci } 1356881f68fSopenharmony_ci 1366881f68fSopenharmony_ci memcpy(&cd->clop, clop, sizeof(cd->clop)); 1376881f68fSopenharmony_ci cd->max_read = 131072; 1386881f68fSopenharmony_ci cd->dev_major = ci->dev_major; 1396881f68fSopenharmony_ci cd->dev_minor = ci->dev_minor; 1406881f68fSopenharmony_ci cd->dev_info_len = dev_info_len; 1416881f68fSopenharmony_ci cd->flags = ci->flags; 1426881f68fSopenharmony_ci cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv, cd->dev_info); 1436881f68fSopenharmony_ci 1446881f68fSopenharmony_ci return cd; 1456881f68fSopenharmony_ci} 1466881f68fSopenharmony_ci 1476881f68fSopenharmony_cistruct fuse_session *cuse_lowlevel_new(struct fuse_args *args, 1486881f68fSopenharmony_ci const struct cuse_info *ci, 1496881f68fSopenharmony_ci const struct cuse_lowlevel_ops *clop, 1506881f68fSopenharmony_ci void *userdata) 1516881f68fSopenharmony_ci{ 1526881f68fSopenharmony_ci struct fuse_lowlevel_ops lop; 1536881f68fSopenharmony_ci struct cuse_data *cd; 1546881f68fSopenharmony_ci struct fuse_session *se; 1556881f68fSopenharmony_ci 1566881f68fSopenharmony_ci cd = cuse_prep_data(ci, clop); 1576881f68fSopenharmony_ci if (!cd) 1586881f68fSopenharmony_ci return NULL; 1596881f68fSopenharmony_ci 1606881f68fSopenharmony_ci memset(&lop, 0, sizeof(lop)); 1616881f68fSopenharmony_ci lop.init = clop->init; 1626881f68fSopenharmony_ci lop.destroy = clop->destroy; 1636881f68fSopenharmony_ci lop.open = clop->open ? cuse_fll_open : NULL; 1646881f68fSopenharmony_ci lop.read = clop->read ? cuse_fll_read : NULL; 1656881f68fSopenharmony_ci lop.write = clop->write ? cuse_fll_write : NULL; 1666881f68fSopenharmony_ci lop.flush = clop->flush ? cuse_fll_flush : NULL; 1676881f68fSopenharmony_ci lop.release = clop->release ? cuse_fll_release : NULL; 1686881f68fSopenharmony_ci lop.fsync = clop->fsync ? cuse_fll_fsync : NULL; 1696881f68fSopenharmony_ci lop.ioctl = clop->ioctl ? cuse_fll_ioctl : NULL; 1706881f68fSopenharmony_ci lop.poll = clop->poll ? cuse_fll_poll : NULL; 1716881f68fSopenharmony_ci 1726881f68fSopenharmony_ci se = fuse_session_new(args, &lop, sizeof(lop), userdata); 1736881f68fSopenharmony_ci if (!se) { 1746881f68fSopenharmony_ci free(cd); 1756881f68fSopenharmony_ci return NULL; 1766881f68fSopenharmony_ci } 1776881f68fSopenharmony_ci se->cuse_data = cd; 1786881f68fSopenharmony_ci 1796881f68fSopenharmony_ci return se; 1806881f68fSopenharmony_ci} 1816881f68fSopenharmony_ci 1826881f68fSopenharmony_cistatic int cuse_reply_init(fuse_req_t req, struct cuse_init_out *arg, 1836881f68fSopenharmony_ci char *dev_info, unsigned dev_info_len) 1846881f68fSopenharmony_ci{ 1856881f68fSopenharmony_ci struct iovec iov[3]; 1866881f68fSopenharmony_ci 1876881f68fSopenharmony_ci iov[1].iov_base = arg; 1886881f68fSopenharmony_ci iov[1].iov_len = sizeof(struct cuse_init_out); 1896881f68fSopenharmony_ci iov[2].iov_base = dev_info; 1906881f68fSopenharmony_ci iov[2].iov_len = dev_info_len; 1916881f68fSopenharmony_ci 1926881f68fSopenharmony_ci return fuse_send_reply_iov_nofree(req, 0, iov, 3); 1936881f68fSopenharmony_ci} 1946881f68fSopenharmony_ci 1956881f68fSopenharmony_civoid cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) 1966881f68fSopenharmony_ci{ 1976881f68fSopenharmony_ci struct fuse_init_in *arg = (struct fuse_init_in *) inarg; 1986881f68fSopenharmony_ci struct cuse_init_out outarg; 1996881f68fSopenharmony_ci struct fuse_session *se = req->se; 2006881f68fSopenharmony_ci struct cuse_data *cd = se->cuse_data; 2016881f68fSopenharmony_ci size_t bufsize = se->bufsize; 2026881f68fSopenharmony_ci struct cuse_lowlevel_ops *clop = req_clop(req); 2036881f68fSopenharmony_ci 2046881f68fSopenharmony_ci (void) nodeid; 2056881f68fSopenharmony_ci if (se->debug) { 2066881f68fSopenharmony_ci fuse_log(FUSE_LOG_DEBUG, "CUSE_INIT: %u.%u\n", arg->major, arg->minor); 2076881f68fSopenharmony_ci fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags); 2086881f68fSopenharmony_ci } 2096881f68fSopenharmony_ci se->conn.proto_major = arg->major; 2106881f68fSopenharmony_ci se->conn.proto_minor = arg->minor; 2116881f68fSopenharmony_ci se->conn.capable = 0; 2126881f68fSopenharmony_ci se->conn.want = 0; 2136881f68fSopenharmony_ci 2146881f68fSopenharmony_ci if (arg->major < 7) { 2156881f68fSopenharmony_ci fuse_log(FUSE_LOG_ERR, "cuse: unsupported protocol version: %u.%u\n", 2166881f68fSopenharmony_ci arg->major, arg->minor); 2176881f68fSopenharmony_ci fuse_reply_err(req, EPROTO); 2186881f68fSopenharmony_ci return; 2196881f68fSopenharmony_ci } 2206881f68fSopenharmony_ci 2216881f68fSopenharmony_ci if (bufsize < FUSE_MIN_READ_BUFFER) { 2226881f68fSopenharmony_ci fuse_log(FUSE_LOG_ERR, "cuse: warning: buffer size too small: %zu\n", 2236881f68fSopenharmony_ci bufsize); 2246881f68fSopenharmony_ci bufsize = FUSE_MIN_READ_BUFFER; 2256881f68fSopenharmony_ci } 2266881f68fSopenharmony_ci 2276881f68fSopenharmony_ci bufsize -= 4096; 2286881f68fSopenharmony_ci if (bufsize < se->conn.max_write) 2296881f68fSopenharmony_ci se->conn.max_write = bufsize; 2306881f68fSopenharmony_ci 2316881f68fSopenharmony_ci se->got_init = 1; 2326881f68fSopenharmony_ci if (se->op.init) 2336881f68fSopenharmony_ci se->op.init(se->userdata, &se->conn); 2346881f68fSopenharmony_ci 2356881f68fSopenharmony_ci memset(&outarg, 0, sizeof(outarg)); 2366881f68fSopenharmony_ci outarg.major = FUSE_KERNEL_VERSION; 2376881f68fSopenharmony_ci outarg.minor = FUSE_KERNEL_MINOR_VERSION; 2386881f68fSopenharmony_ci outarg.flags = cd->flags; 2396881f68fSopenharmony_ci outarg.max_read = cd->max_read; 2406881f68fSopenharmony_ci outarg.max_write = se->conn.max_write; 2416881f68fSopenharmony_ci outarg.dev_major = cd->dev_major; 2426881f68fSopenharmony_ci outarg.dev_minor = cd->dev_minor; 2436881f68fSopenharmony_ci 2446881f68fSopenharmony_ci if (se->debug) { 2456881f68fSopenharmony_ci fuse_log(FUSE_LOG_DEBUG, " CUSE_INIT: %u.%u\n", 2466881f68fSopenharmony_ci outarg.major, outarg.minor); 2476881f68fSopenharmony_ci fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags); 2486881f68fSopenharmony_ci fuse_log(FUSE_LOG_DEBUG, " max_read=0x%08x\n", outarg.max_read); 2496881f68fSopenharmony_ci fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write); 2506881f68fSopenharmony_ci fuse_log(FUSE_LOG_DEBUG, " dev_major=%u\n", outarg.dev_major); 2516881f68fSopenharmony_ci fuse_log(FUSE_LOG_DEBUG, " dev_minor=%u\n", outarg.dev_minor); 2526881f68fSopenharmony_ci fuse_log(FUSE_LOG_DEBUG, " dev_info: %.*s\n", cd->dev_info_len, 2536881f68fSopenharmony_ci cd->dev_info); 2546881f68fSopenharmony_ci } 2556881f68fSopenharmony_ci 2566881f68fSopenharmony_ci cuse_reply_init(req, &outarg, cd->dev_info, cd->dev_info_len); 2576881f68fSopenharmony_ci 2586881f68fSopenharmony_ci if (clop->init_done) 2596881f68fSopenharmony_ci clop->init_done(se->userdata); 2606881f68fSopenharmony_ci 2616881f68fSopenharmony_ci fuse_free_req(req); 2626881f68fSopenharmony_ci} 2636881f68fSopenharmony_ci 2646881f68fSopenharmony_cistruct fuse_session *cuse_lowlevel_setup(int argc, char *argv[], 2656881f68fSopenharmony_ci const struct cuse_info *ci, 2666881f68fSopenharmony_ci const struct cuse_lowlevel_ops *clop, 2676881f68fSopenharmony_ci int *multithreaded, void *userdata) 2686881f68fSopenharmony_ci{ 2696881f68fSopenharmony_ci const char *devname = "/dev/cuse"; 2706881f68fSopenharmony_ci static const struct fuse_opt kill_subtype_opts[] = { 2716881f68fSopenharmony_ci FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_DISCARD), 2726881f68fSopenharmony_ci FUSE_OPT_END 2736881f68fSopenharmony_ci }; 2746881f68fSopenharmony_ci struct fuse_args args = FUSE_ARGS_INIT(argc, argv); 2756881f68fSopenharmony_ci struct fuse_session *se; 2766881f68fSopenharmony_ci struct fuse_cmdline_opts opts; 2776881f68fSopenharmony_ci int fd; 2786881f68fSopenharmony_ci int res; 2796881f68fSopenharmony_ci 2806881f68fSopenharmony_ci if (fuse_parse_cmdline(&args, &opts) == -1) 2816881f68fSopenharmony_ci return NULL; 2826881f68fSopenharmony_ci *multithreaded = !opts.singlethread; 2836881f68fSopenharmony_ci 2846881f68fSopenharmony_ci /* Remove subtype= option */ 2856881f68fSopenharmony_ci res = fuse_opt_parse(&args, NULL, kill_subtype_opts, NULL); 2866881f68fSopenharmony_ci if (res == -1) 2876881f68fSopenharmony_ci goto out1; 2886881f68fSopenharmony_ci 2896881f68fSopenharmony_ci /* 2906881f68fSopenharmony_ci * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos 2916881f68fSopenharmony_ci * would ensue. 2926881f68fSopenharmony_ci */ 2936881f68fSopenharmony_ci do { 2946881f68fSopenharmony_ci fd = open("/dev/null", O_RDWR); 2956881f68fSopenharmony_ci if (fd > 2) 2966881f68fSopenharmony_ci close(fd); 2976881f68fSopenharmony_ci } while (fd >= 0 && fd <= 2); 2986881f68fSopenharmony_ci 2996881f68fSopenharmony_ci se = cuse_lowlevel_new(&args, ci, clop, userdata); 3006881f68fSopenharmony_ci if (se == NULL) 3016881f68fSopenharmony_ci goto out1; 3026881f68fSopenharmony_ci 3036881f68fSopenharmony_ci fd = open(devname, O_RDWR); 3046881f68fSopenharmony_ci if (fd == -1) { 3056881f68fSopenharmony_ci if (errno == ENODEV || errno == ENOENT) 3066881f68fSopenharmony_ci fuse_log(FUSE_LOG_ERR, "cuse: device not found, try 'modprobe cuse' first\n"); 3076881f68fSopenharmony_ci else 3086881f68fSopenharmony_ci fuse_log(FUSE_LOG_ERR, "cuse: failed to open %s: %s\n", 3096881f68fSopenharmony_ci devname, strerror(errno)); 3106881f68fSopenharmony_ci goto err_se; 3116881f68fSopenharmony_ci } 3126881f68fSopenharmony_ci se->fd = fd; 3136881f68fSopenharmony_ci 3146881f68fSopenharmony_ci res = fuse_set_signal_handlers(se); 3156881f68fSopenharmony_ci if (res == -1) 3166881f68fSopenharmony_ci goto err_se; 3176881f68fSopenharmony_ci 3186881f68fSopenharmony_ci res = fuse_daemonize(opts.foreground); 3196881f68fSopenharmony_ci if (res == -1) 3206881f68fSopenharmony_ci goto err_sig; 3216881f68fSopenharmony_ci 3226881f68fSopenharmony_ci fuse_opt_free_args(&args); 3236881f68fSopenharmony_ci return se; 3246881f68fSopenharmony_ci 3256881f68fSopenharmony_cierr_sig: 3266881f68fSopenharmony_ci fuse_remove_signal_handlers(se); 3276881f68fSopenharmony_cierr_se: 3286881f68fSopenharmony_ci fuse_session_destroy(se); 3296881f68fSopenharmony_ciout1: 3306881f68fSopenharmony_ci free(opts.mountpoint); 3316881f68fSopenharmony_ci fuse_opt_free_args(&args); 3326881f68fSopenharmony_ci return NULL; 3336881f68fSopenharmony_ci} 3346881f68fSopenharmony_ci 3356881f68fSopenharmony_civoid cuse_lowlevel_teardown(struct fuse_session *se) 3366881f68fSopenharmony_ci{ 3376881f68fSopenharmony_ci fuse_remove_signal_handlers(se); 3386881f68fSopenharmony_ci fuse_session_destroy(se); 3396881f68fSopenharmony_ci} 3406881f68fSopenharmony_ci 3416881f68fSopenharmony_ciint cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci, 3426881f68fSopenharmony_ci const struct cuse_lowlevel_ops *clop, void *userdata) 3436881f68fSopenharmony_ci{ 3446881f68fSopenharmony_ci struct fuse_session *se; 3456881f68fSopenharmony_ci int multithreaded; 3466881f68fSopenharmony_ci int res; 3476881f68fSopenharmony_ci 3486881f68fSopenharmony_ci se = cuse_lowlevel_setup(argc, argv, ci, clop, &multithreaded, 3496881f68fSopenharmony_ci userdata); 3506881f68fSopenharmony_ci if (se == NULL) 3516881f68fSopenharmony_ci return 1; 3526881f68fSopenharmony_ci 3536881f68fSopenharmony_ci if (multithreaded) { 3546881f68fSopenharmony_ci struct fuse_loop_config *config = fuse_loop_cfg_create(); 3556881f68fSopenharmony_ci res = fuse_session_loop_mt(se, config); 3566881f68fSopenharmony_ci fuse_loop_cfg_destroy(config); 3576881f68fSopenharmony_ci } 3586881f68fSopenharmony_ci else 3596881f68fSopenharmony_ci res = fuse_session_loop(se); 3606881f68fSopenharmony_ci 3616881f68fSopenharmony_ci cuse_lowlevel_teardown(se); 3626881f68fSopenharmony_ci if (res == -1) 3636881f68fSopenharmony_ci return 1; 3646881f68fSopenharmony_ci 3656881f68fSopenharmony_ci return 0; 3666881f68fSopenharmony_ci} 367