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