libfuse: lib/cuse_lowlevel.c Source File

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 LGPL2.txt.
8*/
9
10#include "fuse_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
23struct 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
33static struct cuse_lowlevel_ops *req_clop(fuse_req_t req)
34{
35 return &req->se->cuse_data->clop;
36}
37
38static 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
45static 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
52static 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
59static 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
66static 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
73static 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
80static void cuse_fll_ioctl(fuse_req_t req, fuse_ino_t ino, unsigned 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
89static 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
96static 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
115static 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 fuse_log(FUSE_LOG_ERR, "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 fuse_log(FUSE_LOG_ERR, "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
147struct 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
182static 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
195void _cuse_lowlevel_init(fuse_req_t req, const fuse_ino_t nodeid,
196 const void *op_in, const void *req_payload)
197{
198 const struct fuse_init_in *arg = op_in;
199 (void)req_payload;
200 struct cuse_init_out outarg;
201 struct fuse_session *se = req->se;
202 struct cuse_data *cd = se->cuse_data;
203 size_t bufsize = se->bufsize;
204 struct cuse_lowlevel_ops *clop = req_clop(req);
205
206 (void) nodeid;
207 if (se->debug) {
208 fuse_log(FUSE_LOG_DEBUG, "CUSE_INIT: %u.%u\n", arg->major, arg->minor);
209 fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags);
210 }
211 se->conn.proto_major = arg->major;
212 se->conn.proto_minor = arg->minor;
213
214 /* XXX This is not right.*/
215 se->conn.capable_ext = 0;
216 se->conn.want_ext = 0;
217
218 if (arg->major < 7) {
219 fuse_log(FUSE_LOG_ERR, "cuse: unsupported protocol version: %u.%u\n",
220 arg->major, arg->minor);
221 fuse_reply_err(req, EPROTO);
222 return;
223 }
224
225 if (bufsize < FUSE_MIN_READ_BUFFER) {
226 fuse_log(FUSE_LOG_ERR, "cuse: warning: buffer size too small: %zu\n",
227 bufsize);
228 bufsize = FUSE_MIN_READ_BUFFER;
229 }
230
231 bufsize -= 4096;
232 if (bufsize < se->conn.max_write)
233 se->conn.max_write = bufsize;
234
235 se->got_init = 1;
236 if (se->op.init)
237 se->op.init(se->userdata, &se->conn);
238
239 memset(&outarg, 0, sizeof(outarg));
240 outarg.major = FUSE_KERNEL_VERSION;
241 outarg.minor = FUSE_KERNEL_MINOR_VERSION;
242 outarg.flags = cd->flags;
243 outarg.max_read = cd->max_read;
244 outarg.max_write = se->conn.max_write;
245 outarg.dev_major = cd->dev_major;
246 outarg.dev_minor = cd->dev_minor;
247
248 if (se->debug) {
249 fuse_log(FUSE_LOG_DEBUG, " CUSE_INIT: %u.%u\n",
250 outarg.major, outarg.minor);
251 fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags);
252 fuse_log(FUSE_LOG_DEBUG, " max_read=0x%08x\n", outarg.max_read);
253 fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write);
254 fuse_log(FUSE_LOG_DEBUG, " dev_major=%u\n", outarg.dev_major);
255 fuse_log(FUSE_LOG_DEBUG, " dev_minor=%u\n", outarg.dev_minor);
256 fuse_log(FUSE_LOG_DEBUG, " dev_info: %.*s\n", cd->dev_info_len,
257 cd->dev_info);
258 }
259
260 cuse_reply_init(req, &outarg, cd->dev_info, cd->dev_info_len);
261
262 if (clop->init_done)
263 clop->init_done(se->userdata);
264
265 fuse_free_req(req);
266}
267
268void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
269{
270 _cuse_lowlevel_init(req, nodeid, inarg, NULL);
271}
272
273struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[],
274 const struct cuse_info *ci,
275 const struct cuse_lowlevel_ops *clop,
276 int *multithreaded, void *userdata)
277{
278 const char *devname = "/dev/cuse";
279 static const struct fuse_opt kill_subtype_opts[] = {
280 FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_DISCARD),
281 FUSE_OPT_END
282 };
283 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
284 struct fuse_session *se;
285 struct fuse_cmdline_opts opts;
286 int fd;
287 int res;
288
289 if (fuse_parse_cmdline(&args, &opts) == -1)
290 return NULL;
291 *multithreaded = !opts.singlethread;
292
293 /* Remove subtype= option */
294 res = fuse_opt_parse(&args, NULL, kill_subtype_opts, NULL);
295 if (res == -1)
296 goto out1;
297
298 /*
299 * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
300 * would ensue.
301 */
302 do {
303 fd = open("/dev/null", O_RDWR);
304 if (fd > 2)
305 close(fd);
306 } while (fd >= 0 && fd <= 2);
307
308 se = cuse_lowlevel_new(&args, ci, clop, userdata);
309 if (se == NULL)
310 goto out1;
311
312 fd = open(devname, O_RDWR);
313 if (fd == -1) {
314 if (errno == ENODEV || errno == ENOENT)
315 fuse_log(FUSE_LOG_ERR, "cuse: device not found, try 'modprobe cuse' first\n");
316 else
317 fuse_log(FUSE_LOG_ERR, "cuse: failed to open %s: %s\n",
318 devname, strerror(errno));
319 goto err_se;
320 }
321 se->fd = fd;
322
323 res = fuse_set_signal_handlers(se);
324 if (res == -1)
325 goto err_se;
326
327 res = fuse_daemonize(opts.foreground);
328 if (res == -1)
329 goto err_sig;
330
331 fuse_opt_free_args(&args);
332 return se;
333
334err_sig:
335 fuse_remove_signal_handlers(se);
336err_se:
337 fuse_session_destroy(se);
338out1:
339 free(opts.mountpoint);
340 fuse_opt_free_args(&args);
341 return NULL;
342}
343
344void cuse_lowlevel_teardown(struct fuse_session *se)
345{
346 fuse_remove_signal_handlers(se);
347 fuse_session_destroy(se);
348}
349
350int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
351 const struct cuse_lowlevel_ops *clop, void *userdata)
352{
353 struct fuse_session *se;
354 int multithreaded;
355 int res;
356
357 se = cuse_lowlevel_setup(argc, argv, ci, clop, &multithreaded,
358 userdata);
359 if (se == NULL)
360 return 1;
361
362 if (multithreaded) {
363 struct fuse_loop_config *config = fuse_loop_cfg_create();
364 res = fuse_session_loop_mt(se, config);
365 fuse_loop_cfg_destroy(config);
366 }
367 else
368 res = fuse_session_loop(se);
369
370 cuse_lowlevel_teardown(se);
371 if (res == -1)
372 return 1;
373
374 return 0;
375}
fuse_set_signal_handlers
int fuse_set_signal_handlers(struct fuse_session *se)
Definition fuse_signals.c:140
fuse_remove_signal_handlers
void fuse_remove_signal_handlers(struct fuse_session *se)
Definition fuse_signals.c:187
fuse_daemonize
int fuse_daemonize(int foreground)
Definition helper.c:253
fuse_log
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
fuse_session_destroy
void fuse_session_destroy(struct fuse_session *se)
Definition fuse_lowlevel.c:3010
fuse_reply_err
int fuse_reply_err(fuse_req_t req, int err)
Definition fuse_lowlevel.c:331
fuse_req_t
struct fuse_req * fuse_req_t
Definition fuse_lowlevel.h:50
fuse_session_loop
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
fuse_ino_t
uint64_t fuse_ino_t
Definition fuse_lowlevel.h:47
fuse_opt_free_args
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
FUSE_OPT_KEY
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
fuse_opt_parse
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
FUSE_OPT_KEY_DISCARD
#define FUSE_OPT_KEY_DISCARD
Definition fuse_opt.h:153
FUSE_ARGS_INIT
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
FUSE_OPT_END
#define FUSE_OPT_END
Definition fuse_opt.h:104
fuse_args
Definition fuse_opt.h:109
fuse_args::argc
int argc
Definition fuse_opt.h:111
fuse_args::argv
char ** argv
Definition fuse_opt.h:114
fuse_cmdline_opts
Definition fuse_lowlevel.h:2003
fuse_file_info
Definition fuse_common.h:50
fuse_loop_config
Definition fuse_common.h:139
fuse_lowlevel_ops
Definition fuse_lowlevel.h:206
fuse_opt
Definition fuse_opt.h:77