]> git.proxmox.com Git - ceph.git/blob - ceph/src/client/fuse_ll.cc
import 15.2.4
[ceph.git] / ceph / src / client / fuse_ll.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15 #include <sys/file.h>
16 #include <sys/types.h>
17 #include <sys/wait.h>
18 #include <limits.h>
19 #include <signal.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26
27 // ceph
28 #include "common/errno.h"
29 #include "common/safe_io.h"
30 #include "include/types.h"
31 #include "Client.h"
32 #include "Fh.h"
33 #include "ioctl.h"
34 #include "common/config.h"
35 #include "include/ceph_assert.h"
36 #include "include/cephfs/ceph_ll_client.h"
37 #include "include/ceph_fuse.h"
38
39 #include "fuse_ll.h"
40 #include <fuse_lowlevel.h>
41
42 #define dout_context g_ceph_context
43
44 #define FINO_INO(x) ((x) & ((1ull<<48)-1ull))
45 #define FINO_STAG(x) ((x) >> 48)
46 #define MAKE_FINO(i,s) ((i) | ((int64_t)(s) << 48))
47 #define STAG_MASK 0xffff
48
49 #define MINORBITS 20
50 #define MINORMASK ((1U << MINORBITS) - 1)
51
52 #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
53 #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
54 #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
55
56 static uint32_t new_encode_dev(dev_t dev)
57 {
58 unsigned major = MAJOR(dev);
59 unsigned minor = MINOR(dev);
60 return (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12);
61 }
62
63 static dev_t new_decode_dev(uint32_t dev)
64 {
65 unsigned major = (dev & 0xfff00) >> 8;
66 unsigned minor = (dev & 0xff) | ((dev >> 12) & 0xfff00);
67 return MKDEV(major, minor);
68 }
69
70 class CephFuse::Handle {
71 public:
72 Handle(Client *c, int fd);
73 ~Handle();
74
75 int init(int argc, const char *argv[]);
76 int start();
77 int loop();
78 void finalize();
79
80 uint64_t fino_snap(uint64_t fino);
81 uint64_t make_fake_ino(inodeno_t ino, snapid_t snapid);
82 Inode * iget(fuse_ino_t fino);
83 void iput(Inode *in);
84
85 int fd_on_success;
86 Client *client;
87
88 struct fuse_session *se;
89 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
90 struct fuse_cmdline_opts opts;
91 #else
92 struct fuse_chan *ch;
93 char *mountpoint;
94 #endif
95
96 ceph::mutex stag_lock = ceph::make_mutex("fuse_ll.cc stag_lock");
97 int last_stag;
98
99 ceph::unordered_map<uint64_t,int> snap_stag_map;
100 ceph::unordered_map<int,uint64_t> stag_snap_map;
101
102 pthread_key_t fuse_req_key = 0;
103 void set_fuse_req(fuse_req_t);
104 fuse_req_t get_fuse_req();
105
106 struct fuse_args args;
107 };
108
109 static int getgroups(fuse_req_t req, gid_t **sgids)
110 {
111 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
112 ceph_assert(sgids);
113 int c = fuse_req_getgroups(req, 0, NULL);
114 if (c < 0) {
115 return c;
116 }
117 if (c == 0) {
118 return 0;
119 }
120
121 gid_t *gids = new (std::nothrow) gid_t[c];
122 if (!gids) {
123 return -ENOMEM;
124 }
125 c = fuse_req_getgroups(req, c, gids);
126 if (c < 0) {
127 delete[] gids;
128 } else {
129 *sgids = gids;
130 }
131 return c;
132 #endif
133 return -ENOSYS;
134 }
135
136 static void get_fuse_groups(UserPerm& perms, fuse_req_t req)
137 {
138 if (g_conf().get_val<bool>("fuse_set_user_groups")) {
139 gid_t *gids = NULL;
140 int count = getgroups(req, &gids);
141
142 if (count > 0) {
143 perms.init_gids(gids, count);
144 } else if (count < 0) {
145 derr << __func__ << ": getgroups failed: " << cpp_strerror(-count)
146 << dendl;
147 }
148 }
149 }
150
151
152 static CephFuse::Handle *fuse_ll_req_prepare(fuse_req_t req)
153 {
154 CephFuse::Handle *cfuse = (CephFuse::Handle *)fuse_req_userdata(req);
155 cfuse->set_fuse_req(req);
156 return cfuse;
157 }
158
159 static void fuse_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
160 {
161 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
162 const struct fuse_ctx *ctx = fuse_req_ctx(req);
163 struct fuse_entry_param fe;
164 Inode *i2, *i1 = cfuse->iget(parent); // see below
165 int r;
166 UserPerm perms(ctx->uid, ctx->gid);
167 get_fuse_groups(perms, req);
168
169 if (!i1)
170 {
171 r = cfuse->client->lookup_ino(parent, perms, &i1);
172 if (r < 0) {
173 fuse_reply_err(req, -r);
174 return;
175 }
176 }
177
178 memset(&fe, 0, sizeof(fe));
179 r = cfuse->client->ll_lookup(i1, name, &fe.attr, &i2, perms);
180 if (r >= 0) {
181 fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev);
182 fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev);
183 fuse_reply_entry(req, &fe);
184 } else {
185 fuse_reply_err(req, -r);
186 }
187
188 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
189 // fuse_ll_forget()
190 cfuse->iput(i1);
191 }
192
193 static void fuse_ll_forget(fuse_req_t req, fuse_ino_t ino,
194 long unsigned nlookup)
195 {
196 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
197 cfuse->client->ll_forget(cfuse->iget(ino), nlookup+1);
198 fuse_reply_none(req);
199 }
200
201 static void fuse_ll_getattr(fuse_req_t req, fuse_ino_t ino,
202 struct fuse_file_info *fi)
203 {
204 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
205 const struct fuse_ctx *ctx = fuse_req_ctx(req);
206 Inode *in = cfuse->iget(ino);
207 struct stat stbuf;
208 UserPerm perms(ctx->uid, ctx->gid);
209 get_fuse_groups(perms, req);
210
211 (void) fi; // XXX
212
213 if (cfuse->client->ll_getattr(in, &stbuf, perms)
214 == 0) {
215 stbuf.st_ino = cfuse->make_fake_ino(stbuf.st_ino, stbuf.st_dev);
216 stbuf.st_rdev = new_encode_dev(stbuf.st_rdev);
217 fuse_reply_attr(req, &stbuf, 0);
218 } else
219 fuse_reply_err(req, ENOENT);
220
221 cfuse->iput(in); // iput required
222 }
223
224 static void fuse_ll_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
225 int to_set, struct fuse_file_info *fi)
226 {
227 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
228 const struct fuse_ctx *ctx = fuse_req_ctx(req);
229 Inode *in = cfuse->iget(ino);
230 UserPerm perms(ctx->uid, ctx->gid);
231 get_fuse_groups(perms, req);
232
233 int mask = 0;
234 if (to_set & FUSE_SET_ATTR_MODE) mask |= CEPH_SETATTR_MODE;
235 if (to_set & FUSE_SET_ATTR_UID) mask |= CEPH_SETATTR_UID;
236 if (to_set & FUSE_SET_ATTR_GID) mask |= CEPH_SETATTR_GID;
237 if (to_set & FUSE_SET_ATTR_MTIME) mask |= CEPH_SETATTR_MTIME;
238 if (to_set & FUSE_SET_ATTR_ATIME) mask |= CEPH_SETATTR_ATIME;
239 if (to_set & FUSE_SET_ATTR_SIZE) mask |= CEPH_SETATTR_SIZE;
240 #if !defined(__APPLE__)
241 if (to_set & FUSE_SET_ATTR_MTIME_NOW) mask |= CEPH_SETATTR_MTIME_NOW;
242 if (to_set & FUSE_SET_ATTR_ATIME_NOW) mask |= CEPH_SETATTR_ATIME_NOW;
243 #endif
244
245 int r = cfuse->client->ll_setattr(in, attr, mask, perms);
246 if (r == 0)
247 fuse_reply_attr(req, attr, 0);
248 else
249 fuse_reply_err(req, -r);
250
251 cfuse->iput(in); // iput required
252 }
253
254 // XATTRS
255
256 static void fuse_ll_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
257 const char *value, size_t size,
258 int flags
259 #if defined(__APPLE__)
260 ,uint32_t pos
261 #endif
262 )
263 {
264 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
265 const struct fuse_ctx *ctx = fuse_req_ctx(req);
266 Inode *in = cfuse->iget(ino);
267 UserPerm perms(ctx->uid, ctx->gid);
268 get_fuse_groups(perms, req);
269
270 int r = cfuse->client->ll_setxattr(in, name, value, size, flags, perms);
271 fuse_reply_err(req, -r);
272
273 cfuse->iput(in); // iput required
274 }
275
276 static void fuse_ll_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
277 {
278 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
279 const struct fuse_ctx *ctx = fuse_req_ctx(req);
280 Inode *in = cfuse->iget(ino);
281 char buf[size];
282 UserPerm perms(ctx->uid, ctx->gid);
283 get_fuse_groups(perms, req);
284
285 int r = cfuse->client->ll_listxattr(in, buf, size, perms);
286 if (size == 0 && r >= 0)
287 fuse_reply_xattr(req, r);
288 else if (r >= 0)
289 fuse_reply_buf(req, buf, r);
290 else
291 fuse_reply_err(req, -r);
292
293 cfuse->iput(in); // iput required
294 }
295
296 static void fuse_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
297 size_t size
298 #if defined(__APPLE__)
299 ,uint32_t position
300 #endif
301 )
302 {
303 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
304 const struct fuse_ctx *ctx = fuse_req_ctx(req);
305 Inode *in = cfuse->iget(ino);
306 char buf[size];
307 UserPerm perms(ctx->uid, ctx->gid);
308 get_fuse_groups(perms, req);
309
310 int r = cfuse->client->ll_getxattr(in, name, buf, size, perms);
311 if (size == 0 && r >= 0)
312 fuse_reply_xattr(req, r);
313 else if (r >= 0)
314 fuse_reply_buf(req, buf, r);
315 else
316 fuse_reply_err(req, -r);
317
318 cfuse->iput(in); // iput required
319 }
320
321 static void fuse_ll_removexattr(fuse_req_t req, fuse_ino_t ino,
322 const char *name)
323 {
324 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
325 const struct fuse_ctx *ctx = fuse_req_ctx(req);
326 Inode *in = cfuse->iget(ino);
327 UserPerm perms(ctx->uid, ctx->gid);
328 get_fuse_groups(perms, req);
329
330 int r = cfuse->client->ll_removexattr(in, name, perms);
331 fuse_reply_err(req, -r);
332
333 cfuse->iput(in); // iput required
334 }
335
336 static void fuse_ll_opendir(fuse_req_t req, fuse_ino_t ino,
337 struct fuse_file_info *fi)
338 {
339 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
340 const struct fuse_ctx *ctx = fuse_req_ctx(req);
341 Inode *in = cfuse->iget(ino);
342 void *dirp;
343
344 UserPerm perms(ctx->uid, ctx->gid);
345 get_fuse_groups(perms, req);
346
347 int r = cfuse->client->ll_opendir(in, fi->flags, (dir_result_t **)&dirp,
348 perms);
349 if (r >= 0) {
350 fi->fh = (uint64_t)dirp;
351 fuse_reply_open(req, fi);
352 } else {
353 fuse_reply_err(req, -r);
354 }
355
356 cfuse->iput(in); // iput required
357 }
358
359 static void fuse_ll_readlink(fuse_req_t req, fuse_ino_t ino)
360 {
361 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
362 const struct fuse_ctx *ctx = fuse_req_ctx(req);
363 Inode *in = cfuse->iget(ino);
364 char buf[PATH_MAX + 1]; // leave room for a null terminator
365 UserPerm perms(ctx->uid, ctx->gid);
366 get_fuse_groups(perms, req);
367
368 int r = cfuse->client->ll_readlink(in, buf, sizeof(buf) - 1, perms);
369 if (r >= 0) {
370 buf[r] = '\0';
371 fuse_reply_readlink(req, buf);
372 } else {
373 fuse_reply_err(req, -r);
374 }
375
376 cfuse->iput(in); // iput required
377 }
378
379 static void fuse_ll_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
380 mode_t mode, dev_t rdev)
381 {
382 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
383 const struct fuse_ctx *ctx = fuse_req_ctx(req);
384 Inode *i2, *i1 = cfuse->iget(parent);
385 struct fuse_entry_param fe;
386 UserPerm perms(ctx->uid, ctx->gid);
387 get_fuse_groups(perms, req);
388
389 memset(&fe, 0, sizeof(fe));
390
391 int r = cfuse->client->ll_mknod(i1, name, mode, new_decode_dev(rdev),
392 &fe.attr, &i2, perms);
393 if (r == 0) {
394 fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev);
395 fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev);
396 fuse_reply_entry(req, &fe);
397 } else {
398 fuse_reply_err(req, -r);
399 }
400
401 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
402 // fuse_ll_forget()
403 cfuse->iput(i1); // iput required
404 }
405
406 static void fuse_ll_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
407 mode_t mode)
408 {
409 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
410 const struct fuse_ctx *ctx = fuse_req_ctx(req);
411 Inode *i2, *i1;
412 struct fuse_entry_param fe;
413
414 memset(&fe, 0, sizeof(fe));
415 UserPerm perm(ctx->uid, ctx->gid);
416 get_fuse_groups(perm, req);
417 #ifdef HAVE_SYS_SYNCFS
418 auto fuse_multithreaded = cfuse->client->cct->_conf.get_val<bool>(
419 "fuse_multithreaded");
420 auto fuse_syncfs_on_mksnap = cfuse->client->cct->_conf.get_val<bool>(
421 "fuse_syncfs_on_mksnap");
422 if (cfuse->fino_snap(parent) == CEPH_SNAPDIR &&
423 fuse_multithreaded && fuse_syncfs_on_mksnap) {
424 int err = 0;
425 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
426 int fd = ::open(cfuse->opts.mountpoint, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
427 #else
428 int fd = ::open(cfuse->mountpoint, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
429 #endif
430 if (fd < 0) {
431 err = errno;
432 } else {
433 int r = ::syncfs(fd);
434 if (r < 0)
435 err = errno;
436 ::close(fd);
437 }
438 if (err) {
439 fuse_reply_err(req, err);
440 return;
441 }
442 }
443 #endif
444
445 i1 = cfuse->iget(parent);
446 int r = cfuse->client->ll_mkdir(i1, name, mode, &fe.attr, &i2, perm);
447 if (r == 0) {
448 fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev);
449 fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev);
450 fuse_reply_entry(req, &fe);
451 } else {
452 fuse_reply_err(req, -r);
453 }
454
455 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
456 // fuse_ll_forget()
457 cfuse->iput(i1); // iput required
458 }
459
460 static void fuse_ll_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
461 {
462 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
463 const struct fuse_ctx *ctx = fuse_req_ctx(req);
464 Inode *in = cfuse->iget(parent);
465 UserPerm perm(ctx->uid, ctx->gid);
466 get_fuse_groups(perm, req);
467
468 int r = cfuse->client->ll_unlink(in, name, perm);
469 fuse_reply_err(req, -r);
470
471 cfuse->iput(in); // iput required
472 }
473
474 static void fuse_ll_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
475 {
476 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
477 const struct fuse_ctx *ctx = fuse_req_ctx(req);
478 Inode *in = cfuse->iget(parent);
479 UserPerm perms(ctx->uid, ctx->gid);
480 get_fuse_groups(perms, req);
481
482 int r = cfuse->client->ll_rmdir(in, name, perms);
483 fuse_reply_err(req, -r);
484
485 cfuse->iput(in); // iput required
486 }
487
488 static void fuse_ll_symlink(fuse_req_t req, const char *existing,
489 fuse_ino_t parent, const char *name)
490 {
491 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
492 const struct fuse_ctx *ctx = fuse_req_ctx(req);
493 Inode *i2, *i1 = cfuse->iget(parent);
494 struct fuse_entry_param fe;
495 UserPerm perms(ctx->uid, ctx->gid);
496 get_fuse_groups(perms, req);
497
498 memset(&fe, 0, sizeof(fe));
499
500 int r = cfuse->client->ll_symlink(i1, name, existing, &fe.attr, &i2, perms);
501 if (r == 0) {
502 fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev);
503 fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev);
504 fuse_reply_entry(req, &fe);
505 } else {
506 fuse_reply_err(req, -r);
507 }
508
509 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
510 // fuse_ll_forget()
511 cfuse->iput(i1); // iput required
512 }
513
514 static void fuse_ll_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
515 fuse_ino_t newparent, const char *newname
516 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
517 , unsigned int flags
518 #endif
519 )
520 {
521 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
522 const struct fuse_ctx *ctx = fuse_req_ctx(req);
523 Inode *in = cfuse->iget(parent);
524 Inode *nin = cfuse->iget(newparent);
525 UserPerm perm(ctx->uid, ctx->gid);
526 get_fuse_groups(perm, req);
527
528 int r = cfuse->client->ll_rename(in, name, nin, newname, perm);
529 fuse_reply_err(req, -r);
530
531 cfuse->iput(in); // iputs required
532 cfuse->iput(nin);
533 }
534
535 static void fuse_ll_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
536 const char *newname)
537 {
538 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
539 const struct fuse_ctx *ctx = fuse_req_ctx(req);
540 Inode *in = cfuse->iget(ino);
541 Inode *nin = cfuse->iget(newparent);
542 struct fuse_entry_param fe;
543
544 memset(&fe, 0, sizeof(fe));
545 UserPerm perm(ctx->uid, ctx->gid);
546 get_fuse_groups(perm, req);
547
548 /*
549 * Note that we could successfully link, but then fail the subsequent
550 * getattr and return an error. Perhaps we should ignore getattr errors,
551 * but then how do we tell FUSE that the attrs are bogus?
552 */
553 int r = cfuse->client->ll_link(in, nin, newname, perm);
554 if (r == 0) {
555 r = cfuse->client->ll_getattr(in, &fe.attr, perm);
556 if (r == 0) {
557 fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev);
558 fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev);
559 fuse_reply_entry(req, &fe);
560 }
561 }
562
563 if (r != 0) {
564 /*
565 * Many ll operations in libcephfs return an extra inode reference, but
566 * ll_link currently does not. Still, FUSE needs one for the new dentry,
567 * so we commandeer the reference taken earlier when ll_link is successful.
568 * On error however, we must put that reference.
569 */
570 cfuse->iput(in);
571 fuse_reply_err(req, -r);
572 }
573
574 cfuse->iput(nin);
575 }
576
577 static void fuse_ll_open(fuse_req_t req, fuse_ino_t ino,
578 struct fuse_file_info *fi)
579 {
580 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
581 const struct fuse_ctx *ctx = fuse_req_ctx(req);
582 Inode *in = cfuse->iget(ino);
583 Fh *fh = NULL;
584 UserPerm perms(ctx->uid, ctx->gid);
585 get_fuse_groups(perms, req);
586
587 int r = cfuse->client->ll_open(in, fi->flags, &fh, perms);
588 if (r == 0) {
589 fi->fh = (uint64_t)fh;
590 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
591 auto fuse_disable_pagecache = cfuse->client->cct->_conf.get_val<bool>(
592 "fuse_disable_pagecache");
593 auto fuse_use_invalidate_cb = cfuse->client->cct->_conf.get_val<bool>(
594 "fuse_use_invalidate_cb");
595 if (fuse_disable_pagecache)
596 fi->direct_io = 1;
597 else if (fuse_use_invalidate_cb)
598 fi->keep_cache = 1;
599 #endif
600 fuse_reply_open(req, fi);
601 } else {
602 fuse_reply_err(req, -r);
603 }
604
605 cfuse->iput(in); // iput required
606 }
607
608 static void fuse_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
609 struct fuse_file_info *fi)
610 {
611 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
612 Fh *fh = reinterpret_cast<Fh*>(fi->fh);
613 bufferlist bl;
614 int r = cfuse->client->ll_read(fh, off, size, &bl);
615 if (r >= 0)
616 fuse_reply_buf(req, bl.c_str(), bl.length());
617 else
618 fuse_reply_err(req, -r);
619 }
620
621 static void fuse_ll_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
622 size_t size, off_t off, struct fuse_file_info *fi)
623 {
624 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
625 Fh *fh = reinterpret_cast<Fh*>(fi->fh);
626 int r = cfuse->client->ll_write(fh, off, size, buf);
627 if (r >= 0)
628 fuse_reply_write(req, r);
629 else
630 fuse_reply_err(req, -r);
631 }
632
633 static void fuse_ll_flush(fuse_req_t req, fuse_ino_t ino,
634 struct fuse_file_info *fi)
635 {
636 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
637 Fh *fh = reinterpret_cast<Fh*>(fi->fh);
638 int r = cfuse->client->ll_flush(fh);
639 fuse_reply_err(req, -r);
640 }
641
642 #ifdef FUSE_IOCTL_COMPAT
643 static void fuse_ll_ioctl(fuse_req_t req, fuse_ino_t ino,
644 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 5)
645 unsigned int cmd,
646 #else
647 int cmd,
648 #endif
649 void *arg, struct fuse_file_info *fi,
650 unsigned flags, const void *in_buf, size_t in_bufsz, size_t out_bufsz)
651 {
652 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
653
654 if (flags & FUSE_IOCTL_COMPAT) {
655 fuse_reply_err(req, ENOSYS);
656 return;
657 }
658
659 switch (static_cast<unsigned>(cmd)) {
660 case CEPH_IOC_GET_LAYOUT: {
661 file_layout_t layout;
662 struct ceph_ioctl_layout l;
663 Fh *fh = (Fh*)fi->fh;
664 cfuse->client->ll_file_layout(fh, &layout);
665 l.stripe_unit = layout.stripe_unit;
666 l.stripe_count = layout.stripe_count;
667 l.object_size = layout.object_size;
668 l.data_pool = layout.pool_id;
669 fuse_reply_ioctl(req, 0, &l, sizeof(struct ceph_ioctl_layout));
670 }
671 break;
672 default:
673 fuse_reply_err(req, EINVAL);
674 }
675 }
676 #endif
677
678 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
679
680 static void fuse_ll_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
681 off_t offset, off_t length,
682 struct fuse_file_info *fi)
683 {
684 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
685 Fh *fh = (Fh*)fi->fh;
686 int r = cfuse->client->ll_fallocate(fh, mode, offset, length);
687 fuse_reply_err(req, -r);
688 }
689
690 #endif
691
692 static void fuse_ll_release(fuse_req_t req, fuse_ino_t ino,
693 struct fuse_file_info *fi)
694 {
695 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
696 Fh *fh = reinterpret_cast<Fh*>(fi->fh);
697 int r = cfuse->client->ll_release(fh);
698 fuse_reply_err(req, -r);
699 }
700
701 static void fuse_ll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
702 struct fuse_file_info *fi)
703 {
704 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
705 Fh *fh = reinterpret_cast<Fh*>(fi->fh);
706 int r = cfuse->client->ll_fsync(fh, datasync);
707 fuse_reply_err(req, -r);
708 }
709
710 struct readdir_context {
711 fuse_req_t req;
712 char *buf;
713 size_t size;
714 size_t pos; /* in buf */
715 uint64_t snap;
716 };
717
718 /*
719 * return 0 on success, -1 if out of space
720 */
721 static int fuse_ll_add_dirent(void *p, struct dirent *de,
722 struct ceph_statx *stx, off_t next_off,
723 Inode *in)
724 {
725 struct readdir_context *c = (struct readdir_context *)p;
726 CephFuse::Handle *cfuse = (CephFuse::Handle *)fuse_req_userdata(c->req);
727
728 struct stat st;
729 st.st_ino = cfuse->make_fake_ino(stx->stx_ino, c->snap);
730 st.st_mode = stx->stx_mode;
731 st.st_rdev = new_encode_dev(stx->stx_rdev);
732
733 size_t room = c->size - c->pos;
734 size_t entrysize = fuse_add_direntry(c->req, c->buf + c->pos, room,
735 de->d_name, &st, next_off);
736 if (entrysize > room)
737 return -ENOSPC;
738
739 /* success */
740 c->pos += entrysize;
741 return 0;
742 }
743
744 static void fuse_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
745 off_t off, struct fuse_file_info *fi)
746 {
747 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
748
749 dir_result_t *dirp = reinterpret_cast<dir_result_t*>(fi->fh);
750 cfuse->client->seekdir(dirp, off);
751
752 struct readdir_context rc;
753 rc.req = req;
754 rc.buf = new char[size];
755 rc.size = size;
756 rc.pos = 0;
757 rc.snap = cfuse->fino_snap(ino);
758
759 int r = cfuse->client->readdir_r_cb(dirp, fuse_ll_add_dirent, &rc);
760 if (r == 0 || r == -ENOSPC) /* ignore ENOSPC from our callback */
761 fuse_reply_buf(req, rc.buf, rc.pos);
762 else
763 fuse_reply_err(req, -r);
764 delete[] rc.buf;
765 }
766
767 static void fuse_ll_releasedir(fuse_req_t req, fuse_ino_t ino,
768 struct fuse_file_info *fi)
769 {
770 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
771 dir_result_t *dirp = reinterpret_cast<dir_result_t*>(fi->fh);
772 cfuse->client->ll_releasedir(dirp);
773 fuse_reply_err(req, 0);
774 }
775
776 static void fuse_ll_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
777 struct fuse_file_info *fi)
778 {
779 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
780 dir_result_t *dirp = reinterpret_cast<dir_result_t*>(fi->fh);
781 int r = cfuse->client->ll_fsyncdir(dirp);
782 fuse_reply_err(req, -r);
783 }
784
785 static void fuse_ll_access(fuse_req_t req, fuse_ino_t ino, int mask)
786 {
787 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
788 const struct fuse_ctx *ctx = fuse_req_ctx(req);
789 Inode *in = cfuse->iget(ino);
790 UserPerm perms(ctx->uid, ctx->gid);
791 get_fuse_groups(perms, req);
792
793 int r = cfuse->client->inode_permission(in, perms, mask);
794 fuse_reply_err(req, -r);
795 cfuse->iput(in);
796 }
797
798 static void fuse_ll_create(fuse_req_t req, fuse_ino_t parent, const char *name,
799 mode_t mode, struct fuse_file_info *fi)
800 {
801 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
802 const struct fuse_ctx *ctx = fuse_req_ctx(req);
803 Inode *i1 = cfuse->iget(parent), *i2;
804 struct fuse_entry_param fe;
805 Fh *fh = NULL;
806 UserPerm perms(ctx->uid, ctx->gid);
807 get_fuse_groups(perms, req);
808
809 memset(&fe, 0, sizeof(fe));
810
811 // pass &i2 for the created inode so that ll_create takes an initial ll_ref
812 int r = cfuse->client->ll_create(i1, name, mode, fi->flags, &fe.attr, &i2,
813 &fh, perms);
814 if (r == 0) {
815 fi->fh = (uint64_t)fh;
816 fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev);
817 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
818 auto fuse_disable_pagecache = cfuse->client->cct->_conf.get_val<bool>(
819 "fuse_disable_pagecache");
820 auto fuse_use_invalidate_cb = cfuse->client->cct->_conf.get_val<bool>(
821 "fuse_use_invalidate_cb");
822 if (fuse_disable_pagecache)
823 fi->direct_io = 1;
824 else if (fuse_use_invalidate_cb)
825 fi->keep_cache = 1;
826 #endif
827 fuse_reply_create(req, &fe, fi);
828 } else
829 fuse_reply_err(req, -r);
830 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
831 // fuse_ll_forget()
832 cfuse->iput(i1); // iput required
833 }
834
835 static void fuse_ll_statfs(fuse_req_t req, fuse_ino_t ino)
836 {
837 struct statvfs stbuf;
838 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
839 Inode *in = cfuse->iget(ino);
840 const struct fuse_ctx *ctx = fuse_req_ctx(req);
841 UserPerm perms(ctx->uid, ctx->gid);
842 get_fuse_groups(perms, req);
843
844 int r = cfuse->client->ll_statfs(in, &stbuf, perms);
845 if (r == 0)
846 fuse_reply_statfs(req, &stbuf);
847 else
848 fuse_reply_err(req, -r);
849
850 cfuse->iput(in); // iput required
851 }
852
853 static void fuse_ll_getlk(fuse_req_t req, fuse_ino_t ino,
854 struct fuse_file_info *fi, struct flock *lock)
855 {
856 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
857 Fh *fh = reinterpret_cast<Fh*>(fi->fh);
858
859 int r = cfuse->client->ll_getlk(fh, lock, fi->lock_owner);
860 if (r == 0)
861 fuse_reply_lock(req, lock);
862 else
863 fuse_reply_err(req, -r);
864 }
865
866 static void fuse_ll_setlk(fuse_req_t req, fuse_ino_t ino,
867 struct fuse_file_info *fi, struct flock *lock, int sleep)
868 {
869 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
870 Fh *fh = reinterpret_cast<Fh*>(fi->fh);
871
872 // must use multithread if operation may block
873 auto fuse_multithreaded = cfuse->client->cct->_conf.get_val<bool>(
874 "fuse_multithreaded");
875 if (!fuse_multithreaded && sleep && lock->l_type != F_UNLCK) {
876 fuse_reply_err(req, EDEADLK);
877 return;
878 }
879
880 int r = cfuse->client->ll_setlk(fh, lock, fi->lock_owner, sleep);
881 fuse_reply_err(req, -r);
882 }
883
884 static void fuse_ll_interrupt(fuse_req_t req, void* data)
885 {
886 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
887 cfuse->client->ll_interrupt(data);
888 }
889
890 static void switch_interrupt_cb(void *handle, void* data)
891 {
892 CephFuse::Handle *cfuse = (CephFuse::Handle *)handle;
893 fuse_req_t req = cfuse->get_fuse_req();
894
895 if (data)
896 fuse_req_interrupt_func(req, fuse_ll_interrupt, data);
897 else
898 fuse_req_interrupt_func(req, NULL, NULL);
899 }
900
901 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
902 static void fuse_ll_flock(fuse_req_t req, fuse_ino_t ino,
903 struct fuse_file_info *fi, int cmd)
904 {
905 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
906 Fh *fh = (Fh*)fi->fh;
907
908 // must use multithread if operation may block
909 auto fuse_multithreaded = cfuse->client->cct->_conf.get_val<bool>(
910 "fuse_multithreaded");
911 if (!fuse_multithreaded && !(cmd & (LOCK_NB | LOCK_UN))) {
912 fuse_reply_err(req, EDEADLK);
913 return;
914 }
915
916 int r = cfuse->client->ll_flock(fh, cmd, fi->lock_owner);
917 fuse_reply_err(req, -r);
918 }
919 #endif
920
921 #if !defined(__APPLE__)
922 static mode_t umask_cb(void *handle)
923 {
924 CephFuse::Handle *cfuse = (CephFuse::Handle *)handle;
925 fuse_req_t req = cfuse->get_fuse_req();
926 const struct fuse_ctx *ctx = fuse_req_ctx(req);
927 return ctx->umask;
928 }
929 #endif
930
931 static void ino_invalidate_cb(void *handle, vinodeno_t vino, int64_t off,
932 int64_t len)
933 {
934 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
935 CephFuse::Handle *cfuse = (CephFuse::Handle *)handle;
936 fuse_ino_t fino = cfuse->make_fake_ino(vino.ino, vino.snapid);
937 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
938 fuse_lowlevel_notify_inval_inode(cfuse->se, fino, off, len);
939 #else
940 fuse_lowlevel_notify_inval_inode(cfuse->ch, fino, off, len);
941 #endif
942 #endif
943 }
944
945 static void dentry_invalidate_cb(void *handle, vinodeno_t dirino,
946 vinodeno_t ino, const char *name, size_t len)
947 {
948 CephFuse::Handle *cfuse = (CephFuse::Handle *)handle;
949 fuse_ino_t fdirino = cfuse->make_fake_ino(dirino.ino, dirino.snapid);
950 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
951 fuse_ino_t fino = 0;
952 if (ino.ino != inodeno_t())
953 fino = cfuse->make_fake_ino(ino.ino, ino.snapid);
954 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
955 fuse_lowlevel_notify_delete(cfuse->se, fdirino, fino, name, len);
956 #else
957 fuse_lowlevel_notify_delete(cfuse->ch, fdirino, fino, name, len);
958 #endif
959 #elif FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
960 fuse_lowlevel_notify_inval_entry(cfuse->ch, fdirino, name, len);
961 #endif
962 }
963
964 static int remount_cb(void *handle)
965 {
966 // used for trimming kernel dcache. when remounting a file system, linux kernel
967 // trims all unused dentries in the file system
968 char cmd[128+PATH_MAX];
969 CephFuse::Handle *cfuse = (CephFuse::Handle *)handle;
970 snprintf(cmd, sizeof(cmd), "LIBMOUNT_FSTAB=/dev/null mount -i -o remount %s",
971 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
972 cfuse->opts.mountpoint);
973 #else
974 cfuse->mountpoint);
975 #endif
976 int r = system(cmd);
977 if (r != 0 && r != -1) {
978 r = WEXITSTATUS(r);
979 }
980
981 return r;
982 }
983
984 static void do_init(void *data, fuse_conn_info *conn)
985 {
986 CephFuse::Handle *cfuse = (CephFuse::Handle *)data;
987 Client *client = cfuse->client;
988
989 #if !defined(__APPLE__)
990 if (!client->fuse_default_permissions && client->ll_handle_umask()) {
991 // apply umask in userspace if posix acl is enabled
992 if(conn->capable & FUSE_CAP_DONT_MASK)
993 conn->want |= FUSE_CAP_DONT_MASK;
994 }
995 if(conn->capable & FUSE_CAP_EXPORT_SUPPORT)
996 conn->want |= FUSE_CAP_EXPORT_SUPPORT;
997 #endif
998
999 if (cfuse->fd_on_success) {
1000 //cout << "fuse init signaling on fd " << fd_on_success << std::endl;
1001 // see Preforker::daemonize(), ceph-fuse's parent process expects a `-1`
1002 // from a daemonized child process.
1003 uint32_t r = -1;
1004 int err = safe_write(cfuse->fd_on_success, &r, sizeof(r));
1005 if (err) {
1006 derr << "fuse_ll: do_init: safe_write failed with error "
1007 << cpp_strerror(err) << dendl;
1008 ceph_abort();
1009 }
1010 //cout << "fuse init done signaling on fd " << fd_on_success << std::endl;
1011
1012 // close stdout, etc.
1013 ::close(0);
1014 ::close(1);
1015 ::close(2);
1016 }
1017 }
1018
1019 const static struct fuse_lowlevel_ops fuse_ll_oper = {
1020 init: do_init,
1021 destroy: 0,
1022 lookup: fuse_ll_lookup,
1023 forget: fuse_ll_forget,
1024 getattr: fuse_ll_getattr,
1025 setattr: fuse_ll_setattr,
1026 readlink: fuse_ll_readlink,
1027 mknod: fuse_ll_mknod,
1028 mkdir: fuse_ll_mkdir,
1029 unlink: fuse_ll_unlink,
1030 rmdir: fuse_ll_rmdir,
1031 symlink: fuse_ll_symlink,
1032 rename: fuse_ll_rename,
1033 link: fuse_ll_link,
1034 open: fuse_ll_open,
1035 read: fuse_ll_read,
1036 write: fuse_ll_write,
1037 flush: fuse_ll_flush,
1038 release: fuse_ll_release,
1039 fsync: fuse_ll_fsync,
1040 opendir: fuse_ll_opendir,
1041 readdir: fuse_ll_readdir,
1042 releasedir: fuse_ll_releasedir,
1043 fsyncdir: fuse_ll_fsyncdir,
1044 statfs: fuse_ll_statfs,
1045 setxattr: fuse_ll_setxattr,
1046 getxattr: fuse_ll_getxattr,
1047 listxattr: fuse_ll_listxattr,
1048 removexattr: fuse_ll_removexattr,
1049 access: fuse_ll_access,
1050 create: fuse_ll_create,
1051 getlk: fuse_ll_getlk,
1052 setlk: fuse_ll_setlk,
1053 bmap: 0,
1054 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
1055 #ifdef FUSE_IOCTL_COMPAT
1056 ioctl: fuse_ll_ioctl,
1057 #else
1058 ioctl: 0,
1059 #endif
1060 poll: 0,
1061 #endif
1062 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
1063 write_buf: 0,
1064 retrieve_reply: 0,
1065 forget_multi: 0,
1066 flock: fuse_ll_flock,
1067 #endif
1068 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
1069 fallocate: fuse_ll_fallocate
1070 #endif
1071 };
1072
1073
1074 CephFuse::Handle::Handle(Client *c, int fd) :
1075 fd_on_success(fd),
1076 client(c),
1077 se(NULL),
1078 #if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
1079 ch(NULL),
1080 mountpoint(NULL),
1081 #endif
1082 last_stag(0)
1083 {
1084 snap_stag_map[CEPH_NOSNAP] = 0;
1085 stag_snap_map[0] = CEPH_NOSNAP;
1086 memset(&args, 0, sizeof(args));
1087 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1088 memset(&opts, 0, sizeof(opts));
1089 #endif
1090 }
1091
1092 CephFuse::Handle::~Handle()
1093 {
1094 fuse_opt_free_args(&args);
1095 }
1096
1097 void CephFuse::Handle::finalize()
1098 {
1099 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1100 if (se) {
1101 fuse_remove_signal_handlers(se);
1102 fuse_session_unmount(se);
1103 fuse_session_destroy(se);
1104 }
1105 if (opts.mountpoint)
1106 free(opts.mountpoint);
1107 #else
1108 if (se)
1109 fuse_remove_signal_handlers(se);
1110 if (ch)
1111 fuse_session_remove_chan(ch);
1112 if (se)
1113 fuse_session_destroy(se);
1114 if (ch)
1115 fuse_unmount(mountpoint, ch);
1116 #endif
1117
1118 pthread_key_delete(fuse_req_key);
1119 }
1120
1121 int CephFuse::Handle::init(int argc, const char *argv[])
1122 {
1123
1124 int r = pthread_key_create(&fuse_req_key, NULL);
1125 if (r) {
1126 derr << "pthread_key_create failed." << dendl;
1127 return r;
1128 }
1129
1130 // set up fuse argc/argv
1131 int newargc = 0;
1132 const char **newargv = (const char **) malloc((argc + 10) * sizeof(char *));
1133 if(!newargv)
1134 return ENOMEM;
1135
1136 newargv[newargc++] = argv[0];
1137 newargv[newargc++] = "-f"; // stay in foreground
1138
1139 auto fuse_allow_other = client->cct->_conf.get_val<bool>(
1140 "fuse_allow_other");
1141 auto fuse_default_permissions = client->cct->_conf.get_val<bool>(
1142 "fuse_default_permissions");
1143 #if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
1144 auto fuse_big_writes = client->cct->_conf.get_val<bool>(
1145 "fuse_big_writes");
1146 auto fuse_max_write = client->cct->_conf.get_val<Option::size_t>(
1147 "fuse_max_write");
1148 auto fuse_atomic_o_trunc = client->cct->_conf.get_val<bool>(
1149 "fuse_atomic_o_trunc");
1150 #endif
1151 auto fuse_debug = client->cct->_conf.get_val<bool>(
1152 "fuse_debug");
1153
1154 if (fuse_allow_other) {
1155 newargv[newargc++] = "-o";
1156 newargv[newargc++] = "allow_other";
1157 }
1158 if (fuse_default_permissions) {
1159 newargv[newargc++] = "-o";
1160 newargv[newargc++] = "default_permissions";
1161 }
1162 #if defined(__linux__)
1163 #if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
1164 if (fuse_big_writes) {
1165 newargv[newargc++] = "-o";
1166 newargv[newargc++] = "big_writes";
1167 }
1168 if (fuse_max_write > 0) {
1169 char strsplice[65];
1170 newargv[newargc++] = "-o";
1171 newargv[newargc++] = strsplice;
1172 sprintf(strsplice, "max_write=%zu", (size_t)fuse_max_write);
1173 newargv[newargc++] = strsplice;
1174 }
1175 if (fuse_atomic_o_trunc) {
1176 newargv[newargc++] = "-o";
1177 newargv[newargc++] = "atomic_o_trunc";
1178 }
1179 #endif
1180 #endif
1181 if (fuse_debug)
1182 newargv[newargc++] = "-d";
1183
1184 for (int argctr = 1; argctr < argc; argctr++)
1185 newargv[newargc++] = argv[argctr];
1186
1187 derr << "init, newargv = " << newargv << " newargc=" << newargc << dendl;
1188 struct fuse_args a = FUSE_ARGS_INIT(newargc, (char**)newargv);
1189 args = a; // Roundabout construction b/c FUSE_ARGS_INIT is for initialization not assignment
1190
1191 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1192 if (fuse_parse_cmdline(&args, &opts) == -1) {
1193 #else
1194 if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) == -1) {
1195 #endif
1196 derr << "fuse_parse_cmdline failed." << dendl;
1197 fuse_opt_free_args(&args);
1198 free(newargv);
1199 return EINVAL;
1200 }
1201
1202 ceph_assert(args.allocated); // Checking fuse has realloc'd args so we can free newargv
1203 free(newargv);
1204 return 0;
1205 }
1206
1207 int CephFuse::Handle::start()
1208 {
1209 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1210 se = fuse_session_new(&args, &fuse_ll_oper, sizeof(fuse_ll_oper), this);
1211 #else
1212 ch = fuse_mount(mountpoint, &args);
1213 if (!ch) {
1214 derr << "fuse_mount(mountpoint=" << mountpoint << ") failed." << dendl;
1215 return EIO;
1216 }
1217
1218 se = fuse_lowlevel_new(&args, &fuse_ll_oper, sizeof(fuse_ll_oper), this);
1219 #endif
1220 if (!se) {
1221 derr << "fuse_lowlevel_new failed" << dendl;
1222 return EDOM;
1223 }
1224
1225 signal(SIGTERM, SIG_DFL);
1226 signal(SIGINT, SIG_DFL);
1227 if (fuse_set_signal_handlers(se) == -1) {
1228 derr << "fuse_set_signal_handlers failed" << dendl;
1229 return ENOSYS;
1230 }
1231
1232 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1233 if (fuse_session_mount(se, opts.mountpoint) != 0) {
1234 derr << "fuse_session_mount failed" << dendl;
1235 return ENOSYS;
1236 }
1237 #else
1238 fuse_session_add_chan(se, ch);
1239 #endif
1240
1241
1242 struct ceph_client_callback_args args = {
1243 handle: this,
1244 ino_cb: client->cct->_conf.get_val<bool>("fuse_use_invalidate_cb") ?
1245 ino_invalidate_cb : NULL,
1246 dentry_cb: dentry_invalidate_cb,
1247 switch_intr_cb: switch_interrupt_cb,
1248 #if defined(__linux__)
1249 remount_cb: remount_cb,
1250 #endif
1251 #if !defined(__APPLE__)
1252 umask_cb: umask_cb,
1253 #endif
1254 };
1255 client->ll_register_callbacks(&args);
1256
1257 return 0;
1258 }
1259
1260 int CephFuse::Handle::loop()
1261 {
1262 auto fuse_multithreaded = client->cct->_conf.get_val<bool>(
1263 "fuse_multithreaded");
1264 if (fuse_multithreaded) {
1265 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 1)
1266 {
1267 struct fuse_loop_config conf = { 0 };
1268
1269 conf.clone_fd = opts.clone_fd;
1270 return fuse_session_loop_mt(se, &conf);
1271 }
1272 #elif FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1273 return fuse_session_loop_mt(se, opts.clone_fd);
1274 #else
1275 return fuse_session_loop_mt(se);
1276 #endif
1277 } else {
1278 return fuse_session_loop(se);
1279 }
1280 }
1281
1282 uint64_t CephFuse::Handle::fino_snap(uint64_t fino)
1283 {
1284 if (fino == FUSE_ROOT_ID)
1285 return CEPH_NOSNAP;
1286
1287 if (client->use_faked_inos()) {
1288 vinodeno_t vino = client->map_faked_ino(fino);
1289 return vino.snapid;
1290 } else {
1291 std::lock_guard l(stag_lock);
1292 uint64_t stag = FINO_STAG(fino);
1293 ceph_assert(stag_snap_map.count(stag));
1294 return stag_snap_map[stag];
1295 }
1296 }
1297
1298 Inode * CephFuse::Handle::iget(fuse_ino_t fino)
1299 {
1300 if (fino == FUSE_ROOT_ID)
1301 return client->get_root();
1302
1303 if (client->use_faked_inos()) {
1304 return client->ll_get_inode((ino_t)fino);
1305 } else {
1306 vinodeno_t vino(FINO_INO(fino), fino_snap(fino));
1307 return client->ll_get_inode(vino);
1308 }
1309 }
1310
1311 void CephFuse::Handle::iput(Inode *in)
1312 {
1313 client->ll_put(in);
1314 }
1315
1316 uint64_t CephFuse::Handle::make_fake_ino(inodeno_t ino, snapid_t snapid)
1317 {
1318 if (client->use_faked_inos()) {
1319 // already faked by libcephfs
1320 if (ino == client->get_root_ino())
1321 return FUSE_ROOT_ID;
1322
1323 return ino;
1324 } else {
1325 if (snapid == CEPH_NOSNAP && ino == client->get_root_ino())
1326 return FUSE_ROOT_ID;
1327
1328 std::lock_guard l(stag_lock);
1329 auto p = snap_stag_map.find(snapid);
1330 if (p != snap_stag_map.end()) {
1331 inodeno_t fino = MAKE_FINO(ino, p->second);
1332 return fino;
1333 }
1334
1335 int first = last_stag & STAG_MASK;
1336 int stag = (++last_stag) & STAG_MASK;
1337 for (; stag != first; stag = (++last_stag) & STAG_MASK) {
1338 if (stag == 0)
1339 continue;
1340
1341 auto p = stag_snap_map.find(stag);
1342 if (p == stag_snap_map.end()) {
1343 snap_stag_map[snapid] = stag;
1344 stag_snap_map[stag] = snapid;
1345 break;
1346 }
1347
1348 if (!client->ll_get_snap_ref(p->second)) {
1349 snap_stag_map.erase(p->second);
1350 snap_stag_map[snapid] = stag;
1351 p->second = snapid;
1352 break;
1353 }
1354 }
1355 if (stag == first)
1356 ceph_abort_msg("run out of stag");
1357
1358 inodeno_t fino = MAKE_FINO(ino, stag);
1359 //cout << "make_fake_ino " << ino << "." << snapid << " -> " << fino << std::endl;
1360 return fino;
1361 }
1362 }
1363
1364 void CephFuse::Handle::set_fuse_req(fuse_req_t req)
1365 {
1366 pthread_setspecific(fuse_req_key, (void*)req);
1367 }
1368
1369 fuse_req_t CephFuse::Handle::get_fuse_req()
1370 {
1371 return (fuse_req_t) pthread_getspecific(fuse_req_key);
1372 }
1373
1374
1375 CephFuse::CephFuse(Client *c, int fd) : _handle(new CephFuse::Handle(c, fd))
1376 {
1377 }
1378
1379 CephFuse::~CephFuse()
1380 {
1381 delete _handle;
1382 }
1383
1384 int CephFuse::init(int argc, const char *argv[])
1385 {
1386 return _handle->init(argc, argv);
1387 }
1388
1389 int CephFuse::start()
1390 {
1391 return _handle->start();
1392 }
1393
1394 int CephFuse::loop()
1395 {
1396 return _handle->loop();
1397 }
1398
1399 void CephFuse::finalize()
1400 {
1401 return _handle->finalize();
1402 }
1403
1404 std::string CephFuse::get_mount_point() const
1405 {
1406 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1407 if (_handle->opts.mountpoint) {
1408 return _handle->opts.mountpoint;
1409 #else
1410 if (_handle->mountpoint) {
1411 return _handle->mountpoint;
1412 #endif
1413 } else {
1414 return "";
1415 }
1416 }