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