util/ulockmgr_server.c: conditionally define closefrom (fix glibc-2.34+)
[platform/upstream/fuse.git] / lib / 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 "cuse_lowlevel.h"
11 #include "fuse_kernel.h"
12 #include "fuse_i.h"
13 #include "fuse_opt.h"
14 #include "fuse_misc.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->f->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         struct fuse_ll *ll;
156
157         cd = cuse_prep_data(ci, clop);
158         if (!cd)
159                 return NULL;
160
161         memset(&lop, 0, sizeof(lop));
162         lop.init        = clop->init;
163         lop.destroy     = clop->destroy;
164         lop.open        = clop->open            ? cuse_fll_open         : NULL;
165         lop.read        = clop->read            ? cuse_fll_read         : NULL;
166         lop.write       = clop->write           ? cuse_fll_write        : NULL;
167         lop.flush       = clop->flush           ? cuse_fll_flush        : NULL;
168         lop.release     = clop->release         ? cuse_fll_release      : NULL;
169         lop.fsync       = clop->fsync           ? cuse_fll_fsync        : NULL;
170         lop.ioctl       = clop->ioctl           ? cuse_fll_ioctl        : NULL;
171         lop.poll        = clop->poll            ? cuse_fll_poll         : NULL;
172
173         se = fuse_lowlevel_new_common(args, &lop, sizeof(lop), userdata);
174         if (!se) {
175                 free(cd);
176                 return NULL;
177         }
178         ll = se->data;
179         ll->cuse_data = cd;
180
181         return se;
182 }
183
184 static int cuse_reply_init(fuse_req_t req, struct cuse_init_out *arg,
185                            char *dev_info, unsigned dev_info_len)
186 {
187         struct iovec iov[3];
188
189         iov[1].iov_base = arg;
190         iov[1].iov_len = sizeof(struct cuse_init_out);
191         iov[2].iov_base = dev_info;
192         iov[2].iov_len = dev_info_len;
193
194         return fuse_send_reply_iov_nofree(req, 0, iov, 3);
195 }
196
197 void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
198 {
199         struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
200         struct cuse_init_out outarg;
201         struct fuse_ll *f = req->f;
202         struct cuse_data *cd = f->cuse_data;
203         size_t bufsize = fuse_chan_bufsize(req->ch);
204         struct cuse_lowlevel_ops *clop = req_clop(req);
205
206         (void) nodeid;
207         if (f->debug) {
208                 fprintf(stderr, "CUSE_INIT: %u.%u\n", arg->major, arg->minor);
209                 fprintf(stderr, "flags=0x%08x\n", arg->flags);
210         }
211         f->conn.proto_major = arg->major;
212         f->conn.proto_minor = arg->minor;
213         f->conn.capable = 0;
214         f->conn.want = 0;
215
216         if (arg->major < 7) {
217                 fprintf(stderr, "cuse: unsupported protocol version: %u.%u\n",
218                         arg->major, arg->minor);
219                 fuse_reply_err(req, EPROTO);
220                 return;
221         }
222
223         if (bufsize < FUSE_MIN_READ_BUFFER) {
224                 fprintf(stderr, "cuse: warning: buffer size too small: %zu\n",
225                         bufsize);
226                 bufsize = FUSE_MIN_READ_BUFFER;
227         }
228
229         bufsize -= 4096;
230         if (bufsize < f->conn.max_write)
231                 f->conn.max_write = bufsize;
232
233         f->got_init = 1;
234         if (f->op.init)
235                 f->op.init(f->userdata, &f->conn);
236
237         memset(&outarg, 0, sizeof(outarg));
238         outarg.major = FUSE_KERNEL_VERSION;
239         outarg.minor = FUSE_KERNEL_MINOR_VERSION;
240         outarg.flags = cd->flags;
241         outarg.max_read = cd->max_read;
242         outarg.max_write = f->conn.max_write;
243         outarg.dev_major = cd->dev_major;
244         outarg.dev_minor = cd->dev_minor;
245
246         if (f->debug) {
247                 fprintf(stderr, "   CUSE_INIT: %u.%u\n",
248                         outarg.major, outarg.minor);
249                 fprintf(stderr, "   flags=0x%08x\n", outarg.flags);
250                 fprintf(stderr, "   max_read=0x%08x\n", outarg.max_read);
251                 fprintf(stderr, "   max_write=0x%08x\n", outarg.max_write);
252                 fprintf(stderr, "   dev_major=%u\n", outarg.dev_major);
253                 fprintf(stderr, "   dev_minor=%u\n", outarg.dev_minor);
254                 fprintf(stderr, "   dev_info: %.*s\n", cd->dev_info_len,
255                         cd->dev_info);
256         }
257
258         cuse_reply_init(req, &outarg, cd->dev_info, cd->dev_info_len);
259
260         if (clop->init_done)
261                 clop->init_done(f->userdata);
262
263         fuse_free_req(req);
264 }
265
266 struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[],
267                                          const struct cuse_info *ci,
268                                          const struct cuse_lowlevel_ops *clop,
269                                          int *multithreaded, void *userdata)
270 {
271         const char *devname = "/dev/cuse";
272         static const struct fuse_opt kill_subtype_opts[] = {
273                 FUSE_OPT_KEY("subtype=",  FUSE_OPT_KEY_DISCARD),
274                 FUSE_OPT_END
275         };
276         struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
277         struct fuse_session *se;
278         struct fuse_chan *ch;
279         int fd;
280         int foreground;
281         int res;
282
283         res = fuse_parse_cmdline(&args, NULL, multithreaded, &foreground);
284         if (res == -1)
285                 goto err_args;
286
287         res = fuse_opt_parse(&args, NULL, kill_subtype_opts, NULL);
288         if (res == -1)
289                 goto err_args;
290
291         /*
292          * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
293          * would ensue.
294          */
295         do {
296                 fd = open("/dev/null", O_RDWR);
297                 if (fd > 2)
298                         close(fd);
299         } while (fd >= 0 && fd <= 2);
300
301         se = cuse_lowlevel_new(&args, ci, clop, userdata);
302         fuse_opt_free_args(&args);
303         if (se == NULL)
304                 goto err_args;
305
306         fd = open(devname, O_RDWR);
307         if (fd == -1) {
308                 if (errno == ENODEV || errno == ENOENT)
309                         fprintf(stderr, "cuse: device not found, try 'modprobe cuse' first\n");
310                 else
311                         fprintf(stderr, "cuse: failed to open %s: %s\n",
312                                 devname, strerror(errno));
313                 goto err_se;
314         }
315
316         ch = fuse_kern_chan_new(fd);
317         if (!ch) {
318                 close(fd);
319                 goto err_se;
320         }
321
322         fuse_session_add_chan(se, ch);
323
324         res = fuse_set_signal_handlers(se);
325         if (res == -1)
326                 goto err_se;
327
328         res = fuse_daemonize(foreground);
329         if (res == -1)
330                 goto err_sig;
331
332         return se;
333
334 err_sig:
335         fuse_remove_signal_handlers(se);
336 err_se:
337         fuse_session_destroy(se);
338 err_args:
339         fuse_opt_free_args(&args);
340         return NULL;
341 }
342
343 void cuse_lowlevel_teardown(struct fuse_session *se)
344 {
345         fuse_remove_signal_handlers(se);
346         fuse_session_destroy(se);
347 }
348
349 int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
350                        const struct cuse_lowlevel_ops *clop, void *userdata)
351 {
352         struct fuse_session *se;
353         int multithreaded;
354         int res;
355
356         se = cuse_lowlevel_setup(argc, argv, ci, clop, &multithreaded,
357                                  userdata);
358         if (se == NULL)
359                 return 1;
360
361         if (multithreaded)
362                 res = fuse_session_loop_mt(se);
363         else
364                 res = fuse_session_loop(se);
365
366         cuse_lowlevel_teardown(se);
367         if (res == -1)
368                 return 1;
369
370         return 0;
371 }