libfuse
cuse_lowlevel.c
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 "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 
23 struct 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 
33 static struct cuse_lowlevel_ops *req_clop(fuse_req_t req)
34 {
35  return &req->se->cuse_data->clop;
36 }
37 
38 static 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 
45 static 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 
52 static 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 
59 static 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 
66 static 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 
73 static 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 
80 static void cuse_fll_ioctl(fuse_req_t req, fuse_ino_t ino, 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 
89 static 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 
96 static 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 
115 static 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  fprintf(stderr, "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  fprintf(stderr, "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 
147 struct 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 
182 static 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 
195 void 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  fprintf(stderr, "CUSE_INIT: %u.%u\n", arg->major, arg->minor);
207  fprintf(stderr, "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  fprintf(stderr, "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  fprintf(stderr, "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  fprintf(stderr, " CUSE_INIT: %u.%u\n",
246  outarg.major, outarg.minor);
247  fprintf(stderr, " flags=0x%08x\n", outarg.flags);
248  fprintf(stderr, " max_read=0x%08x\n", outarg.max_read);
249  fprintf(stderr, " max_write=0x%08x\n", outarg.max_write);
250  fprintf(stderr, " dev_major=%u\n", outarg.dev_major);
251  fprintf(stderr, " dev_minor=%u\n", outarg.dev_minor);
252  fprintf(stderr, " 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 
264 struct 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),
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  fprintf(stderr, "cuse: device not found, try 'modprobe cuse' first\n");
307  else
308  fprintf(stderr, "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  return se;
323 
324 err_sig:
326 err_se:
328 out1:
329  free(opts.mountpoint);
330  fuse_opt_free_args(&args);
331  return NULL;
332 }
333 
334 void cuse_lowlevel_teardown(struct fuse_session *se)
335 {
338 }
339 
340 int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
341  const struct cuse_lowlevel_ops *clop, void *userdata)
342 {
343  struct fuse_session *se;
344  int multithreaded;
345  int res;
346 
347  se = cuse_lowlevel_setup(argc, argv, ci, clop, &multithreaded,
348  userdata);
349  if (se == NULL)
350  return 1;
351 
352  if (multithreaded) {
353  struct fuse_loop_config config;
354  config.clone_fd = 0;
355  config.max_idle_threads = 10;
356  res = fuse_session_loop_mt_32(se, &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 }
int fuse_parse_cmdline(struct fuse_args *args, struct fuse_cmdline_opts *opts)
Definition: helper.c:197
struct fuse_req * fuse_req_t
Definition: fuse_lowlevel.h:49
uint64_t fuse_ino_t
Definition: fuse_lowlevel.h:46
int fuse_session_loop(struct fuse_session *se)
Definition: fuse_loop.c:19
int fuse_daemonize(int foreground)
Definition: helper.c:220
#define FUSE_ARGS_INIT(argc, argv)
Definition: fuse_opt.h:123
int fuse_reply_err(fuse_req_t req, int err)
int fuse_set_signal_handlers(struct fuse_session *se)
Definition: fuse_signals.c:62
void fuse_session_destroy(struct fuse_session *se)
void fuse_opt_free_args(struct fuse_args *args)
Definition: fuse_opt.c:33
struct fuse_session * fuse_session_new(struct fuse_args *args, const struct fuse_lowlevel_ops *op, size_t op_size, void *userdata)
#define FUSE_OPT_KEY(templ, key)
Definition: fuse_opt.h:98
#define FUSE_OPT_END
Definition: fuse_opt.h:104
#define FUSE_OPT_KEY_DISCARD
Definition: fuse_opt.h:153
void fuse_remove_signal_handlers(struct fuse_session *se)
Definition: fuse_signals.c:79
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