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 #include "common/errno.h"
28 #include "common/safe_io.h"
29 #include "include/types.h"
33 #include "common/config.h"
34 #include "include/assert.h"
35 #include "include/cephfs/ceph_statx.h"
39 #include <fuse_lowlevel.h>
41 #define dout_context g_ceph_context
43 #define FINO_INO(x) ((x) & ((1ull<<48)-1ull))
44 #define FINO_STAG(x) ((x) >> 48)
45 #define MAKE_FINO(i,s) ((i) | ((s) << 48))
48 #define MINORMASK ((1U << MINORBITS) - 1)
50 #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
51 #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
52 #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
54 static uint32_t new_encode_dev(dev_t dev
)
56 unsigned major
= MAJOR(dev
);
57 unsigned minor
= MINOR(dev
);
58 return (minor
& 0xff) | (major
<< 8) | ((minor
& ~0xff) << 12);
61 static dev_t
new_decode_dev(uint32_t dev
)
63 unsigned major
= (dev
& 0xfff00) >> 8;
64 unsigned minor
= (dev
& 0xff) | ((dev
>> 12) & 0xfff00);
65 return MKDEV(major
, minor
);
68 class CephFuse::Handle
{
70 Handle(Client
*c
, int fd
);
73 int init(int argc
, const char *argv
[]);
78 uint64_t fino_snap(uint64_t fino
);
79 uint64_t make_fake_ino(inodeno_t ino
, snapid_t snapid
);
80 Inode
* iget(fuse_ino_t fino
);
87 struct fuse_session
*se
;
93 ceph::unordered_map
<uint64_t,int> snap_stag_map
;
94 ceph::unordered_map
<int,uint64_t> stag_snap_map
;
96 pthread_key_t fuse_req_key
;
97 void set_fuse_req(fuse_req_t
);
98 fuse_req_t
get_fuse_req();
100 struct fuse_args args
;
103 static int getgroups(fuse_req_t req
, gid_t
**sgids
)
105 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
107 int c
= fuse_req_getgroups(req
, 0, NULL
);
115 *sgids
= (gid_t
*)malloc(c
*sizeof(**sgids
));
119 c
= fuse_req_getgroups(req
, c
, *sgids
);
129 static int getgroups_cb(void *handle
, gid_t
**sgids
)
131 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*) handle
;
132 fuse_req_t req
= cfuse
->get_fuse_req();
133 return getgroups(req
, sgids
);
136 #define GET_GROUPS(perms, req) { \
137 if (cfuse->client->cct->_conf->fuse_set_user_groups) { \
138 gid_t *gids = NULL; \
139 int count = getgroups(req, &gids); \
140 perms.init_gids(gids, count); \
145 static CephFuse::Handle
*fuse_ll_req_prepare(fuse_req_t req
)
147 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)fuse_req_userdata(req
);
148 cfuse
->set_fuse_req(req
);
152 static void fuse_ll_lookup(fuse_req_t req
, fuse_ino_t parent
, const char *name
)
154 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
155 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
156 struct fuse_entry_param fe
;
157 Inode
*i2
, *i1
= cfuse
->iget(parent
); // see below
159 UserPerm
perms(ctx
->uid
, ctx
->gid
);
160 GET_GROUPS(perms
, req
);
162 memset(&fe
, 0, sizeof(fe
));
163 r
= cfuse
->client
->ll_lookup(i1
, name
, &fe
.attr
, &i2
, perms
);
165 fe
.ino
= cfuse
->make_fake_ino(fe
.attr
.st_ino
, fe
.attr
.st_dev
);
166 fe
.attr
.st_rdev
= new_encode_dev(fe
.attr
.st_rdev
);
167 fuse_reply_entry(req
, &fe
);
169 fuse_reply_err(req
, -r
);
172 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
177 static void fuse_ll_forget(fuse_req_t req
, fuse_ino_t ino
,
178 long unsigned nlookup
)
180 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
181 cfuse
->client
->ll_forget(cfuse
->iget(ino
), nlookup
+1);
182 fuse_reply_none(req
);
185 static void fuse_ll_getattr(fuse_req_t req
, fuse_ino_t ino
,
186 struct fuse_file_info
*fi
)
188 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
189 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
190 Inode
*in
= cfuse
->iget(ino
);
192 UserPerm
perms(ctx
->uid
, ctx
->gid
);
193 GET_GROUPS(perms
, req
);
197 if (cfuse
->client
->ll_getattr(in
, &stbuf
, perms
)
199 stbuf
.st_ino
= cfuse
->make_fake_ino(stbuf
.st_ino
, stbuf
.st_dev
);
200 stbuf
.st_rdev
= new_encode_dev(stbuf
.st_rdev
);
201 fuse_reply_attr(req
, &stbuf
, 0);
203 fuse_reply_err(req
, ENOENT
);
205 cfuse
->iput(in
); // iput required
208 static void fuse_ll_setattr(fuse_req_t req
, fuse_ino_t ino
, struct stat
*attr
,
209 int to_set
, struct fuse_file_info
*fi
)
211 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
212 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
213 Inode
*in
= cfuse
->iget(ino
);
214 UserPerm
perms(ctx
->uid
, ctx
->gid
);
215 GET_GROUPS(perms
, req
);
218 if (to_set
& FUSE_SET_ATTR_MODE
) mask
|= CEPH_SETATTR_MODE
;
219 if (to_set
& FUSE_SET_ATTR_UID
) mask
|= CEPH_SETATTR_UID
;
220 if (to_set
& FUSE_SET_ATTR_GID
) mask
|= CEPH_SETATTR_GID
;
221 if (to_set
& FUSE_SET_ATTR_MTIME
) mask
|= CEPH_SETATTR_MTIME
;
222 if (to_set
& FUSE_SET_ATTR_ATIME
) mask
|= CEPH_SETATTR_ATIME
;
223 if (to_set
& FUSE_SET_ATTR_SIZE
) mask
|= CEPH_SETATTR_SIZE
;
225 if (to_set
& FUSE_SET_ATTR_MTIME_NOW
) mask
|= CEPH_SETATTR_MTIME_NOW
;
226 if (to_set
& FUSE_SET_ATTR_ATIME_NOW
) mask
|= CEPH_SETATTR_ATIME_NOW
;
229 int r
= cfuse
->client
->ll_setattr(in
, attr
, mask
, perms
);
231 fuse_reply_attr(req
, attr
, 0);
233 fuse_reply_err(req
, -r
);
235 cfuse
->iput(in
); // iput required
240 static void fuse_ll_setxattr(fuse_req_t req
, fuse_ino_t ino
, const char *name
,
241 const char *value
, size_t size
,
248 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
249 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
250 Inode
*in
= cfuse
->iget(ino
);
251 UserPerm
perms(ctx
->uid
, ctx
->gid
);
252 GET_GROUPS(perms
, req
);
254 int r
= cfuse
->client
->ll_setxattr(in
, name
, value
, size
, flags
, perms
);
255 fuse_reply_err(req
, -r
);
257 cfuse
->iput(in
); // iput required
260 static void fuse_ll_listxattr(fuse_req_t req
, fuse_ino_t ino
, size_t size
)
262 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
263 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
264 Inode
*in
= cfuse
->iget(ino
);
266 UserPerm
perms(ctx
->uid
, ctx
->gid
);
267 GET_GROUPS(perms
, req
);
269 int r
= cfuse
->client
->ll_listxattr(in
, buf
, size
, perms
);
270 if (size
== 0 && r
>= 0)
271 fuse_reply_xattr(req
, r
);
273 fuse_reply_buf(req
, buf
, r
);
275 fuse_reply_err(req
, -r
);
277 cfuse
->iput(in
); // iput required
280 static void fuse_ll_getxattr(fuse_req_t req
, fuse_ino_t ino
, const char *name
,
287 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
288 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
289 Inode
*in
= cfuse
->iget(ino
);
291 UserPerm
perms(ctx
->uid
, ctx
->gid
);
292 GET_GROUPS(perms
, req
);
294 int r
= cfuse
->client
->ll_getxattr(in
, name
, buf
, size
, perms
);
295 if (size
== 0 && r
>= 0)
296 fuse_reply_xattr(req
, r
);
298 fuse_reply_buf(req
, buf
, r
);
300 fuse_reply_err(req
, -r
);
302 cfuse
->iput(in
); // iput required
305 static void fuse_ll_removexattr(fuse_req_t req
, fuse_ino_t ino
,
308 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
309 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
310 Inode
*in
= cfuse
->iget(ino
);
311 UserPerm
perms(ctx
->uid
, ctx
->gid
);
312 GET_GROUPS(perms
, req
);
314 int r
= cfuse
->client
->ll_removexattr(in
, name
, perms
);
315 fuse_reply_err(req
, -r
);
317 cfuse
->iput(in
); // iput required
320 static void fuse_ll_opendir(fuse_req_t req
, fuse_ino_t ino
,
321 struct fuse_file_info
*fi
)
323 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
324 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
325 Inode
*in
= cfuse
->iget(ino
);
328 UserPerm
perms(ctx
->uid
, ctx
->gid
);
329 GET_GROUPS(perms
, req
);
331 int r
= cfuse
->client
->ll_opendir(in
, fi
->flags
, (dir_result_t
**)&dirp
,
334 fi
->fh
= (uint64_t)dirp
;
335 fuse_reply_open(req
, fi
);
337 fuse_reply_err(req
, -r
);
340 cfuse
->iput(in
); // iput required
343 static void fuse_ll_readlink(fuse_req_t req
, fuse_ino_t ino
)
345 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
346 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
347 Inode
*in
= cfuse
->iget(ino
);
348 char buf
[PATH_MAX
+ 1]; // leave room for a null terminator
349 UserPerm
perms(ctx
->uid
, ctx
->gid
);
350 GET_GROUPS(perms
, req
);
352 int r
= cfuse
->client
->ll_readlink(in
, buf
, sizeof(buf
) - 1, perms
);
355 fuse_reply_readlink(req
, buf
);
357 fuse_reply_err(req
, -r
);
360 cfuse
->iput(in
); // iput required
363 static void fuse_ll_mknod(fuse_req_t req
, fuse_ino_t parent
, const char *name
,
364 mode_t mode
, dev_t rdev
)
366 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
367 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
368 Inode
*i2
, *i1
= cfuse
->iget(parent
);
369 struct fuse_entry_param fe
;
370 UserPerm
perms(ctx
->uid
, ctx
->gid
);
371 GET_GROUPS(perms
, req
);
373 memset(&fe
, 0, sizeof(fe
));
375 int r
= cfuse
->client
->ll_mknod(i1
, name
, mode
, new_decode_dev(rdev
),
376 &fe
.attr
, &i2
, perms
);
378 fe
.ino
= cfuse
->make_fake_ino(fe
.attr
.st_ino
, fe
.attr
.st_dev
);
379 fe
.attr
.st_rdev
= new_encode_dev(fe
.attr
.st_rdev
);
380 fuse_reply_entry(req
, &fe
);
382 fuse_reply_err(req
, -r
);
385 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
387 cfuse
->iput(i1
); // iput required
390 static void fuse_ll_mkdir(fuse_req_t req
, fuse_ino_t parent
, const char *name
,
393 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
394 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
396 struct fuse_entry_param fe
;
398 memset(&fe
, 0, sizeof(fe
));
399 UserPerm
perm(ctx
->uid
, ctx
->gid
);
400 GET_GROUPS(perm
, req
);
401 #ifdef HAVE_SYS_SYNCFS
402 if (cfuse
->fino_snap(parent
) == CEPH_SNAPDIR
&&
403 cfuse
->client
->cct
->_conf
->fuse_multithreaded
&&
404 cfuse
->client
->cct
->_conf
->fuse_syncfs_on_mksnap
) {
406 int fd
= ::open(cfuse
->mountpoint
, O_RDONLY
| O_DIRECTORY
);
410 int r
= ::syncfs(fd
);
416 fuse_reply_err(req
, err
);
422 i1
= cfuse
->iget(parent
);
423 int r
= cfuse
->client
->ll_mkdir(i1
, name
, mode
, &fe
.attr
, &i2
, perm
);
425 fe
.ino
= cfuse
->make_fake_ino(fe
.attr
.st_ino
, fe
.attr
.st_dev
);
426 fe
.attr
.st_rdev
= new_encode_dev(fe
.attr
.st_rdev
);
427 fuse_reply_entry(req
, &fe
);
429 fuse_reply_err(req
, -r
);
432 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
434 cfuse
->iput(i1
); // iput required
437 static void fuse_ll_unlink(fuse_req_t req
, fuse_ino_t parent
, const char *name
)
439 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
440 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
441 Inode
*in
= cfuse
->iget(parent
);
442 UserPerm
perm(ctx
->uid
, ctx
->gid
);
443 GET_GROUPS(perm
, req
);
445 int r
= cfuse
->client
->ll_unlink(in
, name
, perm
);
446 fuse_reply_err(req
, -r
);
448 cfuse
->iput(in
); // iput required
451 static void fuse_ll_rmdir(fuse_req_t req
, fuse_ino_t parent
, const char *name
)
453 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
454 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
455 Inode
*in
= cfuse
->iget(parent
);
456 UserPerm
perms(ctx
->uid
, ctx
->gid
);
457 GET_GROUPS(perms
, req
);
459 int r
= cfuse
->client
->ll_rmdir(in
, name
, perms
);
460 fuse_reply_err(req
, -r
);
462 cfuse
->iput(in
); // iput required
465 static void fuse_ll_symlink(fuse_req_t req
, const char *existing
,
466 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
*i2
, *i1
= cfuse
->iget(parent
);
471 struct fuse_entry_param fe
;
472 UserPerm
perms(ctx
->uid
, ctx
->gid
);
473 GET_GROUPS(perms
, req
);
475 memset(&fe
, 0, sizeof(fe
));
477 int r
= cfuse
->client
->ll_symlink(i1
, name
, existing
, &fe
.attr
, &i2
, perms
);
479 fe
.ino
= cfuse
->make_fake_ino(fe
.attr
.st_ino
, fe
.attr
.st_dev
);
480 fe
.attr
.st_rdev
= new_encode_dev(fe
.attr
.st_rdev
);
481 fuse_reply_entry(req
, &fe
);
483 fuse_reply_err(req
, -r
);
486 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
488 cfuse
->iput(i1
); // iput required
491 static void fuse_ll_rename(fuse_req_t req
, fuse_ino_t parent
, const char *name
,
492 fuse_ino_t newparent
, const char *newname
)
494 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
495 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
496 Inode
*in
= cfuse
->iget(parent
);
497 Inode
*nin
= cfuse
->iget(newparent
);
498 UserPerm
perm(ctx
->uid
, ctx
->gid
);
499 GET_GROUPS(perm
, req
);
501 int r
= cfuse
->client
->ll_rename(in
, name
, nin
, newname
, perm
);
502 fuse_reply_err(req
, -r
);
504 cfuse
->iput(in
); // iputs required
508 static void fuse_ll_link(fuse_req_t req
, fuse_ino_t ino
, fuse_ino_t newparent
,
511 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
512 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
513 Inode
*in
= cfuse
->iget(ino
);
514 Inode
*nin
= cfuse
->iget(newparent
);
515 struct fuse_entry_param fe
;
517 memset(&fe
, 0, sizeof(fe
));
518 UserPerm
perm(ctx
->uid
, ctx
->gid
);
519 GET_GROUPS(perm
, req
);
522 * Note that we could successfully link, but then fail the subsequent
523 * getattr and return an error. Perhaps we should ignore getattr errors,
524 * but then how do we tell FUSE that the attrs are bogus?
526 int r
= cfuse
->client
->ll_link(in
, nin
, newname
, perm
);
528 r
= cfuse
->client
->ll_getattr(in
, &fe
.attr
, perm
);
530 fe
.ino
= cfuse
->make_fake_ino(fe
.attr
.st_ino
, fe
.attr
.st_dev
);
531 fe
.attr
.st_rdev
= new_encode_dev(fe
.attr
.st_rdev
);
532 fuse_reply_entry(req
, &fe
);
538 * Many ll operations in libcephfs return an extra inode reference, but
539 * ll_link currently does not. Still, FUSE needs one for the new dentry,
540 * so we commandeer the reference taken earlier when ll_link is successful.
541 * On error however, we must put that reference.
544 fuse_reply_err(req
, -r
);
550 static void fuse_ll_open(fuse_req_t req
, fuse_ino_t ino
,
551 struct fuse_file_info
*fi
)
553 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
554 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
555 Inode
*in
= cfuse
->iget(ino
);
557 UserPerm
perms(ctx
->uid
, ctx
->gid
);
558 GET_GROUPS(perms
, req
);
560 int r
= cfuse
->client
->ll_open(in
, fi
->flags
, &fh
, perms
);
562 fi
->fh
= (uint64_t)fh
;
563 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
564 if (cfuse
->client
->cct
->_conf
->fuse_disable_pagecache
)
566 else if (cfuse
->client
->cct
->_conf
->fuse_use_invalidate_cb
)
569 fuse_reply_open(req
, fi
);
571 fuse_reply_err(req
, -r
);
574 cfuse
->iput(in
); // iput required
577 static void fuse_ll_read(fuse_req_t req
, fuse_ino_t ino
, size_t size
, off_t off
,
578 struct fuse_file_info
*fi
)
580 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
581 Fh
*fh
= reinterpret_cast<Fh
*>(fi
->fh
);
583 int r
= cfuse
->client
->ll_read(fh
, off
, size
, &bl
);
585 fuse_reply_buf(req
, bl
.c_str(), bl
.length());
587 fuse_reply_err(req
, -r
);
590 static void fuse_ll_write(fuse_req_t req
, fuse_ino_t ino
, const char *buf
,
591 size_t size
, off_t off
, struct fuse_file_info
*fi
)
593 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
594 Fh
*fh
= reinterpret_cast<Fh
*>(fi
->fh
);
595 int r
= cfuse
->client
->ll_write(fh
, off
, size
, buf
);
597 fuse_reply_write(req
, r
);
599 fuse_reply_err(req
, -r
);
602 static void fuse_ll_flush(fuse_req_t req
, fuse_ino_t ino
,
603 struct fuse_file_info
*fi
)
605 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
606 Fh
*fh
= reinterpret_cast<Fh
*>(fi
->fh
);
607 int r
= cfuse
->client
->ll_flush(fh
);
608 fuse_reply_err(req
, -r
);
611 #ifdef FUSE_IOCTL_COMPAT
612 static void fuse_ll_ioctl(fuse_req_t req
, fuse_ino_t ino
, int cmd
, void *arg
, struct fuse_file_info
*fi
,
613 unsigned flags
, const void *in_buf
, size_t in_bufsz
, size_t out_bufsz
)
615 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
617 if (flags
& FUSE_IOCTL_COMPAT
) {
618 fuse_reply_err(req
, ENOSYS
);
622 switch (static_cast<unsigned>(cmd
)) {
623 case CEPH_IOC_GET_LAYOUT
: {
624 file_layout_t layout
;
625 struct ceph_ioctl_layout l
;
626 Fh
*fh
= (Fh
*)fi
->fh
;
627 cfuse
->client
->ll_file_layout(fh
, &layout
);
628 l
.stripe_unit
= layout
.stripe_unit
;
629 l
.stripe_count
= layout
.stripe_count
;
630 l
.object_size
= layout
.object_size
;
631 l
.data_pool
= layout
.pool_id
;
632 fuse_reply_ioctl(req
, 0, &l
, sizeof(struct ceph_ioctl_layout
));
636 fuse_reply_err(req
, EINVAL
);
641 #if FUSE_VERSION > FUSE_MAKE_VERSION(2, 9)
643 static void fuse_ll_fallocate(fuse_req_t req
, fuse_ino_t ino
, int mode
,
644 off_t offset
, off_t length
,
645 struct fuse_file_info
*fi
)
647 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
648 Fh
*fh
= (Fh
*)fi
->fh
;
649 int r
= cfuse
->client
->ll_fallocate(fh
, mode
, offset
, length
);
650 fuse_reply_err(req
, -r
);
655 static void fuse_ll_release(fuse_req_t req
, fuse_ino_t ino
,
656 struct fuse_file_info
*fi
)
658 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
659 Fh
*fh
= reinterpret_cast<Fh
*>(fi
->fh
);
660 int r
= cfuse
->client
->ll_release(fh
);
661 fuse_reply_err(req
, -r
);
664 static void fuse_ll_fsync(fuse_req_t req
, fuse_ino_t ino
, int datasync
,
665 struct fuse_file_info
*fi
)
667 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
668 Fh
*fh
= reinterpret_cast<Fh
*>(fi
->fh
);
669 int r
= cfuse
->client
->ll_fsync(fh
, datasync
);
670 fuse_reply_err(req
, -r
);
673 struct readdir_context
{
677 size_t pos
; /* in buf */
682 * return 0 on success, -1 if out of space
684 static int fuse_ll_add_dirent(void *p
, struct dirent
*de
,
685 struct ceph_statx
*stx
, off_t next_off
,
688 struct readdir_context
*c
= (struct readdir_context
*)p
;
689 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)fuse_req_userdata(c
->req
);
692 st
.st_ino
= cfuse
->make_fake_ino(stx
->stx_ino
, c
->snap
);
693 st
.st_mode
= stx
->stx_mode
;
694 st
.st_rdev
= new_encode_dev(stx
->stx_rdev
);
696 size_t room
= c
->size
- c
->pos
;
697 size_t entrysize
= fuse_add_direntry(c
->req
, c
->buf
+ c
->pos
, room
,
698 de
->d_name
, &st
, next_off
);
699 if (entrysize
> room
)
707 static void fuse_ll_readdir(fuse_req_t req
, fuse_ino_t ino
, size_t size
,
708 off_t off
, struct fuse_file_info
*fi
)
710 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
712 dir_result_t
*dirp
= reinterpret_cast<dir_result_t
*>(fi
->fh
);
713 cfuse
->client
->seekdir(dirp
, off
);
715 struct readdir_context rc
;
717 rc
.buf
= new char[size
];
720 rc
.snap
= cfuse
->fino_snap(ino
);
722 int r
= cfuse
->client
->readdir_r_cb(dirp
, fuse_ll_add_dirent
, &rc
);
723 if (r
== 0 || r
== -ENOSPC
) /* ignore ENOSPC from our callback */
724 fuse_reply_buf(req
, rc
.buf
, rc
.pos
);
726 fuse_reply_err(req
, -r
);
730 static void fuse_ll_releasedir(fuse_req_t req
, fuse_ino_t ino
,
731 struct fuse_file_info
*fi
)
733 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
734 dir_result_t
*dirp
= reinterpret_cast<dir_result_t
*>(fi
->fh
);
735 cfuse
->client
->ll_releasedir(dirp
);
736 fuse_reply_err(req
, 0);
739 static void fuse_ll_fsyncdir(fuse_req_t req
, fuse_ino_t ino
, int datasync
,
740 struct fuse_file_info
*fi
)
742 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
743 dir_result_t
*dirp
= reinterpret_cast<dir_result_t
*>(fi
->fh
);
744 int r
= cfuse
->client
->ll_fsyncdir(dirp
);
745 fuse_reply_err(req
, -r
);
748 static void fuse_ll_access(fuse_req_t req
, fuse_ino_t ino
, int mask
)
750 fuse_reply_err(req
, 0);
753 static void fuse_ll_create(fuse_req_t req
, fuse_ino_t parent
, const char *name
,
754 mode_t mode
, struct fuse_file_info
*fi
)
756 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
757 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
758 Inode
*i1
= cfuse
->iget(parent
), *i2
;
759 struct fuse_entry_param fe
;
761 UserPerm
perms(ctx
->uid
, ctx
->gid
);
762 GET_GROUPS(perms
, req
);
764 memset(&fe
, 0, sizeof(fe
));
766 // pass &i2 for the created inode so that ll_create takes an initial ll_ref
767 int r
= cfuse
->client
->ll_create(i1
, name
, mode
, fi
->flags
, &fe
.attr
, &i2
,
770 fi
->fh
= (uint64_t)fh
;
771 fe
.ino
= cfuse
->make_fake_ino(fe
.attr
.st_ino
, fe
.attr
.st_dev
);
772 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
773 if (cfuse
->client
->cct
->_conf
->fuse_disable_pagecache
)
775 else if (cfuse
->client
->cct
->_conf
->fuse_use_invalidate_cb
)
778 fuse_reply_create(req
, &fe
, fi
);
780 fuse_reply_err(req
, -r
);
781 // XXX NB, we dont iput(i2) because FUSE will do so in a matching
783 cfuse
->iput(i1
); // iput required
786 static void fuse_ll_statfs(fuse_req_t req
, fuse_ino_t ino
)
788 struct statvfs stbuf
;
789 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
790 Inode
*in
= cfuse
->iget(ino
);
791 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
792 UserPerm
perms(ctx
->uid
, ctx
->gid
);
793 GET_GROUPS(perms
, req
);
795 int r
= cfuse
->client
->ll_statfs(in
, &stbuf
, perms
);
797 fuse_reply_statfs(req
, &stbuf
);
799 fuse_reply_err(req
, -r
);
801 cfuse
->iput(in
); // iput required
804 static void fuse_ll_getlk(fuse_req_t req
, fuse_ino_t ino
,
805 struct fuse_file_info
*fi
, struct flock
*lock
)
807 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
808 Fh
*fh
= reinterpret_cast<Fh
*>(fi
->fh
);
810 int r
= cfuse
->client
->ll_getlk(fh
, lock
, fi
->lock_owner
);
812 fuse_reply_lock(req
, lock
);
814 fuse_reply_err(req
, -r
);
817 static void fuse_ll_setlk(fuse_req_t req
, fuse_ino_t ino
,
818 struct fuse_file_info
*fi
, struct flock
*lock
, int sleep
)
820 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
821 Fh
*fh
= reinterpret_cast<Fh
*>(fi
->fh
);
823 // must use multithread if operation may block
824 if (!cfuse
->client
->cct
->_conf
->fuse_multithreaded
&&
825 sleep
&& lock
->l_type
!= F_UNLCK
) {
826 fuse_reply_err(req
, EDEADLK
);
830 int r
= cfuse
->client
->ll_setlk(fh
, lock
, fi
->lock_owner
, sleep
);
831 fuse_reply_err(req
, -r
);
834 static void fuse_ll_interrupt(fuse_req_t req
, void* data
)
836 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
837 cfuse
->client
->ll_interrupt(data
);
840 static void switch_interrupt_cb(void *handle
, void* data
)
842 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)handle
;
843 fuse_req_t req
= cfuse
->get_fuse_req();
846 fuse_req_interrupt_func(req
, fuse_ll_interrupt
, data
);
848 fuse_req_interrupt_func(req
, NULL
, NULL
);
851 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
852 static void fuse_ll_flock(fuse_req_t req
, fuse_ino_t ino
,
853 struct fuse_file_info
*fi
, int cmd
)
855 CephFuse::Handle
*cfuse
= fuse_ll_req_prepare(req
);
856 Fh
*fh
= (Fh
*)fi
->fh
;
858 // must use multithread if operation may block
859 if (!cfuse
->client
->cct
->_conf
->fuse_multithreaded
&&
860 !(cmd
& (LOCK_NB
| LOCK_UN
))) {
861 fuse_reply_err(req
, EDEADLK
);
865 int r
= cfuse
->client
->ll_flock(fh
, cmd
, fi
->lock_owner
);
866 fuse_reply_err(req
, -r
);
871 static mode_t
umask_cb(void *handle
)
873 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)handle
;
874 fuse_req_t req
= cfuse
->get_fuse_req();
875 const struct fuse_ctx
*ctx
= fuse_req_ctx(req
);
880 static void ino_invalidate_cb(void *handle
, vinodeno_t vino
, int64_t off
,
883 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
884 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)handle
;
885 fuse_ino_t fino
= cfuse
->make_fake_ino(vino
.ino
, vino
.snapid
);
886 fuse_lowlevel_notify_inval_inode(cfuse
->ch
, fino
, off
, len
);
890 static void dentry_invalidate_cb(void *handle
, vinodeno_t dirino
,
891 vinodeno_t ino
, string
& name
)
893 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)handle
;
894 fuse_ino_t fdirino
= cfuse
->make_fake_ino(dirino
.ino
, dirino
.snapid
);
895 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
897 if (ino
.ino
!= inodeno_t())
898 fino
= cfuse
->make_fake_ino(ino
.ino
, ino
.snapid
);
899 fuse_lowlevel_notify_delete(cfuse
->ch
, fdirino
, fino
, name
.c_str(), name
.length());
900 #elif FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
901 fuse_lowlevel_notify_inval_entry(cfuse
->ch
, fdirino
, name
.c_str(), name
.length());
905 static int remount_cb(void *handle
)
907 // used for trimming kernel dcache. when remounting a file system, linux kernel
908 // trims all unused dentries in the file system
910 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)handle
;
911 snprintf(cmd
, sizeof(cmd
), "mount -i -o remount %s", cfuse
->mountpoint
);
913 if (r
!= 0 && r
!= -1) {
920 static void do_init(void *data
, fuse_conn_info
*conn
)
922 CephFuse::Handle
*cfuse
= (CephFuse::Handle
*)data
;
923 Client
*client
= cfuse
->client
;
926 if (!client
->cct
->_conf
->fuse_default_permissions
&&
927 client
->ll_handle_umask()) {
928 // apply umask in userspace if posix acl is enabled
929 if(conn
->capable
& FUSE_CAP_DONT_MASK
)
930 conn
->want
|= FUSE_CAP_DONT_MASK
;
934 if (cfuse
->fd_on_success
) {
935 //cout << "fuse init signaling on fd " << fd_on_success << std::endl;
936 // see Preforker::daemonize(), ceph-fuse's parent process expects a `-1`
937 // from a daemonized child process.
939 int err
= safe_write(cfuse
->fd_on_success
, &r
, sizeof(r
));
941 derr
<< "fuse_ll: do_init: safe_write failed with error "
942 << cpp_strerror(err
) << dendl
;
945 //cout << "fuse init done signaling on fd " << fd_on_success << std::endl;
947 // close stdout, etc.
954 const static struct fuse_lowlevel_ops fuse_ll_oper
= {
957 lookup
: fuse_ll_lookup
,
958 forget
: fuse_ll_forget
,
959 getattr
: fuse_ll_getattr
,
960 setattr
: fuse_ll_setattr
,
961 readlink
: fuse_ll_readlink
,
962 mknod
: fuse_ll_mknod
,
963 mkdir
: fuse_ll_mkdir
,
964 unlink
: fuse_ll_unlink
,
965 rmdir
: fuse_ll_rmdir
,
966 symlink
: fuse_ll_symlink
,
967 rename
: fuse_ll_rename
,
971 write
: fuse_ll_write
,
972 flush
: fuse_ll_flush
,
973 release
: fuse_ll_release
,
974 fsync
: fuse_ll_fsync
,
975 opendir
: fuse_ll_opendir
,
976 readdir
: fuse_ll_readdir
,
977 releasedir
: fuse_ll_releasedir
,
978 fsyncdir
: fuse_ll_fsyncdir
,
979 statfs
: fuse_ll_statfs
,
980 setxattr
: fuse_ll_setxattr
,
981 getxattr
: fuse_ll_getxattr
,
982 listxattr
: fuse_ll_listxattr
,
983 removexattr
: fuse_ll_removexattr
,
984 access
: fuse_ll_access
,
985 create
: fuse_ll_create
,
986 getlk
: fuse_ll_getlk
,
987 setlk
: fuse_ll_setlk
,
989 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
990 #ifdef FUSE_IOCTL_COMPAT
991 ioctl
: fuse_ll_ioctl
,
997 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
1001 flock
: fuse_ll_flock
,
1003 #if FUSE_VERSION > FUSE_MAKE_VERSION(2, 9)
1004 fallocate
: fuse_ll_fallocate
1009 CephFuse::Handle::Handle(Client
*c
, int fd
) :
1015 stag_lock("fuse_ll.cc stag_lock"),
1018 snap_stag_map
[CEPH_NOSNAP
] = 0;
1019 stag_snap_map
[0] = CEPH_NOSNAP
;
1020 memset(&args
, 0, sizeof(args
));
1023 CephFuse::Handle::~Handle()
1025 fuse_opt_free_args(&args
);
1028 void CephFuse::Handle::finalize()
1031 fuse_remove_signal_handlers(se
);
1033 fuse_session_remove_chan(ch
);
1035 fuse_session_destroy(se
);
1037 fuse_unmount(mountpoint
, ch
);
1039 pthread_key_delete(fuse_req_key
);
1042 int CephFuse::Handle::init(int argc
, const char *argv
[])
1045 int r
= pthread_key_create(&fuse_req_key
, NULL
);
1047 derr
<< "pthread_key_create failed." << dendl
;
1051 // set up fuse argc/argv
1053 const char **newargv
= (const char **) malloc((argc
+ 10) * sizeof(char *));
1057 newargv
[newargc
++] = argv
[0];
1058 newargv
[newargc
++] = "-f"; // stay in foreground
1060 if (client
->cct
->_conf
->fuse_allow_other
) {
1061 newargv
[newargc
++] = "-o";
1062 newargv
[newargc
++] = "allow_other";
1064 if (client
->cct
->_conf
->fuse_default_permissions
) {
1065 newargv
[newargc
++] = "-o";
1066 newargv
[newargc
++] = "default_permissions";
1068 #if defined(__linux__)
1069 if (client
->cct
->_conf
->fuse_big_writes
) {
1070 newargv
[newargc
++] = "-o";
1071 newargv
[newargc
++] = "big_writes";
1073 if (client
->cct
->_conf
->fuse_atomic_o_trunc
) {
1074 newargv
[newargc
++] = "-o";
1075 newargv
[newargc
++] = "atomic_o_trunc";
1078 if (client
->cct
->_conf
->fuse_debug
)
1079 newargv
[newargc
++] = "-d";
1081 for (int argctr
= 1; argctr
< argc
; argctr
++)
1082 newargv
[newargc
++] = argv
[argctr
];
1084 derr
<< "init, newargv = " << newargv
<< " newargc=" << newargc
<< dendl
;
1085 struct fuse_args a
= FUSE_ARGS_INIT(newargc
, (char**)newargv
);
1086 args
= a
; // Roundabout construction b/c FUSE_ARGS_INIT is for initialization not assignment
1088 if (fuse_parse_cmdline(&args
, &mountpoint
, NULL
, NULL
) == -1) {
1089 derr
<< "fuse_parse_cmdline failed." << dendl
;
1090 fuse_opt_free_args(&args
);
1095 assert(args
.allocated
); // Checking fuse has realloc'd args so we can free newargv
1100 int CephFuse::Handle::start()
1102 ch
= fuse_mount(mountpoint
, &args
);
1104 derr
<< "fuse_mount(mountpoint=" << mountpoint
<< ") failed." << dendl
;
1108 se
= fuse_lowlevel_new(&args
, &fuse_ll_oper
, sizeof(fuse_ll_oper
), this);
1110 derr
<< "fuse_lowlevel_new failed" << dendl
;
1114 signal(SIGTERM
, SIG_DFL
);
1115 signal(SIGINT
, SIG_DFL
);
1116 if (fuse_set_signal_handlers(se
) == -1) {
1117 derr
<< "fuse_set_signal_handlers failed" << dendl
;
1121 fuse_session_add_chan(se
, ch
);
1124 struct client_callback_args args
= {
1126 ino_cb
: client
->cct
->_conf
->fuse_use_invalidate_cb
? ino_invalidate_cb
: NULL
,
1127 dentry_cb
: dentry_invalidate_cb
,
1128 switch_intr_cb
: switch_interrupt_cb
,
1129 #if defined(__linux__)
1130 remount_cb
: remount_cb
,
1132 getgroups_cb
: getgroups_cb
,
1133 #if !defined(DARWIN)
1137 client
->ll_register_callbacks(&args
);
1142 int CephFuse::Handle::loop()
1144 if (client
->cct
->_conf
->fuse_multithreaded
) {
1145 return fuse_session_loop_mt(se
);
1147 return fuse_session_loop(se
);
1151 uint64_t CephFuse::Handle::fino_snap(uint64_t fino
)
1153 if (fino
== FUSE_ROOT_ID
)
1156 if (client
->use_faked_inos()) {
1157 vinodeno_t vino
= client
->map_faked_ino(fino
);
1160 Mutex::Locker
l(stag_lock
);
1161 uint64_t stag
= FINO_STAG(fino
);
1162 assert(stag_snap_map
.count(stag
));
1163 return stag_snap_map
[stag
];
1167 Inode
* CephFuse::Handle::iget(fuse_ino_t fino
)
1169 if (fino
== FUSE_ROOT_ID
)
1170 return client
->get_root();
1172 if (client
->use_faked_inos()) {
1173 return client
->ll_get_inode((ino_t
)fino
);
1175 vinodeno_t
vino(FINO_INO(fino
), fino_snap(fino
));
1176 return client
->ll_get_inode(vino
);
1180 void CephFuse::Handle::iput(Inode
*in
)
1185 uint64_t CephFuse::Handle::make_fake_ino(inodeno_t ino
, snapid_t snapid
)
1187 if (client
->use_faked_inos()) {
1188 // already faked by libcephfs
1189 if (ino
== client
->get_root_ino())
1190 return FUSE_ROOT_ID
;
1194 if (snapid
== CEPH_NOSNAP
&& ino
== client
->get_root_ino())
1195 return FUSE_ROOT_ID
;
1197 Mutex::Locker
l(stag_lock
);
1199 if (snap_stag_map
.count(snapid
) == 0) {
1201 snap_stag_map
[snapid
] = stag
;
1202 stag_snap_map
[stag
] = snapid
;
1204 stag
= snap_stag_map
[snapid
];
1205 inodeno_t fino
= MAKE_FINO(ino
, stag
);
1206 //cout << "make_fake_ino " << ino << "." << snapid << " -> " << fino << std::endl;
1211 void CephFuse::Handle::set_fuse_req(fuse_req_t req
)
1213 pthread_setspecific(fuse_req_key
, (void*)req
);
1216 fuse_req_t
CephFuse::Handle::get_fuse_req()
1218 return (fuse_req_t
) pthread_getspecific(fuse_req_key
);
1222 CephFuse::CephFuse(Client
*c
, int fd
) : _handle(new CephFuse::Handle(c
, fd
))
1226 CephFuse::~CephFuse()
1231 int CephFuse::init(int argc
, const char *argv
[])
1233 return _handle
->init(argc
, argv
);
1236 int CephFuse::start()
1238 return _handle
->start();
1241 int CephFuse::loop()
1243 return _handle
->loop();
1246 void CephFuse::finalize()
1248 return _handle
->finalize();
1251 std::string
CephFuse::get_mount_point() const
1253 if (_handle
->mountpoint
) {
1254 return _handle
->mountpoint
;