libfuse
cuse.c
Go to the documentation of this file.
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 
6  This program can be distributed under the terms of the GNU GPL.
7  See the file COPYING.
8 
9 */
10 
34 #define FUSE_USE_VERSION 31
35 
36 #include <cuse_lowlevel.h>
37 #include <fuse_opt.h>
38 #include <stddef.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <errno.h>
44 
45 #include "ioctl.h"
46 
47 static void *cusexmp_buf;
48 static size_t cusexmp_size;
49 
50 static const char *usage =
51 "usage: cusexmp [options]\n"
52 "\n"
53 "options:\n"
54 " --help|-h print this help message\n"
55 " --maj=MAJ|-M MAJ device major number\n"
56 " --min=MIN|-m MIN device minor number\n"
57 " --name=NAME|-n NAME device name (mandatory)\n"
58 " -d -o debug enable debug output (implies -f)\n"
59 " -f foreground operation\n"
60 " -s disable multi-threaded operation\n"
61 "\n";
62 
63 static int cusexmp_resize(size_t new_size)
64 {
65  void *new_buf;
66 
67  if (new_size == cusexmp_size)
68  return 0;
69 
70  new_buf = realloc(cusexmp_buf, new_size);
71  if (!new_buf && new_size)
72  return -ENOMEM;
73 
74  if (new_size > cusexmp_size)
75  memset(new_buf + cusexmp_size, 0, new_size - cusexmp_size);
76 
77  cusexmp_buf = new_buf;
78  cusexmp_size = new_size;
79 
80  return 0;
81 }
82 
83 static int cusexmp_expand(size_t new_size)
84 {
85  if (new_size > cusexmp_size)
86  return cusexmp_resize(new_size);
87  return 0;
88 }
89 
90 static void cusexmp_open(fuse_req_t req, struct fuse_file_info *fi)
91 {
92  fuse_reply_open(req, fi);
93 }
94 
95 static void cusexmp_read(fuse_req_t req, size_t size, off_t off,
96  struct fuse_file_info *fi)
97 {
98  (void)fi;
99 
100  if (off >= cusexmp_size)
101  off = cusexmp_size;
102  if (size > cusexmp_size - off)
103  size = cusexmp_size - off;
104 
105  fuse_reply_buf(req, cusexmp_buf + off, size);
106 }
107 
108 static void cusexmp_write(fuse_req_t req, const char *buf, size_t size,
109  off_t off, struct fuse_file_info *fi)
110 {
111  (void)fi;
112 
113  if (cusexmp_expand(off + size)) {
114  fuse_reply_err(req, ENOMEM);
115  return;
116  }
117 
118  memcpy(cusexmp_buf + off, buf, size);
119  fuse_reply_write(req, size);
120 }
121 
122 static void fioc_do_rw(fuse_req_t req, void *addr, const void *in_buf,
123  size_t in_bufsz, size_t out_bufsz, int is_read)
124 {
125  const struct fioc_rw_arg *arg;
126  struct iovec in_iov[2], out_iov[3], iov[3];
127  size_t cur_size;
128 
129  /* read in arg */
130  in_iov[0].iov_base = addr;
131  in_iov[0].iov_len = sizeof(*arg);
132  if (!in_bufsz) {
133  fuse_reply_ioctl_retry(req, in_iov, 1, NULL, 0);
134  return;
135  }
136  arg = in_buf;
137  in_buf += sizeof(*arg);
138  in_bufsz -= sizeof(*arg);
139 
140  /* prepare size outputs */
141  out_iov[0].iov_base =
142  addr + offsetof(struct fioc_rw_arg, prev_size);
143  out_iov[0].iov_len = sizeof(arg->prev_size);
144 
145  out_iov[1].iov_base =
146  addr + offsetof(struct fioc_rw_arg, new_size);
147  out_iov[1].iov_len = sizeof(arg->new_size);
148 
149  /* prepare client buf */
150  if (is_read) {
151  out_iov[2].iov_base = arg->buf;
152  out_iov[2].iov_len = arg->size;
153  if (!out_bufsz) {
154  fuse_reply_ioctl_retry(req, in_iov, 1, out_iov, 3);
155  return;
156  }
157  } else {
158  in_iov[1].iov_base = arg->buf;
159  in_iov[1].iov_len = arg->size;
160  if (arg->size && !in_bufsz) {
161  fuse_reply_ioctl_retry(req, in_iov, 2, out_iov, 2);
162  return;
163  }
164  }
165 
166  /* we're all set */
167  cur_size = cusexmp_size;
168  iov[0].iov_base = &cur_size;
169  iov[0].iov_len = sizeof(cur_size);
170 
171  iov[1].iov_base = &cusexmp_size;
172  iov[1].iov_len = sizeof(cusexmp_size);
173 
174  if (is_read) {
175  size_t off = arg->offset;
176  size_t size = arg->size;
177 
178  if (off >= cusexmp_size)
179  off = cusexmp_size;
180  if (size > cusexmp_size - off)
181  size = cusexmp_size - off;
182 
183  iov[2].iov_base = cusexmp_buf + off;
184  iov[2].iov_len = size;
185  fuse_reply_ioctl_iov(req, size, iov, 3);
186  } else {
187  if (cusexmp_expand(arg->offset + in_bufsz)) {
188  fuse_reply_err(req, ENOMEM);
189  return;
190  }
191 
192  memcpy(cusexmp_buf + arg->offset, in_buf, in_bufsz);
193  fuse_reply_ioctl_iov(req, in_bufsz, iov, 2);
194  }
195 }
196 
197 static void cusexmp_ioctl(fuse_req_t req, int cmd, void *arg,
198  struct fuse_file_info *fi, unsigned flags,
199  const void *in_buf, size_t in_bufsz, size_t out_bufsz)
200 {
201  int is_read = 0;
202 
203  (void)fi;
204 
205  if (flags & FUSE_IOCTL_COMPAT) {
206  fuse_reply_err(req, ENOSYS);
207  return;
208  }
209 
210  switch (cmd) {
211  case FIOC_GET_SIZE:
212  if (!out_bufsz) {
213  struct iovec iov = { arg, sizeof(size_t) };
214 
215  fuse_reply_ioctl_retry(req, NULL, 0, &iov, 1);
216  } else
217  fuse_reply_ioctl(req, 0, &cusexmp_size,
218  sizeof(cusexmp_size));
219  break;
220 
221  case FIOC_SET_SIZE:
222  if (!in_bufsz) {
223  struct iovec iov = { arg, sizeof(size_t) };
224 
225  fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
226  } else {
227  cusexmp_resize(*(size_t *)in_buf);
228  fuse_reply_ioctl(req, 0, NULL, 0);
229  }
230  break;
231 
232  case FIOC_READ:
233  is_read = 1;
234  /* fall through */
235  case FIOC_WRITE:
236  fioc_do_rw(req, arg, in_buf, in_bufsz, out_bufsz, is_read);
237  break;
238 
239  default:
240  fuse_reply_err(req, EINVAL);
241  }
242 }
243 
244 struct cusexmp_param {
245  unsigned major;
246  unsigned minor;
247  char *dev_name;
248  int is_help;
249 };
250 
251 #define CUSEXMP_OPT(t, p) { t, offsetof(struct cusexmp_param, p), 1 }
252 
253 static const struct fuse_opt cusexmp_opts[] = {
254  CUSEXMP_OPT("-M %u", major),
255  CUSEXMP_OPT("--maj=%u", major),
256  CUSEXMP_OPT("-m %u", minor),
257  CUSEXMP_OPT("--min=%u", minor),
258  CUSEXMP_OPT("-n %s", dev_name),
259  CUSEXMP_OPT("--name=%s", dev_name),
260  FUSE_OPT_KEY("-h", 0),
261  FUSE_OPT_KEY("--help", 0),
263 };
264 
265 static int cusexmp_process_arg(void *data, const char *arg, int key,
266  struct fuse_args *outargs)
267 {
268  struct cusexmp_param *param = data;
269 
270  (void)outargs;
271  (void)arg;
272 
273  switch (key) {
274  case 0:
275  param->is_help = 1;
276  fprintf(stderr, "%s", usage);
277  return fuse_opt_add_arg(outargs, "-ho");
278  default:
279  return 1;
280  }
281 }
282 
283 static const struct cuse_lowlevel_ops cusexmp_clop = {
284  .open = cusexmp_open,
285  .read = cusexmp_read,
286  .write = cusexmp_write,
287  .ioctl = cusexmp_ioctl,
288 };
289 
290 int main(int argc, char **argv)
291 {
292  struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
293  struct cusexmp_param param = { 0, 0, NULL, 0 };
294  char dev_name[128] = "DEVNAME=";
295  const char *dev_info_argv[] = { dev_name };
296  struct cuse_info ci;
297 
298  if (fuse_opt_parse(&args, &param, cusexmp_opts, cusexmp_process_arg)) {
299  printf("failed to parse option\n");
300  return 1;
301  }
302 
303  if (!param.is_help) {
304  if (!param.dev_name) {
305  fprintf(stderr, "Error: device name missing\n");
306  return 1;
307  }
308  strncat(dev_name, param.dev_name, sizeof(dev_name) - 9);
309  }
310 
311  memset(&ci, 0, sizeof(ci));
312  ci.dev_major = param.major;
313  ci.dev_minor = param.minor;
314  ci.dev_info_argc = 1;
315  ci.dev_info_argv = dev_info_argv;
316  ci.flags = CUSE_UNRESTRICTED_IOCTL;
317 
318  return cuse_lowlevel_main(args.argc, args.argv, &ci, &cusexmp_clop,
319  NULL);
320 }
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
int argc
Definition: fuse_opt.h:111
int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, size_t in_count, const struct iovec *out_iov, size_t out_count)
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition: fuse_opt.c:397
struct fuse_req * fuse_req_t
Definition: fuse_lowlevel.h:49
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition: fuse_opt.c:54
int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, int count)
#define FUSE_OPT_KEY(templ, key)
Definition: fuse_opt.h:98
#define FUSE_IOCTL_COMPAT
Definition: fuse_common.h:318
#define FUSE_OPT_END
Definition: fuse_opt.h:104
char ** argv
Definition: fuse_opt.h:114
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
#define FUSE_ARGS_INIT(argc, argv)
Definition: fuse_opt.h:123
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)