You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

371 lines
9.0 KiB

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