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.

294 lines
6.4 KiB

  1. /*
  2. CUSE example: Character device in Userspace
  3. Copyright (C) 2008-2009 SUSE Linux Products GmbH
  4. Copyright (C) 2008-2009 Tejun Heo <tj@kernel.org>
  5. This program can be distributed under the terms of the GNU GPL.
  6. See the file COPYING.
  7. gcc -Wall cusexmp.c `pkg-config fuse --cflags --libs` -o cusexmp
  8. */
  9. #define FUSE_USE_VERSION 29
  10. #include <cuse_lowlevel.h>
  11. #include <fuse_opt.h>
  12. #include <stddef.h>
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <string.h>
  16. #include <unistd.h>
  17. #include <errno.h>
  18. #include "fioc.h"
  19. static void *cusexmp_buf;
  20. static size_t cusexmp_size;
  21. static const char *usage =
  22. "usage: cusexmp [options]\n"
  23. "\n"
  24. "options:\n"
  25. " --help|-h print this help message\n"
  26. " --maj=MAJ|-M MAJ device major number\n"
  27. " --min=MIN|-m MIN device minor number\n"
  28. " --name=NAME|-n NAME device name (mandatory)\n"
  29. "\n";
  30. static int cusexmp_resize(size_t new_size)
  31. {
  32. void *new_buf;
  33. if (new_size == cusexmp_size)
  34. return 0;
  35. new_buf = realloc(cusexmp_buf, new_size);
  36. if (!new_buf && new_size)
  37. return -ENOMEM;
  38. if (new_size > cusexmp_size)
  39. memset(new_buf + cusexmp_size, 0, new_size - cusexmp_size);
  40. cusexmp_buf = new_buf;
  41. cusexmp_size = new_size;
  42. return 0;
  43. }
  44. static int cusexmp_expand(size_t new_size)
  45. {
  46. if (new_size > cusexmp_size)
  47. return cusexmp_resize(new_size);
  48. return 0;
  49. }
  50. static void cusexmp_open(fuse_req_t req, struct fuse_file_info *fi)
  51. {
  52. fuse_reply_open(req, fi);
  53. }
  54. static void cusexmp_read(fuse_req_t req, size_t size, off_t off,
  55. struct fuse_file_info *fi)
  56. {
  57. (void)fi;
  58. if (off >= cusexmp_size)
  59. off = cusexmp_size;
  60. if (size > cusexmp_size - off)
  61. size = cusexmp_size - off;
  62. fuse_reply_buf(req, cusexmp_buf + off, size);
  63. }
  64. static void cusexmp_write(fuse_req_t req, const char *buf, size_t size,
  65. off_t off, struct fuse_file_info *fi)
  66. {
  67. (void)fi;
  68. if (cusexmp_expand(off + size)) {
  69. fuse_reply_err(req, ENOMEM);
  70. return;
  71. }
  72. memcpy(cusexmp_buf + off, buf, size);
  73. fuse_reply_write(req, size);
  74. }
  75. static void fioc_do_rw(fuse_req_t req, void *addr, const void *in_buf,
  76. size_t in_bufsz, size_t out_bufsz, int is_read)
  77. {
  78. const struct fioc_rw_arg *arg;
  79. struct iovec in_iov[2], out_iov[3], iov[3];
  80. size_t cur_size;
  81. /* read in arg */
  82. in_iov[0].iov_base = addr;
  83. in_iov[0].iov_len = sizeof(*arg);
  84. if (!in_bufsz) {
  85. fuse_reply_ioctl_retry(req, in_iov, 1, NULL, 0);
  86. return;
  87. }
  88. arg = in_buf;
  89. in_buf += sizeof(*arg);
  90. in_bufsz -= sizeof(*arg);
  91. /* prepare size outputs */
  92. out_iov[0].iov_base =
  93. addr + (unsigned long)&(((struct fioc_rw_arg *)0)->prev_size);
  94. out_iov[0].iov_len = sizeof(arg->prev_size);
  95. out_iov[1].iov_base =
  96. addr + (unsigned long)&(((struct fioc_rw_arg *)0)->new_size);
  97. out_iov[1].iov_len = sizeof(arg->new_size);
  98. /* prepare client buf */
  99. if (is_read) {
  100. out_iov[2].iov_base = arg->buf;
  101. out_iov[2].iov_len = arg->size;
  102. if (!out_bufsz) {
  103. fuse_reply_ioctl_retry(req, in_iov, 1, out_iov, 3);
  104. return;
  105. }
  106. } else {
  107. in_iov[1].iov_base = arg->buf;
  108. in_iov[1].iov_len = arg->size;
  109. if (arg->size && !in_bufsz) {
  110. fuse_reply_ioctl_retry(req, in_iov, 2, out_iov, 2);
  111. return;
  112. }
  113. }
  114. /* we're all set */
  115. cur_size = cusexmp_size;
  116. iov[0].iov_base = &cur_size;
  117. iov[0].iov_len = sizeof(cur_size);
  118. iov[1].iov_base = &cusexmp_size;
  119. iov[1].iov_len = sizeof(cusexmp_size);
  120. if (is_read) {
  121. size_t off = arg->offset;
  122. size_t size = arg->size;
  123. if (off >= cusexmp_size)
  124. off = cusexmp_size;
  125. if (size > cusexmp_size - off)
  126. size = cusexmp_size - off;
  127. iov[2].iov_base = cusexmp_buf + off;
  128. iov[2].iov_len = size;
  129. fuse_reply_ioctl_iov(req, size, iov, 3);
  130. } else {
  131. if (cusexmp_expand(arg->offset + in_bufsz)) {
  132. fuse_reply_err(req, ENOMEM);
  133. return;
  134. }
  135. memcpy(cusexmp_buf + arg->offset, in_buf, in_bufsz);
  136. fuse_reply_ioctl_iov(req, in_bufsz, iov, 2);
  137. }
  138. }
  139. static void cusexmp_ioctl(fuse_req_t req, int cmd, void *arg,
  140. struct fuse_file_info *fi, unsigned flags,
  141. const void *in_buf, size_t in_bufsz, size_t out_bufsz)
  142. {
  143. int is_read = 0;
  144. (void)fi;
  145. if (flags & FUSE_IOCTL_COMPAT) {
  146. fuse_reply_err(req, ENOSYS);
  147. return;
  148. }
  149. switch (cmd) {
  150. case FIOC_GET_SIZE:
  151. if (!out_bufsz) {
  152. struct iovec iov = { arg, sizeof(size_t) };
  153. fuse_reply_ioctl_retry(req, NULL, 0, &iov, 1);
  154. } else
  155. fuse_reply_ioctl(req, 0, &cusexmp_size,
  156. sizeof(cusexmp_size));
  157. break;
  158. case FIOC_SET_SIZE:
  159. if (!in_bufsz) {
  160. struct iovec iov = { arg, sizeof(size_t) };
  161. fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
  162. } else {
  163. cusexmp_resize(*(size_t *)in_buf);
  164. fuse_reply_ioctl(req, 0, NULL, 0);
  165. }
  166. break;
  167. case FIOC_READ:
  168. is_read = 1;
  169. case FIOC_WRITE:
  170. fioc_do_rw(req, arg, in_buf, in_bufsz, out_bufsz, is_read);
  171. break;
  172. default:
  173. fuse_reply_err(req, EINVAL);
  174. }
  175. }
  176. struct cusexmp_param {
  177. unsigned major;
  178. unsigned minor;
  179. char *dev_name;
  180. int is_help;
  181. };
  182. #define CUSEXMP_OPT(t, p) { t, offsetof(struct cusexmp_param, p), 1 }
  183. static const struct fuse_opt cusexmp_opts[] = {
  184. CUSEXMP_OPT("-M %u", major),
  185. CUSEXMP_OPT("--maj=%u", major),
  186. CUSEXMP_OPT("-m %u", minor),
  187. CUSEXMP_OPT("--min=%u", minor),
  188. CUSEXMP_OPT("-n %s", dev_name),
  189. CUSEXMP_OPT("--name=%s", dev_name),
  190. FUSE_OPT_KEY("-h", 0),
  191. FUSE_OPT_KEY("--help", 0),
  192. FUSE_OPT_END
  193. };
  194. static int cusexmp_process_arg(void *data, const char *arg, int key,
  195. struct fuse_args *outargs)
  196. {
  197. struct cusexmp_param *param = data;
  198. (void)outargs;
  199. (void)arg;
  200. switch (key) {
  201. case 0:
  202. param->is_help = 1;
  203. fprintf(stderr, "%s", usage);
  204. return fuse_opt_add_arg(outargs, "-ho");
  205. default:
  206. return 1;
  207. }
  208. }
  209. static const struct cuse_lowlevel_ops cusexmp_clop = {
  210. .open = cusexmp_open,
  211. .read = cusexmp_read,
  212. .write = cusexmp_write,
  213. .ioctl = cusexmp_ioctl,
  214. };
  215. int main(int argc, char **argv)
  216. {
  217. struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
  218. struct cusexmp_param param = { 0, 0, NULL, 0 };
  219. char dev_name[128] = "DEVNAME=";
  220. const char *dev_info_argv[] = { dev_name };
  221. struct cuse_info ci;
  222. if (fuse_opt_parse(&args, &param, cusexmp_opts, cusexmp_process_arg)) {
  223. printf("failed to parse option\n");
  224. return 1;
  225. }
  226. if (!param.is_help) {
  227. if (!param.dev_name) {
  228. fprintf(stderr, "Error: device name missing\n");
  229. return 1;
  230. }
  231. strncat(dev_name, param.dev_name, sizeof(dev_name) - 9);
  232. }
  233. memset(&ci, 0, sizeof(ci));
  234. ci.dev_major = param.major;
  235. ci.dev_minor = param.minor;
  236. ci.dev_info_argc = 1;
  237. ci.dev_info_argv = dev_info_argv;
  238. ci.flags = CUSE_UNRESTRICTED_IOCTL;
  239. return cuse_lowlevel_main(args.argc, args.argv, &ci, &cusexmp_clop,
  240. NULL);
  241. }