libfuse: remove fuse_chan_bufsize()
[fuse.git] / lib / cuse_lowlevel.c
blob0e2fe180e48e28b17605919cb5a5e8dc04addbc7
1 /*
2 CUSE: Character device in Userspace
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
6 This program can be distributed under the terms of the GNU LGPLv2.
7 See the file COPYING.LIB.
8 */
10 #include "cuse_lowlevel.h"
11 #include "fuse_kernel.h"
12 #include "fuse_i.h"
13 #include "fuse_opt.h"
15 #include <stdio.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include <stddef.h>
19 #include <errno.h>
20 #include <unistd.h>
22 struct cuse_data {
23 struct cuse_lowlevel_ops clop;
24 unsigned max_read;
25 unsigned dev_major;
26 unsigned dev_minor;
27 unsigned flags;
28 unsigned dev_info_len;
29 char dev_info[];
32 static struct cuse_lowlevel_ops *req_clop(fuse_req_t req)
34 return &req->f->cuse_data->clop;
37 static void cuse_fll_open(fuse_req_t req, fuse_ino_t ino,
38 struct fuse_file_info *fi)
40 (void)ino;
41 req_clop(req)->open(req, fi);
44 static void cuse_fll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
45 off_t off, struct fuse_file_info *fi)
47 (void)ino;
48 req_clop(req)->read(req, size, off, fi);
51 static void cuse_fll_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
52 size_t size, off_t off, struct fuse_file_info *fi)
54 (void)ino;
55 req_clop(req)->write(req, buf, size, off, fi);
58 static void cuse_fll_flush(fuse_req_t req, fuse_ino_t ino,
59 struct fuse_file_info *fi)
61 (void)ino;
62 req_clop(req)->flush(req, fi);
65 static void cuse_fll_release(fuse_req_t req, fuse_ino_t ino,
66 struct fuse_file_info *fi)
68 (void)ino;
69 req_clop(req)->release(req, fi);
72 static void cuse_fll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
73 struct fuse_file_info *fi)
75 (void)ino;
76 req_clop(req)->fsync(req, datasync, fi);
79 static void cuse_fll_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg,
80 struct fuse_file_info *fi, unsigned int flags,
81 const void *in_buf, size_t in_bufsz, size_t out_bufsz)
83 (void)ino;
84 req_clop(req)->ioctl(req, cmd, arg, fi, flags, in_buf, in_bufsz,
85 out_bufsz);
88 static void cuse_fll_poll(fuse_req_t req, fuse_ino_t ino,
89 struct fuse_file_info *fi, struct fuse_pollhandle *ph)
91 (void)ino;
92 req_clop(req)->poll(req, fi, ph);
95 static size_t cuse_pack_info(int argc, const char **argv, char *buf)
97 size_t size = 0;
98 int i;
100 for (i = 0; i < argc; i++) {
101 size_t len;
103 len = strlen(argv[i]) + 1;
104 size += len;
105 if (buf) {
106 memcpy(buf, argv[i], len);
107 buf += len;
111 return size;
114 static struct cuse_data *cuse_prep_data(const struct cuse_info *ci,
115 const struct cuse_lowlevel_ops *clop)
117 struct cuse_data *cd;
118 size_t dev_info_len;
120 dev_info_len = cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv,
121 NULL);
123 if (dev_info_len > CUSE_INIT_INFO_MAX) {
124 fprintf(stderr, "cuse: dev_info (%zu) too large, limit=%u\n",
125 dev_info_len, CUSE_INIT_INFO_MAX);
126 return NULL;
129 cd = calloc(1, sizeof(*cd) + dev_info_len);
130 if (!cd) {
131 fprintf(stderr, "cuse: failed to allocate cuse_data\n");
132 return NULL;
135 memcpy(&cd->clop, clop, sizeof(cd->clop));
136 cd->max_read = 131072;
137 cd->dev_major = ci->dev_major;
138 cd->dev_minor = ci->dev_minor;
139 cd->dev_info_len = dev_info_len;
140 cd->flags = ci->flags;
141 cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv, cd->dev_info);
143 return cd;
146 struct fuse_session *cuse_lowlevel_new(struct fuse_args *args,
147 const struct cuse_info *ci,
148 const struct cuse_lowlevel_ops *clop,
149 void *userdata)
151 struct fuse_lowlevel_ops lop;
152 struct cuse_data *cd;
153 struct fuse_session *se;
154 struct fuse_ll *ll;
156 cd = cuse_prep_data(ci, clop);
157 if (!cd)
158 return NULL;
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;
172 se = fuse_lowlevel_new(args, &lop, sizeof(lop), userdata);
173 if (!se) {
174 free(cd);
175 return NULL;
177 ll = se->data;
178 ll->cuse_data = cd;
180 return se;
183 static int cuse_reply_init(fuse_req_t req, struct cuse_init_out *arg,
184 char *dev_info, unsigned dev_info_len)
186 struct iovec iov[3];
188 iov[1].iov_base = arg;
189 iov[1].iov_len = sizeof(struct cuse_init_out);
190 iov[2].iov_base = dev_info;
191 iov[2].iov_len = dev_info_len;
193 return fuse_send_reply_iov_nofree(req, 0, iov, 3);
196 void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
198 struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
199 struct cuse_init_out outarg;
200 struct fuse_ll *f = req->f;
201 struct cuse_data *cd = f->cuse_data;
202 size_t bufsize = f->bufsize;
203 struct cuse_lowlevel_ops *clop = req_clop(req);
205 (void) nodeid;
206 if (f->debug) {
207 fprintf(stderr, "CUSE_INIT: %u.%u\n", arg->major, arg->minor);
208 fprintf(stderr, "flags=0x%08x\n", arg->flags);
210 f->conn.proto_major = arg->major;
211 f->conn.proto_minor = arg->minor;
212 f->conn.capable = 0;
213 f->conn.want = 0;
215 if (arg->major < 7) {
216 fprintf(stderr, "cuse: unsupported protocol version: %u.%u\n",
217 arg->major, arg->minor);
218 fuse_reply_err(req, EPROTO);
219 return;
222 if (bufsize < FUSE_MIN_READ_BUFFER) {
223 fprintf(stderr, "cuse: warning: buffer size too small: %zu\n",
224 bufsize);
225 bufsize = FUSE_MIN_READ_BUFFER;
228 bufsize -= 4096;
229 if (bufsize < f->conn.max_write)
230 f->conn.max_write = bufsize;
232 f->got_init = 1;
233 if (f->op.init)
234 f->op.init(f->userdata, &f->conn);
236 memset(&outarg, 0, sizeof(outarg));
237 outarg.major = FUSE_KERNEL_VERSION;
238 outarg.minor = FUSE_KERNEL_MINOR_VERSION;
239 outarg.flags = cd->flags;
240 outarg.max_read = cd->max_read;
241 outarg.max_write = f->conn.max_write;
242 outarg.dev_major = cd->dev_major;
243 outarg.dev_minor = cd->dev_minor;
245 if (f->debug) {
246 fprintf(stderr, " CUSE_INIT: %u.%u\n",
247 outarg.major, outarg.minor);
248 fprintf(stderr, " flags=0x%08x\n", outarg.flags);
249 fprintf(stderr, " max_read=0x%08x\n", outarg.max_read);
250 fprintf(stderr, " max_write=0x%08x\n", outarg.max_write);
251 fprintf(stderr, " dev_major=%u\n", outarg.dev_major);
252 fprintf(stderr, " dev_minor=%u\n", outarg.dev_minor);
253 fprintf(stderr, " dev_info: %.*s\n", cd->dev_info_len,
254 cd->dev_info);
257 cuse_reply_init(req, &outarg, cd->dev_info, cd->dev_info_len);
259 if (clop->init_done)
260 clop->init_done(f->userdata);
262 fuse_free_req(req);
265 struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[],
266 const struct cuse_info *ci,
267 const struct cuse_lowlevel_ops *clop,
268 int *multithreaded, void *userdata)
270 const char *devname = "/dev/cuse";
271 static const struct fuse_opt kill_subtype_opts[] = {
272 FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_DISCARD),
273 FUSE_OPT_END
275 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
276 struct fuse_session *se;
277 struct fuse_chan *ch;
278 int fd;
279 int foreground;
280 int res;
282 res = fuse_parse_cmdline(&args, NULL, multithreaded, &foreground);
283 if (res == -1)
284 goto err_args;
286 res = fuse_opt_parse(&args, NULL, kill_subtype_opts, NULL);
287 if (res == -1)
288 goto err_args;
291 * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
292 * would ensue.
294 do {
295 fd = open("/dev/null", O_RDWR);
296 if (fd > 2)
297 close(fd);
298 } while (fd >= 0 && fd <= 2);
300 se = cuse_lowlevel_new(&args, ci, clop, userdata);
301 fuse_opt_free_args(&args);
302 if (se == NULL)
303 goto err_args;
305 fd = open(devname, O_RDWR);
306 if (fd == -1) {
307 if (errno == ENODEV || errno == ENOENT)
308 fprintf(stderr, "cuse: device not found, try 'modprobe cuse' first\n");
309 else
310 fprintf(stderr, "cuse: failed to open %s: %s\n",
311 devname, strerror(errno));
312 goto err_se;
315 ch = fuse_kern_chan_new(fd);
316 if (!ch) {
317 close(fd);
318 goto err_se;
321 fuse_session_add_chan(se, ch);
323 res = fuse_set_signal_handlers(se);
324 if (res == -1)
325 goto err_se;
327 res = fuse_daemonize(foreground);
328 if (res == -1)
329 goto err_sig;
331 return se;
333 err_sig:
334 fuse_remove_signal_handlers(se);
335 err_se:
336 fuse_session_destroy(se);
337 err_args:
338 fuse_opt_free_args(&args);
339 return NULL;
342 void cuse_lowlevel_teardown(struct fuse_session *se)
344 fuse_remove_signal_handlers(se);
345 fuse_session_destroy(se);
348 int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
349 const struct cuse_lowlevel_ops *clop, void *userdata)
351 struct fuse_session *se;
352 int multithreaded;
353 int res;
355 se = cuse_lowlevel_setup(argc, argv, ci, clop, &multithreaded,
356 userdata);
357 if (se == NULL)
358 return 1;
360 if (multithreaded)
361 res = fuse_session_loop_mt(se);
362 else
363 res = fuse_session_loop(se);
365 cuse_lowlevel_teardown(se);
366 if (res == -1)
367 return 1;
369 return 0;