1/* 2 CUSE: Character device in Userspace 3 Copyright (C) 2008 SUSE Linux Products GmbH 4 Copyright (C) 2008 Tejun Heo <teheo@suse.de> 5 6 This program can be distributed under the terms of the GNU LGPLv2. 7 See the file COPYING.LIB. 8*/ 9 10#include "fuse_config.h" 11#include "cuse_lowlevel.h" 12#include "fuse_kernel.h" 13#include "fuse_i.h" 14#include "fuse_opt.h" 15 16#include <stdio.h> 17#include <string.h> 18#include <stdlib.h> 19#include <stddef.h> 20#include <errno.h> 21#include <unistd.h> 22 23struct cuse_data { 24 struct cuse_lowlevel_ops clop; 25 unsigned max_read; 26 unsigned dev_major; 27 unsigned dev_minor; 28 unsigned flags; 29 unsigned dev_info_len; 30 char dev_info[]; 31}; 32 33static struct cuse_lowlevel_ops *req_clop(fuse_req_t req) 34{ 35 return &req->se->cuse_data->clop; 36} 37 38static void cuse_fll_open(fuse_req_t req, fuse_ino_t ino, 39 struct fuse_file_info *fi) 40{ 41 (void)ino; 42 req_clop(req)->open(req, fi); 43} 44 45static void cuse_fll_read(fuse_req_t req, fuse_ino_t ino, size_t size, 46 off_t off, struct fuse_file_info *fi) 47{ 48 (void)ino; 49 req_clop(req)->read(req, size, off, fi); 50} 51 52static void cuse_fll_write(fuse_req_t req, fuse_ino_t ino, const char *buf, 53 size_t size, off_t off, struct fuse_file_info *fi) 54{ 55 (void)ino; 56 req_clop(req)->write(req, buf, size, off, fi); 57} 58 59static void cuse_fll_flush(fuse_req_t req, fuse_ino_t ino, 60 struct fuse_file_info *fi) 61{ 62 (void)ino; 63 req_clop(req)->flush(req, fi); 64} 65 66static void cuse_fll_release(fuse_req_t req, fuse_ino_t ino, 67 struct fuse_file_info *fi) 68{ 69 (void)ino; 70 req_clop(req)->release(req, fi); 71} 72 73static void cuse_fll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, 74 struct fuse_file_info *fi) 75{ 76 (void)ino; 77 req_clop(req)->fsync(req, datasync, fi); 78} 79 80static void cuse_fll_ioctl(fuse_req_t req, fuse_ino_t ino, unsigned int cmd, void *arg, 81 struct fuse_file_info *fi, unsigned int flags, 82 const void *in_buf, size_t in_bufsz, size_t out_bufsz) 83{ 84 (void)ino; 85 req_clop(req)->ioctl(req, cmd, arg, fi, flags, in_buf, in_bufsz, 86 out_bufsz); 87} 88 89static void cuse_fll_poll(fuse_req_t req, fuse_ino_t ino, 90 struct fuse_file_info *fi, struct fuse_pollhandle *ph) 91{ 92 (void)ino; 93 req_clop(req)->poll(req, fi, ph); 94} 95 96static size_t cuse_pack_info(int argc, const char **argv, char *buf) 97{ 98 size_t size = 0; 99 int i; 100 101 for (i = 0; i < argc; i++) { 102 size_t len; 103 104 len = strlen(argv[i]) + 1; 105 size += len; 106 if (buf) { 107 memcpy(buf, argv[i], len); 108 buf += len; 109 } 110 } 111 112 return size; 113} 114 115static struct cuse_data *cuse_prep_data(const struct cuse_info *ci, 116 const struct cuse_lowlevel_ops *clop) 117{ 118 struct cuse_data *cd; 119 size_t dev_info_len; 120 121 dev_info_len = cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv, 122 NULL); 123 124 if (dev_info_len > CUSE_INIT_INFO_MAX) { 125 fuse_log(FUSE_LOG_ERR, "cuse: dev_info (%zu) too large, limit=%u\n", 126 dev_info_len, CUSE_INIT_INFO_MAX); 127 return NULL; 128 } 129 130 cd = calloc(1, sizeof(*cd) + dev_info_len); 131 if (!cd) { 132 fuse_log(FUSE_LOG_ERR, "cuse: failed to allocate cuse_data\n"); 133 return NULL; 134 } 135 136 memcpy(&cd->clop, clop, sizeof(cd->clop)); 137 cd->max_read = 131072; 138 cd->dev_major = ci->dev_major; 139 cd->dev_minor = ci->dev_minor; 140 cd->dev_info_len = dev_info_len; 141 cd->flags = ci->flags; 142 cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv, cd->dev_info); 143 144 return cd; 145} 146 147struct fuse_session *cuse_lowlevel_new(struct fuse_args *args, 148 const struct cuse_info *ci, 149 const struct cuse_lowlevel_ops *clop, 150 void *userdata) 151{ 152 struct fuse_lowlevel_ops lop; 153 struct cuse_data *cd; 154 struct fuse_session *se; 155 156 cd = cuse_prep_data(ci, clop); 157 if (!cd) 158 return NULL; 159 160 memset(&lop, 0, sizeof(lop)); 161 lop.init = clop->init; 162 lop.destroy = clop->destroy; 163 lop.open = clop->open ? cuse_fll_open : NULL; 164 lop.read = clop->read ? cuse_fll_read : NULL; 165 lop.write = clop->write ? cuse_fll_write : NULL; 166 lop.flush = clop->flush ? cuse_fll_flush : NULL; 167 lop.release = clop->release ? cuse_fll_release : NULL; 168 lop.fsync = clop->fsync ? cuse_fll_fsync : NULL; 169 lop.ioctl = clop->ioctl ? cuse_fll_ioctl : NULL; 170 lop.poll = clop->poll ? cuse_fll_poll : NULL; 171 172 se = fuse_session_new(args, &lop, sizeof(lop), userdata); 173 if (!se) { 174 free(cd); 175 return NULL; 176 } 177 se->cuse_data = cd; 178 179 return se; 180} 181 182static int cuse_reply_init(fuse_req_t req, struct cuse_init_out *arg, 183 char *dev_info, unsigned dev_info_len) 184{ 185 struct iovec iov[3]; 186 187 iov[1].iov_base = arg; 188 iov[1].iov_len = sizeof(struct cuse_init_out); 189 iov[2].iov_base = dev_info; 190 iov[2].iov_len = dev_info_len; 191 192 return fuse_send_reply_iov_nofree(req, 0, iov, 3); 193} 194 195void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) 196{ 197 struct fuse_init_in *arg = (struct fuse_init_in *) inarg; 198 struct cuse_init_out outarg; 199 struct fuse_session *se = req->se; 200 struct cuse_data *cd = se->cuse_data; 201 size_t bufsize = se->bufsize; 202 struct cuse_lowlevel_ops *clop = req_clop(req); 203 204 (void) nodeid; 205 if (se->debug) { 206 fuse_log(FUSE_LOG_DEBUG, "CUSE_INIT: %u.%u\n", arg->major, arg->minor); 207 fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags); 208 } 209 se->conn.proto_major = arg->major; 210 se->conn.proto_minor = arg->minor; 211 se->conn.capable = 0; 212 se->conn.want = 0; 213 214 if (arg->major < 7) { 215 fuse_log(FUSE_LOG_ERR, "cuse: unsupported protocol version: %u.%u\n", 216 arg->major, arg->minor); 217 fuse_reply_err(req, EPROTO); 218 return; 219 } 220 221 if (bufsize < FUSE_MIN_READ_BUFFER) { 222 fuse_log(FUSE_LOG_ERR, "cuse: warning: buffer size too small: %zu\n", 223 bufsize); 224 bufsize = FUSE_MIN_READ_BUFFER; 225 } 226 227 bufsize -= 4096; 228 if (bufsize < se->conn.max_write) 229 se->conn.max_write = bufsize; 230 231 se->got_init = 1; 232 if (se->op.init) 233 se->op.init(se->userdata, &se->conn); 234 235 memset(&outarg, 0, sizeof(outarg)); 236 outarg.major = FUSE_KERNEL_VERSION; 237 outarg.minor = FUSE_KERNEL_MINOR_VERSION; 238 outarg.flags = cd->flags; 239 outarg.max_read = cd->max_read; 240 outarg.max_write = se->conn.max_write; 241 outarg.dev_major = cd->dev_major; 242 outarg.dev_minor = cd->dev_minor; 243 244 if (se->debug) { 245 fuse_log(FUSE_LOG_DEBUG, " CUSE_INIT: %u.%u\n", 246 outarg.major, outarg.minor); 247 fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags); 248 fuse_log(FUSE_LOG_DEBUG, " max_read=0x%08x\n", outarg.max_read); 249 fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write); 250 fuse_log(FUSE_LOG_DEBUG, " dev_major=%u\n", outarg.dev_major); 251 fuse_log(FUSE_LOG_DEBUG, " dev_minor=%u\n", outarg.dev_minor); 252 fuse_log(FUSE_LOG_DEBUG, " dev_info: %.*s\n", cd->dev_info_len, 253 cd->dev_info); 254 } 255 256 cuse_reply_init(req, &outarg, cd->dev_info, cd->dev_info_len); 257 258 if (clop->init_done) 259 clop->init_done(se->userdata); 260 261 fuse_free_req(req); 262} 263 264struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[], 265 const struct cuse_info *ci, 266 const struct cuse_lowlevel_ops *clop, 267 int *multithreaded, void *userdata) 268{ 269 const char *devname = "/dev/cuse"; 270 static const struct fuse_opt kill_subtype_opts[] = { 271 FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_DISCARD), 272 FUSE_OPT_END 273 }; 274 struct fuse_args args = FUSE_ARGS_INIT(argc, argv); 275 struct fuse_session *se; 276 struct fuse_cmdline_opts opts; 277 int fd; 278 int res; 279 280 if (fuse_parse_cmdline(&args, &opts) == -1) 281 return NULL; 282 *multithreaded = !opts.singlethread; 283 284 /* Remove subtype= option */ 285 res = fuse_opt_parse(&args, NULL, kill_subtype_opts, NULL); 286 if (res == -1) 287 goto out1; 288 289 /* 290 * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos 291 * would ensue. 292 */ 293 do { 294 fd = open("/dev/null", O_RDWR); 295 if (fd > 2) 296 close(fd); 297 } while (fd >= 0 && fd <= 2); 298 299 se = cuse_lowlevel_new(&args, ci, clop, userdata); 300 if (se == NULL) 301 goto out1; 302 303 fd = open(devname, O_RDWR); 304 if (fd == -1) { 305 if (errno == ENODEV || errno == ENOENT) 306 fuse_log(FUSE_LOG_ERR, "cuse: device not found, try 'modprobe cuse' first\n"); 307 else 308 fuse_log(FUSE_LOG_ERR, "cuse: failed to open %s: %s\n", 309 devname, strerror(errno)); 310 goto err_se; 311 } 312 se->fd = fd; 313 314 res = fuse_set_signal_handlers(se); 315 if (res == -1) 316 goto err_se; 317 318 res = fuse_daemonize(opts.foreground); 319 if (res == -1) 320 goto err_sig; 321 322 fuse_opt_free_args(&args); 323 return se; 324 325err_sig: 326 fuse_remove_signal_handlers(se); 327err_se: 328 fuse_session_destroy(se); 329out1: 330 free(opts.mountpoint); 331 fuse_opt_free_args(&args); 332 return NULL; 333} 334 335void cuse_lowlevel_teardown(struct fuse_session *se) 336{ 337 fuse_remove_signal_handlers(se); 338 fuse_session_destroy(se); 339} 340 341int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci, 342 const struct cuse_lowlevel_ops *clop, void *userdata) 343{ 344 struct fuse_session *se; 345 int multithreaded; 346 int res; 347 348 se = cuse_lowlevel_setup(argc, argv, ci, clop, &multithreaded, 349 userdata); 350 if (se == NULL) 351 return 1; 352 353 if (multithreaded) { 354 struct fuse_loop_config *config = fuse_loop_cfg_create(); 355 res = fuse_session_loop_mt(se, config); 356 fuse_loop_cfg_destroy(config); 357 } 358 else 359 res = fuse_session_loop(se); 360 361 cuse_lowlevel_teardown(se); 362 if (res == -1) 363 return 1; 364 365 return 0; 366} 367