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