1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
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.
16 #include <sys/types.h>
27 #if defined(__linux__)
30 #include <sys/xattr.h>
31 #include <linux/magic.h>
35 #include "common/errno.h"
36 #include "common/safe_io.h"
37 #include "include/types.h"
41 #include "common/config.h"
42 #include "include/ceph_assert.h"
43 #include "include/cephfs/ceph_ll_client.h"
44 #include "include/ceph_fuse.h"
47 #include <fuse_lowlevel.h>
49 #define dout_context g_ceph_context
51 #define FINO_INO(x) ((x) & ((1ull<<48)-1ull))
52 #define FINO_STAG(x) ((x) >> 48)
53 #define MAKE_FINO(i,s) ((i) | ((int64_t)(s) << 48))
54 #define STAG_MASK 0xffff
57 #define MINORMASK ((1U << MINORBITS) - 1)
59 #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
60 #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
61 #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
63 #if defined(__linux__)
64 #ifndef FUSE_SUPER_MAGIC
65 #define FUSE_SUPER_MAGIC 0x65735546
68 #define _CEPH_CLIENT_ID "ceph.client_id"
73 static const ceph::unordered_map
<int,int> cephfs_errno_to_system_errno
= {
74 {CEPHFS_EBLOCKLISTED
, ESHUTDOWN
},
75 {CEPHFS_EPERM
, EPERM
},
76 {CEPHFS_ESTALE
, ESTALE
},
77 {CEPHFS_ENOSPC
, ENOSPC
},
78 {CEPHFS_ETIMEDOUT
, ETIMEDOUT
},
80 {CEPHFS_ENOTCONN
, ENOTCONN
},
81 {CEPHFS_EEXIST
, EEXIST
},
82 {CEPHFS_EINTR
, EINTR
},
83 {CEPHFS_EINVAL
, EINVAL
},
84 {CEPHFS_EBADF
, EBADF
},
85 {CEPHFS_EROFS
, EROFS
},
86 {CEPHFS_EAGAIN
, EAGAIN
},
87 {CEPHFS_EACCES
, EACCES
},
88 {CEPHFS_ELOOP
, ELOOP
},
89 {CEPHFS_EISDIR
, EISDIR
},
90 {CEPHFS_ENOENT
, ENOENT
},
91 {CEPHFS_ENOTDIR
, ENOTDIR
},
92 {CEPHFS_ENAMETOOLONG
, ENAMETOOLONG
},
93 {CEPHFS_EBUSY
, EBUSY
},
94 {CEPHFS_EDQUOT
, EDQUOT
},
95 {CEPHFS_EFBIG
, EFBIG
},
96 {CEPHFS_ERANGE
, ERANGE
},
97 {CEPHFS_ENXIO
, ENXIO
},
98 {CEPHFS_ECANCELED
, ECANCELED
},
99 {CEPHFS_ENODATA
, ENODATA
},
100 {CEPHFS_EOPNOTSUPP
, EOPNOTSUPP
},
101 {CEPHFS_EXDEV
, EXDEV
},
102 {CEPHFS_ENOMEM
, ENOMEM
},
103 {CEPHFS_ENOTRECOVERABLE
, ENOTRECOVERABLE
},
104 {CEPHFS_ENOSYS
, ENOSYS
},
105 {CEPHFS_ENOTEMPTY
, ENOTEMPTY
},
106 {CEPHFS_EDEADLK
, EDEADLK
},
108 {CEPHFS_EMLINK
, EMLINK
},
109 {CEPHFS_ETIME
, ETIME
},
110 {CEPHFS_EOLDSNAPC
, EIO
} // forcing to EIO for now
116 static int get_sys_errno(int cephfs_errno
)
118 if (cephfs_errno
== 0)
121 auto it
= cephfs_errno_to_system_errno
.find(cephfs_errno
);
122 if (it
!= cephfs_errno_to_system_errno
.end())
127 static uint32_t new_encode_dev(dev_t dev
)
129 unsigned major
= MAJOR(dev
);
130 unsigned minor
= MINOR(dev
);
131 return (minor
& 0xff) | (major
<< 8) | ((minor
& ~0xff) << 12);
134 static dev_t
new_decode_dev(uint32_t dev
)
136 unsigned major
= (dev
& 0xfff00) >> 8;
137 unsigned minor
= (dev
& 0xff) | ((dev
>> 12) & 0xfff00);
138 return MKDEV(major
, minor
);
141 class CephFuse::Handle
{
143 Handle(Client
*c
, int fd
);
146 int init(int argc
, const char *argv
[]);
151 uint64_t fino_snap(uint64_t fino
);
152 uint64_t make_fake_ino(inodeno_t ino
, snapid_t snapid
);
153 Inode
* iget(fuse_ino_t fino
);
154 void iput(Inode
*in
);
159 struct fuse_session
*se
;
160 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
161 struct fuse_cmdline_opts opts
;
162 struct fuse_conn_info_opts
*conn_opts
;
164 struct fuse_chan
*ch
;
168 ceph::mutex stag_lock
= ceph::make_mutex("fuse_ll.cc stag_lock");
171 ceph::unordered_map
<uint64_t,int> snap_stag_map
;
172 ceph::unordered_map
<int,uint64_t> stag_snap_map
;
174 pthread_key_t fuse_req_key
= 0;
175 void set_fuse_req(fuse_req_t
);
176 fuse_req_t
get_fuse_req();
178 struct fuse_args args
;
181 #if defined(__linux__)
182 static int already_fuse_mounted(const char *path
, bool &already_mounted
)
184 struct statx path_statx
;
185 struct statx parent_statx
;
186 char path_copy
[PATH_MAX
] = {0};
187 char *parent_path
= NULL
;
190 already_mounted
= false;
192 strncpy(path_copy
, path
, sizeof(path_copy
)-1);
193 parent_path
= dirname(path_copy
);
195 // get stat information for original path
196 if (-1 == statx(AT_FDCWD
, path
, AT_STATX_DONT_SYNC
, STATX_INO
, &path_statx
)) {
198 derr
<< "fuse_ll: already_fuse_mounted: statx(" << path
<< ") failed with error "
199 << cpp_strerror(err
) << dendl
;
203 // if path isn't directory, then it can't be a mountpoint.
204 if (!(path_statx
.stx_mode
& S_IFDIR
)) {
206 derr
<< "fuse_ll: already_fuse_mounted: "
207 << path
<< " is not a directory" << dendl
;
211 // get stat information for parent path
212 if (-1 == statx(AT_FDCWD
, parent_path
, AT_STATX_DONT_SYNC
, STATX_INO
, &parent_statx
)) {
214 derr
<< "fuse_ll: already_fuse_mounted: statx(" << parent_path
<< ") failed with error "
215 << cpp_strerror(err
) << dendl
;
219 // if original path and parent have different device ids,
220 // then the path is a mount point
221 // or, if they refer to the same path, then it's probably
222 // the root directory '/' and therefore path is a mountpoint
223 if( path_statx
.stx_dev_major
!= parent_statx
.stx_dev_major
||
224 path_statx
.stx_dev_minor
!= parent_statx
.stx_dev_minor
||
225 ( path_statx
.stx_dev_major
== parent_statx
.stx_dev_major
&&
226 path_statx
.stx_dev_minor
== parent_statx
.stx_dev_minor
&&
227 path_statx
.stx_ino
== parent_statx
.stx_ino
230 struct statfs path_statfs
;
231 if (-1 == statfs(path
, &path_statfs
)) {
233 derr
<< "fuse_ll: already_fuse_mounted: statfs(" << path
<< ") failed with error "
234 << cpp_strerror(err
) << dendl
;
238 if(FUSE_SUPER_MAGIC
== path_statfs
.f_type
) {
239 // if getxattr returns positive length means value exist for ceph.client_id
240 // then ceph fuse is already mounted on path
241 char client_id
[128] = {0};
242 if (getxattr(path
, _CEPH_CLIENT_ID
, &client_id
, sizeof(client_id
)) > 0) {
243 already_mounted
= true;
244 derr
<< path
<< " already mounted by " << client_id
<< dendl
;
251 #else // non-linux platforms
252 static int already_fuse_mounted(const char *path
, bool &already_mounted
)
254 already_mounted
= false;
259 static int getgroups(fuse_req_t req
, gid_t
**sgids
)
261 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
263 int c
= fuse_req_getgroups(req
, 0, NULL
);
271 gid_t
*gids
= new (std::nothrow
) gid_t
[c
];
273 return -get_sys_errno(CEPHFS_ENOMEM
);
275 c
= fuse_req_getgroups(req
, c
, gids
);
283 return -get_sys_errno(CEPHFS_ENOSYS
);
286 static void get_fuse_groups(UserPerm
& perms
, fuse_req_t req
)
288 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)fuse_req_userdata(req
);
289 if (cfuse
->client
->cct
->_conf
.get_val
<bool>("fuse_set_user_groups")) {
291 int count
= getgroups(req
, &gids
);
294 perms
.init_gids(gids
, count
);
295 } else if (count
< 0) {
296 derr
<< __func__
<< ": getgroups failed: " << cpp_strerror(-count
)
303 static CephFuse::Handle
*fuse_ll_req_prepare(fuse_req_t req
)
305 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)fuse_req_userdata(req
);
306 cfuse
->set_fuse_req(req
);
310 static void fuse_ll_lookup(fuse_req_t req
, fuse_ino_t parent
, const char *name
)
312 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
313 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
314 struct fuse_entry_param fe
;
315 Inode
*i2
, *i1
= cfuse
->iget(parent
); // see below
317 UserPerm
perms(ctx
->uid
, ctx
->gid
);
318 get_fuse_groups(perms
, req
);
322 r
= cfuse
->client
->lookup_ino(parent
, perms
, &i1
);
324 fuse_reply_err(req
, get_sys_errno(-r
));
329 memset(&fe
, 0, sizeof(fe
));
330 r
= cfuse
->client
->ll_lookup(i1
, name
, &fe
.attr
, &i2
, perms
);
332 fe
.ino
= cfuse
->make_fake_ino(fe
.attr
.st_ino
, fe
.attr
.st_dev
);
333 fe
.attr
.st_rdev
= new_encode_dev(fe
.attr
.st_rdev
);
334 fuse_reply_entry(req
, &fe
);
336 fuse_reply_err(req
, get_sys_errno(-r
));
339 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
344 // fuse3 has changed forget function signature
345 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
346 static void fuse_ll_forget(fuse_req_t req
, fuse_ino_t ino
,
349 static void fuse_ll_forget(fuse_req_t req
, fuse_ino_t ino
,
350 long unsigned nlookup
)
353 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
354 cfuse
->client
->ll_forget(cfuse
->iget(ino
), nlookup
+1);
355 fuse_reply_none(req
);
358 static void fuse_ll_getattr(fuse_req_t req
, fuse_ino_t ino
,
359 struct fuse_file_info
*fi
)
361 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
362 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
363 Inode
*in
= cfuse
->iget(ino
);
365 UserPerm
perms(ctx
->uid
, ctx
->gid
);
366 get_fuse_groups(perms
, req
);
370 if (cfuse
->client
->ll_getattr(in
, &stbuf
, perms
)
372 stbuf
.st_ino
= cfuse
->make_fake_ino(stbuf
.st_ino
, stbuf
.st_dev
);
373 stbuf
.st_rdev
= new_encode_dev(stbuf
.st_rdev
);
374 fuse_reply_attr(req
, &stbuf
, 0);
376 fuse_reply_err(req
, ENOENT
);
378 cfuse
->iput(in
); // iput required
381 static void fuse_ll_setattr(fuse_req_t req
, fuse_ino_t ino
, struct stat
*attr
,
382 int to_set
, struct fuse_file_info
*fi
)
384 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
385 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
386 Inode
*in
= cfuse
->iget(ino
);
387 UserPerm
perms(ctx
->uid
, ctx
->gid
);
388 get_fuse_groups(perms
, req
);
391 if (to_set
& FUSE_SET_ATTR_MODE
) mask
|= CEPH_SETATTR_MODE
;
392 if (to_set
& FUSE_SET_ATTR_UID
) mask
|= CEPH_SETATTR_UID
;
393 if (to_set
& FUSE_SET_ATTR_GID
) mask
|= CEPH_SETATTR_GID
;
394 if (to_set
& FUSE_SET_ATTR_MTIME
) mask
|= CEPH_SETATTR_MTIME
;
395 if (to_set
& FUSE_SET_ATTR_ATIME
) mask
|= CEPH_SETATTR_ATIME
;
396 if (to_set
& FUSE_SET_ATTR_SIZE
) mask
|= CEPH_SETATTR_SIZE
;
397 #if !defined(__APPLE__)
398 if (to_set
& FUSE_SET_ATTR_MTIME_NOW
) mask
|= CEPH_SETATTR_MTIME_NOW
;
399 if (to_set
& FUSE_SET_ATTR_ATIME_NOW
) mask
|= CEPH_SETATTR_ATIME_NOW
;
402 int r
= cfuse
->client
->ll_setattr(in
, attr
, mask
, perms
);
404 fuse_reply_attr(req
, attr
, 0);
406 fuse_reply_err(req
, get_sys_errno(-r
));
408 cfuse
->iput(in
); // iput required
413 static void fuse_ll_setxattr(fuse_req_t req
, fuse_ino_t ino
, const char *name
,
414 const char *value
, size_t size
,
416 #if defined(__APPLE__)
421 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
422 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
423 Inode
*in
= cfuse
->iget(ino
);
424 UserPerm
perms(ctx
->uid
, ctx
->gid
);
425 get_fuse_groups(perms
, req
);
427 int r
= cfuse
->client
->ll_setxattr(in
, name
, value
, size
, flags
, perms
);
428 fuse_reply_err(req
, get_sys_errno(-r
));
430 cfuse
->iput(in
); // iput required
433 static void fuse_ll_listxattr(fuse_req_t req
, fuse_ino_t ino
, size_t size
)
435 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
436 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
437 Inode
*in
= cfuse
->iget(ino
);
439 UserPerm
perms(ctx
->uid
, ctx
->gid
);
440 get_fuse_groups(perms
, req
);
442 int r
= cfuse
->client
->ll_listxattr(in
, buf
, size
, perms
);
443 if (size
== 0 && r
>= 0)
444 fuse_reply_xattr(req
, r
);
446 fuse_reply_buf(req
, buf
, r
);
448 fuse_reply_err(req
, get_sys_errno(-r
));
450 cfuse
->iput(in
); // iput required
453 static void fuse_ll_getxattr(fuse_req_t req
, fuse_ino_t ino
, const char *name
,
455 #if defined(__APPLE__)
460 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
461 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
462 Inode
*in
= cfuse
->iget(ino
);
464 UserPerm
perms(ctx
->uid
, ctx
->gid
);
465 get_fuse_groups(perms
, req
);
467 int r
= cfuse
->client
->ll_getxattr(in
, name
, buf
, size
, perms
);
468 if (size
== 0 && r
>= 0)
469 fuse_reply_xattr(req
, r
);
471 fuse_reply_buf(req
, buf
, r
);
473 fuse_reply_err(req
, get_sys_errno(-r
));
475 cfuse
->iput(in
); // iput required
478 static void fuse_ll_removexattr(fuse_req_t req
, fuse_ino_t ino
,
481 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
482 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
483 Inode
*in
= cfuse
->iget(ino
);
484 UserPerm
perms(ctx
->uid
, ctx
->gid
);
485 get_fuse_groups(perms
, req
);
487 int r
= cfuse
->client
->ll_removexattr(in
, name
, perms
);
488 fuse_reply_err(req
, get_sys_errno(-r
));
490 cfuse
->iput(in
); // iput required
493 static void fuse_ll_opendir(fuse_req_t req
, fuse_ino_t ino
,
494 struct fuse_file_info
*fi
)
496 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
497 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
498 Inode
*in
= cfuse
->iget(ino
);
501 UserPerm
perms(ctx
->uid
, ctx
->gid
);
502 get_fuse_groups(perms
, req
);
504 int r
= cfuse
->client
->ll_opendir(in
, fi
->flags
, (dir_result_t
**)&dirp
,
507 fi
->fh
= (uint64_t)dirp
;
508 fuse_reply_open(req
, fi
);
510 fuse_reply_err(req
, get_sys_errno(-r
));
513 cfuse
->iput(in
); // iput required
516 static void fuse_ll_readlink(fuse_req_t req
, fuse_ino_t ino
)
518 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
519 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
520 Inode
*in
= cfuse
->iget(ino
);
521 char buf
[PATH_MAX
+ 1]; // leave room for a null terminator
522 UserPerm
perms(ctx
->uid
, ctx
->gid
);
523 get_fuse_groups(perms
, req
);
525 int r
= cfuse
->client
->ll_readlink(in
, buf
, sizeof(buf
) - 1, perms
);
528 fuse_reply_readlink(req
, buf
);
530 fuse_reply_err(req
, get_sys_errno(-r
));
533 cfuse
->iput(in
); // iput required
536 static void fuse_ll_mknod(fuse_req_t req
, fuse_ino_t parent
, const char *name
,
537 mode_t mode
, dev_t rdev
)
539 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
540 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
541 Inode
*i2
, *i1
= cfuse
->iget(parent
);
542 struct fuse_entry_param fe
;
543 UserPerm
perms(ctx
->uid
, ctx
->gid
);
544 get_fuse_groups(perms
, req
);
546 memset(&fe
, 0, sizeof(fe
));
548 int r
= cfuse
->client
->ll_mknod(i1
, name
, mode
, new_decode_dev(rdev
),
549 &fe
.attr
, &i2
, perms
);
551 fe
.ino
= cfuse
->make_fake_ino(fe
.attr
.st_ino
, fe
.attr
.st_dev
);
552 fe
.attr
.st_rdev
= new_encode_dev(fe
.attr
.st_rdev
);
553 fuse_reply_entry(req
, &fe
);
555 fuse_reply_err(req
, get_sys_errno(-r
));
558 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
560 cfuse
->iput(i1
); // iput required
563 static void fuse_ll_mkdir(fuse_req_t req
, fuse_ino_t parent
, const char *name
,
566 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
567 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
569 struct fuse_entry_param fe
;
571 memset(&fe
, 0, sizeof(fe
));
572 UserPerm
perm(ctx
->uid
, ctx
->gid
);
573 get_fuse_groups(perm
, req
);
574 #ifdef HAVE_SYS_SYNCFS
575 auto fuse_multithreaded
= cfuse
->client
->cct
->_conf
.get_val
<bool>(
576 "fuse_multithreaded");
577 auto fuse_syncfs_on_mksnap
= cfuse
->client
->cct
->_conf
.get_val
<bool>(
578 "fuse_syncfs_on_mksnap");
579 if (cfuse
->fino_snap(parent
) == CEPH_SNAPDIR
&&
580 fuse_multithreaded
&& fuse_syncfs_on_mksnap
) {
582 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
583 int fd
= ::open(cfuse
->opts
.mountpoint
, O_RDONLY
| O_DIRECTORY
| O_CLOEXEC
);
585 int fd
= ::open(cfuse
->mountpoint
, O_RDONLY
| O_DIRECTORY
| O_CLOEXEC
);
590 int r
= ::syncfs(fd
);
596 fuse_reply_err(req
, err
);
602 i1
= cfuse
->iget(parent
);
603 int r
= cfuse
->client
->ll_mkdir(i1
, name
, mode
, &fe
.attr
, &i2
, perm
);
605 fe
.ino
= cfuse
->make_fake_ino(fe
.attr
.st_ino
, fe
.attr
.st_dev
);
606 fe
.attr
.st_rdev
= new_encode_dev(fe
.attr
.st_rdev
);
607 fuse_reply_entry(req
, &fe
);
609 fuse_reply_err(req
, get_sys_errno(-r
));
612 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
614 cfuse
->iput(i1
); // iput required
617 static void fuse_ll_unlink(fuse_req_t req
, fuse_ino_t parent
, const char *name
)
619 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
620 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
621 Inode
*in
= cfuse
->iget(parent
);
622 UserPerm
perm(ctx
->uid
, ctx
->gid
);
623 get_fuse_groups(perm
, req
);
625 int r
= cfuse
->client
->ll_unlink(in
, name
, perm
);
626 fuse_reply_err(req
, get_sys_errno(-r
));
628 cfuse
->iput(in
); // iput required
631 static void fuse_ll_rmdir(fuse_req_t req
, fuse_ino_t parent
, const char *name
)
633 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
634 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
635 Inode
*in
= cfuse
->iget(parent
);
636 UserPerm
perms(ctx
->uid
, ctx
->gid
);
637 get_fuse_groups(perms
, req
);
639 int r
= cfuse
->client
->ll_rmdir(in
, name
, perms
);
640 fuse_reply_err(req
, get_sys_errno(-r
));
642 cfuse
->iput(in
); // iput required
645 static void fuse_ll_symlink(fuse_req_t req
, const char *existing
,
646 fuse_ino_t parent
, const char *name
)
648 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
649 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
650 Inode
*i2
, *i1
= cfuse
->iget(parent
);
651 struct fuse_entry_param fe
;
652 UserPerm
perms(ctx
->uid
, ctx
->gid
);
653 get_fuse_groups(perms
, req
);
655 memset(&fe
, 0, sizeof(fe
));
657 int r
= cfuse
->client
->ll_symlink(i1
, name
, existing
, &fe
.attr
, &i2
, perms
);
659 fe
.ino
= cfuse
->make_fake_ino(fe
.attr
.st_ino
, fe
.attr
.st_dev
);
660 fe
.attr
.st_rdev
= new_encode_dev(fe
.attr
.st_rdev
);
661 fuse_reply_entry(req
, &fe
);
663 fuse_reply_err(req
, get_sys_errno(-r
));
666 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
668 cfuse
->iput(i1
); // iput required
671 static void fuse_ll_rename(fuse_req_t req
, fuse_ino_t parent
, const char *name
,
672 fuse_ino_t newparent
, const char *newname
673 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
678 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
679 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
680 Inode
*in
= cfuse
->iget(parent
);
681 Inode
*nin
= cfuse
->iget(newparent
);
682 UserPerm
perm(ctx
->uid
, ctx
->gid
);
683 get_fuse_groups(perm
, req
);
685 int r
= cfuse
->client
->ll_rename(in
, name
, nin
, newname
, perm
);
686 fuse_reply_err(req
, get_sys_errno(-r
));
688 cfuse
->iput(in
); // iputs required
692 static void fuse_ll_link(fuse_req_t req
, fuse_ino_t ino
, fuse_ino_t newparent
,
695 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
696 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
697 Inode
*in
= cfuse
->iget(ino
);
698 Inode
*nin
= cfuse
->iget(newparent
);
699 struct fuse_entry_param fe
;
701 memset(&fe
, 0, sizeof(fe
));
702 UserPerm
perm(ctx
->uid
, ctx
->gid
);
703 get_fuse_groups(perm
, req
);
706 * Note that we could successfully link, but then fail the subsequent
707 * getattr and return an error. Perhaps we should ignore getattr errors,
708 * but then how do we tell FUSE that the attrs are bogus?
710 int r
= cfuse
->client
->ll_link(in
, nin
, newname
, perm
);
712 r
= cfuse
->client
->ll_getattr(in
, &fe
.attr
, perm
);
714 fe
.ino
= cfuse
->make_fake_ino(fe
.attr
.st_ino
, fe
.attr
.st_dev
);
715 fe
.attr
.st_rdev
= new_encode_dev(fe
.attr
.st_rdev
);
716 fuse_reply_entry(req
, &fe
);
722 * Many ll operations in libcephfs return an extra inode reference, but
723 * ll_link currently does not. Still, FUSE needs one for the new dentry,
724 * so we commandeer the reference taken earlier when ll_link is successful.
725 * On error however, we must put that reference.
728 fuse_reply_err(req
, get_sys_errno(-r
));
734 static void fuse_ll_open(fuse_req_t req
, fuse_ino_t ino
,
735 struct fuse_file_info
*fi
)
737 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
738 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
739 Inode
*in
= cfuse
->iget(ino
);
741 UserPerm
perms(ctx
->uid
, ctx
->gid
);
742 get_fuse_groups(perms
, req
);
744 int r
= cfuse
->client
->ll_open(in
, fi
->flags
, &fh
, perms
);
746 fi
->fh
= (uint64_t)fh
;
747 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
748 auto fuse_disable_pagecache
= cfuse
->client
->cct
->_conf
.get_val
<bool>(
749 "fuse_disable_pagecache");
750 auto fuse_use_invalidate_cb
= cfuse
->client
->cct
->_conf
.get_val
<bool>(
751 "fuse_use_invalidate_cb");
752 if (fuse_disable_pagecache
)
754 else if (fuse_use_invalidate_cb
)
757 fuse_reply_open(req
, fi
);
759 fuse_reply_err(req
, get_sys_errno(-r
));
762 cfuse
->iput(in
); // iput required
765 static void fuse_ll_read(fuse_req_t req
, fuse_ino_t ino
, size_t size
, off_t off
,
766 struct fuse_file_info
*fi
)
768 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
769 Fh
*fh
= reinterpret_cast<Fh
*>(fi
->fh
);
771 int r
= cfuse
->client
->ll_read(fh
, off
, size
, &bl
);
775 struct fuse_bufvec
*bufv
;
777 if (bl
.get_num_buffers() > IOV_MAX
)
780 bl
.prepare_iov(&iov
);
781 len
= sizeof(struct fuse_bufvec
) + sizeof(struct fuse_buf
) * (iov
.size() - 1);
782 bufv
= (struct fuse_bufvec
*)calloc(1, len
);
785 bufv
->count
= iov
.size();
787 bufv
->buf
[i
].mem
= v
.iov_base
;
788 bufv
->buf
[i
++].size
= v
.iov_len
;
790 fuse_reply_data(req
, bufv
, FUSE_BUF_SPLICE_MOVE
);
794 iov
.insert(iov
.begin(), {0}); // the first one is reserved for fuse_out_header
795 fuse_reply_iov(req
, &iov
[0], iov
.size());
797 fuse_reply_err(req
, get_sys_errno(-r
));
800 static void fuse_ll_write(fuse_req_t req
, fuse_ino_t ino
, const char *buf
,
801 size_t size
, off_t off
, struct fuse_file_info
*fi
)
803 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
804 Fh
*fh
= reinterpret_cast<Fh
*>(fi
->fh
);
805 int r
= cfuse
->client
->ll_write(fh
, off
, size
, buf
);
807 fuse_reply_write(req
, r
);
809 fuse_reply_err(req
, get_sys_errno(-r
));
812 static void fuse_ll_flush(fuse_req_t req
, fuse_ino_t ino
,
813 struct fuse_file_info
*fi
)
815 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
816 Fh
*fh
= reinterpret_cast<Fh
*>(fi
->fh
);
817 int r
= cfuse
->client
->ll_flush(fh
);
818 fuse_reply_err(req
, get_sys_errno(-r
));
821 #ifdef FUSE_IOCTL_COMPAT
822 static void fuse_ll_ioctl(fuse_req_t req
, fuse_ino_t ino
,
823 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 5)
828 void *arg
, struct fuse_file_info
*fi
,
829 unsigned flags
, const void *in_buf
, size_t in_bufsz
, size_t out_bufsz
)
831 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
833 if (flags
& FUSE_IOCTL_COMPAT
) {
834 fuse_reply_err(req
, ENOSYS
);
838 switch (static_cast<unsigned>(cmd
)) {
839 case CEPH_IOC_GET_LAYOUT
: {
840 file_layout_t layout
;
841 struct ceph_ioctl_layout l
;
842 Fh
*fh
= (Fh
*)fi
->fh
;
843 cfuse
->client
->ll_file_layout(fh
, &layout
);
844 l
.stripe_unit
= layout
.stripe_unit
;
845 l
.stripe_count
= layout
.stripe_count
;
846 l
.object_size
= layout
.object_size
;
847 l
.data_pool
= layout
.pool_id
;
848 fuse_reply_ioctl(req
, 0, &l
, sizeof(struct ceph_ioctl_layout
));
852 fuse_reply_err(req
, EINVAL
);
857 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
859 static void fuse_ll_fallocate(fuse_req_t req
, fuse_ino_t ino
, int mode
,
860 off_t offset
, off_t length
,
861 struct fuse_file_info
*fi
)
863 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
864 Fh
*fh
= (Fh
*)fi
->fh
;
865 int r
= cfuse
->client
->ll_fallocate(fh
, mode
, offset
, length
);
866 fuse_reply_err(req
, get_sys_errno(-r
));
871 static void fuse_ll_release(fuse_req_t req
, fuse_ino_t ino
,
872 struct fuse_file_info
*fi
)
874 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
875 Fh
*fh
= reinterpret_cast<Fh
*>(fi
->fh
);
876 int r
= cfuse
->client
->ll_release(fh
);
877 fuse_reply_err(req
, get_sys_errno(-r
));
880 static void fuse_ll_fsync(fuse_req_t req
, fuse_ino_t ino
, int datasync
,
881 struct fuse_file_info
*fi
)
883 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
884 Fh
*fh
= reinterpret_cast<Fh
*>(fi
->fh
);
885 int r
= cfuse
->client
->ll_fsync(fh
, datasync
);
886 fuse_reply_err(req
, get_sys_errno(-r
));
889 struct readdir_context
{
893 size_t pos
; /* in buf */
898 * return 0 on success, -1 if out of space
900 static int fuse_ll_add_dirent(void *p
, struct dirent
*de
,
901 struct ceph_statx
*stx
, off_t next_off
,
904 struct readdir_context
*c
= (struct readdir_context
*)p
;
905 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)fuse_req_userdata(c
->req
);
908 st
.st_ino
= cfuse
->make_fake_ino(stx
->stx_ino
, c
->snap
);
909 st
.st_mode
= stx
->stx_mode
;
910 st
.st_rdev
= new_encode_dev(stx
->stx_rdev
);
912 size_t room
= c
->size
- c
->pos
;
913 size_t entrysize
= fuse_add_direntry(c
->req
, c
->buf
+ c
->pos
, room
,
914 de
->d_name
, &st
, next_off
);
915 if (entrysize
> room
)
923 static void fuse_ll_readdir(fuse_req_t req
, fuse_ino_t ino
, size_t size
,
924 off_t off
, struct fuse_file_info
*fi
)
926 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
928 dir_result_t
*dirp
= reinterpret_cast<dir_result_t
*>(fi
->fh
);
929 cfuse
->client
->seekdir(dirp
, off
);
931 struct readdir_context rc
;
933 rc
.buf
= new char[size
];
936 rc
.snap
= cfuse
->fino_snap(ino
);
938 int r
= cfuse
->client
->readdir_r_cb(dirp
, fuse_ll_add_dirent
, &rc
);
939 if (r
== 0 || r
== -CEPHFS_ENOSPC
) /* ignore ENOSPC from our callback */
940 fuse_reply_buf(req
, rc
.buf
, rc
.pos
);
942 fuse_reply_err(req
, get_sys_errno(-r
));
946 static void fuse_ll_releasedir(fuse_req_t req
, fuse_ino_t ino
,
947 struct fuse_file_info
*fi
)
949 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
950 dir_result_t
*dirp
= reinterpret_cast<dir_result_t
*>(fi
->fh
);
951 cfuse
->client
->ll_releasedir(dirp
);
952 fuse_reply_err(req
, 0);
955 static void fuse_ll_fsyncdir(fuse_req_t req
, fuse_ino_t ino
, int datasync
,
956 struct fuse_file_info
*fi
)
958 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
959 dir_result_t
*dirp
= reinterpret_cast<dir_result_t
*>(fi
->fh
);
960 int r
= cfuse
->client
->ll_fsyncdir(dirp
);
961 fuse_reply_err(req
, get_sys_errno(-r
));
964 static void fuse_ll_access(fuse_req_t req
, fuse_ino_t ino
, int mask
)
966 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
967 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
968 Inode
*in
= cfuse
->iget(ino
);
969 UserPerm
perms(ctx
->uid
, ctx
->gid
);
970 get_fuse_groups(perms
, req
);
972 int r
= cfuse
->client
->inode_permission(in
, perms
, mask
);
973 fuse_reply_err(req
, get_sys_errno(-r
));
977 static void fuse_ll_create(fuse_req_t req
, fuse_ino_t parent
, const char *name
,
978 mode_t mode
, struct fuse_file_info
*fi
)
980 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
981 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
982 Inode
*i1
= cfuse
->iget(parent
), *i2
;
983 struct fuse_entry_param fe
;
985 UserPerm
perms(ctx
->uid
, ctx
->gid
);
986 get_fuse_groups(perms
, req
);
988 memset(&fe
, 0, sizeof(fe
));
990 // pass &i2 for the created inode so that ll_create takes an initial ll_ref
991 int r
= cfuse
->client
->ll_create(i1
, name
, mode
, fi
->flags
, &fe
.attr
, &i2
,
994 fi
->fh
= (uint64_t)fh
;
995 fe
.ino
= cfuse
->make_fake_ino(fe
.attr
.st_ino
, fe
.attr
.st_dev
);
996 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
997 auto fuse_disable_pagecache
= cfuse
->client
->cct
->_conf
.get_val
<bool>(
998 "fuse_disable_pagecache");
999 auto fuse_use_invalidate_cb
= cfuse
->client
->cct
->_conf
.get_val
<bool>(
1000 "fuse_use_invalidate_cb");
1001 if (fuse_disable_pagecache
)
1003 else if (fuse_use_invalidate_cb
)
1006 fuse_reply_create(req
, &fe
, fi
);
1008 fuse_reply_err(req
, get_sys_errno(-r
));
1009 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
1011 cfuse
->iput(i1
); // iput required
1014 static void fuse_ll_statfs(fuse_req_t req
, fuse_ino_t ino
)
1016 struct statvfs stbuf
;
1017 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
1018 Inode
*in
= cfuse
->iget(ino
);
1019 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
1020 UserPerm
perms(ctx
->uid
, ctx
->gid
);
1021 get_fuse_groups(perms
, req
);
1023 int r
= cfuse
->client
->ll_statfs(in
, &stbuf
, perms
);
1025 fuse_reply_statfs(req
, &stbuf
);
1027 fuse_reply_err(req
, get_sys_errno(-r
));
1029 cfuse
->iput(in
); // iput required
1032 static void fuse_ll_getlk(fuse_req_t req
, fuse_ino_t ino
,
1033 struct fuse_file_info
*fi
, struct flock
*lock
)
1035 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
1036 Fh
*fh
= reinterpret_cast<Fh
*>(fi
->fh
);
1038 int r
= cfuse
->client
->ll_getlk(fh
, lock
, fi
->lock_owner
);
1040 fuse_reply_lock(req
, lock
);
1042 fuse_reply_err(req
, get_sys_errno(-r
));
1045 static void fuse_ll_setlk(fuse_req_t req
, fuse_ino_t ino
,
1046 struct fuse_file_info
*fi
, struct flock
*lock
, int sleep
)
1048 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
1049 Fh
*fh
= reinterpret_cast<Fh
*>(fi
->fh
);
1051 // must use multithread if operation may block
1052 auto fuse_multithreaded
= cfuse
->client
->cct
->_conf
.get_val
<bool>(
1053 "fuse_multithreaded");
1054 if (!fuse_multithreaded
&& sleep
&& lock
->l_type
!= F_UNLCK
) {
1055 fuse_reply_err(req
, EDEADLK
);
1059 int r
= cfuse
->client
->ll_setlk(fh
, lock
, fi
->lock_owner
, sleep
);
1060 fuse_reply_err(req
, get_sys_errno(-r
));
1063 static void fuse_ll_interrupt(fuse_req_t req
, void* data
)
1065 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
1066 cfuse
->client
->ll_interrupt(data
);
1069 static void switch_interrupt_cb(void *handle
, void* data
)
1071 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)handle
;
1072 fuse_req_t req
= cfuse
->get_fuse_req();
1075 fuse_req_interrupt_func(req
, fuse_ll_interrupt
, data
);
1077 fuse_req_interrupt_func(req
, NULL
, NULL
);
1080 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
1081 static void fuse_ll_flock(fuse_req_t req
, fuse_ino_t ino
,
1082 struct fuse_file_info
*fi
, int cmd
)
1084 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
1085 Fh
*fh
= (Fh
*)fi
->fh
;
1087 // must use multithread if operation may block
1088 auto fuse_multithreaded
= cfuse
->client
->cct
->_conf
.get_val
<bool>(
1089 "fuse_multithreaded");
1090 if (!fuse_multithreaded
&& !(cmd
& (LOCK_NB
| LOCK_UN
))) {
1091 fuse_reply_err(req
, EDEADLK
);
1095 int r
= cfuse
->client
->ll_flock(fh
, cmd
, fi
->lock_owner
);
1096 fuse_reply_err(req
, get_sys_errno(-r
));
1100 #if !defined(__APPLE__)
1101 static mode_t
umask_cb(void *handle
)
1103 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)handle
;
1104 fuse_req_t req
= cfuse
->get_fuse_req();
1105 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
1110 static void ino_invalidate_cb(void *handle
, vinodeno_t vino
, int64_t off
,
1113 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
1114 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)handle
;
1115 fuse_ino_t fino
= cfuse
->make_fake_ino(vino
.ino
, vino
.snapid
);
1116 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1117 fuse_lowlevel_notify_inval_inode(cfuse
->se
, fino
, off
, len
);
1119 fuse_lowlevel_notify_inval_inode(cfuse
->ch
, fino
, off
, len
);
1124 static void dentry_invalidate_cb(void *handle
, vinodeno_t dirino
,
1125 vinodeno_t ino
, const char *name
, size_t len
)
1127 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)handle
;
1128 fuse_ino_t fdirino
= cfuse
->make_fake_ino(dirino
.ino
, dirino
.snapid
);
1129 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
1130 fuse_ino_t fino
= 0;
1131 if (ino
.ino
!= inodeno_t())
1132 fino
= cfuse
->make_fake_ino(ino
.ino
, ino
.snapid
);
1133 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1134 fuse_lowlevel_notify_delete(cfuse
->se
, fdirino
, fino
, name
, len
);
1136 fuse_lowlevel_notify_delete(cfuse
->ch
, fdirino
, fino
, name
, len
);
1138 #elif FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
1139 fuse_lowlevel_notify_inval_entry(cfuse
->ch
, fdirino
, name
, len
);
1143 static int remount_cb(void *handle
)
1145 // used for trimming kernel dcache. when remounting a file system, linux kernel
1146 // trims all unused dentries in the file system
1147 char cmd
[128+PATH_MAX
];
1148 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)handle
;
1149 snprintf(cmd
, sizeof(cmd
), "LIBMOUNT_FSTAB=/dev/null mount -i -o remount %s",
1150 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1151 cfuse
->opts
.mountpoint
);
1155 int r
= system(cmd
);
1156 if (r
!= 0 && r
!= -1) {
1163 static void do_init(void *data
, fuse_conn_info
*conn
)
1165 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)data
;
1166 Client
*client
= cfuse
->client
;
1168 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1169 fuse_apply_conn_info_opts(cfuse
->conn_opts
, conn
);
1172 if(conn
->capable
& FUSE_CAP_SPLICE_MOVE
)
1173 conn
->want
|= FUSE_CAP_SPLICE_MOVE
;
1175 #if !defined(__APPLE__)
1176 if (!client
->fuse_default_permissions
&& client
->ll_handle_umask()) {
1177 // apply umask in userspace if posix acl is enabled
1178 if(conn
->capable
& FUSE_CAP_DONT_MASK
)
1179 conn
->want
|= FUSE_CAP_DONT_MASK
;
1181 if(conn
->capable
& FUSE_CAP_EXPORT_SUPPORT
)
1182 conn
->want
|= FUSE_CAP_EXPORT_SUPPORT
;
1185 if (cfuse
->fd_on_success
) {
1186 //cout << "fuse init signaling on fd " << fd_on_success << std::endl;
1187 // see Preforker::daemonize(), ceph-fuse's parent process expects a `-1`
1188 // from a daemonized child process.
1190 int err
= safe_write(cfuse
->fd_on_success
, &r
, sizeof(r
));
1192 derr
<< "fuse_ll: do_init: safe_write failed with error "
1193 << cpp_strerror(err
) << dendl
;
1196 //cout << "fuse init done signaling on fd " << fd_on_success << std::endl;
1198 // close stdout, etc.
1205 const static struct fuse_lowlevel_ops fuse_ll_oper
= {
1208 lookup
: fuse_ll_lookup
,
1209 forget
: fuse_ll_forget
,
1210 getattr
: fuse_ll_getattr
,
1211 setattr
: fuse_ll_setattr
,
1212 readlink
: fuse_ll_readlink
,
1213 mknod
: fuse_ll_mknod
,
1214 mkdir
: fuse_ll_mkdir
,
1215 unlink
: fuse_ll_unlink
,
1216 rmdir
: fuse_ll_rmdir
,
1217 symlink
: fuse_ll_symlink
,
1218 rename
: fuse_ll_rename
,
1222 write
: fuse_ll_write
,
1223 flush
: fuse_ll_flush
,
1224 release
: fuse_ll_release
,
1225 fsync
: fuse_ll_fsync
,
1226 opendir
: fuse_ll_opendir
,
1227 readdir
: fuse_ll_readdir
,
1228 releasedir
: fuse_ll_releasedir
,
1229 fsyncdir
: fuse_ll_fsyncdir
,
1230 statfs
: fuse_ll_statfs
,
1231 setxattr
: fuse_ll_setxattr
,
1232 getxattr
: fuse_ll_getxattr
,
1233 listxattr
: fuse_ll_listxattr
,
1234 removexattr
: fuse_ll_removexattr
,
1235 access
: fuse_ll_access
,
1236 create
: fuse_ll_create
,
1237 getlk
: fuse_ll_getlk
,
1238 setlk
: fuse_ll_setlk
,
1240 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
1241 #ifdef FUSE_IOCTL_COMPAT
1242 ioctl
: fuse_ll_ioctl
,
1248 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
1252 flock
: fuse_ll_flock
,
1254 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
1255 fallocate
: fuse_ll_fallocate
1260 CephFuse::Handle::Handle(Client
*c
, int fd
) :
1264 #if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
1270 snap_stag_map
[CEPH_NOSNAP
] = 0;
1271 stag_snap_map
[0] = CEPH_NOSNAP
;
1272 memset(&args
, 0, sizeof(args
));
1273 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1274 memset(&opts
, 0, sizeof(opts
));
1278 CephFuse::Handle::~Handle()
1280 fuse_opt_free_args(&args
);
1283 void CephFuse::Handle::finalize()
1285 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1287 fuse_remove_signal_handlers(se
);
1288 fuse_session_unmount(se
);
1289 fuse_session_destroy(se
);
1293 if (opts
.mountpoint
)
1294 free(opts
.mountpoint
);
1297 fuse_remove_signal_handlers(se
);
1299 fuse_session_remove_chan(ch
);
1301 fuse_session_destroy(se
);
1303 fuse_unmount(mountpoint
, ch
);
1306 pthread_key_delete(fuse_req_key
);
1309 int CephFuse::Handle::init(int argc
, const char *argv
[])
1312 int r
= pthread_key_create(&fuse_req_key
, NULL
);
1314 derr
<< "pthread_key_create failed." << dendl
;
1318 // set up fuse argc/argv
1320 const char **newargv
= (const char **) malloc((argc
+ 17) * sizeof(char *));
1324 newargv
[newargc
++] = argv
[0];
1325 newargv
[newargc
++] = "-f"; // stay in foreground
1327 auto fuse_allow_other
= client
->cct
->_conf
.get_val
<bool>(
1328 "fuse_allow_other");
1329 auto fuse_default_permissions
= client
->cct
->_conf
.get_val
<bool>(
1330 "fuse_default_permissions");
1331 #if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
1332 auto fuse_big_writes
= client
->cct
->_conf
.get_val
<bool>(
1335 auto fuse_max_write
= client
->cct
->_conf
.get_val
<Option::size_t>(
1337 auto fuse_atomic_o_trunc
= client
->cct
->_conf
.get_val
<bool>(
1338 "fuse_atomic_o_trunc");
1339 auto fuse_splice_read
= client
->cct
->_conf
.get_val
<bool>(
1340 "fuse_splice_read");
1341 auto fuse_splice_write
= client
->cct
->_conf
.get_val
<bool>(
1342 "fuse_splice_write");
1343 auto fuse_splice_move
= client
->cct
->_conf
.get_val
<bool>(
1344 "fuse_splice_move");
1345 auto fuse_debug
= client
->cct
->_conf
.get_val
<bool>(
1348 if (fuse_allow_other
) {
1349 newargv
[newargc
++] = "-o";
1350 newargv
[newargc
++] = "allow_other";
1352 if (fuse_default_permissions
) {
1353 newargv
[newargc
++] = "-o";
1354 newargv
[newargc
++] = "default_permissions";
1356 #if defined(__linux__)
1357 #if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
1358 if (fuse_big_writes
) {
1359 newargv
[newargc
++] = "-o";
1360 newargv
[newargc
++] = "big_writes";
1363 if (fuse_max_write
> 0) {
1365 newargv
[newargc
++] = "-o";
1366 sprintf(strsplice
, "max_write=%zu", (size_t)fuse_max_write
);
1367 newargv
[newargc
++] = strsplice
;
1369 if (fuse_atomic_o_trunc
) {
1370 newargv
[newargc
++] = "-o";
1371 newargv
[newargc
++] = "atomic_o_trunc";
1373 if (fuse_splice_read
) {
1374 newargv
[newargc
++] = "-o";
1375 newargv
[newargc
++] = "splice_read";
1377 if (fuse_splice_write
) {
1378 newargv
[newargc
++] = "-o";
1379 newargv
[newargc
++] = "splice_write";
1381 if (fuse_splice_move
) {
1382 newargv
[newargc
++] = "-o";
1383 newargv
[newargc
++] = "splice_move";
1387 newargv
[newargc
++] = "-d";
1389 for (int argctr
= 1; argctr
< argc
; argctr
++)
1390 newargv
[newargc
++] = argv
[argctr
];
1392 derr
<< "init, newargv = " << newargv
<< " newargc=" << newargc
<< dendl
;
1393 struct fuse_args a
= FUSE_ARGS_INIT(newargc
, (char**)newargv
);
1394 args
= a
; // Roundabout construction b/c FUSE_ARGS_INIT is for initialization not assignment
1396 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1397 if (fuse_parse_cmdline(&args
, &opts
) == -1) {
1399 if (fuse_parse_cmdline(&args
, &mountpoint
, NULL
, NULL
) == -1) {
1401 derr
<< "fuse_parse_cmdline failed." << dendl
;
1402 fuse_opt_free_args(&args
);
1407 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1408 derr
<< "init, args.argv = " << args
.argv
<< " args.argc=" << args
.argc
<< dendl
;
1409 conn_opts
= fuse_parse_conn_info_opts(&args
);
1411 derr
<< "fuse_parse_conn_info_opts failed" << dendl
;
1412 fuse_opt_free_args(&args
);
1418 ceph_assert(args
.allocated
); // Checking fuse has realloc'd args so we can free newargv
1421 struct ceph_client_callback_args cb_args
= {
1423 ino_cb
: client
->cct
->_conf
.get_val
<bool>("fuse_use_invalidate_cb") ?
1424 ino_invalidate_cb
: NULL
,
1425 dentry_cb
: dentry_invalidate_cb
,
1426 switch_intr_cb
: switch_interrupt_cb
,
1427 #if defined(__linux__)
1428 remount_cb
: remount_cb
,
1430 #if !defined(__APPLE__)
1434 r
= client
->ll_register_callbacks2(&cb_args
);
1436 derr
<< "registering callbacks failed: " << r
<< dendl
;
1443 int CephFuse::Handle::start()
1445 bool is_mounted
= false;
1446 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1447 int err
= already_fuse_mounted(opts
.mountpoint
, is_mounted
);
1449 int err
= already_fuse_mounted(mountpoint
, is_mounted
);
1459 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1460 se
= fuse_session_new(&args
, &fuse_ll_oper
, sizeof(fuse_ll_oper
), this);
1462 derr
<< "fuse_session_new failed" << dendl
;
1466 ch
= fuse_mount(mountpoint
, &args
);
1468 derr
<< "fuse_mount(mountpoint=" << mountpoint
<< ") failed." << dendl
;
1472 se
= fuse_lowlevel_new(&args
, &fuse_ll_oper
, sizeof(fuse_ll_oper
), this);
1474 derr
<< "fuse_lowlevel_new failed" << dendl
;
1479 signal(SIGTERM
, SIG_DFL
);
1480 signal(SIGINT
, SIG_DFL
);
1481 if (fuse_set_signal_handlers(se
) == -1) {
1482 derr
<< "fuse_set_signal_handlers failed" << dendl
;
1486 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1487 if (fuse_session_mount(se
, opts
.mountpoint
) != 0) {
1488 derr
<< "fuse_session_mount failed" << dendl
;
1492 fuse_session_add_chan(se
, ch
);
1498 int CephFuse::Handle::loop()
1500 auto fuse_multithreaded
= client
->cct
->_conf
.get_val
<bool>(
1501 "fuse_multithreaded");
1502 if (fuse_multithreaded
) {
1503 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 1)
1505 struct fuse_loop_config conf
= { 0 };
1507 conf
.clone_fd
= opts
.clone_fd
;
1508 return fuse_session_loop_mt(se
, &conf
);
1510 #elif FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1511 return fuse_session_loop_mt(se
, opts
.clone_fd
);
1513 return fuse_session_loop_mt(se
);
1516 return fuse_session_loop(se
);
1520 uint64_t CephFuse::Handle::fino_snap(uint64_t fino
)
1522 if (fino
== FUSE_ROOT_ID
)
1525 if (client
->use_faked_inos()) {
1526 vinodeno_t vino
= client
->map_faked_ino(fino
);
1529 std::lock_guard
l(stag_lock
);
1530 uint64_t stag
= FINO_STAG(fino
);
1531 ceph_assert(stag_snap_map
.count(stag
));
1532 return stag_snap_map
[stag
];
1536 Inode
* CephFuse::Handle::iget(fuse_ino_t fino
)
1538 if (fino
== FUSE_ROOT_ID
)
1539 return client
->get_root();
1541 if (client
->use_faked_inos()) {
1542 return client
->ll_get_inode((ino_t
)fino
);
1544 vinodeno_t
vino(FINO_INO(fino
), fino_snap(fino
));
1545 return client
->ll_get_inode(vino
);
1549 void CephFuse::Handle::iput(Inode
*in
)
1554 uint64_t CephFuse::Handle::make_fake_ino(inodeno_t ino
, snapid_t snapid
)
1556 if (client
->use_faked_inos()) {
1557 // already faked by libcephfs
1558 if (ino
== client
->get_root_ino())
1559 return FUSE_ROOT_ID
;
1563 if (snapid
== CEPH_NOSNAP
&& ino
== client
->get_root_ino())
1564 return FUSE_ROOT_ID
;
1566 std::lock_guard
l(stag_lock
);
1567 auto p
= snap_stag_map
.find(snapid
);
1568 if (p
!= snap_stag_map
.end()) {
1569 inodeno_t fino
= MAKE_FINO(ino
, p
->second
);
1573 int first
= last_stag
& STAG_MASK
;
1574 int stag
= (++last_stag
) & STAG_MASK
;
1575 for (; stag
!= first
; stag
= (++last_stag
) & STAG_MASK
) {
1579 auto p
= stag_snap_map
.find(stag
);
1580 if (p
== stag_snap_map
.end()) {
1581 snap_stag_map
[snapid
] = stag
;
1582 stag_snap_map
[stag
] = snapid
;
1586 if (!client
->ll_get_snap_ref(p
->second
)) {
1587 snap_stag_map
.erase(p
->second
);
1588 snap_stag_map
[snapid
] = stag
;
1594 ceph_abort_msg("run out of stag");
1596 inodeno_t fino
= MAKE_FINO(ino
, stag
);
1597 //cout << "make_fake_ino " << ino << "." << snapid << " -> " << fino << std::endl;
1602 void CephFuse::Handle::set_fuse_req(fuse_req_t req
)
1604 pthread_setspecific(fuse_req_key
, (void*)req
);
1607 fuse_req_t
CephFuse::Handle::get_fuse_req()
1609 return (fuse_req_t
) pthread_getspecific(fuse_req_key
);
1613 CephFuse::CephFuse(Client
*c
, int fd
) : _handle(new CephFuse::Handle(c
, fd
))
1617 CephFuse::~CephFuse()
1622 int CephFuse::init(int argc
, const char *argv
[])
1624 return _handle
->init(argc
, argv
);
1627 int CephFuse::start()
1629 return _handle
->start();
1632 int CephFuse::loop()
1634 return _handle
->loop();
1637 void CephFuse::finalize()
1639 return _handle
->finalize();
1642 std::string
CephFuse::get_mount_point() const
1644 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
1645 if (_handle
->opts
.mountpoint
) {
1646 return _handle
->opts
.mountpoint
;
1648 if (_handle
->mountpoint
) {
1649 return _handle
->mountpoint
;