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>
28 #include "common/errno.h"
29 #include "common/safe_io.h"
30 #include "include/types.h"
34 #include "common/config.h"
35 #include "include/ceph_assert.h"
36 #include "include/cephfs/ceph_statx.h"
40 #include <fuse_lowlevel.h>
42 #define dout_context g_ceph_context
44 #define FINO_INO(x) ((x) & ((1ull<<48)-1ull))
45 #define FINO_STAG(x) ((x) >> 48)
46 #define MAKE_FINO(i,s) ((i) | ((int64_t)(s) << 48))
47 #define STAG_MASK 0xffff
50 #define MINORMASK ((1U << MINORBITS) - 1)
52 #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
53 #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
54 #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
56 static uint32_t new_encode_dev(dev_t dev
)
58 unsigned major
= MAJOR(dev
);
59 unsigned minor
= MINOR(dev
);
60 return (minor
& 0xff) | (major
<< 8) | ((minor
& ~0xff) << 12);
63 static dev_t
new_decode_dev(uint32_t dev
)
65 unsigned major
= (dev
& 0xfff00) >> 8;
66 unsigned minor
= (dev
& 0xff) | ((dev
>> 12) & 0xfff00);
67 return MKDEV(major
, minor
);
70 class CephFuse::Handle
{
72 Handle(Client
*c
, int fd
);
75 int init(int argc
, const char *argv
[]);
80 uint64_t fino_snap(uint64_t fino
);
81 uint64_t make_fake_ino(inodeno_t ino
, snapid_t snapid
);
82 Inode
* iget(fuse_ino_t fino
);
89 struct fuse_session
*se
;
95 ceph::unordered_map
<uint64_t,int> snap_stag_map
;
96 ceph::unordered_map
<int,uint64_t> stag_snap_map
;
98 pthread_key_t fuse_req_key
= 0;
99 void set_fuse_req(fuse_req_t
);
100 fuse_req_t
get_fuse_req();
102 struct fuse_args args
;
105 static int getgroups(fuse_req_t req
, gid_t
**sgids
)
107 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
109 int c
= fuse_req_getgroups(req
, 0, NULL
);
117 gid_t
*gids
= new (std::nothrow
) gid_t
[c
];
121 c
= fuse_req_getgroups(req
, c
, gids
);
132 static void get_fuse_groups(UserPerm
& perms
, fuse_req_t req
)
134 if (g_conf().get_val
<bool>("fuse_set_user_groups")) {
136 int count
= getgroups(req
, &gids
);
139 perms
.init_gids(gids
, count
);
140 } else if (count
< 0) {
141 derr
<< __func__
<< ": getgroups failed: " << cpp_strerror(-count
)
148 static CephFuse::Handle
*fuse_ll_req_prepare(fuse_req_t req
)
150 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)fuse_req_userdata(req
);
151 cfuse
->set_fuse_req(req
);
155 static void fuse_ll_lookup(fuse_req_t req
, fuse_ino_t parent
, const char *name
)
157 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
158 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
159 struct fuse_entry_param fe
;
160 Inode
*i2
, *i1
= cfuse
->iget(parent
); // see below
162 UserPerm
perms(ctx
->uid
, ctx
->gid
);
163 get_fuse_groups(perms
, req
);
167 r
= cfuse
->client
->lookup_ino(parent
, perms
, &i1
);
169 fuse_reply_err(req
, -r
);
174 memset(&fe
, 0, sizeof(fe
));
175 r
= cfuse
->client
->ll_lookup(i1
, name
, &fe
.attr
, &i2
, perms
);
177 fe
.ino
= cfuse
->make_fake_ino(fe
.attr
.st_ino
, fe
.attr
.st_dev
);
178 fe
.attr
.st_rdev
= new_encode_dev(fe
.attr
.st_rdev
);
179 fuse_reply_entry(req
, &fe
);
181 fuse_reply_err(req
, -r
);
184 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
189 static void fuse_ll_forget(fuse_req_t req
, fuse_ino_t ino
,
190 long unsigned nlookup
)
192 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
193 cfuse
->client
->ll_forget(cfuse
->iget(ino
), nlookup
+1);
194 fuse_reply_none(req
);
197 static void fuse_ll_getattr(fuse_req_t req
, fuse_ino_t ino
,
198 struct fuse_file_info
*fi
)
200 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
201 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
202 Inode
*in
= cfuse
->iget(ino
);
204 UserPerm
perms(ctx
->uid
, ctx
->gid
);
205 get_fuse_groups(perms
, req
);
209 if (cfuse
->client
->ll_getattr(in
, &stbuf
, perms
)
211 stbuf
.st_ino
= cfuse
->make_fake_ino(stbuf
.st_ino
, stbuf
.st_dev
);
212 stbuf
.st_rdev
= new_encode_dev(stbuf
.st_rdev
);
213 fuse_reply_attr(req
, &stbuf
, 0);
215 fuse_reply_err(req
, ENOENT
);
217 cfuse
->iput(in
); // iput required
220 static void fuse_ll_setattr(fuse_req_t req
, fuse_ino_t ino
, struct stat
*attr
,
221 int to_set
, struct fuse_file_info
*fi
)
223 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
224 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
225 Inode
*in
= cfuse
->iget(ino
);
226 UserPerm
perms(ctx
->uid
, ctx
->gid
);
227 get_fuse_groups(perms
, req
);
230 if (to_set
& FUSE_SET_ATTR_MODE
) mask
|= CEPH_SETATTR_MODE
;
231 if (to_set
& FUSE_SET_ATTR_UID
) mask
|= CEPH_SETATTR_UID
;
232 if (to_set
& FUSE_SET_ATTR_GID
) mask
|= CEPH_SETATTR_GID
;
233 if (to_set
& FUSE_SET_ATTR_MTIME
) mask
|= CEPH_SETATTR_MTIME
;
234 if (to_set
& FUSE_SET_ATTR_ATIME
) mask
|= CEPH_SETATTR_ATIME
;
235 if (to_set
& FUSE_SET_ATTR_SIZE
) mask
|= CEPH_SETATTR_SIZE
;
236 #if !defined(__APPLE__)
237 if (to_set
& FUSE_SET_ATTR_MTIME_NOW
) mask
|= CEPH_SETATTR_MTIME_NOW
;
238 if (to_set
& FUSE_SET_ATTR_ATIME_NOW
) mask
|= CEPH_SETATTR_ATIME_NOW
;
241 int r
= cfuse
->client
->ll_setattr(in
, attr
, mask
, perms
);
243 fuse_reply_attr(req
, attr
, 0);
245 fuse_reply_err(req
, -r
);
247 cfuse
->iput(in
); // iput required
252 static void fuse_ll_setxattr(fuse_req_t req
, fuse_ino_t ino
, const char *name
,
253 const char *value
, size_t size
,
255 #if defined(__APPLE__)
260 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
261 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
262 Inode
*in
= cfuse
->iget(ino
);
263 UserPerm
perms(ctx
->uid
, ctx
->gid
);
264 get_fuse_groups(perms
, req
);
266 int r
= cfuse
->client
->ll_setxattr(in
, name
, value
, size
, flags
, perms
);
267 fuse_reply_err(req
, -r
);
269 cfuse
->iput(in
); // iput required
272 static void fuse_ll_listxattr(fuse_req_t req
, fuse_ino_t ino
, size_t size
)
274 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
275 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
276 Inode
*in
= cfuse
->iget(ino
);
278 UserPerm
perms(ctx
->uid
, ctx
->gid
);
279 get_fuse_groups(perms
, req
);
281 int r
= cfuse
->client
->ll_listxattr(in
, buf
, size
, perms
);
282 if (size
== 0 && r
>= 0)
283 fuse_reply_xattr(req
, r
);
285 fuse_reply_buf(req
, buf
, r
);
287 fuse_reply_err(req
, -r
);
289 cfuse
->iput(in
); // iput required
292 static void fuse_ll_getxattr(fuse_req_t req
, fuse_ino_t ino
, const char *name
,
294 #if defined(__APPLE__)
299 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
300 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
301 Inode
*in
= cfuse
->iget(ino
);
303 UserPerm
perms(ctx
->uid
, ctx
->gid
);
304 get_fuse_groups(perms
, req
);
306 int r
= cfuse
->client
->ll_getxattr(in
, name
, buf
, size
, perms
);
307 if (size
== 0 && r
>= 0)
308 fuse_reply_xattr(req
, r
);
310 fuse_reply_buf(req
, buf
, r
);
312 fuse_reply_err(req
, -r
);
314 cfuse
->iput(in
); // iput required
317 static void fuse_ll_removexattr(fuse_req_t req
, fuse_ino_t ino
,
320 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
321 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
322 Inode
*in
= cfuse
->iget(ino
);
323 UserPerm
perms(ctx
->uid
, ctx
->gid
);
324 get_fuse_groups(perms
, req
);
326 int r
= cfuse
->client
->ll_removexattr(in
, name
, perms
);
327 fuse_reply_err(req
, -r
);
329 cfuse
->iput(in
); // iput required
332 static void fuse_ll_opendir(fuse_req_t req
, fuse_ino_t ino
,
333 struct fuse_file_info
*fi
)
335 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
336 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
337 Inode
*in
= cfuse
->iget(ino
);
340 UserPerm
perms(ctx
->uid
, ctx
->gid
);
341 get_fuse_groups(perms
, req
);
343 int r
= cfuse
->client
->ll_opendir(in
, fi
->flags
, (dir_result_t
**)&dirp
,
346 fi
->fh
= (uint64_t)dirp
;
347 fuse_reply_open(req
, fi
);
349 fuse_reply_err(req
, -r
);
352 cfuse
->iput(in
); // iput required
355 static void fuse_ll_readlink(fuse_req_t req
, fuse_ino_t ino
)
357 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
358 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
359 Inode
*in
= cfuse
->iget(ino
);
360 char buf
[PATH_MAX
+ 1]; // leave room for a null terminator
361 UserPerm
perms(ctx
->uid
, ctx
->gid
);
362 get_fuse_groups(perms
, req
);
364 int r
= cfuse
->client
->ll_readlink(in
, buf
, sizeof(buf
) - 1, perms
);
367 fuse_reply_readlink(req
, buf
);
369 fuse_reply_err(req
, -r
);
372 cfuse
->iput(in
); // iput required
375 static void fuse_ll_mknod(fuse_req_t req
, fuse_ino_t parent
, const char *name
,
376 mode_t mode
, dev_t rdev
)
378 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
379 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
380 Inode
*i2
, *i1
= cfuse
->iget(parent
);
381 struct fuse_entry_param fe
;
382 UserPerm
perms(ctx
->uid
, ctx
->gid
);
383 get_fuse_groups(perms
, req
);
385 memset(&fe
, 0, sizeof(fe
));
387 int r
= cfuse
->client
->ll_mknod(i1
, name
, mode
, new_decode_dev(rdev
),
388 &fe
.attr
, &i2
, perms
);
390 fe
.ino
= cfuse
->make_fake_ino(fe
.attr
.st_ino
, fe
.attr
.st_dev
);
391 fe
.attr
.st_rdev
= new_encode_dev(fe
.attr
.st_rdev
);
392 fuse_reply_entry(req
, &fe
);
394 fuse_reply_err(req
, -r
);
397 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
399 cfuse
->iput(i1
); // iput required
402 static void fuse_ll_mkdir(fuse_req_t req
, fuse_ino_t parent
, const char *name
,
405 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
406 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
408 struct fuse_entry_param fe
;
410 memset(&fe
, 0, sizeof(fe
));
411 UserPerm
perm(ctx
->uid
, ctx
->gid
);
412 get_fuse_groups(perm
, req
);
413 #ifdef HAVE_SYS_SYNCFS
414 auto fuse_multithreaded
= cfuse
->client
->cct
->_conf
.get_val
<bool>(
415 "fuse_multithreaded");
416 auto fuse_syncfs_on_mksnap
= cfuse
->client
->cct
->_conf
.get_val
<bool>(
417 "fuse_syncfs_on_mksnap");
418 if (cfuse
->fino_snap(parent
) == CEPH_SNAPDIR
&&
419 fuse_multithreaded
&& fuse_syncfs_on_mksnap
) {
421 int fd
= ::open(cfuse
->mountpoint
, O_RDONLY
| O_DIRECTORY
| O_CLOEXEC
);
425 int r
= ::syncfs(fd
);
431 fuse_reply_err(req
, err
);
437 i1
= cfuse
->iget(parent
);
438 int r
= cfuse
->client
->ll_mkdir(i1
, name
, mode
, &fe
.attr
, &i2
, perm
);
440 fe
.ino
= cfuse
->make_fake_ino(fe
.attr
.st_ino
, fe
.attr
.st_dev
);
441 fe
.attr
.st_rdev
= new_encode_dev(fe
.attr
.st_rdev
);
442 fuse_reply_entry(req
, &fe
);
444 fuse_reply_err(req
, -r
);
447 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
449 cfuse
->iput(i1
); // iput required
452 static void fuse_ll_unlink(fuse_req_t req
, fuse_ino_t parent
, const char *name
)
454 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
455 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
456 Inode
*in
= cfuse
->iget(parent
);
457 UserPerm
perm(ctx
->uid
, ctx
->gid
);
458 get_fuse_groups(perm
, req
);
460 int r
= cfuse
->client
->ll_unlink(in
, name
, perm
);
461 fuse_reply_err(req
, -r
);
463 cfuse
->iput(in
); // iput required
466 static void fuse_ll_rmdir(fuse_req_t req
, fuse_ino_t parent
, const char *name
)
468 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
469 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
470 Inode
*in
= cfuse
->iget(parent
);
471 UserPerm
perms(ctx
->uid
, ctx
->gid
);
472 get_fuse_groups(perms
, req
);
474 int r
= cfuse
->client
->ll_rmdir(in
, name
, perms
);
475 fuse_reply_err(req
, -r
);
477 cfuse
->iput(in
); // iput required
480 static void fuse_ll_symlink(fuse_req_t req
, const char *existing
,
481 fuse_ino_t parent
, const char *name
)
483 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
484 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
485 Inode
*i2
, *i1
= cfuse
->iget(parent
);
486 struct fuse_entry_param fe
;
487 UserPerm
perms(ctx
->uid
, ctx
->gid
);
488 get_fuse_groups(perms
, req
);
490 memset(&fe
, 0, sizeof(fe
));
492 int r
= cfuse
->client
->ll_symlink(i1
, name
, existing
, &fe
.attr
, &i2
, perms
);
494 fe
.ino
= cfuse
->make_fake_ino(fe
.attr
.st_ino
, fe
.attr
.st_dev
);
495 fe
.attr
.st_rdev
= new_encode_dev(fe
.attr
.st_rdev
);
496 fuse_reply_entry(req
, &fe
);
498 fuse_reply_err(req
, -r
);
501 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
503 cfuse
->iput(i1
); // iput required
506 static void fuse_ll_rename(fuse_req_t req
, fuse_ino_t parent
, const char *name
,
507 fuse_ino_t newparent
, const char *newname
)
509 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
510 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
511 Inode
*in
= cfuse
->iget(parent
);
512 Inode
*nin
= cfuse
->iget(newparent
);
513 UserPerm
perm(ctx
->uid
, ctx
->gid
);
514 get_fuse_groups(perm
, req
);
516 int r
= cfuse
->client
->ll_rename(in
, name
, nin
, newname
, perm
);
517 fuse_reply_err(req
, -r
);
519 cfuse
->iput(in
); // iputs required
523 static void fuse_ll_link(fuse_req_t req
, fuse_ino_t ino
, fuse_ino_t newparent
,
526 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
527 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
528 Inode
*in
= cfuse
->iget(ino
);
529 Inode
*nin
= cfuse
->iget(newparent
);
530 struct fuse_entry_param fe
;
532 memset(&fe
, 0, sizeof(fe
));
533 UserPerm
perm(ctx
->uid
, ctx
->gid
);
534 get_fuse_groups(perm
, req
);
537 * Note that we could successfully link, but then fail the subsequent
538 * getattr and return an error. Perhaps we should ignore getattr errors,
539 * but then how do we tell FUSE that the attrs are bogus?
541 int r
= cfuse
->client
->ll_link(in
, nin
, newname
, perm
);
543 r
= cfuse
->client
->ll_getattr(in
, &fe
.attr
, perm
);
545 fe
.ino
= cfuse
->make_fake_ino(fe
.attr
.st_ino
, fe
.attr
.st_dev
);
546 fe
.attr
.st_rdev
= new_encode_dev(fe
.attr
.st_rdev
);
547 fuse_reply_entry(req
, &fe
);
553 * Many ll operations in libcephfs return an extra inode reference, but
554 * ll_link currently does not. Still, FUSE needs one for the new dentry,
555 * so we commandeer the reference taken earlier when ll_link is successful.
556 * On error however, we must put that reference.
559 fuse_reply_err(req
, -r
);
565 static void fuse_ll_open(fuse_req_t req
, fuse_ino_t ino
,
566 struct fuse_file_info
*fi
)
568 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
569 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
570 Inode
*in
= cfuse
->iget(ino
);
572 UserPerm
perms(ctx
->uid
, ctx
->gid
);
573 get_fuse_groups(perms
, req
);
575 int r
= cfuse
->client
->ll_open(in
, fi
->flags
, &fh
, perms
);
577 fi
->fh
= (uint64_t)fh
;
578 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
579 auto fuse_disable_pagecache
= cfuse
->client
->cct
->_conf
.get_val
<bool>(
580 "fuse_disable_pagecache");
581 auto fuse_use_invalidate_cb
= cfuse
->client
->cct
->_conf
.get_val
<bool>(
582 "fuse_use_invalidate_cb");
583 if (fuse_disable_pagecache
)
585 else if (fuse_use_invalidate_cb
)
588 fuse_reply_open(req
, fi
);
590 fuse_reply_err(req
, -r
);
593 cfuse
->iput(in
); // iput required
596 static void fuse_ll_read(fuse_req_t req
, fuse_ino_t ino
, size_t size
, off_t off
,
597 struct fuse_file_info
*fi
)
599 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
600 Fh
*fh
= reinterpret_cast<Fh
*>(fi
->fh
);
602 int r
= cfuse
->client
->ll_read(fh
, off
, size
, &bl
);
604 fuse_reply_buf(req
, bl
.c_str(), bl
.length());
606 fuse_reply_err(req
, -r
);
609 static void fuse_ll_write(fuse_req_t req
, fuse_ino_t ino
, const char *buf
,
610 size_t size
, off_t off
, struct fuse_file_info
*fi
)
612 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
613 Fh
*fh
= reinterpret_cast<Fh
*>(fi
->fh
);
614 int r
= cfuse
->client
->ll_write(fh
, off
, size
, buf
);
616 fuse_reply_write(req
, r
);
618 fuse_reply_err(req
, -r
);
621 static void fuse_ll_flush(fuse_req_t req
, fuse_ino_t ino
,
622 struct fuse_file_info
*fi
)
624 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
625 Fh
*fh
= reinterpret_cast<Fh
*>(fi
->fh
);
626 int r
= cfuse
->client
->ll_flush(fh
);
627 fuse_reply_err(req
, -r
);
630 #ifdef FUSE_IOCTL_COMPAT
631 static void fuse_ll_ioctl(fuse_req_t req
, fuse_ino_t ino
, int cmd
, void *arg
, struct fuse_file_info
*fi
,
632 unsigned flags
, const void *in_buf
, size_t in_bufsz
, size_t out_bufsz
)
634 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
636 if (flags
& FUSE_IOCTL_COMPAT
) {
637 fuse_reply_err(req
, ENOSYS
);
641 switch (static_cast<unsigned>(cmd
)) {
642 case CEPH_IOC_GET_LAYOUT
: {
643 file_layout_t layout
;
644 struct ceph_ioctl_layout l
;
645 Fh
*fh
= (Fh
*)fi
->fh
;
646 cfuse
->client
->ll_file_layout(fh
, &layout
);
647 l
.stripe_unit
= layout
.stripe_unit
;
648 l
.stripe_count
= layout
.stripe_count
;
649 l
.object_size
= layout
.object_size
;
650 l
.data_pool
= layout
.pool_id
;
651 fuse_reply_ioctl(req
, 0, &l
, sizeof(struct ceph_ioctl_layout
));
655 fuse_reply_err(req
, EINVAL
);
660 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
662 static void fuse_ll_fallocate(fuse_req_t req
, fuse_ino_t ino
, int mode
,
663 off_t offset
, off_t length
,
664 struct fuse_file_info
*fi
)
666 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
667 Fh
*fh
= (Fh
*)fi
->fh
;
668 int r
= cfuse
->client
->ll_fallocate(fh
, mode
, offset
, length
);
669 fuse_reply_err(req
, -r
);
674 static void fuse_ll_release(fuse_req_t req
, fuse_ino_t ino
,
675 struct fuse_file_info
*fi
)
677 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
678 Fh
*fh
= reinterpret_cast<Fh
*>(fi
->fh
);
679 int r
= cfuse
->client
->ll_release(fh
);
680 fuse_reply_err(req
, -r
);
683 static void fuse_ll_fsync(fuse_req_t req
, fuse_ino_t ino
, int datasync
,
684 struct fuse_file_info
*fi
)
686 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
687 Fh
*fh
= reinterpret_cast<Fh
*>(fi
->fh
);
688 int r
= cfuse
->client
->ll_fsync(fh
, datasync
);
689 fuse_reply_err(req
, -r
);
692 struct readdir_context
{
696 size_t pos
; /* in buf */
701 * return 0 on success, -1 if out of space
703 static int fuse_ll_add_dirent(void *p
, struct dirent
*de
,
704 struct ceph_statx
*stx
, off_t next_off
,
707 struct readdir_context
*c
= (struct readdir_context
*)p
;
708 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)fuse_req_userdata(c
->req
);
711 st
.st_ino
= cfuse
->make_fake_ino(stx
->stx_ino
, c
->snap
);
712 st
.st_mode
= stx
->stx_mode
;
713 st
.st_rdev
= new_encode_dev(stx
->stx_rdev
);
715 size_t room
= c
->size
- c
->pos
;
716 size_t entrysize
= fuse_add_direntry(c
->req
, c
->buf
+ c
->pos
, room
,
717 de
->d_name
, &st
, next_off
);
718 if (entrysize
> room
)
726 static void fuse_ll_readdir(fuse_req_t req
, fuse_ino_t ino
, size_t size
,
727 off_t off
, struct fuse_file_info
*fi
)
729 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
731 dir_result_t
*dirp
= reinterpret_cast<dir_result_t
*>(fi
->fh
);
732 cfuse
->client
->seekdir(dirp
, off
);
734 struct readdir_context rc
;
736 rc
.buf
= new char[size
];
739 rc
.snap
= cfuse
->fino_snap(ino
);
741 int r
= cfuse
->client
->readdir_r_cb(dirp
, fuse_ll_add_dirent
, &rc
);
742 if (r
== 0 || r
== -ENOSPC
) /* ignore ENOSPC from our callback */
743 fuse_reply_buf(req
, rc
.buf
, rc
.pos
);
745 fuse_reply_err(req
, -r
);
749 static void fuse_ll_releasedir(fuse_req_t req
, fuse_ino_t ino
,
750 struct fuse_file_info
*fi
)
752 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
753 dir_result_t
*dirp
= reinterpret_cast<dir_result_t
*>(fi
->fh
);
754 cfuse
->client
->ll_releasedir(dirp
);
755 fuse_reply_err(req
, 0);
758 static void fuse_ll_fsyncdir(fuse_req_t req
, fuse_ino_t ino
, int datasync
,
759 struct fuse_file_info
*fi
)
761 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
762 dir_result_t
*dirp
= reinterpret_cast<dir_result_t
*>(fi
->fh
);
763 int r
= cfuse
->client
->ll_fsyncdir(dirp
);
764 fuse_reply_err(req
, -r
);
767 static void fuse_ll_access(fuse_req_t req
, fuse_ino_t ino
, int mask
)
769 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
770 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
771 Inode
*in
= cfuse
->iget(ino
);
772 UserPerm
perms(ctx
->uid
, ctx
->gid
);
773 get_fuse_groups(perms
, req
);
775 int r
= cfuse
->client
->inode_permission(in
, perms
, mask
);
776 fuse_reply_err(req
, -r
);
780 static void fuse_ll_create(fuse_req_t req
, fuse_ino_t parent
, const char *name
,
781 mode_t mode
, struct fuse_file_info
*fi
)
783 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
784 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
785 Inode
*i1
= cfuse
->iget(parent
), *i2
;
786 struct fuse_entry_param fe
;
788 UserPerm
perms(ctx
->uid
, ctx
->gid
);
789 get_fuse_groups(perms
, req
);
791 memset(&fe
, 0, sizeof(fe
));
793 // pass &i2 for the created inode so that ll_create takes an initial ll_ref
794 int r
= cfuse
->client
->ll_create(i1
, name
, mode
, fi
->flags
, &fe
.attr
, &i2
,
797 fi
->fh
= (uint64_t)fh
;
798 fe
.ino
= cfuse
->make_fake_ino(fe
.attr
.st_ino
, fe
.attr
.st_dev
);
799 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
800 auto fuse_disable_pagecache
= cfuse
->client
->cct
->_conf
.get_val
<bool>(
801 "fuse_disable_pagecache");
802 auto fuse_use_invalidate_cb
= cfuse
->client
->cct
->_conf
.get_val
<bool>(
803 "fuse_use_invalidate_cb");
804 if (fuse_disable_pagecache
)
806 else if (fuse_use_invalidate_cb
)
809 fuse_reply_create(req
, &fe
, fi
);
811 fuse_reply_err(req
, -r
);
812 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
814 cfuse
->iput(i1
); // iput required
817 static void fuse_ll_statfs(fuse_req_t req
, fuse_ino_t ino
)
819 struct statvfs stbuf
;
820 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
821 Inode
*in
= cfuse
->iget(ino
);
822 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
823 UserPerm
perms(ctx
->uid
, ctx
->gid
);
824 get_fuse_groups(perms
, req
);
826 int r
= cfuse
->client
->ll_statfs(in
, &stbuf
, perms
);
828 fuse_reply_statfs(req
, &stbuf
);
830 fuse_reply_err(req
, -r
);
832 cfuse
->iput(in
); // iput required
835 static void fuse_ll_getlk(fuse_req_t req
, fuse_ino_t ino
,
836 struct fuse_file_info
*fi
, struct flock
*lock
)
838 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
839 Fh
*fh
= reinterpret_cast<Fh
*>(fi
->fh
);
841 int r
= cfuse
->client
->ll_getlk(fh
, lock
, fi
->lock_owner
);
843 fuse_reply_lock(req
, lock
);
845 fuse_reply_err(req
, -r
);
848 static void fuse_ll_setlk(fuse_req_t req
, fuse_ino_t ino
,
849 struct fuse_file_info
*fi
, struct flock
*lock
, int sleep
)
851 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
852 Fh
*fh
= reinterpret_cast<Fh
*>(fi
->fh
);
854 // must use multithread if operation may block
855 auto fuse_multithreaded
= cfuse
->client
->cct
->_conf
.get_val
<bool>(
856 "fuse_multithreaded");
857 if (!fuse_multithreaded
&& sleep
&& lock
->l_type
!= F_UNLCK
) {
858 fuse_reply_err(req
, EDEADLK
);
862 int r
= cfuse
->client
->ll_setlk(fh
, lock
, fi
->lock_owner
, sleep
);
863 fuse_reply_err(req
, -r
);
866 static void fuse_ll_interrupt(fuse_req_t req
, void* data
)
868 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
869 cfuse
->client
->ll_interrupt(data
);
872 static void switch_interrupt_cb(void *handle
, void* data
)
874 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)handle
;
875 fuse_req_t req
= cfuse
->get_fuse_req();
878 fuse_req_interrupt_func(req
, fuse_ll_interrupt
, data
);
880 fuse_req_interrupt_func(req
, NULL
, NULL
);
883 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
884 static void fuse_ll_flock(fuse_req_t req
, fuse_ino_t ino
,
885 struct fuse_file_info
*fi
, int cmd
)
887 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
888 Fh
*fh
= (Fh
*)fi
->fh
;
890 // must use multithread if operation may block
891 auto fuse_multithreaded
= cfuse
->client
->cct
->_conf
.get_val
<bool>(
892 "fuse_multithreaded");
893 if (!fuse_multithreaded
&& !(cmd
& (LOCK_NB
| LOCK_UN
))) {
894 fuse_reply_err(req
, EDEADLK
);
898 int r
= cfuse
->client
->ll_flock(fh
, cmd
, fi
->lock_owner
);
899 fuse_reply_err(req
, -r
);
903 #if !defined(__APPLE__)
904 static mode_t
umask_cb(void *handle
)
906 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)handle
;
907 fuse_req_t req
= cfuse
->get_fuse_req();
908 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
913 static void ino_invalidate_cb(void *handle
, vinodeno_t vino
, int64_t off
,
916 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
917 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)handle
;
918 fuse_ino_t fino
= cfuse
->make_fake_ino(vino
.ino
, vino
.snapid
);
919 fuse_lowlevel_notify_inval_inode(cfuse
->ch
, fino
, off
, len
);
923 static void dentry_invalidate_cb(void *handle
, vinodeno_t dirino
,
924 vinodeno_t ino
, string
& name
)
926 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)handle
;
927 fuse_ino_t fdirino
= cfuse
->make_fake_ino(dirino
.ino
, dirino
.snapid
);
928 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
930 if (ino
.ino
!= inodeno_t())
931 fino
= cfuse
->make_fake_ino(ino
.ino
, ino
.snapid
);
932 fuse_lowlevel_notify_delete(cfuse
->ch
, fdirino
, fino
, name
.c_str(), name
.length());
933 #elif FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
934 fuse_lowlevel_notify_inval_entry(cfuse
->ch
, fdirino
, name
.c_str(), name
.length());
938 static int remount_cb(void *handle
)
940 // used for trimming kernel dcache. when remounting a file system, linux kernel
941 // trims all unused dentries in the file system
942 char cmd
[128+PATH_MAX
];
943 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)handle
;
944 snprintf(cmd
, sizeof(cmd
), "mount -i -o remount %s", cfuse
->mountpoint
);
946 if (r
!= 0 && r
!= -1) {
953 static void do_init(void *data
, fuse_conn_info
*conn
)
955 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)data
;
956 Client
*client
= cfuse
->client
;
958 #if !defined(__APPLE__)
959 auto fuse_default_permissions
= client
->cct
->_conf
.get_val
<bool>(
960 "fuse_default_permissions");
961 if (!fuse_default_permissions
&& client
->ll_handle_umask()) {
962 // apply umask in userspace if posix acl is enabled
963 if(conn
->capable
& FUSE_CAP_DONT_MASK
)
964 conn
->want
|= FUSE_CAP_DONT_MASK
;
966 if(conn
->capable
& FUSE_CAP_EXPORT_SUPPORT
)
967 conn
->want
|= FUSE_CAP_EXPORT_SUPPORT
;
970 if (cfuse
->fd_on_success
) {
971 //cout << "fuse init signaling on fd " << fd_on_success << std::endl;
972 // see Preforker::daemonize(), ceph-fuse's parent process expects a `-1`
973 // from a daemonized child process.
975 int err
= safe_write(cfuse
->fd_on_success
, &r
, sizeof(r
));
977 derr
<< "fuse_ll: do_init: safe_write failed with error "
978 << cpp_strerror(err
) << dendl
;
981 //cout << "fuse init done signaling on fd " << fd_on_success << std::endl;
983 // close stdout, etc.
990 const static struct fuse_lowlevel_ops fuse_ll_oper
= {
993 lookup
: fuse_ll_lookup
,
994 forget
: fuse_ll_forget
,
995 getattr
: fuse_ll_getattr
,
996 setattr
: fuse_ll_setattr
,
997 readlink
: fuse_ll_readlink
,
998 mknod
: fuse_ll_mknod
,
999 mkdir
: fuse_ll_mkdir
,
1000 unlink
: fuse_ll_unlink
,
1001 rmdir
: fuse_ll_rmdir
,
1002 symlink
: fuse_ll_symlink
,
1003 rename
: fuse_ll_rename
,
1007 write
: fuse_ll_write
,
1008 flush
: fuse_ll_flush
,
1009 release
: fuse_ll_release
,
1010 fsync
: fuse_ll_fsync
,
1011 opendir
: fuse_ll_opendir
,
1012 readdir
: fuse_ll_readdir
,
1013 releasedir
: fuse_ll_releasedir
,
1014 fsyncdir
: fuse_ll_fsyncdir
,
1015 statfs
: fuse_ll_statfs
,
1016 setxattr
: fuse_ll_setxattr
,
1017 getxattr
: fuse_ll_getxattr
,
1018 listxattr
: fuse_ll_listxattr
,
1019 removexattr
: fuse_ll_removexattr
,
1020 access
: fuse_ll_access
,
1021 create
: fuse_ll_create
,
1022 getlk
: fuse_ll_getlk
,
1023 setlk
: fuse_ll_setlk
,
1025 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
1026 #ifdef FUSE_IOCTL_COMPAT
1027 ioctl
: fuse_ll_ioctl
,
1033 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
1037 flock
: fuse_ll_flock
,
1039 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
1040 fallocate
: fuse_ll_fallocate
1045 CephFuse::Handle::Handle(Client
*c
, int fd
) :
1051 stag_lock("fuse_ll.cc stag_lock"),
1054 snap_stag_map
[CEPH_NOSNAP
] = 0;
1055 stag_snap_map
[0] = CEPH_NOSNAP
;
1056 memset(&args
, 0, sizeof(args
));
1059 CephFuse::Handle::~Handle()
1061 fuse_opt_free_args(&args
);
1064 void CephFuse::Handle::finalize()
1067 fuse_remove_signal_handlers(se
);
1069 fuse_session_remove_chan(ch
);
1071 fuse_session_destroy(se
);
1073 fuse_unmount(mountpoint
, ch
);
1075 pthread_key_delete(fuse_req_key
);
1078 int CephFuse::Handle::init(int argc
, const char *argv
[])
1081 int r
= pthread_key_create(&fuse_req_key
, NULL
);
1083 derr
<< "pthread_key_create failed." << dendl
;
1087 // set up fuse argc/argv
1089 const char **newargv
= (const char **) malloc((argc
+ 10) * sizeof(char *));
1093 newargv
[newargc
++] = argv
[0];
1094 newargv
[newargc
++] = "-f"; // stay in foreground
1096 auto fuse_allow_other
= client
->cct
->_conf
.get_val
<bool>(
1097 "fuse_allow_other");
1098 auto fuse_default_permissions
= client
->cct
->_conf
.get_val
<bool>(
1099 "fuse_default_permissions");
1100 auto fuse_big_writes
= client
->cct
->_conf
.get_val
<bool>(
1102 auto fuse_atomic_o_trunc
= client
->cct
->_conf
.get_val
<bool>(
1103 "fuse_atomic_o_trunc");
1104 auto fuse_debug
= client
->cct
->_conf
.get_val
<bool>(
1106 auto fuse_max_write
= client
->cct
->_conf
.get_val
<Option::size_t>(
1109 if (fuse_allow_other
) {
1110 newargv
[newargc
++] = "-o";
1111 newargv
[newargc
++] = "allow_other";
1113 if (fuse_default_permissions
) {
1114 newargv
[newargc
++] = "-o";
1115 newargv
[newargc
++] = "default_permissions";
1117 #if defined(__linux__)
1118 if (fuse_big_writes
) {
1119 newargv
[newargc
++] = "-o";
1120 newargv
[newargc
++] = "big_writes";
1122 if (fuse_max_write
> 0) {
1124 newargv
[newargc
++] = "-o";
1125 newargv
[newargc
++] = strsplice
;
1126 sprintf(strsplice
, "max_write=%zu", (size_t)fuse_max_write
);
1127 newargv
[newargc
++] = strsplice
;
1129 if (fuse_atomic_o_trunc
) {
1130 newargv
[newargc
++] = "-o";
1131 newargv
[newargc
++] = "atomic_o_trunc";
1135 newargv
[newargc
++] = "-d";
1137 for (int argctr
= 1; argctr
< argc
; argctr
++)
1138 newargv
[newargc
++] = argv
[argctr
];
1140 derr
<< "init, newargv = " << newargv
<< " newargc=" << newargc
<< dendl
;
1141 struct fuse_args a
= FUSE_ARGS_INIT(newargc
, (char**)newargv
);
1142 args
= a
; // Roundabout construction b/c FUSE_ARGS_INIT is for initialization not assignment
1144 if (fuse_parse_cmdline(&args
, &mountpoint
, NULL
, NULL
) == -1) {
1145 derr
<< "fuse_parse_cmdline failed." << dendl
;
1146 fuse_opt_free_args(&args
);
1151 ceph_assert(args
.allocated
); // Checking fuse has realloc'd args so we can free newargv
1156 int CephFuse::Handle::start()
1158 ch
= fuse_mount(mountpoint
, &args
);
1160 derr
<< "fuse_mount(mountpoint=" << mountpoint
<< ") failed." << dendl
;
1164 se
= fuse_lowlevel_new(&args
, &fuse_ll_oper
, sizeof(fuse_ll_oper
), this);
1166 derr
<< "fuse_lowlevel_new failed" << dendl
;
1170 signal(SIGTERM
, SIG_DFL
);
1171 signal(SIGINT
, SIG_DFL
);
1172 if (fuse_set_signal_handlers(se
) == -1) {
1173 derr
<< "fuse_set_signal_handlers failed" << dendl
;
1177 fuse_session_add_chan(se
, ch
);
1180 struct client_callback_args args
= {
1182 ino_cb
: client
->cct
->_conf
.get_val
<bool>("fuse_use_invalidate_cb") ?
1183 ino_invalidate_cb
: NULL
,
1184 dentry_cb
: dentry_invalidate_cb
,
1185 switch_intr_cb
: switch_interrupt_cb
,
1186 #if defined(__linux__)
1187 remount_cb
: remount_cb
,
1189 #if !defined(__APPLE__)
1193 client
->ll_register_callbacks(&args
);
1198 int CephFuse::Handle::loop()
1200 auto fuse_multithreaded
= client
->cct
->_conf
.get_val
<bool>(
1201 "fuse_multithreaded");
1202 if (fuse_multithreaded
) {
1203 return fuse_session_loop_mt(se
);
1205 return fuse_session_loop(se
);
1209 uint64_t CephFuse::Handle::fino_snap(uint64_t fino
)
1211 if (fino
== FUSE_ROOT_ID
)
1214 if (client
->use_faked_inos()) {
1215 vinodeno_t vino
= client
->map_faked_ino(fino
);
1218 std::lock_guard
l(stag_lock
);
1219 uint64_t stag
= FINO_STAG(fino
);
1220 ceph_assert(stag_snap_map
.count(stag
));
1221 return stag_snap_map
[stag
];
1225 Inode
* CephFuse::Handle::iget(fuse_ino_t fino
)
1227 if (fino
== FUSE_ROOT_ID
)
1228 return client
->get_root();
1230 if (client
->use_faked_inos()) {
1231 return client
->ll_get_inode((ino_t
)fino
);
1233 vinodeno_t
vino(FINO_INO(fino
), fino_snap(fino
));
1234 return client
->ll_get_inode(vino
);
1238 void CephFuse::Handle::iput(Inode
*in
)
1243 uint64_t CephFuse::Handle::make_fake_ino(inodeno_t ino
, snapid_t snapid
)
1245 if (client
->use_faked_inos()) {
1246 // already faked by libcephfs
1247 if (ino
== client
->get_root_ino())
1248 return FUSE_ROOT_ID
;
1252 if (snapid
== CEPH_NOSNAP
&& ino
== client
->get_root_ino())
1253 return FUSE_ROOT_ID
;
1255 std::lock_guard
l(stag_lock
);
1256 auto p
= snap_stag_map
.find(snapid
);
1257 if (p
!= snap_stag_map
.end()) {
1258 inodeno_t fino
= MAKE_FINO(ino
, p
->second
);
1262 int first
= last_stag
& STAG_MASK
;
1263 int stag
= (++last_stag
) & STAG_MASK
;
1264 for (; stag
!= first
; stag
= (++last_stag
) & STAG_MASK
) {
1268 auto p
= stag_snap_map
.find(stag
);
1269 if (p
== stag_snap_map
.end()) {
1270 snap_stag_map
[snapid
] = stag
;
1271 stag_snap_map
[stag
] = snapid
;
1275 if (!client
->ll_get_snap_ref(p
->second
)) {
1276 snap_stag_map
.erase(p
->second
);
1277 snap_stag_map
[snapid
] = stag
;
1283 ceph_abort_msg("run out of stag");
1285 inodeno_t fino
= MAKE_FINO(ino
, stag
);
1286 //cout << "make_fake_ino " << ino << "." << snapid << " -> " << fino << std::endl;
1291 void CephFuse::Handle::set_fuse_req(fuse_req_t req
)
1293 pthread_setspecific(fuse_req_key
, (void*)req
);
1296 fuse_req_t
CephFuse::Handle::get_fuse_req()
1298 return (fuse_req_t
) pthread_getspecific(fuse_req_key
);
1302 CephFuse::CephFuse(Client
*c
, int fd
) : _handle(new CephFuse::Handle(c
, fd
))
1306 CephFuse::~CephFuse()
1311 int CephFuse::init(int argc
, const char *argv
[])
1313 return _handle
->init(argc
, argv
);
1316 int CephFuse::start()
1318 return _handle
->start();
1321 int CephFuse::loop()
1323 return _handle
->loop();
1326 void CephFuse::finalize()
1328 return _handle
->finalize();
1331 std::string
CephFuse::get_mount_point() const
1333 if (_handle
->mountpoint
) {
1334 return _handle
->mountpoint
;