blob: 496800e73255f3e859ffbd8139bd62b5e9900fd3 [file] [log] [blame]
Steve Kondik2111ad72013-07-07 12:07:44 -07001/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file COPYING.LIB
7*/
8
9#include "config.h"
10#include "fuse_lowlevel.h"
11#include "fuse_kernel.h"
12#include "fuse_opt.h"
13#include "fuse_i.h"
14#include "fuse_misc.h"
15#include "fuse_lowlevel_compat.h"
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <stddef.h>
20#include <string.h>
21#include <unistd.h>
22#include <limits.h>
23#include <errno.h>
24
25#define PARAM(inarg) (((const char *)(inarg)) + sizeof(*(inarg)))
26#define OFFSET_MAX 0x7fffffffffffffffLL
27
28struct fuse_ll;
29
30struct fuse_req {
31 struct fuse_ll *f;
32 uint64_t unique;
33 int ctr;
34 pthread_mutex_t lock;
35 struct fuse_ctx ctx;
36 struct fuse_chan *ch;
37 int interrupted;
38 union {
39 struct {
40 uint64_t unique;
41 } i;
42 struct {
43 fuse_interrupt_func_t func;
44 void *data;
45 } ni;
46 } u;
47 struct fuse_req *next;
48 struct fuse_req *prev;
49};
50
51struct fuse_ll {
52 int debug;
53 int allow_root;
54 struct fuse_lowlevel_ops op;
55 int got_init;
56 void *userdata;
57 uid_t owner;
58 struct fuse_conn_info conn;
59 struct fuse_req list;
60 struct fuse_req interrupts;
61 pthread_mutex_t lock;
62 int got_destroy;
63};
64
65static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr)
66{
67 attr->ino = stbuf->st_ino;
68 attr->mode = stbuf->st_mode;
69 attr->nlink = stbuf->st_nlink;
70 attr->uid = stbuf->st_uid;
71 attr->gid = stbuf->st_gid;
Steve Kondike68cb602016-08-28 00:45:36 -070072#if defined(__SOLARIS__) && defined(_LP64)
73 /* Must pack the device the old way (attr->rdev limited to 32 bits) */
74 attr->rdev = ((major(stbuf->st_rdev) & 0x3fff) << 18)
75 | (minor(stbuf->st_rdev) & 0x3ffff);
76#else
Steve Kondik2111ad72013-07-07 12:07:44 -070077 attr->rdev = stbuf->st_rdev;
Steve Kondike68cb602016-08-28 00:45:36 -070078#endif
Steve Kondik2111ad72013-07-07 12:07:44 -070079 attr->size = stbuf->st_size;
80 attr->blocks = stbuf->st_blocks;
81 attr->atime = stbuf->st_atime;
82 attr->mtime = stbuf->st_mtime;
83 attr->ctime = stbuf->st_ctime;
84 attr->atimensec = ST_ATIM_NSEC(stbuf);
85 attr->mtimensec = ST_MTIM_NSEC(stbuf);
86 attr->ctimensec = ST_CTIM_NSEC(stbuf);
87#ifdef POSIXACLS
88 attr->filling = 0; /* JPA trying to be safe */
89#endif
90}
91
92static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf)
93{
94 stbuf->st_mode = attr->mode;
95 stbuf->st_uid = attr->uid;
96 stbuf->st_gid = attr->gid;
97 stbuf->st_size = attr->size;
98 stbuf->st_atime = attr->atime;
99 stbuf->st_mtime = attr->mtime;
100 ST_ATIM_NSEC_SET(stbuf, attr->atimensec);
101 ST_MTIM_NSEC_SET(stbuf, attr->mtimensec);
102}
103
104static size_t iov_length(const struct iovec *iov, size_t count)
105{
106 size_t seg;
107 size_t ret = 0;
108
109 for (seg = 0; seg < count; seg++)
110 ret += iov[seg].iov_len;
111 return ret;
112}
113
114static void list_init_req(struct fuse_req *req)
115{
116 req->next = req;
117 req->prev = req;
118}
119
120static void list_del_req(struct fuse_req *req)
121{
122 struct fuse_req *prev = req->prev;
123 struct fuse_req *next = req->next;
124 prev->next = next;
125 next->prev = prev;
126}
127
128static void list_add_req(struct fuse_req *req, struct fuse_req *next)
129{
130 struct fuse_req *prev = next->prev;
131 req->next = next;
132 req->prev = prev;
133 prev->next = req;
134 next->prev = req;
135}
136
137static void destroy_req(fuse_req_t req)
138{
139 pthread_mutex_destroy(&req->lock);
140 free(req);
141}
142
143static void free_req(fuse_req_t req)
144{
145 int ctr;
146 struct fuse_ll *f = req->f;
147
148 pthread_mutex_lock(&req->lock);
149 req->u.ni.func = NULL;
150 req->u.ni.data = NULL;
151 pthread_mutex_unlock(&req->lock);
152
153 pthread_mutex_lock(&f->lock);
154 list_del_req(req);
155 ctr = --req->ctr;
156 pthread_mutex_unlock(&f->lock);
157 if (!ctr)
158 destroy_req(req);
159}
160
161static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov,
162 int count)
163{
164 struct fuse_out_header out;
165 int res;
166
167 if (error <= -1000 || error > 0) {
168 fprintf(stderr, "fuse: bad error value: %i\n", error);
169 error = -ERANGE;
170 }
171
172 out.unique = req->unique;
173 out.error = error;
174 iov[0].iov_base = &out;
175 iov[0].iov_len = sizeof(struct fuse_out_header);
176 out.len = iov_length(iov, count);
177
178 if (req->f->debug)
179 fprintf(stderr, " unique: %llu, error: %i (%s), outsize: %i\n",
180 (unsigned long long) out.unique, out.error,
181 strerror(-out.error), out.len);
182 res = fuse_chan_send(req->ch, iov, count);
183 free_req(req);
184
185 return res;
186}
187
188static int send_reply(fuse_req_t req, int error, const void *arg,
189 size_t argsize)
190{
191 struct iovec iov[2];
192 int count = 1;
193 if (argsize) {
Steve Kondik79165c32015-11-09 19:43:00 -0800194 /* Note : const qualifier dropped */
195 iov[1].iov_base = (void *)(uintptr_t) arg;
Steve Kondik2111ad72013-07-07 12:07:44 -0700196 iov[1].iov_len = argsize;
197 count++;
198 }
199 return send_reply_iov(req, error, iov, count);
200}
201
202#if 0 /* not used */
203int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count)
204{
205 int res;
206 struct iovec *padded_iov;
207
208 padded_iov = malloc((count + 1) * sizeof(struct iovec));
209 if (padded_iov == NULL)
210 return fuse_reply_err(req, -ENOMEM);
211
212 memcpy(padded_iov + 1, iov, count * sizeof(struct iovec));
213 count++;
214
215 res = send_reply_iov(req, 0, padded_iov, count);
216 free(padded_iov);
217
218 return res;
219}
220#endif
221
222size_t fuse_dirent_size(size_t namelen)
223{
224 return FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + namelen);
225}
226
227char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf,
228 off_t off)
229{
230 unsigned namelen = strlen(name);
231 unsigned entlen = FUSE_NAME_OFFSET + namelen;
232 unsigned entsize = fuse_dirent_size(namelen);
233 unsigned padlen = entsize - entlen;
234 struct fuse_dirent *dirent = (struct fuse_dirent *) buf;
235
236 dirent->ino = stbuf->st_ino;
237 dirent->off = off;
238 dirent->namelen = namelen;
239 dirent->type = (stbuf->st_mode & 0170000) >> 12;
240 strncpy(dirent->name, name, namelen);
241 if (padlen)
242 memset(buf + entlen, 0, padlen);
243
244 return buf + entsize;
245}
246
247size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
248 const char *name, const struct stat *stbuf, off_t off)
249{
250 size_t entsize;
251
252 (void) req;
253 entsize = fuse_dirent_size(strlen(name));
254 if (entsize <= bufsize && buf)
255 fuse_add_dirent(buf, name, stbuf, off);
256 return entsize;
257}
258
259static void convert_statfs(const struct statvfs *stbuf,
260 struct fuse_kstatfs *kstatfs)
261{
262 kstatfs->bsize = stbuf->f_bsize;
263 kstatfs->frsize = stbuf->f_frsize;
264 kstatfs->blocks = stbuf->f_blocks;
265 kstatfs->bfree = stbuf->f_bfree;
266 kstatfs->bavail = stbuf->f_bavail;
267 kstatfs->files = stbuf->f_files;
268 kstatfs->ffree = stbuf->f_ffree;
269 kstatfs->namelen = stbuf->f_namemax;
270}
271
272static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize)
273{
274 return send_reply(req, 0, arg, argsize);
275}
276
277int fuse_reply_err(fuse_req_t req, int err)
278{
279 return send_reply(req, -err, NULL, 0);
280}
281
282void fuse_reply_none(fuse_req_t req)
283{
284 fuse_chan_send(req->ch, NULL, 0);
285 free_req(req);
286}
287
288static unsigned long calc_timeout_sec(double t)
289{
290 if (t > (double) ULONG_MAX)
291 return ULONG_MAX;
292 else if (t < 0.0)
293 return 0;
294 else
295 return (unsigned long) t;
296}
297
298static unsigned int calc_timeout_nsec(double t)
299{
300 unsigned long secs = calc_timeout_sec(t);
301 double f = t - (double)secs;
302 if (f < 0.0)
303 return 0;
304 else if (f >= 0.999999999)
305 return 999999999;
306 else
307 return (unsigned int) (f * 1.0e9);
308}
309
310static void fill_entry(struct fuse_entry_out *arg,
311 const struct fuse_entry_param *e)
312{
313 arg->nodeid = e->ino;
314 arg->generation = e->generation;
315 arg->entry_valid = calc_timeout_sec(e->entry_timeout);
316 arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout);
317 arg->attr_valid = calc_timeout_sec(e->attr_timeout);
318 arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout);
319 convert_stat(&e->attr, &arg->attr);
320}
321
322static void fill_open(struct fuse_open_out *arg,
323 const struct fuse_file_info *f)
324{
325 arg->fh = f->fh;
326 if (f->direct_io)
327 arg->open_flags |= FOPEN_DIRECT_IO;
328 if (f->keep_cache)
329 arg->open_flags |= FOPEN_KEEP_CACHE;
330}
331
332int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
333{
334 struct fuse_entry_out arg;
335
336 /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant
337 negative entry */
338 if (!e->ino && req->f->conn.proto_minor < 4)
339 return fuse_reply_err(req, ENOENT);
340
341 memset(&arg, 0, sizeof(arg));
342 fill_entry(&arg, e);
Steve Kondik2111ad72013-07-07 12:07:44 -0700343 return send_reply_ok(req, &arg, (req->f->conn.proto_minor >= 12
344 ? sizeof(arg) : FUSE_COMPAT_ENTRY_OUT_SIZE));
Steve Kondik2111ad72013-07-07 12:07:44 -0700345}
346
347int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
348 const struct fuse_file_info *f)
349{
350 struct {
351 struct fuse_entry_out e;
352 struct fuse_open_out o;
353 } arg;
354
355 memset(&arg, 0, sizeof(arg));
356 fill_entry(&arg.e, e);
Steve Kondik2111ad72013-07-07 12:07:44 -0700357 if (req->f->conn.proto_minor < 12) {
358 fill_open((struct fuse_open_out*)
359 ((char*)&arg + FUSE_COMPAT_ENTRY_OUT_SIZE), f);
360 return send_reply_ok(req, &arg,
361 FUSE_COMPAT_ENTRY_OUT_SIZE + sizeof(struct fuse_open_out));
362 } else {
363 fill_open(&arg.o, f);
364 return send_reply_ok(req, &arg, sizeof(arg));
365 }
Steve Kondik2111ad72013-07-07 12:07:44 -0700366}
367
368int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
369 double attr_timeout)
370{
371 struct fuse_attr_out arg;
372
373 memset(&arg, 0, sizeof(arg));
374 arg.attr_valid = calc_timeout_sec(attr_timeout);
375 arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
376 convert_stat(attr, &arg.attr);
377
Steve Kondik2111ad72013-07-07 12:07:44 -0700378 return send_reply_ok(req, &arg, (req->f->conn.proto_minor >= 12
379 ? sizeof(arg) : FUSE_COMPAT_FUSE_ATTR_OUT_SIZE));
Steve Kondik2111ad72013-07-07 12:07:44 -0700380}
381
382int fuse_reply_readlink(fuse_req_t req, const char *linkname)
383{
384 return send_reply_ok(req, linkname, strlen(linkname));
385}
386
387int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f)
388{
389 struct fuse_open_out arg;
390
391 memset(&arg, 0, sizeof(arg));
392 fill_open(&arg, f);
393 return send_reply_ok(req, &arg, sizeof(arg));
394}
395
396int fuse_reply_write(fuse_req_t req, size_t count)
397{
398 struct fuse_write_out arg;
399
400 memset(&arg, 0, sizeof(arg));
401 arg.size = count;
402
403 return send_reply_ok(req, &arg, sizeof(arg));
404}
405
406int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
407{
408 return send_reply_ok(req, buf, size);
409}
410
411int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
412{
413 struct fuse_statfs_out arg;
414 size_t size = req->f->conn.proto_minor < 4 ? FUSE_COMPAT_STATFS_SIZE : sizeof(arg);
415
416 memset(&arg, 0, sizeof(arg));
417 convert_statfs(stbuf, &arg.st);
418
419 return send_reply_ok(req, &arg, size);
420}
421
422int fuse_reply_xattr(fuse_req_t req, size_t count)
423{
424 struct fuse_getxattr_out arg;
425
426 memset(&arg, 0, sizeof(arg));
427 arg.size = count;
428
429 return send_reply_ok(req, &arg, sizeof(arg));
430}
431
432int fuse_reply_lock(fuse_req_t req, struct flock *lock)
433{
434 struct fuse_lk_out arg;
435
436 memset(&arg, 0, sizeof(arg));
437 arg.lk.type = lock->l_type;
438 if (lock->l_type != F_UNLCK) {
439 arg.lk.start = lock->l_start;
440 if (lock->l_len == 0)
441 arg.lk.end = OFFSET_MAX;
442 else
443 arg.lk.end = lock->l_start + lock->l_len - 1;
444 }
445 arg.lk.pid = lock->l_pid;
446 return send_reply_ok(req, &arg, sizeof(arg));
447}
448
449int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
450{
451 struct fuse_bmap_out arg;
452
453 memset(&arg, 0, sizeof(arg));
454 arg.block = idx;
455
456 return send_reply_ok(req, &arg, sizeof(arg));
457}
458
Steve Kondik79165c32015-11-09 19:43:00 -0800459int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
460{
461 struct fuse_ioctl_out arg;
462 struct iovec iov[3];
463 size_t count = 1;
464
465 memset(&arg, 0, sizeof(arg));
466 arg.result = result;
467 iov[count].iov_base = &arg;
468 iov[count].iov_len = sizeof(arg);
469 count++;
470
471 if (size) {
472 /* Note : const qualifier dropped */
473 iov[count].iov_base = (char *)(uintptr_t) buf;
474 iov[count].iov_len = size;
475 count++;
476 }
477
478 return send_reply_iov(req, 0, iov, count);
479}
480
Steve Kondik2111ad72013-07-07 12:07:44 -0700481static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
482{
483 const char *name = (const char *) inarg;
484
485 if (req->f->op.lookup)
486 req->f->op.lookup(req, nodeid, name);
487 else
488 fuse_reply_err(req, ENOSYS);
489}
490
491static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
492{
493 const struct fuse_forget_in *arg = (const struct fuse_forget_in *) inarg;
494
495 if (req->f->op.forget)
496 req->f->op.forget(req, nodeid, arg->nlookup);
497 else
498 fuse_reply_none(req);
499}
500
501static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
502{
503 (void) inarg;
504
505 if (req->f->op.getattr)
506 req->f->op.getattr(req, nodeid, NULL);
507 else
508 fuse_reply_err(req, ENOSYS);
509}
510
511static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
512{
513 const struct fuse_setattr_in *arg = (const struct fuse_setattr_in *) inarg;
514
515 if (req->f->op.setattr) {
516 struct fuse_file_info *fi = NULL;
517 struct fuse_file_info fi_store;
518 struct stat stbuf;
519 memset(&stbuf, 0, sizeof(stbuf));
520 convert_attr(arg, &stbuf);
521 if (arg->valid & FATTR_FH) {
522 memset(&fi_store, 0, sizeof(fi_store));
523 fi = &fi_store;
524 fi->fh = arg->fh;
525 fi->fh_old = fi->fh;
526 }
527 req->f->op.setattr(req, nodeid, &stbuf, arg->valid & ~FATTR_FH, fi);
528 } else
529 fuse_reply_err(req, ENOSYS);
530}
531
532static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
533{
534 const struct fuse_access_in *arg = (const struct fuse_access_in *) inarg;
535
536 if (req->f->op.access)
537 req->f->op.access(req, nodeid, arg->mask);
538 else
539 fuse_reply_err(req, ENOSYS);
540}
541
542static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
543{
544 (void) inarg;
545
546 if (req->f->op.readlink)
547 req->f->op.readlink(req, nodeid);
548 else
549 fuse_reply_err(req, ENOSYS);
550}
551
552static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
553{
554 const struct fuse_mknod_in *arg = (const struct fuse_mknod_in *) inarg;
555 const char *name = PARAM(arg);
556
Steve Kondik2111ad72013-07-07 12:07:44 -0700557 if (req->f->conn.proto_minor >= 12)
558 req->ctx.umask = arg->umask;
559 else
Steve Kondik2111ad72013-07-07 12:07:44 -0700560 name = (const char *) inarg + FUSE_COMPAT_MKNOD_IN_SIZE;
561
Steve Kondike68cb602016-08-28 00:45:36 -0700562 if (req->f->op.mknod) {
563#if defined(__SOLARIS__) && defined(_LP64)
564 /* Must unpack the device, as arg->rdev is limited to 32 bits */
565 req->f->op.mknod(req, nodeid, name, arg->mode,
566 makedev((arg->rdev >> 18) & 0x3ffff,
567 arg->rdev & 0x3fff));
568#else
Steve Kondik2111ad72013-07-07 12:07:44 -0700569 req->f->op.mknod(req, nodeid, name, arg->mode, arg->rdev);
Steve Kondike68cb602016-08-28 00:45:36 -0700570#endif
571 } else
Steve Kondik2111ad72013-07-07 12:07:44 -0700572 fuse_reply_err(req, ENOSYS);
573}
574
575static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
576{
577 const struct fuse_mkdir_in *arg = (const struct fuse_mkdir_in *) inarg;
578
Steve Kondik2111ad72013-07-07 12:07:44 -0700579 if (req->f->conn.proto_minor >= 12)
580 req->ctx.umask = arg->umask;
Steve Kondik2111ad72013-07-07 12:07:44 -0700581
582 if (req->f->op.mkdir)
583 req->f->op.mkdir(req, nodeid, PARAM(arg), arg->mode);
584 else
585 fuse_reply_err(req, ENOSYS);
586}
587
588static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
589{
590 const char *name = (const char *) inarg;
591
592 if (req->f->op.unlink)
593 req->f->op.unlink(req, nodeid, name);
594 else
595 fuse_reply_err(req, ENOSYS);
596}
597
598static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
599{
600 const char *name = (const char *) inarg;
601
602 if (req->f->op.rmdir)
603 req->f->op.rmdir(req, nodeid, name);
604 else
605 fuse_reply_err(req, ENOSYS);
606}
607
608static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
609{
610 const char *name = (const char *) inarg;
611 const char *linkname = ((const char *) inarg) + strlen((const char *) inarg) + 1;
612
613 if (req->f->op.symlink)
614 req->f->op.symlink(req, linkname, nodeid, name);
615 else
616 fuse_reply_err(req, ENOSYS);
617}
618
619static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
620{
621 const struct fuse_rename_in *arg = (const struct fuse_rename_in *) inarg;
622 const char *oldname = PARAM(arg);
623 const char *newname = oldname + strlen(oldname) + 1;
624
625 if (req->f->op.rename)
626 req->f->op.rename(req, nodeid, oldname, arg->newdir, newname);
627 else
628 fuse_reply_err(req, ENOSYS);
629}
630
631static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
632{
633 const struct fuse_link_in *arg = (const struct fuse_link_in *) inarg;
634
635 if (req->f->op.link)
636 req->f->op.link(req, arg->oldnodeid, nodeid, PARAM(arg));
637 else
638 fuse_reply_err(req, ENOSYS);
639}
640
641static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
642{
643 const struct fuse_create_in *arg = (const struct fuse_create_in *) inarg;
644
645 if (req->f->op.create) {
646 struct fuse_file_info fi;
647 const char *name = PARAM(arg);
648
649 memset(&fi, 0, sizeof(fi));
650 fi.flags = arg->flags;
651
Steve Kondik2111ad72013-07-07 12:07:44 -0700652 if (req->f->conn.proto_minor >= 12)
653 req->ctx.umask = arg->umask;
654 else
Steve Kondik2111ad72013-07-07 12:07:44 -0700655 name = (const char *) inarg + sizeof(struct fuse_open_in);
656
657 req->f->op.create(req, nodeid, name, arg->mode, &fi);
658 } else
659 fuse_reply_err(req, ENOSYS);
660}
661
662static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
663{
664 const struct fuse_open_in *arg = (const struct fuse_open_in *) inarg;
665 struct fuse_file_info fi;
666
667 memset(&fi, 0, sizeof(fi));
668 fi.flags = arg->flags;
669
670 if (req->f->op.open)
671 req->f->op.open(req, nodeid, &fi);
672 else
673 fuse_reply_open(req, &fi);
674}
675
676static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
677{
678 const struct fuse_read_in *arg = (const struct fuse_read_in *) inarg;
679
680 if (req->f->op.read) {
681 struct fuse_file_info fi;
682
683 memset(&fi, 0, sizeof(fi));
684 fi.fh = arg->fh;
685 fi.fh_old = fi.fh;
686 req->f->op.read(req, nodeid, arg->size, arg->offset, &fi);
687 } else
688 fuse_reply_err(req, ENOSYS);
689}
690
691static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
692{
693 const struct fuse_write_in *arg = (const struct fuse_write_in *) inarg;
694 struct fuse_file_info fi;
695
696 memset(&fi, 0, sizeof(fi));
697 fi.fh = arg->fh;
698 fi.fh_old = fi.fh;
699 fi.writepage = arg->write_flags & 1;
700
701 if (req->f->op.write) {
Steve Kondik2111ad72013-07-07 12:07:44 -0700702 const char *buf;
703
704 if (req->f->conn.proto_minor >= 12)
705 buf = PARAM(arg);
706 else
707 buf = ((const char*)arg) + FUSE_COMPAT_WRITE_IN_SIZE;
708 req->f->op.write(req, nodeid, buf, arg->size, arg->offset, &fi);
Steve Kondik2111ad72013-07-07 12:07:44 -0700709 } else
710 fuse_reply_err(req, ENOSYS);
711}
712
713static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
714{
715 const struct fuse_flush_in *arg = (const struct fuse_flush_in *) inarg;
716 struct fuse_file_info fi;
717
718 memset(&fi, 0, sizeof(fi));
719 fi.fh = arg->fh;
720 fi.fh_old = fi.fh;
721 fi.flush = 1;
722 if (req->f->conn.proto_minor >= 7)
723 fi.lock_owner = arg->lock_owner;
724
725 if (req->f->op.flush)
726 req->f->op.flush(req, nodeid, &fi);
727 else
728 fuse_reply_err(req, ENOSYS);
729}
730
731static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
732{
733 const struct fuse_release_in *arg = (const struct fuse_release_in *) inarg;
734 struct fuse_file_info fi;
735
736 memset(&fi, 0, sizeof(fi));
737 fi.flags = arg->flags;
738 fi.fh = arg->fh;
739 fi.fh_old = fi.fh;
740 if (req->f->conn.proto_minor >= 8) {
741 fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0;
742 fi.lock_owner = arg->lock_owner;
743 }
744
745 if (req->f->op.release)
746 req->f->op.release(req, nodeid, &fi);
747 else
748 fuse_reply_err(req, 0);
749}
750
751static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
752{
753 const struct fuse_fsync_in *arg = (const struct fuse_fsync_in *) inarg;
754 struct fuse_file_info fi;
755
756 memset(&fi, 0, sizeof(fi));
757 fi.fh = arg->fh;
758 fi.fh_old = fi.fh;
759
760 if (req->f->op.fsync)
761 req->f->op.fsync(req, nodeid, arg->fsync_flags & 1, &fi);
762 else
763 fuse_reply_err(req, ENOSYS);
764}
765
766static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
767{
768 const struct fuse_open_in *arg = (const struct fuse_open_in *) inarg;
769 struct fuse_file_info fi;
770
771 memset(&fi, 0, sizeof(fi));
772 fi.flags = arg->flags;
773
774 if (req->f->op.opendir)
775 req->f->op.opendir(req, nodeid, &fi);
776 else
777 fuse_reply_open(req, &fi);
778}
779
780static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
781{
782 const struct fuse_read_in *arg = (const struct fuse_read_in *) inarg;
783 struct fuse_file_info fi;
784
785 memset(&fi, 0, sizeof(fi));
786 fi.fh = arg->fh;
787 fi.fh_old = fi.fh;
788
789 if (req->f->op.readdir)
790 req->f->op.readdir(req, nodeid, arg->size, arg->offset, &fi);
791 else
792 fuse_reply_err(req, ENOSYS);
793}
794
795static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
796{
797 const struct fuse_release_in *arg = (const struct fuse_release_in *) inarg;
798 struct fuse_file_info fi;
799
800 memset(&fi, 0, sizeof(fi));
801 fi.flags = arg->flags;
802 fi.fh = arg->fh;
803 fi.fh_old = fi.fh;
804
805 if (req->f->op.releasedir)
806 req->f->op.releasedir(req, nodeid, &fi);
807 else
808 fuse_reply_err(req, 0);
809}
810
811static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
812{
813 const struct fuse_fsync_in *arg = (const struct fuse_fsync_in *) inarg;
814 struct fuse_file_info fi;
815
816 memset(&fi, 0, sizeof(fi));
817 fi.fh = arg->fh;
818 fi.fh_old = fi.fh;
819
820 if (req->f->op.fsyncdir)
821 req->f->op.fsyncdir(req, nodeid, arg->fsync_flags & 1, &fi);
822 else
823 fuse_reply_err(req, ENOSYS);
824}
825
826static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
827{
828 (void) nodeid;
829 (void) inarg;
830
831 if (req->f->op.statfs)
832 req->f->op.statfs(req, nodeid);
833 else {
834 struct statvfs buf = {
835 .f_namemax = 255,
836 .f_bsize = 512,
837 };
838 fuse_reply_statfs(req, &buf);
839 }
840}
841
842static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
843{
844 const struct fuse_setxattr_in *arg = (const struct fuse_setxattr_in *) inarg;
845 const char *name = PARAM(arg);
846 const char *value = name + strlen(name) + 1;
847
848 if (req->f->op.setxattr)
849 req->f->op.setxattr(req, nodeid, name, value, arg->size, arg->flags);
850 else
851 fuse_reply_err(req, ENOSYS);
852}
853
854static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
855{
856 const struct fuse_getxattr_in *arg = (const struct fuse_getxattr_in *) inarg;
857
858 if (req->f->op.getxattr)
859 req->f->op.getxattr(req, nodeid, PARAM(arg), arg->size);
860 else
861 fuse_reply_err(req, ENOSYS);
862}
863
864static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
865{
866 const struct fuse_getxattr_in *arg = (const struct fuse_getxattr_in *) inarg;
867
868 if (req->f->op.listxattr)
869 req->f->op.listxattr(req, nodeid, arg->size);
870 else
871 fuse_reply_err(req, ENOSYS);
872}
873
874static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
875{
876 const char *name = (const char *) inarg;
877
878 if (req->f->op.removexattr)
879 req->f->op.removexattr(req, nodeid, name);
880 else
881 fuse_reply_err(req, ENOSYS);
882}
883
884static void convert_fuse_file_lock(const struct fuse_file_lock *fl,
885 struct flock *flock)
886{
887 memset(flock, 0, sizeof(struct flock));
888 flock->l_type = fl->type;
889 flock->l_whence = SEEK_SET;
890 flock->l_start = fl->start;
891 if (fl->end == OFFSET_MAX)
892 flock->l_len = 0;
893 else
894 flock->l_len = fl->end - fl->start + 1;
895 flock->l_pid = fl->pid;
896}
897
898static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
899{
900 const struct fuse_lk_in *arg = (const struct fuse_lk_in *) inarg;
901 struct fuse_file_info fi;
902 struct flock flock;
903
904 memset(&fi, 0, sizeof(fi));
905 fi.fh = arg->fh;
906 fi.lock_owner = arg->owner;
907
908 convert_fuse_file_lock(&arg->lk, &flock);
909 if (req->f->op.getlk)
910 req->f->op.getlk(req, nodeid, &fi, &flock);
911 else
912 fuse_reply_err(req, ENOSYS);
913}
914
915static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid,
916 const void *inarg, int should_sleep)
917{
918 const struct fuse_lk_in *arg = (const struct fuse_lk_in *) inarg;
919 struct fuse_file_info fi;
920 struct flock flock;
921
922 memset(&fi, 0, sizeof(fi));
923 fi.fh = arg->fh;
924 fi.lock_owner = arg->owner;
925
926 convert_fuse_file_lock(&arg->lk, &flock);
927 if (req->f->op.setlk)
928 req->f->op.setlk(req, nodeid, &fi, &flock, should_sleep);
929 else
930 fuse_reply_err(req, ENOSYS);
931}
932
933static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
934{
935 do_setlk_common(req, nodeid, inarg, 0);
936}
937
938static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
939{
940 do_setlk_common(req, nodeid, inarg, 1);
941}
942
943static int find_interrupted(struct fuse_ll *f, struct fuse_req *req)
944{
945 struct fuse_req *curr;
946
947 for (curr = f->list.next; curr != &f->list; curr = curr->next) {
948 if (curr->unique == req->u.i.unique) {
949 curr->ctr++;
950 pthread_mutex_unlock(&f->lock);
951
952 /* Ugh, ugly locking */
953 pthread_mutex_lock(&curr->lock);
954 pthread_mutex_lock(&f->lock);
955 curr->interrupted = 1;
956 pthread_mutex_unlock(&f->lock);
957 if (curr->u.ni.func)
958 curr->u.ni.func(curr, curr->u.ni.data);
959 pthread_mutex_unlock(&curr->lock);
960
961 pthread_mutex_lock(&f->lock);
962 curr->ctr--;
963 if (!curr->ctr)
964 destroy_req(curr);
965
966 return 1;
967 }
968 }
969 for (curr = f->interrupts.next; curr != &f->interrupts;
970 curr = curr->next) {
971 if (curr->u.i.unique == req->u.i.unique)
972 return 1;
973 }
974 return 0;
975}
976
977static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
978{
979 const struct fuse_interrupt_in *arg = (const struct fuse_interrupt_in *) inarg;
980 struct fuse_ll *f = req->f;
981
982 (void) nodeid;
983 if (f->debug)
984 fprintf(stderr, "INTERRUPT: %llu\n", (unsigned long long) arg->unique);
985
986 req->u.i.unique = arg->unique;
987
988 pthread_mutex_lock(&f->lock);
989 if (find_interrupted(f, req))
990 destroy_req(req);
991 else
992 list_add_req(req, &f->interrupts);
993 pthread_mutex_unlock(&f->lock);
994}
995
996static struct fuse_req *check_interrupt(struct fuse_ll *f, struct fuse_req *req)
997{
998 struct fuse_req *curr;
999
1000 for (curr = f->interrupts.next; curr != &f->interrupts; curr = curr->next) {
1001 if (curr->u.i.unique == req->unique) {
1002 req->interrupted = 1;
1003 list_del_req(curr);
1004 free(curr);
1005 return NULL;
1006 }
1007 }
1008 curr = f->interrupts.next;
1009 if (curr != &f->interrupts) {
1010 list_del_req(curr);
1011 list_init_req(curr);
1012 return curr;
1013 } else
1014 return NULL;
1015}
1016
1017static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1018{
1019 const struct fuse_bmap_in *arg = (const struct fuse_bmap_in *) inarg;
1020
1021 if (req->f->op.bmap)
1022 req->f->op.bmap(req, nodeid, arg->blocksize, arg->block);
1023 else
1024 fuse_reply_err(req, ENOSYS);
1025}
1026
Steve Kondik79165c32015-11-09 19:43:00 -08001027static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1028{
1029 const struct fuse_ioctl_in *arg = (const struct fuse_ioctl_in *) inarg;
1030 unsigned int flags = arg->flags;
1031 const void *in_buf = arg->in_size ? PARAM(arg) : NULL;
1032 struct fuse_file_info fi;
1033
1034 if (flags & FUSE_IOCTL_DIR &&
1035 !(req->f->conn.want & FUSE_CAP_IOCTL_DIR)) {
1036 fuse_reply_err(req, ENOTTY);
1037 return;
1038 }
1039
1040 memset(&fi, 0, sizeof(fi));
1041 fi.fh = arg->fh;
1042
1043/* TODO JPA (need req->ioctl_64bit in obscure fuse_req_t)
1044// probably a 64 bit ioctl on a 32-bit cpu
1045// this is to forward a request from the kernel
1046 if (sizeof(void *) == 4 && req->f->conn.proto_minor >= 16 &&
1047 !(flags & FUSE_IOCTL_32BIT)) {
1048 req->ioctl_64bit = 1;
1049 }
1050*/
1051
1052 if (req->f->op.ioctl)
1053 req->f->op.ioctl(req, nodeid, arg->cmd,
1054 (void *)(uintptr_t)arg->arg, &fi, flags,
1055 in_buf, arg->in_size, arg->out_size);
1056 else
1057 fuse_reply_err(req, ENOSYS);
1058}
1059
Steve Kondik2111ad72013-07-07 12:07:44 -07001060static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1061{
1062 const struct fuse_init_in *arg = (const struct fuse_init_in *) inarg;
1063 struct fuse_init_out outarg;
1064 struct fuse_ll *f = req->f;
1065 size_t bufsize = fuse_chan_bufsize(req->ch);
1066
1067 (void) nodeid;
1068 if (f->debug) {
1069 fprintf(stderr, "INIT: %u.%u\n", arg->major, arg->minor);
1070 if (arg->major > 7 || (arg->major == 7 && arg->minor >= 6)) {
1071 fprintf(stderr, "flags=0x%08x\n", arg->flags);
1072 fprintf(stderr, "max_readahead=0x%08x\n", arg->max_readahead);
1073 }
1074 }
1075 f->conn.proto_major = arg->major;
1076 f->conn.proto_minor = arg->minor;
1077
1078 if (arg->major < 7) {
1079 fprintf(stderr, "fuse: unsupported protocol version: %u.%u\n",
1080 arg->major, arg->minor);
1081 fuse_reply_err(req, EPROTO);
1082 return;
1083 }
1084
1085 if (arg->major > 7 || (arg->major == 7 && arg->minor >= 6)) {
1086 if (f->conn.async_read)
1087 f->conn.async_read = arg->flags & FUSE_ASYNC_READ;
1088 if (arg->max_readahead < f->conn.max_readahead)
1089 f->conn.max_readahead = arg->max_readahead;
1090#ifdef POSIXACLS
1091 if (arg->flags & FUSE_DONT_MASK)
1092 f->conn.capable |= FUSE_CAP_DONT_MASK;
1093#endif
1094 if (arg->flags & FUSE_BIG_WRITES)
1095 f->conn.capable |= FUSE_CAP_BIG_WRITES;
Steve Kondik79165c32015-11-09 19:43:00 -08001096 if (arg->flags & FUSE_HAS_IOCTL_DIR)
1097 f->conn.capable |= FUSE_CAP_IOCTL_DIR;
Steve Kondik2111ad72013-07-07 12:07:44 -07001098 } else {
1099 f->conn.async_read = 0;
1100 f->conn.max_readahead = 0;
1101 }
1102
1103 if (bufsize < FUSE_MIN_READ_BUFFER) {
1104 fprintf(stderr, "fuse: warning: buffer size too small: %zu\n",
1105 bufsize);
1106 bufsize = FUSE_MIN_READ_BUFFER;
1107 }
1108
1109 bufsize -= 4096;
1110 if (bufsize < f->conn.max_write)
1111 f->conn.max_write = bufsize;
1112
1113 f->got_init = 1;
1114 if (f->op.init)
1115 f->op.init(f->userdata, &f->conn);
1116
1117 memset(&outarg, 0, sizeof(outarg));
1118 outarg.major = FUSE_KERNEL_VERSION;
1119 /*
Steve Kondik79165c32015-11-09 19:43:00 -08001120 * Suggest using protocol 7.18 when available, and fallback
1121 * to 7.12 or even earlier when running on an old kernel.
1122 * Protocol 7.12 has the ability to process the umask
1123 * conditionnally (as needed if POSIXACLS is set)
1124 * Protocol 7.18 has the ability to process the ioctls
Steve Kondik2111ad72013-07-07 12:07:44 -07001125 */
Steve Kondik79165c32015-11-09 19:43:00 -08001126 if (arg->major > 7 || (arg->major == 7 && arg->minor >= 18)) {
Steve Kondik2111ad72013-07-07 12:07:44 -07001127 outarg.minor = FUSE_KERNEL_MINOR_VERSION;
Steve Kondik79165c32015-11-09 19:43:00 -08001128 if (f->conn.want & FUSE_CAP_IOCTL_DIR)
1129 outarg.flags |= FUSE_HAS_IOCTL_DIR;
1130#ifdef POSIXACLS
1131 if (f->conn.want & FUSE_CAP_DONT_MASK)
1132 outarg.flags |= FUSE_DONT_MASK;
Steve Kondik2111ad72013-07-07 12:07:44 -07001133#endif
Steve Kondik79165c32015-11-09 19:43:00 -08001134 } else {
1135 /* Never use a version more recent than supported by the kernel */
1136 if ((arg->major < FUSE_KERNEL_MAJOR_FALLBACK)
1137 || ((arg->major == FUSE_KERNEL_MAJOR_FALLBACK)
1138 && (arg->minor < FUSE_KERNEL_MINOR_FALLBACK))) {
1139 outarg.major = arg->major;
1140 outarg.minor = arg->minor;
1141 } else {
1142 outarg.major = FUSE_KERNEL_MAJOR_FALLBACK;
1143 outarg.minor = FUSE_KERNEL_MINOR_FALLBACK;
1144#ifdef POSIXACLS
1145 if (f->conn.want & FUSE_CAP_DONT_MASK)
1146 outarg.flags |= FUSE_DONT_MASK;
1147#endif
1148 }
1149 }
Steve Kondik2111ad72013-07-07 12:07:44 -07001150 if (f->conn.async_read)
1151 outarg.flags |= FUSE_ASYNC_READ;
1152 if (f->op.getlk && f->op.setlk)
1153 outarg.flags |= FUSE_POSIX_LOCKS;
Steve Kondik2111ad72013-07-07 12:07:44 -07001154 if (f->conn.want & FUSE_CAP_BIG_WRITES)
1155 outarg.flags |= FUSE_BIG_WRITES;
1156 outarg.max_readahead = f->conn.max_readahead;
1157 outarg.max_write = f->conn.max_write;
1158
1159 if (f->debug) {
1160 fprintf(stderr, " INIT: %u.%u\n", outarg.major, outarg.minor);
1161 fprintf(stderr, " flags=0x%08x\n", outarg.flags);
1162 fprintf(stderr, " max_readahead=0x%08x\n", outarg.max_readahead);
1163 fprintf(stderr, " max_write=0x%08x\n", outarg.max_write);
1164 }
1165
1166 send_reply_ok(req, &outarg, arg->minor < 5 ? 8 : sizeof(outarg));
1167}
1168
1169static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1170{
1171 struct fuse_ll *f = req->f;
1172
1173 (void) nodeid;
1174 (void) inarg;
1175
1176 f->got_destroy = 1;
1177 if (f->op.destroy)
1178 f->op.destroy(f->userdata);
1179
1180 send_reply_ok(req, NULL, 0);
1181}
1182
1183void *fuse_req_userdata(fuse_req_t req)
1184{
1185 return req->f->userdata;
1186}
1187
1188const struct fuse_ctx *fuse_req_ctx(fuse_req_t req)
1189{
1190 return &req->ctx;
1191}
1192
1193void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func,
1194 void *data)
1195{
1196 pthread_mutex_lock(&req->lock);
1197 req->u.ni.func = func;
1198 req->u.ni.data = data;
1199 if (req->interrupted && func)
1200 func(req, data);
1201 pthread_mutex_unlock(&req->lock);
1202}
1203
1204int fuse_req_interrupted(fuse_req_t req)
1205{
1206 int interrupted;
1207
1208 pthread_mutex_lock(&req->f->lock);
1209 interrupted = req->interrupted;
1210 pthread_mutex_unlock(&req->f->lock);
1211
1212 return interrupted;
1213}
1214
1215static struct {
1216 void (*func)(fuse_req_t, fuse_ino_t, const void *);
1217 const char *name;
1218} fuse_ll_ops[] = {
1219 [FUSE_LOOKUP] = { do_lookup, "LOOKUP" },
1220 [FUSE_FORGET] = { do_forget, "FORGET" },
1221 [FUSE_GETATTR] = { do_getattr, "GETATTR" },
1222 [FUSE_SETATTR] = { do_setattr, "SETATTR" },
1223 [FUSE_READLINK] = { do_readlink, "READLINK" },
1224 [FUSE_SYMLINK] = { do_symlink, "SYMLINK" },
1225 [FUSE_MKNOD] = { do_mknod, "MKNOD" },
1226 [FUSE_MKDIR] = { do_mkdir, "MKDIR" },
1227 [FUSE_UNLINK] = { do_unlink, "UNLINK" },
1228 [FUSE_RMDIR] = { do_rmdir, "RMDIR" },
1229 [FUSE_RENAME] = { do_rename, "RENAME" },
1230 [FUSE_LINK] = { do_link, "LINK" },
1231 [FUSE_OPEN] = { do_open, "OPEN" },
1232 [FUSE_READ] = { do_read, "READ" },
1233 [FUSE_WRITE] = { do_write, "WRITE" },
1234 [FUSE_STATFS] = { do_statfs, "STATFS" },
1235 [FUSE_RELEASE] = { do_release, "RELEASE" },
1236 [FUSE_FSYNC] = { do_fsync, "FSYNC" },
1237 [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" },
1238 [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" },
1239 [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" },
1240 [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" },
1241 [FUSE_FLUSH] = { do_flush, "FLUSH" },
1242 [FUSE_INIT] = { do_init, "INIT" },
1243 [FUSE_OPENDIR] = { do_opendir, "OPENDIR" },
1244 [FUSE_READDIR] = { do_readdir, "READDIR" },
1245 [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" },
1246 [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" },
1247 [FUSE_GETLK] = { do_getlk, "GETLK" },
1248 [FUSE_SETLK] = { do_setlk, "SETLK" },
1249 [FUSE_SETLKW] = { do_setlkw, "SETLKW" },
1250 [FUSE_ACCESS] = { do_access, "ACCESS" },
1251 [FUSE_CREATE] = { do_create, "CREATE" },
1252 [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" },
1253 [FUSE_BMAP] = { do_bmap, "BMAP" },
Steve Kondik79165c32015-11-09 19:43:00 -08001254 [FUSE_IOCTL] = { do_ioctl, "IOCTL" },
Steve Kondik2111ad72013-07-07 12:07:44 -07001255 [FUSE_DESTROY] = { do_destroy, "DESTROY" },
1256};
1257
1258#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
1259
1260static const char *opname(enum fuse_opcode opcode)
1261{
1262 if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name)
1263 return "???";
1264 else
1265 return fuse_ll_ops[opcode].name;
1266}
1267
1268static void fuse_ll_process(void *data, const char *buf, size_t len,
1269 struct fuse_chan *ch)
1270{
1271 struct fuse_ll *f = (struct fuse_ll *) data;
1272 const struct fuse_in_header *in = (const struct fuse_in_header *) buf;
1273 const void *inarg = buf + sizeof(struct fuse_in_header);
1274 struct fuse_req *req;
1275
1276 if (f->debug)
1277 fprintf(stderr, "unique: %llu, opcode: %s (%i), nodeid: %lu, insize: %zu\n",
1278 (unsigned long long) in->unique,
1279 opname((enum fuse_opcode) in->opcode), in->opcode,
1280 (unsigned long) in->nodeid, len);
1281
1282 req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req));
1283 if (req == NULL) {
1284 fprintf(stderr, "fuse: failed to allocate request\n");
1285 return;
1286 }
1287
1288 req->f = f;
1289 req->unique = in->unique;
1290 req->ctx.uid = in->uid;
1291 req->ctx.gid = in->gid;
1292 req->ctx.pid = in->pid;
1293 req->ch = ch;
1294 req->ctr = 1;
1295 list_init_req(req);
1296 fuse_mutex_init(&req->lock);
1297
1298 if (!f->got_init && in->opcode != FUSE_INIT)
1299 fuse_reply_err(req, EIO);
1300 else if (f->allow_root && in->uid != f->owner && in->uid != 0 &&
1301 in->opcode != FUSE_INIT && in->opcode != FUSE_READ &&
1302 in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC &&
1303 in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR &&
1304 in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR) {
1305 fuse_reply_err(req, EACCES);
1306 } else if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
1307 fuse_reply_err(req, ENOSYS);
1308 else {
1309 if (in->opcode != FUSE_INTERRUPT) {
1310 struct fuse_req *intr;
1311 pthread_mutex_lock(&f->lock);
1312 intr = check_interrupt(f, req);
1313 list_add_req(req, &f->list);
1314 pthread_mutex_unlock(&f->lock);
1315 if (intr)
1316 fuse_reply_err(intr, EAGAIN);
1317 }
1318 fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
1319 }
1320}
1321
1322enum {
1323 KEY_HELP,
1324 KEY_VERSION,
1325};
1326
1327static struct fuse_opt fuse_ll_opts[] = {
1328 { "debug", offsetof(struct fuse_ll, debug), 1 },
1329 { "-d", offsetof(struct fuse_ll, debug), 1 },
1330 { "allow_root", offsetof(struct fuse_ll, allow_root), 1 },
1331 { "max_write=%u", offsetof(struct fuse_ll, conn.max_write), 0 },
1332 { "max_readahead=%u", offsetof(struct fuse_ll, conn.max_readahead), 0 },
1333 { "async_read", offsetof(struct fuse_ll, conn.async_read), 1 },
1334 { "sync_read", offsetof(struct fuse_ll, conn.async_read), 0 },
1335 FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_DISCARD),
1336 FUSE_OPT_KEY("-h", KEY_HELP),
1337 FUSE_OPT_KEY("--help", KEY_HELP),
1338 FUSE_OPT_KEY("-V", KEY_VERSION),
1339 FUSE_OPT_KEY("--version", KEY_VERSION),
1340 FUSE_OPT_END
1341};
1342
1343static void fuse_ll_version(void)
1344{
1345 fprintf(stderr, "using FUSE kernel interface version %i.%i\n",
1346 FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
1347}
1348
1349static void fuse_ll_help(void)
1350{
1351 fprintf(stderr,
1352" -o max_write=N set maximum size of write requests\n"
1353" -o max_readahead=N set maximum readahead\n"
1354" -o async_read perform reads asynchronously (default)\n"
1355" -o sync_read perform reads synchronously\n");
1356}
1357
1358static int fuse_ll_opt_proc(void *data, const char *arg, int key,
1359 struct fuse_args *outargs)
1360{
1361 (void) data; (void) outargs;
1362
1363 switch (key) {
1364 case KEY_HELP:
1365 fuse_ll_help();
1366 break;
1367
1368 case KEY_VERSION:
1369 fuse_ll_version();
1370 break;
1371
1372 default:
1373 fprintf(stderr, "fuse: unknown option `%s'\n", arg);
1374 }
1375
1376 return -1;
1377}
1378
1379#ifdef __SOLARIS__
1380
1381int fuse_lowlevel_is_lib_option(const char *opt)
1382{
1383 return fuse_opt_match(fuse_ll_opts, opt);
1384}
1385
1386#endif /* __SOLARIS__ */
1387
1388static void fuse_ll_destroy(void *data)
1389{
1390 struct fuse_ll *f = (struct fuse_ll *) data;
1391
1392 if (f->got_init && !f->got_destroy) {
1393 if (f->op.destroy)
1394 f->op.destroy(f->userdata);
1395 }
1396
1397 pthread_mutex_destroy(&f->lock);
1398 free(f);
1399}
1400
1401struct fuse_session *fuse_lowlevel_new(struct fuse_args *args,
1402 const struct fuse_lowlevel_ops *op,
1403 size_t op_size, void *userdata)
1404{
1405 struct fuse_ll *f;
1406 struct fuse_session *se;
1407 struct fuse_session_ops sop = {
1408 .process = fuse_ll_process,
1409 .destroy = fuse_ll_destroy,
1410 };
1411
1412 if (sizeof(struct fuse_lowlevel_ops) < op_size) {
1413 fprintf(stderr, "fuse: warning: library too old, some operations may not work\n");
1414 op_size = sizeof(struct fuse_lowlevel_ops);
1415 }
1416
1417 f = (struct fuse_ll *) calloc(1, sizeof(struct fuse_ll));
1418 if (f == NULL) {
1419 fprintf(stderr, "fuse: failed to allocate fuse object\n");
1420 goto out;
1421 }
1422
1423 f->conn.async_read = 1;
1424 f->conn.max_write = UINT_MAX;
1425 f->conn.max_readahead = UINT_MAX;
1426 list_init_req(&f->list);
1427 list_init_req(&f->interrupts);
1428 fuse_mutex_init(&f->lock);
1429
1430 if (fuse_opt_parse(args, f, fuse_ll_opts, fuse_ll_opt_proc) == -1)
1431 goto out_free;
1432
1433 memcpy(&f->op, op, op_size);
1434 f->owner = getuid();
1435 f->userdata = userdata;
1436
1437 se = fuse_session_new(&sop, f);
1438 if (!se)
1439 goto out_free;
1440
1441 return se;
1442
1443 out_free:
1444 free(f);
1445 out:
1446 return NULL;
1447}