]> git.proxmox.com Git - ceph.git/blame - ceph/src/client/fuse_ll.cc
update ceph source to reef 18.1.2
[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
33c7a0ef
TL
27#if defined(__linux__)
28#include <libgen.h>
29#include <sys/vfs.h>
30#include <sys/xattr.h>
31#include <linux/magic.h>
32#endif
33
7c673cae
FG
34// ceph
35#include "common/errno.h"
36#include "common/safe_io.h"
37#include "include/types.h"
38#include "Client.h"
39#include "Fh.h"
40#include "ioctl.h"
41#include "common/config.h"
11fdf7f2 42#include "include/ceph_assert.h"
e306af50
TL
43#include "include/cephfs/ceph_ll_client.h"
44#include "include/ceph_fuse.h"
7c673cae
FG
45
46#include "fuse_ll.h"
7c673cae
FG
47#include <fuse_lowlevel.h>
48
49#define dout_context g_ceph_context
50
51#define FINO_INO(x) ((x) & ((1ull<<48)-1ull))
52#define FINO_STAG(x) ((x) >> 48)
11fdf7f2
TL
53#define MAKE_FINO(i,s) ((i) | ((int64_t)(s) << 48))
54#define STAG_MASK 0xffff
2a845540
TL
55#define G_NOSNAP_STAG 0 // for all CEPH_NOSNAP
56#define G_SNAPDIR_STAG 1 // for all CEPH_SNAPDIR
7c673cae
FG
57
58#define MINORBITS 20
59#define MINORMASK ((1U << MINORBITS) - 1)
60
61#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
62#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
63#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
64
33c7a0ef
TL
65#if defined(__linux__)
66#ifndef FUSE_SUPER_MAGIC
67#define FUSE_SUPER_MAGIC 0x65735546
68#endif
69
70#define _CEPH_CLIENT_ID "ceph.client_id"
71#endif
72
2a845540
TL
73/*
74 * The dedicated struct for snapid <-> stag map for each ceph
75 * inode, and the stag is a number in range [2, 0xffff], and
76 * the stag number 0 is reserved for CEPH_NOSNAP and 1 is
77 * reserved for CEPH_SNAPDIR.
78 */
79struct ceph_fuse_fake_inode_stag {
80 ceph::unordered_map<uint64_t,int> snap_stag_map; // <snapid, stagid>
81 ceph::unordered_map<int, uint64_t> stag_snap_map; // <stagid, snapid>
82 int last_stag = 1;
83};
84
20effc67
TL
85using namespace std;
86
f67539c2
TL
87static const ceph::unordered_map<int,int> cephfs_errno_to_system_errno = {
88 {CEPHFS_EBLOCKLISTED, ESHUTDOWN},
89 {CEPHFS_EPERM, EPERM},
90 {CEPHFS_ESTALE, ESTALE},
91 {CEPHFS_ENOSPC, ENOSPC},
92 {CEPHFS_ETIMEDOUT, ETIMEDOUT},
93 {CEPHFS_EIO, EIO},
94 {CEPHFS_ENOTCONN, ENOTCONN},
95 {CEPHFS_EEXIST, EEXIST},
96 {CEPHFS_EINTR, EINTR},
97 {CEPHFS_EINVAL, EINVAL},
98 {CEPHFS_EBADF, EBADF},
99 {CEPHFS_EROFS, EROFS},
100 {CEPHFS_EAGAIN, EAGAIN},
101 {CEPHFS_EACCES, EACCES},
102 {CEPHFS_ELOOP, ELOOP},
103 {CEPHFS_EISDIR, EISDIR},
104 {CEPHFS_ENOENT, ENOENT},
105 {CEPHFS_ENOTDIR, ENOTDIR},
106 {CEPHFS_ENAMETOOLONG, ENAMETOOLONG},
107 {CEPHFS_EBUSY, EBUSY},
108 {CEPHFS_EDQUOT, EDQUOT},
109 {CEPHFS_EFBIG, EFBIG},
110 {CEPHFS_ERANGE, ERANGE},
111 {CEPHFS_ENXIO, ENXIO},
112 {CEPHFS_ECANCELED, ECANCELED},
113 {CEPHFS_ENODATA, ENODATA},
114 {CEPHFS_EOPNOTSUPP, EOPNOTSUPP},
115 {CEPHFS_EXDEV, EXDEV},
116 {CEPHFS_ENOMEM, ENOMEM},
117 {CEPHFS_ENOTRECOVERABLE, ENOTRECOVERABLE},
118 {CEPHFS_ENOSYS, ENOSYS},
119 {CEPHFS_ENOTEMPTY, ENOTEMPTY},
120 {CEPHFS_EDEADLK, EDEADLK},
121 {CEPHFS_EDOM, EDOM},
122 {CEPHFS_EMLINK, EMLINK},
123 {CEPHFS_ETIME, ETIME},
124 {CEPHFS_EOLDSNAPC, EIO} // forcing to EIO for now
125};
126
127/* Requirements:
128 * cephfs_errno >= 0
129 */
130static int get_sys_errno(int cephfs_errno)
131{
132 if (cephfs_errno == 0)
133 return 0;
134
135 auto it = cephfs_errno_to_system_errno.find(cephfs_errno);
136 if (it != cephfs_errno_to_system_errno.end())
137 return it->second;
138 return EIO;
139}
140
7c673cae
FG
141static uint32_t new_encode_dev(dev_t dev)
142{
143 unsigned major = MAJOR(dev);
144 unsigned minor = MINOR(dev);
145 return (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12);
146}
147
148static dev_t new_decode_dev(uint32_t dev)
149{
150 unsigned major = (dev & 0xfff00) >> 8;
151 unsigned minor = (dev & 0xff) | ((dev >> 12) & 0xfff00);
152 return MKDEV(major, minor);
153}
154
155class CephFuse::Handle {
156public:
157 Handle(Client *c, int fd);
158 ~Handle();
159
160 int init(int argc, const char *argv[]);
161 int start();
162 int loop();
163 void finalize();
164
165 uint64_t fino_snap(uint64_t fino);
166 uint64_t make_fake_ino(inodeno_t ino, snapid_t snapid);
167 Inode * iget(fuse_ino_t fino);
168 void iput(Inode *in);
169
170 int fd_on_success;
171 Client *client;
172
2a845540 173 struct fuse_session *se = nullptr;
1911f103
TL
174#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
175 struct fuse_cmdline_opts opts;
f67539c2 176 struct fuse_conn_info_opts *conn_opts;
1911f103 177#else
2a845540
TL
178 struct fuse_chan *ch = nullptr;
179 char *mountpoint = nullptr;
1911f103 180#endif
7c673cae 181
9f95a23c 182 ceph::mutex stag_lock = ceph::make_mutex("fuse_ll.cc stag_lock");
7c673cae 183
2a845540
TL
184 // a map of <ceph ino, fino stag/snapid map>
185 ceph::unordered_map<uint64_t, struct ceph_fuse_fake_inode_stag> g_fino_maps;
7c673cae 186
11fdf7f2 187 pthread_key_t fuse_req_key = 0;
7c673cae
FG
188 void set_fuse_req(fuse_req_t);
189 fuse_req_t get_fuse_req();
190
191 struct fuse_args args;
192};
193
33c7a0ef
TL
194#if defined(__linux__)
195static int already_fuse_mounted(const char *path, bool &already_mounted)
196{
197 struct statx path_statx;
198 struct statx parent_statx;
199 char path_copy[PATH_MAX] = {0};
200 char *parent_path = NULL;
201 int err = 0;
202
203 already_mounted = false;
204
205 strncpy(path_copy, path, sizeof(path_copy)-1);
206 parent_path = dirname(path_copy);
207
208 // get stat information for original path
209 if (-1 == statx(AT_FDCWD, path, AT_STATX_DONT_SYNC, STATX_INO, &path_statx)) {
210 err = errno;
211 derr << "fuse_ll: already_fuse_mounted: statx(" << path << ") failed with error "
212 << cpp_strerror(err) << dendl;
213 return err;
214 }
215
216 // if path isn't directory, then it can't be a mountpoint.
217 if (!(path_statx.stx_mode & S_IFDIR)) {
218 err = EINVAL;
219 derr << "fuse_ll: already_fuse_mounted: "
220 << path << " is not a directory" << dendl;
221 return err;
222 }
223
224 // get stat information for parent path
225 if (-1 == statx(AT_FDCWD, parent_path, AT_STATX_DONT_SYNC, STATX_INO, &parent_statx)) {
226 err = errno;
227 derr << "fuse_ll: already_fuse_mounted: statx(" << parent_path << ") failed with error "
228 << cpp_strerror(err) << dendl;
229 return err;
230 }
231
232 // if original path and parent have different device ids,
233 // then the path is a mount point
234 // or, if they refer to the same path, then it's probably
235 // the root directory '/' and therefore path is a mountpoint
236 if( path_statx.stx_dev_major != parent_statx.stx_dev_major ||
237 path_statx.stx_dev_minor != parent_statx.stx_dev_minor ||
238 ( path_statx.stx_dev_major == parent_statx.stx_dev_major &&
239 path_statx.stx_dev_minor == parent_statx.stx_dev_minor &&
240 path_statx.stx_ino == parent_statx.stx_ino
241 )
242 ) {
243 struct statfs path_statfs;
244 if (-1 == statfs(path, &path_statfs)) {
245 err = errno;
246 derr << "fuse_ll: already_fuse_mounted: statfs(" << path << ") failed with error "
247 << cpp_strerror(err) << dendl;
248 return err;
249 }
250
251 if(FUSE_SUPER_MAGIC == path_statfs.f_type) {
252 // if getxattr returns positive length means value exist for ceph.client_id
253 // then ceph fuse is already mounted on path
254 char client_id[128] = {0};
255 if (getxattr(path, _CEPH_CLIENT_ID, &client_id, sizeof(client_id)) > 0) {
256 already_mounted = true;
257 derr << path << " already mounted by " << client_id << dendl;
258 }
259 }
260 }
261
262 return err;
263}
264#else // non-linux platforms
265static int already_fuse_mounted(const char *path, bool &already_mounted)
266{
267 already_mounted = false;
268 return 0;
269}
270#endif
271
7c673cae
FG
272static int getgroups(fuse_req_t req, gid_t **sgids)
273{
274#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
11fdf7f2 275 ceph_assert(sgids);
7c673cae
FG
276 int c = fuse_req_getgroups(req, 0, NULL);
277 if (c < 0) {
278 return c;
279 }
280 if (c == 0) {
281 return 0;
282 }
283
b5b8bbf5
FG
284 gid_t *gids = new (std::nothrow) gid_t[c];
285 if (!gids) {
f67539c2 286 return -get_sys_errno(CEPHFS_ENOMEM);
7c673cae 287 }
b5b8bbf5 288 c = fuse_req_getgroups(req, c, gids);
7c673cae 289 if (c < 0) {
11fdf7f2 290 delete[] gids;
b5b8bbf5
FG
291 } else {
292 *sgids = gids;
7c673cae
FG
293 }
294 return c;
295#endif
f67539c2 296 return -get_sys_errno(CEPHFS_ENOSYS);
7c673cae
FG
297}
298
28e407b8 299static void get_fuse_groups(UserPerm& perms, fuse_req_t req)
7c673cae 300{
adb31ebb
TL
301 CephFuse::Handle *cfuse = (CephFuse::Handle *)fuse_req_userdata(req);
302 if (cfuse->client->cct->_conf.get_val<bool>("fuse_set_user_groups")) {
28e407b8
AA
303 gid_t *gids = NULL;
304 int count = getgroups(req, &gids);
305
306 if (count > 0) {
307 perms.init_gids(gids, count);
308 } else if (count < 0) {
309 derr << __func__ << ": getgroups failed: " << cpp_strerror(-count)
310 << dendl;
311 }
312 }
7c673cae
FG
313}
314
7c673cae
FG
315
316static CephFuse::Handle *fuse_ll_req_prepare(fuse_req_t req)
317{
318 CephFuse::Handle *cfuse = (CephFuse::Handle *)fuse_req_userdata(req);
319 cfuse->set_fuse_req(req);
320 return cfuse;
321}
322
323static void fuse_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
324{
325 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
326 const struct fuse_ctx *ctx = fuse_req_ctx(req);
327 struct fuse_entry_param fe;
328 Inode *i2, *i1 = cfuse->iget(parent); // see below
329 int r;
330 UserPerm perms(ctx->uid, ctx->gid);
28e407b8 331 get_fuse_groups(perms, req);
7c673cae 332
11fdf7f2
TL
333 if (!i1)
334 {
335 r = cfuse->client->lookup_ino(parent, perms, &i1);
336 if (r < 0) {
f67539c2 337 fuse_reply_err(req, get_sys_errno(-r));
11fdf7f2
TL
338 return;
339 }
340 }
341
7c673cae
FG
342 memset(&fe, 0, sizeof(fe));
343 r = cfuse->client->ll_lookup(i1, name, &fe.attr, &i2, perms);
344 if (r >= 0) {
345 fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev);
346 fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev);
347 fuse_reply_entry(req, &fe);
348 } else {
f67539c2 349 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
350 }
351
352 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
353 // fuse_ll_forget()
354 cfuse->iput(i1);
355}
356
20effc67
TL
357// fuse3 has changed forget function signature
358#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
359static void fuse_ll_forget(fuse_req_t req, fuse_ino_t ino,
360 uint64_t nlookup)
361#else
7c673cae
FG
362static void fuse_ll_forget(fuse_req_t req, fuse_ino_t ino,
363 long unsigned nlookup)
20effc67 364#endif
7c673cae
FG
365{
366 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
2a845540
TL
367 Inode *in = cfuse->iget(ino);
368 if (in)
369 cfuse->client->ll_forget(in, nlookup+1);
7c673cae
FG
370 fuse_reply_none(req);
371}
372
373static void fuse_ll_getattr(fuse_req_t req, fuse_ino_t ino,
374 struct fuse_file_info *fi)
375{
376 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
377 const struct fuse_ctx *ctx = fuse_req_ctx(req);
7c673cae
FG
378 struct stat stbuf;
379 UserPerm perms(ctx->uid, ctx->gid);
2a845540
TL
380 Inode *in = cfuse->iget(ino);
381 if (!in) {
382 fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
383 return;
384 }
385
28e407b8 386 get_fuse_groups(perms, req);
2a845540 387
7c673cae
FG
388 (void) fi; // XXX
389
390 if (cfuse->client->ll_getattr(in, &stbuf, perms)
391 == 0) {
392 stbuf.st_ino = cfuse->make_fake_ino(stbuf.st_ino, stbuf.st_dev);
393 stbuf.st_rdev = new_encode_dev(stbuf.st_rdev);
394 fuse_reply_attr(req, &stbuf, 0);
395 } else
396 fuse_reply_err(req, ENOENT);
397
398 cfuse->iput(in); // iput required
399}
400
401static void fuse_ll_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
402 int to_set, struct fuse_file_info *fi)
403{
404 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
405 const struct fuse_ctx *ctx = fuse_req_ctx(req);
7c673cae 406 UserPerm perms(ctx->uid, ctx->gid);
2a845540
TL
407 Inode *in = cfuse->iget(ino);
408 if (!in) {
409 fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
410 return;
411 }
412
28e407b8 413 get_fuse_groups(perms, req);
7c673cae
FG
414
415 int mask = 0;
416 if (to_set & FUSE_SET_ATTR_MODE) mask |= CEPH_SETATTR_MODE;
417 if (to_set & FUSE_SET_ATTR_UID) mask |= CEPH_SETATTR_UID;
418 if (to_set & FUSE_SET_ATTR_GID) mask |= CEPH_SETATTR_GID;
419 if (to_set & FUSE_SET_ATTR_MTIME) mask |= CEPH_SETATTR_MTIME;
420 if (to_set & FUSE_SET_ATTR_ATIME) mask |= CEPH_SETATTR_ATIME;
421 if (to_set & FUSE_SET_ATTR_SIZE) mask |= CEPH_SETATTR_SIZE;
11fdf7f2 422#if !defined(__APPLE__)
7c673cae
FG
423 if (to_set & FUSE_SET_ATTR_MTIME_NOW) mask |= CEPH_SETATTR_MTIME_NOW;
424 if (to_set & FUSE_SET_ATTR_ATIME_NOW) mask |= CEPH_SETATTR_ATIME_NOW;
425#endif
426
427 int r = cfuse->client->ll_setattr(in, attr, mask, perms);
428 if (r == 0)
429 fuse_reply_attr(req, attr, 0);
430 else
f67539c2 431 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
432
433 cfuse->iput(in); // iput required
434}
435
436// XATTRS
437
438static void fuse_ll_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
439 const char *value, size_t size,
440 int flags
11fdf7f2 441#if defined(__APPLE__)
7c673cae
FG
442 ,uint32_t pos
443#endif
444 )
445{
446 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
447 const struct fuse_ctx *ctx = fuse_req_ctx(req);
7c673cae 448 UserPerm perms(ctx->uid, ctx->gid);
2a845540
TL
449 Inode *in = cfuse->iget(ino);
450 if (!in) {
451 fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
452 return;
453 }
454
28e407b8 455 get_fuse_groups(perms, req);
7c673cae
FG
456
457 int r = cfuse->client->ll_setxattr(in, name, value, size, flags, perms);
f67539c2 458 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
459
460 cfuse->iput(in); // iput required
461}
462
463static void fuse_ll_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
464{
465 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
466 const struct fuse_ctx *ctx = fuse_req_ctx(req);
7c673cae
FG
467 char buf[size];
468 UserPerm perms(ctx->uid, ctx->gid);
2a845540
TL
469 Inode *in = cfuse->iget(ino);
470 if (!in) {
471 fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
472 return;
473 }
474
28e407b8 475 get_fuse_groups(perms, req);
7c673cae
FG
476
477 int r = cfuse->client->ll_listxattr(in, buf, size, perms);
478 if (size == 0 && r >= 0)
479 fuse_reply_xattr(req, r);
480 else if (r >= 0)
481 fuse_reply_buf(req, buf, r);
482 else
f67539c2 483 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
484
485 cfuse->iput(in); // iput required
486}
487
488static void fuse_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
489 size_t size
11fdf7f2 490#if defined(__APPLE__)
7c673cae
FG
491 ,uint32_t position
492#endif
493 )
494{
495 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
496 const struct fuse_ctx *ctx = fuse_req_ctx(req);
7c673cae
FG
497 char buf[size];
498 UserPerm perms(ctx->uid, ctx->gid);
2a845540
TL
499 Inode *in = cfuse->iget(ino);
500 if (!in) {
501 fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
502 return;
503 }
504
28e407b8 505 get_fuse_groups(perms, req);
7c673cae
FG
506
507 int r = cfuse->client->ll_getxattr(in, name, buf, size, perms);
508 if (size == 0 && r >= 0)
509 fuse_reply_xattr(req, r);
510 else if (r >= 0)
511 fuse_reply_buf(req, buf, r);
512 else
f67539c2 513 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
514
515 cfuse->iput(in); // iput required
516}
517
518static void fuse_ll_removexattr(fuse_req_t req, fuse_ino_t ino,
519 const char *name)
520{
521 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
522 const struct fuse_ctx *ctx = fuse_req_ctx(req);
7c673cae 523 UserPerm perms(ctx->uid, ctx->gid);
2a845540
TL
524 Inode *in = cfuse->iget(ino);
525 if (!in) {
526 fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
527 return;
528 }
529
28e407b8 530 get_fuse_groups(perms, req);
7c673cae
FG
531
532 int r = cfuse->client->ll_removexattr(in, name, perms);
f67539c2 533 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
534
535 cfuse->iput(in); // iput required
536}
537
538static void fuse_ll_opendir(fuse_req_t req, fuse_ino_t ino,
539 struct fuse_file_info *fi)
540{
541 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
542 const struct fuse_ctx *ctx = fuse_req_ctx(req);
2a845540 543 UserPerm perms(ctx->uid, ctx->gid);
7c673cae 544 void *dirp;
2a845540
TL
545 Inode *in = cfuse->iget(ino);
546 if (!in) {
547 fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
548 return;
549 }
7c673cae 550
28e407b8 551 get_fuse_groups(perms, req);
7c673cae
FG
552
553 int r = cfuse->client->ll_opendir(in, fi->flags, (dir_result_t **)&dirp,
554 perms);
555 if (r >= 0) {
556 fi->fh = (uint64_t)dirp;
557 fuse_reply_open(req, fi);
558 } else {
f67539c2 559 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
560 }
561
562 cfuse->iput(in); // iput required
563}
564
565static void fuse_ll_readlink(fuse_req_t req, fuse_ino_t ino)
566{
567 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
568 const struct fuse_ctx *ctx = fuse_req_ctx(req);
7c673cae
FG
569 char buf[PATH_MAX + 1]; // leave room for a null terminator
570 UserPerm perms(ctx->uid, ctx->gid);
2a845540
TL
571 Inode *in = cfuse->iget(ino);
572 if (!in) {
573 fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
574 return;
575 }
7c673cae 576
2a845540 577 get_fuse_groups(perms, req);
7c673cae
FG
578 int r = cfuse->client->ll_readlink(in, buf, sizeof(buf) - 1, perms);
579 if (r >= 0) {
580 buf[r] = '\0';
581 fuse_reply_readlink(req, buf);
582 } else {
f67539c2 583 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
584 }
585
586 cfuse->iput(in); // iput required
587}
588
589static void fuse_ll_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
590 mode_t mode, dev_t rdev)
591{
592 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
593 const struct fuse_ctx *ctx = fuse_req_ctx(req);
7c673cae
FG
594 struct fuse_entry_param fe;
595 UserPerm perms(ctx->uid, ctx->gid);
2a845540
TL
596 Inode *i2, *i1 = cfuse->iget(parent);
597 if (!i1) {
598 fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
599 return;
600 }
601
28e407b8 602 get_fuse_groups(perms, req);
7c673cae
FG
603
604 memset(&fe, 0, sizeof(fe));
605
606 int r = cfuse->client->ll_mknod(i1, name, mode, new_decode_dev(rdev),
607 &fe.attr, &i2, perms);
608 if (r == 0) {
609 fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev);
610 fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev);
611 fuse_reply_entry(req, &fe);
612 } else {
f67539c2 613 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
614 }
615
616 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
617 // fuse_ll_forget()
618 cfuse->iput(i1); // iput required
619}
620
621static void fuse_ll_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
622 mode_t mode)
623{
624 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
625 const struct fuse_ctx *ctx = fuse_req_ctx(req);
626 Inode *i2, *i1;
627 struct fuse_entry_param fe;
628
629 memset(&fe, 0, sizeof(fe));
630 UserPerm perm(ctx->uid, ctx->gid);
28e407b8 631 get_fuse_groups(perm, req);
7c673cae 632#ifdef HAVE_SYS_SYNCFS
11fdf7f2
TL
633 auto fuse_multithreaded = cfuse->client->cct->_conf.get_val<bool>(
634 "fuse_multithreaded");
635 auto fuse_syncfs_on_mksnap = cfuse->client->cct->_conf.get_val<bool>(
636 "fuse_syncfs_on_mksnap");
7c673cae 637 if (cfuse->fino_snap(parent) == CEPH_SNAPDIR &&
11fdf7f2 638 fuse_multithreaded && fuse_syncfs_on_mksnap) {
7c673cae 639 int err = 0;
1911f103
TL
640#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
641 int fd = ::open(cfuse->opts.mountpoint, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
642#else
91327a77 643 int fd = ::open(cfuse->mountpoint, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
1911f103 644#endif
7c673cae
FG
645 if (fd < 0) {
646 err = errno;
647 } else {
648 int r = ::syncfs(fd);
649 if (r < 0)
650 err = errno;
651 ::close(fd);
652 }
653 if (err) {
654 fuse_reply_err(req, err);
655 return;
656 }
657 }
658#endif
659
660 i1 = cfuse->iget(parent);
2a845540
TL
661 if (!i1) {
662 fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
663 return;
664 }
665
7c673cae
FG
666 int r = cfuse->client->ll_mkdir(i1, name, mode, &fe.attr, &i2, perm);
667 if (r == 0) {
668 fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev);
669 fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev);
670 fuse_reply_entry(req, &fe);
671 } else {
f67539c2 672 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
673 }
674
675 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
676 // fuse_ll_forget()
677 cfuse->iput(i1); // iput required
678}
679
680static void fuse_ll_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
681{
682 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
683 const struct fuse_ctx *ctx = fuse_req_ctx(req);
7c673cae 684 UserPerm perm(ctx->uid, ctx->gid);
2a845540
TL
685 Inode *in = cfuse->iget(parent);
686 if (!in) {
687 fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
688 return;
689 }
690
28e407b8 691 get_fuse_groups(perm, req);
7c673cae
FG
692
693 int r = cfuse->client->ll_unlink(in, name, perm);
f67539c2 694 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
695
696 cfuse->iput(in); // iput required
697}
698
699static void fuse_ll_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
700{
701 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
702 const struct fuse_ctx *ctx = fuse_req_ctx(req);
7c673cae 703 UserPerm perms(ctx->uid, ctx->gid);
2a845540
TL
704 Inode *in = cfuse->iget(parent);
705 if (!in) {
706 fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
707 return;
708 }
709
28e407b8 710 get_fuse_groups(perms, req);
7c673cae
FG
711
712 int r = cfuse->client->ll_rmdir(in, name, perms);
f67539c2 713 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
714
715 cfuse->iput(in); // iput required
716}
717
718static void fuse_ll_symlink(fuse_req_t req, const char *existing,
719 fuse_ino_t parent, const char *name)
720{
721 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
722 const struct fuse_ctx *ctx = fuse_req_ctx(req);
7c673cae
FG
723 struct fuse_entry_param fe;
724 UserPerm perms(ctx->uid, ctx->gid);
2a845540
TL
725 Inode *i2, *i1 = cfuse->iget(parent);
726 if (!i1) {
727 fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
728 return;
729 }
730
28e407b8 731 get_fuse_groups(perms, req);
7c673cae
FG
732
733 memset(&fe, 0, sizeof(fe));
734
735 int r = cfuse->client->ll_symlink(i1, name, existing, &fe.attr, &i2, perms);
736 if (r == 0) {
737 fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev);
738 fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev);
739 fuse_reply_entry(req, &fe);
740 } else {
f67539c2 741 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
742 }
743
744 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
745 // fuse_ll_forget()
746 cfuse->iput(i1); // iput required
747}
748
749static void fuse_ll_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
1911f103
TL
750 fuse_ino_t newparent, const char *newname
751#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
752 , unsigned int flags
753#endif
754 )
7c673cae
FG
755{
756 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
757 const struct fuse_ctx *ctx = fuse_req_ctx(req);
2a845540 758 UserPerm perm(ctx->uid, ctx->gid);
7c673cae
FG
759 Inode *in = cfuse->iget(parent);
760 Inode *nin = cfuse->iget(newparent);
2a845540
TL
761 if (!in || !nin) {
762 fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
763 return;
764 }
765
28e407b8 766 get_fuse_groups(perm, req);
7c673cae
FG
767
768 int r = cfuse->client->ll_rename(in, name, nin, newname, perm);
f67539c2 769 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
770
771 cfuse->iput(in); // iputs required
772 cfuse->iput(nin);
773}
774
775static void fuse_ll_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
776 const char *newname)
777{
778 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
779 const struct fuse_ctx *ctx = fuse_req_ctx(req);
2a845540 780 struct fuse_entry_param fe;
7c673cae
FG
781 Inode *in = cfuse->iget(ino);
782 Inode *nin = cfuse->iget(newparent);
2a845540
TL
783 if (!in || !nin) {
784 fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
785 return;
786 }
7c673cae
FG
787
788 memset(&fe, 0, sizeof(fe));
789 UserPerm perm(ctx->uid, ctx->gid);
28e407b8 790 get_fuse_groups(perm, req);
2a845540 791
7c673cae
FG
792 /*
793 * Note that we could successfully link, but then fail the subsequent
794 * getattr and return an error. Perhaps we should ignore getattr errors,
795 * but then how do we tell FUSE that the attrs are bogus?
796 */
797 int r = cfuse->client->ll_link(in, nin, newname, perm);
798 if (r == 0) {
799 r = cfuse->client->ll_getattr(in, &fe.attr, perm);
800 if (r == 0) {
801 fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev);
802 fe.attr.st_rdev = new_encode_dev(fe.attr.st_rdev);
803 fuse_reply_entry(req, &fe);
804 }
805 }
806
807 if (r != 0) {
808 /*
809 * Many ll operations in libcephfs return an extra inode reference, but
810 * ll_link currently does not. Still, FUSE needs one for the new dentry,
811 * so we commandeer the reference taken earlier when ll_link is successful.
812 * On error however, we must put that reference.
813 */
814 cfuse->iput(in);
f67539c2 815 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
816 }
817
818 cfuse->iput(nin);
819}
820
821static void fuse_ll_open(fuse_req_t req, fuse_ino_t ino,
822 struct fuse_file_info *fi)
823{
824 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
825 const struct fuse_ctx *ctx = fuse_req_ctx(req);
7c673cae
FG
826 Fh *fh = NULL;
827 UserPerm perms(ctx->uid, ctx->gid);
2a845540
TL
828 Inode *in = cfuse->iget(ino);
829 if (!in) {
830 fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
831 return;
832 }
833
28e407b8 834 get_fuse_groups(perms, req);
7c673cae
FG
835
836 int r = cfuse->client->ll_open(in, fi->flags, &fh, perms);
837 if (r == 0) {
838 fi->fh = (uint64_t)fh;
839#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
11fdf7f2
TL
840 auto fuse_disable_pagecache = cfuse->client->cct->_conf.get_val<bool>(
841 "fuse_disable_pagecache");
842 auto fuse_use_invalidate_cb = cfuse->client->cct->_conf.get_val<bool>(
843 "fuse_use_invalidate_cb");
844 if (fuse_disable_pagecache)
7c673cae 845 fi->direct_io = 1;
11fdf7f2 846 else if (fuse_use_invalidate_cb)
7c673cae
FG
847 fi->keep_cache = 1;
848#endif
849 fuse_reply_open(req, fi);
850 } else {
f67539c2 851 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
852 }
853
854 cfuse->iput(in); // iput required
855}
856
857static void fuse_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
858 struct fuse_file_info *fi)
859{
860 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
861 Fh *fh = reinterpret_cast<Fh*>(fi->fh);
862 bufferlist bl;
863 int r = cfuse->client->ll_read(fh, off, size, &bl);
f67539c2
TL
864 if (r >= 0) {
865 vector<iovec> iov;
866 size_t len;
867 struct fuse_bufvec *bufv;
868
869 if (bl.get_num_buffers() > IOV_MAX)
870 bl.rebuild();
871
872 bl.prepare_iov(&iov);
873 len = sizeof(struct fuse_bufvec) + sizeof(struct fuse_buf) * (iov.size() - 1);
874 bufv = (struct fuse_bufvec *)calloc(1, len);
875 if (bufv) {
876 int i = 0;
877 bufv->count = iov.size();
878 for (auto &v: iov) {
879 bufv->buf[i].mem = v.iov_base;
880 bufv->buf[i++].size = v.iov_len;
881 }
882 fuse_reply_data(req, bufv, FUSE_BUF_SPLICE_MOVE);
883 free(bufv);
884 return;
885 }
886 iov.insert(iov.begin(), {0}); // the first one is reserved for fuse_out_header
887 fuse_reply_iov(req, &iov[0], iov.size());
888 } else
889 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
890}
891
892static void fuse_ll_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
893 size_t size, off_t off, struct fuse_file_info *fi)
894{
895 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
896 Fh *fh = reinterpret_cast<Fh*>(fi->fh);
897 int r = cfuse->client->ll_write(fh, off, size, buf);
898 if (r >= 0)
899 fuse_reply_write(req, r);
900 else
f67539c2 901 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
902}
903
904static void fuse_ll_flush(fuse_req_t req, fuse_ino_t ino,
905 struct fuse_file_info *fi)
906{
907 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
908 Fh *fh = reinterpret_cast<Fh*>(fi->fh);
909 int r = cfuse->client->ll_flush(fh);
f67539c2 910 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
911}
912
913#ifdef FUSE_IOCTL_COMPAT
e306af50
TL
914static void fuse_ll_ioctl(fuse_req_t req, fuse_ino_t ino,
915#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 5)
916 unsigned int cmd,
917#else
918 int cmd,
919#endif
920 void *arg, struct fuse_file_info *fi,
7c673cae
FG
921 unsigned flags, const void *in_buf, size_t in_bufsz, size_t out_bufsz)
922{
923 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
924
925 if (flags & FUSE_IOCTL_COMPAT) {
926 fuse_reply_err(req, ENOSYS);
927 return;
928 }
929
930 switch (static_cast<unsigned>(cmd)) {
931 case CEPH_IOC_GET_LAYOUT: {
932 file_layout_t layout;
933 struct ceph_ioctl_layout l;
934 Fh *fh = (Fh*)fi->fh;
935 cfuse->client->ll_file_layout(fh, &layout);
936 l.stripe_unit = layout.stripe_unit;
937 l.stripe_count = layout.stripe_count;
938 l.object_size = layout.object_size;
939 l.data_pool = layout.pool_id;
940 fuse_reply_ioctl(req, 0, &l, sizeof(struct ceph_ioctl_layout));
941 }
942 break;
943 default:
944 fuse_reply_err(req, EINVAL);
945 }
946}
947#endif
948
494da23a 949#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
7c673cae
FG
950
951static void fuse_ll_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
952 off_t offset, off_t length,
953 struct fuse_file_info *fi)
954{
955 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
956 Fh *fh = (Fh*)fi->fh;
957 int r = cfuse->client->ll_fallocate(fh, mode, offset, length);
f67539c2 958 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
959}
960
961#endif
962
963static void fuse_ll_release(fuse_req_t req, fuse_ino_t ino,
964 struct fuse_file_info *fi)
965{
966 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
967 Fh *fh = reinterpret_cast<Fh*>(fi->fh);
968 int r = cfuse->client->ll_release(fh);
f67539c2 969 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
970}
971
972static void fuse_ll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
973 struct fuse_file_info *fi)
974{
975 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
976 Fh *fh = reinterpret_cast<Fh*>(fi->fh);
977 int r = cfuse->client->ll_fsync(fh, datasync);
f67539c2 978 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
979}
980
981struct readdir_context {
982 fuse_req_t req;
983 char *buf;
984 size_t size;
985 size_t pos; /* in buf */
986 uint64_t snap;
987};
988
989/*
990 * return 0 on success, -1 if out of space
991 */
992static int fuse_ll_add_dirent(void *p, struct dirent *de,
993 struct ceph_statx *stx, off_t next_off,
994 Inode *in)
995{
996 struct readdir_context *c = (struct readdir_context *)p;
997 CephFuse::Handle *cfuse = (CephFuse::Handle *)fuse_req_userdata(c->req);
998
999 struct stat st;
1000 st.st_ino = cfuse->make_fake_ino(stx->stx_ino, c->snap);
1001 st.st_mode = stx->stx_mode;
1002 st.st_rdev = new_encode_dev(stx->stx_rdev);
1003
1004 size_t room = c->size - c->pos;
1005 size_t entrysize = fuse_add_direntry(c->req, c->buf + c->pos, room,
1006 de->d_name, &st, next_off);
1007 if (entrysize > room)
1008 return -ENOSPC;
1009
1010 /* success */
1011 c->pos += entrysize;
1012 return 0;
1013}
1014
1015static void fuse_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
1016 off_t off, struct fuse_file_info *fi)
1017{
1018 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
1019
1020 dir_result_t *dirp = reinterpret_cast<dir_result_t*>(fi->fh);
1021 cfuse->client->seekdir(dirp, off);
1022
1023 struct readdir_context rc;
1024 rc.req = req;
2a845540
TL
1025 rc.snap = cfuse->fino_snap(ino);
1026 if (rc.snap == CEPH_MAXSNAP) {
1027 fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
1028 return;
1029 }
7c673cae
FG
1030 rc.buf = new char[size];
1031 rc.size = size;
1032 rc.pos = 0;
7c673cae
FG
1033
1034 int r = cfuse->client->readdir_r_cb(dirp, fuse_ll_add_dirent, &rc);
f67539c2 1035 if (r == 0 || r == -CEPHFS_ENOSPC) /* ignore ENOSPC from our callback */
7c673cae
FG
1036 fuse_reply_buf(req, rc.buf, rc.pos);
1037 else
f67539c2 1038 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
1039 delete[] rc.buf;
1040}
1041
1042static void fuse_ll_releasedir(fuse_req_t req, fuse_ino_t ino,
1043 struct fuse_file_info *fi)
1044{
1045 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
1046 dir_result_t *dirp = reinterpret_cast<dir_result_t*>(fi->fh);
1047 cfuse->client->ll_releasedir(dirp);
1048 fuse_reply_err(req, 0);
1049}
1050
1051static void fuse_ll_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
1052 struct fuse_file_info *fi)
1053{
1054 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
1055 dir_result_t *dirp = reinterpret_cast<dir_result_t*>(fi->fh);
1056 int r = cfuse->client->ll_fsyncdir(dirp);
f67539c2 1057 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
1058}
1059
1060static void fuse_ll_access(fuse_req_t req, fuse_ino_t ino, int mask)
1061{
28e407b8
AA
1062 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
1063 const struct fuse_ctx *ctx = fuse_req_ctx(req);
28e407b8 1064 UserPerm perms(ctx->uid, ctx->gid);
2a845540
TL
1065 Inode *in = cfuse->iget(ino);
1066 if (!in) {
1067 fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
1068 return;
1069 }
1070
28e407b8
AA
1071 get_fuse_groups(perms, req);
1072
1073 int r = cfuse->client->inode_permission(in, perms, mask);
f67539c2 1074 fuse_reply_err(req, get_sys_errno(-r));
28e407b8 1075 cfuse->iput(in);
7c673cae
FG
1076}
1077
1078static void fuse_ll_create(fuse_req_t req, fuse_ino_t parent, const char *name,
1079 mode_t mode, struct fuse_file_info *fi)
1080{
1081 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
1082 const struct fuse_ctx *ctx = fuse_req_ctx(req);
7c673cae
FG
1083 struct fuse_entry_param fe;
1084 Fh *fh = NULL;
1085 UserPerm perms(ctx->uid, ctx->gid);
2a845540
TL
1086 Inode *i1 = cfuse->iget(parent), *i2;
1087 if (!i1) {
1088 fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
1089 return;
1090 }
1091
28e407b8 1092 get_fuse_groups(perms, req);
7c673cae
FG
1093
1094 memset(&fe, 0, sizeof(fe));
1095
1096 // pass &i2 for the created inode so that ll_create takes an initial ll_ref
1097 int r = cfuse->client->ll_create(i1, name, mode, fi->flags, &fe.attr, &i2,
1098 &fh, perms);
1099 if (r == 0) {
1100 fi->fh = (uint64_t)fh;
1101 fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev);
1102#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
11fdf7f2
TL
1103 auto fuse_disable_pagecache = cfuse->client->cct->_conf.get_val<bool>(
1104 "fuse_disable_pagecache");
1105 auto fuse_use_invalidate_cb = cfuse->client->cct->_conf.get_val<bool>(
1106 "fuse_use_invalidate_cb");
1107 if (fuse_disable_pagecache)
7c673cae 1108 fi->direct_io = 1;
11fdf7f2 1109 else if (fuse_use_invalidate_cb)
7c673cae
FG
1110 fi->keep_cache = 1;
1111#endif
1112 fuse_reply_create(req, &fe, fi);
1113 } else
f67539c2 1114 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
1115 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
1116 // fuse_ll_forget()
1117 cfuse->iput(i1); // iput required
1118}
1119
1120static void fuse_ll_statfs(fuse_req_t req, fuse_ino_t ino)
1121{
1122 struct statvfs stbuf;
1123 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
7c673cae
FG
1124 const struct fuse_ctx *ctx = fuse_req_ctx(req);
1125 UserPerm perms(ctx->uid, ctx->gid);
2a845540
TL
1126 Inode *in = cfuse->iget(ino);
1127 if (!in) {
1128 fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
1129 return;
1130 }
1131
28e407b8 1132 get_fuse_groups(perms, req);
7c673cae
FG
1133
1134 int r = cfuse->client->ll_statfs(in, &stbuf, perms);
1135 if (r == 0)
1136 fuse_reply_statfs(req, &stbuf);
1137 else
f67539c2 1138 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
1139
1140 cfuse->iput(in); // iput required
1141}
1142
1143static void fuse_ll_getlk(fuse_req_t req, fuse_ino_t ino,
1144 struct fuse_file_info *fi, struct flock *lock)
1145{
1146 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
1147 Fh *fh = reinterpret_cast<Fh*>(fi->fh);
1148
1149 int r = cfuse->client->ll_getlk(fh, lock, fi->lock_owner);
1150 if (r == 0)
1151 fuse_reply_lock(req, lock);
1152 else
f67539c2 1153 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
1154}
1155
1156static void fuse_ll_setlk(fuse_req_t req, fuse_ino_t ino,
1157 struct fuse_file_info *fi, struct flock *lock, int sleep)
1158{
1159 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
1160 Fh *fh = reinterpret_cast<Fh*>(fi->fh);
1161
1162 // must use multithread if operation may block
11fdf7f2
TL
1163 auto fuse_multithreaded = cfuse->client->cct->_conf.get_val<bool>(
1164 "fuse_multithreaded");
1165 if (!fuse_multithreaded && sleep && lock->l_type != F_UNLCK) {
7c673cae
FG
1166 fuse_reply_err(req, EDEADLK);
1167 return;
1168 }
1169
1170 int r = cfuse->client->ll_setlk(fh, lock, fi->lock_owner, sleep);
f67539c2 1171 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
1172}
1173
1174static void fuse_ll_interrupt(fuse_req_t req, void* data)
1175{
1176 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
1177 cfuse->client->ll_interrupt(data);
1178}
1179
1180static void switch_interrupt_cb(void *handle, void* data)
1181{
1182 CephFuse::Handle *cfuse = (CephFuse::Handle *)handle;
1183 fuse_req_t req = cfuse->get_fuse_req();
1184
1185 if (data)
1186 fuse_req_interrupt_func(req, fuse_ll_interrupt, data);
1187 else
1188 fuse_req_interrupt_func(req, NULL, NULL);
1189}
1190
1191#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
1192static void fuse_ll_flock(fuse_req_t req, fuse_ino_t ino,
1193 struct fuse_file_info *fi, int cmd)
1194{
1195 CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
1196 Fh *fh = (Fh*)fi->fh;
1197
1198 // must use multithread if operation may block
11fdf7f2
TL
1199 auto fuse_multithreaded = cfuse->client->cct->_conf.get_val<bool>(
1200 "fuse_multithreaded");
1201 if (!fuse_multithreaded && !(cmd & (LOCK_NB | LOCK_UN))) {
7c673cae
FG
1202 fuse_reply_err(req, EDEADLK);
1203 return;
1204 }
1205
1206 int r = cfuse->client->ll_flock(fh, cmd, fi->lock_owner);
f67539c2 1207 fuse_reply_err(req, get_sys_errno(-r));
7c673cae
FG
1208}
1209#endif
1210
11fdf7f2 1211#if !defined(__APPLE__)
7c673cae
FG
1212static mode_t umask_cb(void *handle)
1213{
1214 CephFuse::Handle *cfuse = (CephFuse::Handle *)handle;
1215 fuse_req_t req = cfuse->get_fuse_req();
1216 const struct fuse_ctx *ctx = fuse_req_ctx(req);
1217 return ctx->umask;
1218}
1219#endif
1220
1221static void ino_invalidate_cb(void *handle, vinodeno_t vino, int64_t off,
1222 int64_t len)
1223{
1224#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
1225 CephFuse::Handle *cfuse = (CephFuse::Handle *)handle;
1226 fuse_ino_t fino = cfuse->make_fake_ino(vino.ino, vino.snapid);
1911f103
TL
1227#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1228 fuse_lowlevel_notify_inval_inode(cfuse->se, fino, off, len);
1229#else
7c673cae
FG
1230 fuse_lowlevel_notify_inval_inode(cfuse->ch, fino, off, len);
1231#endif
1911f103 1232#endif
7c673cae
FG
1233}
1234
1235static void dentry_invalidate_cb(void *handle, vinodeno_t dirino,
e306af50 1236 vinodeno_t ino, const char *name, size_t len)
7c673cae
FG
1237{
1238 CephFuse::Handle *cfuse = (CephFuse::Handle *)handle;
1239 fuse_ino_t fdirino = cfuse->make_fake_ino(dirino.ino, dirino.snapid);
1240#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
1241 fuse_ino_t fino = 0;
1242 if (ino.ino != inodeno_t())
1243 fino = cfuse->make_fake_ino(ino.ino, ino.snapid);
1911f103 1244#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
e306af50 1245 fuse_lowlevel_notify_delete(cfuse->se, fdirino, fino, name, len);
1911f103 1246#else
e306af50 1247 fuse_lowlevel_notify_delete(cfuse->ch, fdirino, fino, name, len);
1911f103 1248#endif
7c673cae 1249#elif FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
e306af50 1250 fuse_lowlevel_notify_inval_entry(cfuse->ch, fdirino, name, len);
7c673cae
FG
1251#endif
1252}
1253
1254static int remount_cb(void *handle)
1255{
1256 // used for trimming kernel dcache. when remounting a file system, linux kernel
1257 // trims all unused dentries in the file system
11fdf7f2 1258 char cmd[128+PATH_MAX];
7c673cae 1259 CephFuse::Handle *cfuse = (CephFuse::Handle *)handle;
1911f103
TL
1260 snprintf(cmd, sizeof(cmd), "LIBMOUNT_FSTAB=/dev/null mount -i -o remount %s",
1261#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1262 cfuse->opts.mountpoint);
1263#else
1264 cfuse->mountpoint);
1265#endif
7c673cae
FG
1266 int r = system(cmd);
1267 if (r != 0 && r != -1) {
1268 r = WEXITSTATUS(r);
1269 }
1270
1271 return r;
1272}
1273
1274static void do_init(void *data, fuse_conn_info *conn)
1275{
1276 CephFuse::Handle *cfuse = (CephFuse::Handle *)data;
1277 Client *client = cfuse->client;
1278
f67539c2
TL
1279#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1280 fuse_apply_conn_info_opts(cfuse->conn_opts, conn);
1281#endif
1282
1283 if(conn->capable & FUSE_CAP_SPLICE_MOVE)
1284 conn->want |= FUSE_CAP_SPLICE_MOVE;
1285
11fdf7f2 1286#if !defined(__APPLE__)
92f5a8d4 1287 if (!client->fuse_default_permissions && client->ll_handle_umask()) {
7c673cae
FG
1288 // apply umask in userspace if posix acl is enabled
1289 if(conn->capable & FUSE_CAP_DONT_MASK)
1290 conn->want |= FUSE_CAP_DONT_MASK;
1291 }
11fdf7f2
TL
1292 if(conn->capable & FUSE_CAP_EXPORT_SUPPORT)
1293 conn->want |= FUSE_CAP_EXPORT_SUPPORT;
7c673cae
FG
1294#endif
1295
1296 if (cfuse->fd_on_success) {
1297 //cout << "fuse init signaling on fd " << fd_on_success << std::endl;
1298 // see Preforker::daemonize(), ceph-fuse's parent process expects a `-1`
1299 // from a daemonized child process.
1300 uint32_t r = -1;
1301 int err = safe_write(cfuse->fd_on_success, &r, sizeof(r));
1302 if (err) {
1303 derr << "fuse_ll: do_init: safe_write failed with error "
1304 << cpp_strerror(err) << dendl;
1305 ceph_abort();
1306 }
1307 //cout << "fuse init done signaling on fd " << fd_on_success << std::endl;
1308
1309 // close stdout, etc.
1310 ::close(0);
1311 ::close(1);
1312 ::close(2);
1313 }
1314}
1315
1316const static struct fuse_lowlevel_ops fuse_ll_oper = {
1317 init: do_init,
1318 destroy: 0,
1319 lookup: fuse_ll_lookup,
1320 forget: fuse_ll_forget,
1321 getattr: fuse_ll_getattr,
1322 setattr: fuse_ll_setattr,
1323 readlink: fuse_ll_readlink,
1324 mknod: fuse_ll_mknod,
1325 mkdir: fuse_ll_mkdir,
1326 unlink: fuse_ll_unlink,
1327 rmdir: fuse_ll_rmdir,
1328 symlink: fuse_ll_symlink,
1329 rename: fuse_ll_rename,
1330 link: fuse_ll_link,
1331 open: fuse_ll_open,
1332 read: fuse_ll_read,
1333 write: fuse_ll_write,
1334 flush: fuse_ll_flush,
1335 release: fuse_ll_release,
1336 fsync: fuse_ll_fsync,
1337 opendir: fuse_ll_opendir,
1338 readdir: fuse_ll_readdir,
1339 releasedir: fuse_ll_releasedir,
1340 fsyncdir: fuse_ll_fsyncdir,
1341 statfs: fuse_ll_statfs,
1342 setxattr: fuse_ll_setxattr,
1343 getxattr: fuse_ll_getxattr,
1344 listxattr: fuse_ll_listxattr,
1345 removexattr: fuse_ll_removexattr,
1346 access: fuse_ll_access,
1347 create: fuse_ll_create,
1348 getlk: fuse_ll_getlk,
1349 setlk: fuse_ll_setlk,
1350 bmap: 0,
1351#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
1352#ifdef FUSE_IOCTL_COMPAT
1353 ioctl: fuse_ll_ioctl,
1354#else
1355 ioctl: 0,
1356#endif
1357 poll: 0,
1358#endif
1359#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
1360 write_buf: 0,
1361 retrieve_reply: 0,
1362 forget_multi: 0,
1363 flock: fuse_ll_flock,
1364#endif
494da23a 1365#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
7c673cae
FG
1366 fallocate: fuse_ll_fallocate
1367#endif
1368};
1369
1370
1371CephFuse::Handle::Handle(Client *c, int fd) :
1372 fd_on_success(fd),
2a845540 1373 client(c)
7c673cae 1374{
7c673cae 1375 memset(&args, 0, sizeof(args));
1911f103
TL
1376#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1377 memset(&opts, 0, sizeof(opts));
1378#endif
7c673cae
FG
1379}
1380
1381CephFuse::Handle::~Handle()
1382{
1383 fuse_opt_free_args(&args);
1384}
1385
1386void CephFuse::Handle::finalize()
1387{
1911f103
TL
1388#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1389 if (se) {
1390 fuse_remove_signal_handlers(se);
1391 fuse_session_unmount(se);
1392 fuse_session_destroy(se);
1393 }
f67539c2
TL
1394 if (conn_opts)
1395 free(conn_opts);
1911f103
TL
1396 if (opts.mountpoint)
1397 free(opts.mountpoint);
1398#else
7c673cae
FG
1399 if (se)
1400 fuse_remove_signal_handlers(se);
1401 if (ch)
1402 fuse_session_remove_chan(ch);
1403 if (se)
1404 fuse_session_destroy(se);
1405 if (ch)
1406 fuse_unmount(mountpoint, ch);
1911f103 1407#endif
7c673cae
FG
1408
1409 pthread_key_delete(fuse_req_key);
1410}
1411
1412int CephFuse::Handle::init(int argc, const char *argv[])
1413{
1414
1415 int r = pthread_key_create(&fuse_req_key, NULL);
1416 if (r) {
1417 derr << "pthread_key_create failed." << dendl;
1418 return r;
1419 }
1420
1421 // set up fuse argc/argv
1422 int newargc = 0;
f67539c2 1423 const char **newargv = (const char **) malloc((argc + 17) * sizeof(char *));
7c673cae
FG
1424 if(!newargv)
1425 return ENOMEM;
1426
1427 newargv[newargc++] = argv[0];
1428 newargv[newargc++] = "-f"; // stay in foreground
1429
11fdf7f2
TL
1430 auto fuse_allow_other = client->cct->_conf.get_val<bool>(
1431 "fuse_allow_other");
1432 auto fuse_default_permissions = client->cct->_conf.get_val<bool>(
1433 "fuse_default_permissions");
1911f103 1434#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
11fdf7f2
TL
1435 auto fuse_big_writes = client->cct->_conf.get_val<bool>(
1436 "fuse_big_writes");
f67539c2 1437#endif
1911f103
TL
1438 auto fuse_max_write = client->cct->_conf.get_val<Option::size_t>(
1439 "fuse_max_write");
11fdf7f2
TL
1440 auto fuse_atomic_o_trunc = client->cct->_conf.get_val<bool>(
1441 "fuse_atomic_o_trunc");
f67539c2
TL
1442 auto fuse_splice_read = client->cct->_conf.get_val<bool>(
1443 "fuse_splice_read");
1444 auto fuse_splice_write = client->cct->_conf.get_val<bool>(
1445 "fuse_splice_write");
1446 auto fuse_splice_move = client->cct->_conf.get_val<bool>(
1447 "fuse_splice_move");
11fdf7f2
TL
1448 auto fuse_debug = client->cct->_conf.get_val<bool>(
1449 "fuse_debug");
11fdf7f2
TL
1450
1451 if (fuse_allow_other) {
7c673cae
FG
1452 newargv[newargc++] = "-o";
1453 newargv[newargc++] = "allow_other";
1454 }
11fdf7f2 1455 if (fuse_default_permissions) {
7c673cae
FG
1456 newargv[newargc++] = "-o";
1457 newargv[newargc++] = "default_permissions";
1458 }
1459#if defined(__linux__)
1911f103 1460#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
11fdf7f2 1461 if (fuse_big_writes) {
7c673cae
FG
1462 newargv[newargc++] = "-o";
1463 newargv[newargc++] = "big_writes";
1464 }
f67539c2 1465#endif
11fdf7f2
TL
1466 if (fuse_max_write > 0) {
1467 char strsplice[65];
1468 newargv[newargc++] = "-o";
11fdf7f2
TL
1469 sprintf(strsplice, "max_write=%zu", (size_t)fuse_max_write);
1470 newargv[newargc++] = strsplice;
1471 }
1472 if (fuse_atomic_o_trunc) {
7c673cae
FG
1473 newargv[newargc++] = "-o";
1474 newargv[newargc++] = "atomic_o_trunc";
1475 }
f67539c2
TL
1476 if (fuse_splice_read) {
1477 newargv[newargc++] = "-o";
1478 newargv[newargc++] = "splice_read";
1479 }
1480 if (fuse_splice_write) {
1481 newargv[newargc++] = "-o";
1482 newargv[newargc++] = "splice_write";
1483 }
1484 if (fuse_splice_move) {
1485 newargv[newargc++] = "-o";
1486 newargv[newargc++] = "splice_move";
1487 }
7c673cae 1488#endif
11fdf7f2 1489 if (fuse_debug)
7c673cae
FG
1490 newargv[newargc++] = "-d";
1491
1492 for (int argctr = 1; argctr < argc; argctr++)
1493 newargv[newargc++] = argv[argctr];
1494
1495 derr << "init, newargv = " << newargv << " newargc=" << newargc << dendl;
1496 struct fuse_args a = FUSE_ARGS_INIT(newargc, (char**)newargv);
1497 args = a; // Roundabout construction b/c FUSE_ARGS_INIT is for initialization not assignment
1498
1911f103
TL
1499#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1500 if (fuse_parse_cmdline(&args, &opts) == -1) {
1501#else
7c673cae 1502 if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) == -1) {
1911f103 1503#endif
7c673cae
FG
1504 derr << "fuse_parse_cmdline failed." << dendl;
1505 fuse_opt_free_args(&args);
1506 free(newargv);
1507 return EINVAL;
1508 }
1509
f67539c2
TL
1510#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1511 derr << "init, args.argv = " << args.argv << " args.argc=" << args.argc << dendl;
1512 conn_opts = fuse_parse_conn_info_opts(&args);
1513 if (!conn_opts) {
1514 derr << "fuse_parse_conn_info_opts failed" << dendl;
1515 fuse_opt_free_args(&args);
1516 free(newargv);
1517 return EINVAL;
1518 }
1519#endif
1520
11fdf7f2 1521 ceph_assert(args.allocated); // Checking fuse has realloc'd args so we can free newargv
7c673cae 1522 free(newargv);
20effc67
TL
1523
1524 struct ceph_client_callback_args cb_args = {
1525 handle: this,
1526 ino_cb: client->cct->_conf.get_val<bool>("fuse_use_invalidate_cb") ?
1527 ino_invalidate_cb : NULL,
1528 dentry_cb: dentry_invalidate_cb,
1529 switch_intr_cb: switch_interrupt_cb,
1530#if defined(__linux__)
1531 remount_cb: remount_cb,
1532#endif
1533#if !defined(__APPLE__)
1534 umask_cb: umask_cb,
1535#endif
1536 };
1537 r = client->ll_register_callbacks2(&cb_args);
1538 if (r) {
1539 derr << "registering callbacks failed: " << r << dendl;
1540 return r;
1541 }
1542
7c673cae
FG
1543 return 0;
1544}
1545
1546int CephFuse::Handle::start()
1547{
33c7a0ef
TL
1548 bool is_mounted = false;
1549#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1550 int err = already_fuse_mounted(opts.mountpoint, is_mounted);
1551#else
1552 int err = already_fuse_mounted(mountpoint, is_mounted);
1553#endif
1554 if (err) {
1555 return err;
1556 }
1557
1558 if (is_mounted) {
1559 return EBUSY;
1560 }
1561
1911f103
TL
1562#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1563 se = fuse_session_new(&args, &fuse_ll_oper, sizeof(fuse_ll_oper), this);
f67539c2
TL
1564 if (!se) {
1565 derr << "fuse_session_new failed" << dendl;
1566 return EDOM;
1567 }
1911f103 1568#else
7c673cae
FG
1569 ch = fuse_mount(mountpoint, &args);
1570 if (!ch) {
1571 derr << "fuse_mount(mountpoint=" << mountpoint << ") failed." << dendl;
1572 return EIO;
1573 }
1574
1575 se = fuse_lowlevel_new(&args, &fuse_ll_oper, sizeof(fuse_ll_oper), this);
1576 if (!se) {
1577 derr << "fuse_lowlevel_new failed" << dendl;
1578 return EDOM;
1579 }
f67539c2 1580#endif
7c673cae
FG
1581
1582 signal(SIGTERM, SIG_DFL);
1583 signal(SIGINT, SIG_DFL);
1584 if (fuse_set_signal_handlers(se) == -1) {
1585 derr << "fuse_set_signal_handlers failed" << dendl;
1586 return ENOSYS;
1587 }
1588
1911f103
TL
1589#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1590 if (fuse_session_mount(se, opts.mountpoint) != 0) {
1591 derr << "fuse_session_mount failed" << dendl;
1592 return ENOSYS;
1593 }
1594#else
7c673cae 1595 fuse_session_add_chan(se, ch);
1911f103 1596#endif
7c673cae 1597
7c673cae
FG
1598 return 0;
1599}
1600
1601int CephFuse::Handle::loop()
1602{
11fdf7f2
TL
1603 auto fuse_multithreaded = client->cct->_conf.get_val<bool>(
1604 "fuse_multithreaded");
1605 if (fuse_multithreaded) {
1e59de90 1606#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 12)
e306af50 1607 {
1e59de90
TL
1608 struct fuse_loop_config *conf = fuse_loop_cfg_create();
1609 ceph_assert(conf != nullptr);
e306af50 1610
1e59de90
TL
1611 fuse_loop_cfg_set_clone_fd(conf, opts.clone_fd);
1612 fuse_loop_cfg_set_idle_threads(conf, opts.max_idle_threads);
1613 fuse_loop_cfg_set_max_threads(conf, opts.max_threads);
1614
1615 int r = fuse_session_loop_mt(se, conf);
1616
1617 fuse_loop_cfg_destroy(conf);
1618 return r;
1619 }
1620#elif FUSE_VERSION >= FUSE_MAKE_VERSION(3, 1)
1621 {
1622 struct fuse_loop_config conf = {
1623 clone_fd: opts.clone_fd,
1624 max_idle_threads: opts.max_idle_threads
1625 };
e306af50
TL
1626 return fuse_session_loop_mt(se, &conf);
1627 }
1628#elif FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1911f103
TL
1629 return fuse_session_loop_mt(se, opts.clone_fd);
1630#else
7c673cae 1631 return fuse_session_loop_mt(se);
1911f103 1632#endif
7c673cae
FG
1633 } else {
1634 return fuse_session_loop(se);
1635 }
1636}
1637
1638uint64_t CephFuse::Handle::fino_snap(uint64_t fino)
1639{
1640 if (fino == FUSE_ROOT_ID)
1641 return CEPH_NOSNAP;
1642
1643 if (client->use_faked_inos()) {
1644 vinodeno_t vino = client->map_faked_ino(fino);
1645 return vino.snapid;
1646 } else {
11fdf7f2 1647 std::lock_guard l(stag_lock);
7c673cae 1648 uint64_t stag = FINO_STAG(fino);
2a845540
TL
1649 if (stag == 0)
1650 return CEPH_NOSNAP;
1651 else if (stag == 1)
1652 return CEPH_SNAPDIR;
1653
1654 inodeno_t ino = FINO_INO(fino);
1655
1656 // does the fino_maps for the ino exist ?
1657 if (!g_fino_maps.count(ino))
1658 return CEPH_MAXSNAP;
1659
1660 auto &fino_maps = g_fino_maps[ino];
1661
1662 // does the stagid <--> snapid map exist ?
1663 if (!fino_maps.stag_snap_map.count(stag))
1664 return CEPH_MAXSNAP;
1665
1666 // get the snapid
1667 return fino_maps.stag_snap_map[stag];
7c673cae
FG
1668 }
1669}
1670
1671Inode * CephFuse::Handle::iget(fuse_ino_t fino)
1672{
1673 if (fino == FUSE_ROOT_ID)
1674 return client->get_root();
1675
1676 if (client->use_faked_inos()) {
1677 return client->ll_get_inode((ino_t)fino);
1678 } else {
2a845540
TL
1679 uint64_t snap = fino_snap(fino);
1680 if (snap == CEPH_MAXSNAP)
1681 return NULL;
1682 vinodeno_t vino(FINO_INO(fino), snap);
7c673cae
FG
1683 return client->ll_get_inode(vino);
1684 }
1685}
1686
1687void CephFuse::Handle::iput(Inode *in)
1688{
2a845540 1689 client->ll_put(in);
7c673cae
FG
1690}
1691
1692uint64_t CephFuse::Handle::make_fake_ino(inodeno_t ino, snapid_t snapid)
1693{
1694 if (client->use_faked_inos()) {
1695 // already faked by libcephfs
1696 if (ino == client->get_root_ino())
1697 return FUSE_ROOT_ID;
1698
1699 return ino;
1700 } else {
1701 if (snapid == CEPH_NOSNAP && ino == client->get_root_ino())
1702 return FUSE_ROOT_ID;
1703
2a845540
TL
1704 int stag;
1705 if (snapid == CEPH_NOSNAP) {
1706 stag = G_NOSNAP_STAG;
1707 } else if (snapid == CEPH_SNAPDIR) {
1708 stag = G_SNAPDIR_STAG;
1709 } else {
1710 std::lock_guard l(stag_lock);
1711 auto &fino_maps = g_fino_maps[ino]; // will insert it anyway if not exists
11fdf7f2 1712
2a845540
TL
1713 // already exist ?
1714 if (fino_maps.snap_stag_map.count(snapid)) {
1715 inodeno_t fino = MAKE_FINO(ino, fino_maps.snap_stag_map[snapid]);
1716 return fino;
11fdf7f2
TL
1717 }
1718
2a845540
TL
1719 // create a new snapid <--> stagid map
1720 int first = fino_maps.last_stag & STAG_MASK;
1721 stag = (++fino_maps.last_stag) & STAG_MASK;
1722 for (; stag != first; stag = (++fino_maps.last_stag) & STAG_MASK) {
1723 // stag 0 is reserved for CEPH_NOSNAP and 1 for CEPH_SNAPDIR
1724 if (stag == 0 || stag == 1)
1725 continue;
1726
1727 // the new stag is not used ?
1728 if (!fino_maps.stag_snap_map.count(stag)) {
1729 fino_maps.snap_stag_map[snapid] = stag;
1730 fino_maps.stag_snap_map[stag] = snapid;
1731 break;
1732 }
1733
1734 // the stag is already used by a snpaid,
1735 // try to free it
1736 auto _snapid = fino_maps.stag_snap_map[stag];
1737 if (!client->ll_get_snap_ref(_snapid)) {
1738 fino_maps.snap_stag_map.erase(_snapid);
1739 fino_maps.snap_stag_map[snapid] = stag;
1740 fino_maps.stag_snap_map[stag] = snapid;
1741 break;
1742 }
1743 }
1744 if (stag == first) {
1745 /*
1746 * It shouldn't be here because the max snapshots for each
1747 * directory is 4_K, and here we have around 64_K, which is
1748 * from 0xffff - 2, stags could be used for each directory.
1749 *
1750 * More detail please see mds 'mds_max_snaps_per_dir' option.
1751 */
1752 ceph_abort_msg("run out of stag");
11fdf7f2
TL
1753 }
1754 }
11fdf7f2 1755
7c673cae
FG
1756 inodeno_t fino = MAKE_FINO(ino, stag);
1757 //cout << "make_fake_ino " << ino << "." << snapid << " -> " << fino << std::endl;
1758 return fino;
1759 }
1760}
1761
1762void CephFuse::Handle::set_fuse_req(fuse_req_t req)
1763{
1764 pthread_setspecific(fuse_req_key, (void*)req);
1765}
1766
1767fuse_req_t CephFuse::Handle::get_fuse_req()
1768{
1769 return (fuse_req_t) pthread_getspecific(fuse_req_key);
1770}
1771
1772
1773CephFuse::CephFuse(Client *c, int fd) : _handle(new CephFuse::Handle(c, fd))
1774{
1775}
1776
1777CephFuse::~CephFuse()
1778{
1779 delete _handle;
1780}
1781
1782int CephFuse::init(int argc, const char *argv[])
1783{
1784 return _handle->init(argc, argv);
1785}
1786
1787int CephFuse::start()
1788{
1789 return _handle->start();
1790}
1791
1792int CephFuse::loop()
1793{
1794 return _handle->loop();
1795}
1796
1797void CephFuse::finalize()
1798{
1799 return _handle->finalize();
1800}
1801
1802std::string CephFuse::get_mount_point() const
1803{
1911f103
TL
1804#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1805 if (_handle->opts.mountpoint) {
1806 return _handle->opts.mountpoint;
1807#else
7c673cae
FG
1808 if (_handle->mountpoint) {
1809 return _handle->mountpoint;
1911f103 1810#endif
7c673cae
FG
1811 } else {
1812 return "";
1813 }
1814}