4 #define FUSE_USE_VERSION 30
6 #include "include/int_types.h"
17 #include <sys/types.h>
25 #if defined(__FreeBSD__)
26 #include <sys/param.h>
29 #include "include/compat.h"
30 #include "include/rbd/librbd.h"
31 #include "include/ceph_assert.h"
33 #include "common/ceph_argparse.h"
34 #include "common/ceph_context.h"
35 #include "include/ceph_fuse.h"
37 #include "global/global_init.h"
38 #include "global/global_context.h"
40 static int gotrados
= 0;
43 char *mount_image_name
;
47 std::mutex readdir_lock
;
51 rbd_image_info_t rbd_info
;
62 struct rbd_image
*next
;
64 struct rbd_image_data
{
65 struct rbd_image
*images
;
66 rbd_image_spec_t
*image_specs
;
67 size_t image_spec_count
;
69 struct rbd_image_data rbd_image_data
;
71 struct rbd_openimage
{
74 struct rbd_stat rbd_stat
;
76 #define MAX_RBD_IMAGES 128
77 struct rbd_openimage opentbl
[MAX_RBD_IMAGES
];
79 struct rbd_options rbd_options
= {(char*) "rbd", (char*) "", NULL
};
81 #define rbdsize(fd) opentbl[fd].rbd_stat.rbd_info.size
82 #define rbdblksize(fd) opentbl[fd].rbd_stat.rbd_info.obj_size
83 #define rbdblkcnt(fd) opentbl[fd].rbd_stat.rbd_info.num_objs
85 uint64_t imagesize
= 1024ULL * 1024 * 1024;
86 uint64_t imageorder
= 22ULL;
87 uint64_t imagefeatures
= 1ULL;
89 // Minimize calls to rbd_list: marks bracketing of opendir/<ops>/releasedir
93 int connect_to_cluster(rados_t
*pcluster
);
94 void enumerate_images(struct rbd_image_data
*data
);
95 int open_rbd_image(const char *image_name
);
96 int find_openrbd(const char *path
);
98 void simple_err(const char *msg
, int err
);
101 enumerate_images(struct rbd_image_data
*data
)
103 struct rbd_image
**head
= &data
->images
;
104 struct rbd_image
*im
, *next
;
108 for (im
= *head
; im
!= NULL
;) {
114 rbd_image_spec_list_cleanup(data
->image_specs
,
115 data
->image_spec_count
);
116 free(data
->image_specs
);
117 data
->image_specs
= NULL
;
118 data
->image_spec_count
= 0;
122 ret
= rbd_list2(ioctx
, data
->image_specs
,
123 &data
->image_spec_count
);
124 if (ret
== -ERANGE
) {
125 data
->image_specs
= static_cast<rbd_image_spec_t
*>(
126 realloc(data
->image_specs
,
127 sizeof(rbd_image_spec_t
) * data
->image_spec_count
));
128 } else if (ret
< 0) {
129 simple_err("Failed to list images", ret
);
135 if (*nspace_name
!= '\0') {
136 fprintf(stderr
, "pool/namespace %s/%s: ", pool_name
, nspace_name
);
138 fprintf(stderr
, "pool %s: ", pool_name
);
140 for (size_t idx
= 0; idx
< data
->image_spec_count
; ++idx
) {
141 if ((mount_image_name
== NULL
) ||
142 ((strlen(mount_image_name
) > 0) &&
143 (strcmp(data
->image_specs
[idx
].name
, mount_image_name
) == 0))) {
144 fprintf(stderr
, "%s, ", data
->image_specs
[idx
].name
);
145 im
= static_cast<rbd_image
*>(malloc(sizeof(*im
)));
146 im
->image_name
= data
->image_specs
[idx
].name
;
151 fprintf(stderr
, "\n");
155 find_openrbd(const char *path
)
159 /* find in opentbl[] entry if already open */
160 for (i
= 0; i
< MAX_RBD_IMAGES
; i
++) {
161 if ((opentbl
[i
].image_name
!= NULL
) &&
162 (strcmp(opentbl
[i
].image_name
, path
) == 0)) {
170 open_rbd_image(const char *image_name
)
172 struct rbd_image
*im
;
173 struct rbd_openimage
*rbd
= NULL
;
176 if (image_name
== (char *)NULL
)
179 // relies on caller to keep rbd_image_data up to date
180 for (im
= rbd_image_data
.images
; im
!= NULL
; im
= im
->next
) {
181 if (strcmp(im
->image_name
, image_name
) == 0) {
188 /* find in opentbl[] entry if already open */
189 if ((fd
= find_openrbd(image_name
)) != -1) {
193 // allocate an opentbl[] and open the image
194 for (i
= 0; i
< MAX_RBD_IMAGES
; i
++) {
195 if (opentbl
[i
].image
== NULL
) {
198 rbd
->image_name
= strdup(image_name
);
202 if (i
== MAX_RBD_IMAGES
|| !rbd
)
204 int ret
= rbd_open(ioctx
, rbd
->image_name
, &(rbd
->image
), NULL
);
206 simple_err("open_rbd_image: can't open: ", ret
);
210 rbd_stat(rbd
->image
, &(rbd
->rbd_stat
.rbd_info
),
211 sizeof(rbd_image_info_t
));
212 rbd
->rbd_stat
.valid
= 1;
217 iter_images(void *cookie
,
218 void (*iter
)(void *cookie
, const char *image
))
220 struct rbd_image
*im
;
224 for (im
= rbd_image_data
.images
; im
!= NULL
; im
= im
->next
)
225 iter(cookie
, im
->image_name
);
226 readdir_lock
.unlock();
229 static void count_images_cb(void *cookie
, const char *image
)
231 (*((unsigned int *)cookie
))++;
234 static int count_images(void)
236 unsigned int count
= 0;
239 enumerate_images(&rbd_image_data
);
240 readdir_lock
.unlock();
242 iter_images(&count
, count_images_cb
);
248 static int rbdfs_getattr(const char *path
, struct stat
*stbuf
249 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
250 , struct fuse_file_info
*fi
263 memset(stbuf
, 0, sizeof(struct stat
));
265 if (strcmp(path
, "/") == 0) {
268 stbuf
->st_mode
= S_IFDIR
+ 0755;
269 stbuf
->st_nlink
= 2+count_images();
270 stbuf
->st_uid
= getuid();
271 stbuf
->st_gid
= getgid();
272 stbuf
->st_size
= 1024;
273 stbuf
->st_blksize
= 1024;
274 stbuf
->st_blocks
= 1;
275 stbuf
->st_atime
= now
;
276 stbuf
->st_mtime
= now
;
277 stbuf
->st_ctime
= now
;
284 enumerate_images(&rbd_image_data
);
285 readdir_lock
.unlock();
287 fd
= open_rbd_image(path
+ 1);
292 stbuf
->st_mode
= S_IFREG
| 0666;
294 stbuf
->st_uid
= getuid();
295 stbuf
->st_gid
= getgid();
296 stbuf
->st_size
= rbdsize(fd
);
297 stbuf
->st_blksize
= rbdblksize(fd
);
298 stbuf
->st_blocks
= rbdblkcnt(fd
);
299 stbuf
->st_atime
= now
;
300 stbuf
->st_mtime
= now
;
301 stbuf
->st_ctime
= now
;
307 static int rbdfs_open(const char *path
, struct fuse_file_info
*fi
)
318 enumerate_images(&rbd_image_data
);
319 readdir_lock
.unlock();
320 fd
= open_rbd_image(path
+ 1);
328 static int rbdfs_read(const char *path
, char *buf
, size_t size
,
329 off_t offset
, struct fuse_file_info
*fi
)
332 struct rbd_openimage
*rbd
;
337 rbd
= &opentbl
[fi
->fh
];
342 ret
= rbd_read(rbd
->image
, offset
, size
, buf
);
355 static int rbdfs_write(const char *path
, const char *buf
, size_t size
,
356 off_t offset
, struct fuse_file_info
*fi
)
359 struct rbd_openimage
*rbd
;
364 rbd
= &opentbl
[fi
->fh
];
369 if ((size_t)(offset
+ size
) > rbdsize(fi
->fh
)) {
371 fprintf(stderr
, "rbdfs_write resizing %s to 0x%" PRIxMAX
"\n",
373 r
= rbd_resize(rbd
->image
, offset
+size
);
377 r
= rbd_stat(rbd
->image
, &(rbd
->rbd_stat
.rbd_info
),
378 sizeof(rbd_image_info_t
));
382 ret
= rbd_write(rbd
->image
, offset
, size
, buf
);
395 static void rbdfs_statfs_image_cb(void *num
, const char *image
)
399 ((uint64_t *)num
)[0]++;
401 fd
= open_rbd_image(image
);
403 ((uint64_t *)num
)[1] += rbdsize(fd
);
406 static int rbdfs_statfs(const char *path
, struct statvfs
*buf
)
416 enumerate_images(&rbd_image_data
);
417 readdir_lock
.unlock();
418 iter_images(num
, rbdfs_statfs_image_cb
);
420 #define RBDFS_BSIZE 4096
421 buf
->f_bsize
= RBDFS_BSIZE
;
422 buf
->f_frsize
= RBDFS_BSIZE
;
423 buf
->f_blocks
= num
[1] / RBDFS_BSIZE
;
426 buf
->f_files
= num
[0];
431 buf
->f_namemax
= PATH_MAX
;
436 static int rbdfs_fsync(const char *path
, int datasync
,
437 struct fuse_file_info
*fi
)
441 rbd_flush(opentbl
[fi
->fh
].image
);
445 static int rbdfs_opendir(const char *path
, struct fuse_file_info
*fi
)
447 // only one directory, so global "in_opendir" flag should be fine
450 enumerate_images(&rbd_image_data
);
451 readdir_lock
.unlock();
455 struct rbdfs_readdir_info
{
457 fuse_fill_dir_t filler
;
460 static void rbdfs_readdir_cb(void *_info
, const char *name
)
462 struct rbdfs_readdir_info
*info
= (struct rbdfs_readdir_info
*) _info
;
464 filler_compat(info
->filler
, info
->buf
, name
, NULL
, 0);
467 static int rbdfs_readdir(const char *path
, void *buf
, fuse_fill_dir_t filler
,
468 off_t offset
, struct fuse_file_info
*fi
469 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
470 , enum fuse_readdir_flags
474 struct rbdfs_readdir_info info
= { buf
, filler
};
479 fprintf(stderr
, "in readdir, but not inside opendir?\n");
481 if (strcmp(path
, "/") != 0)
484 filler_compat(filler
, buf
, ".", NULL
, 0);
485 filler_compat(filler
, buf
, "..", NULL
, 0);
486 iter_images(&info
, rbdfs_readdir_cb
);
490 static int rbdfs_releasedir(const char *path
, struct fuse_file_info
*fi
)
492 // see opendir comments
495 readdir_lock
.unlock();
500 rbdfs_init(struct fuse_conn_info
*conn
501 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
502 , struct fuse_config
*cfg
508 // init cannot fail, so if we fail here, gotrados remains at 0,
509 // causing other operations to fail immediately with ENXIO
511 ret
= connect_to_cluster(&cluster
);
515 pool_name
= rbd_options
.pool_name
;
516 nspace_name
= rbd_options
.nspace_name
;
517 mount_image_name
= rbd_options
.image_name
;
518 ret
= rados_ioctx_create(cluster
, pool_name
, &ioctx
);
521 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8) && FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
522 conn
->want
|= FUSE_CAP_BIG_WRITES
;
524 rados_ioctx_set_namespace(ioctx
, nspace_name
);
527 // init's return value shows up in fuse_context.private_data,
528 // also to void (*destroy)(void *); useful?
533 rbdfs_destroy(void *unused
)
537 for (int i
= 0; i
< MAX_RBD_IMAGES
; ++i
) {
538 if (opentbl
[i
].image
) {
539 rbd_close(opentbl
[i
].image
);
540 opentbl
[i
].image
= NULL
;
543 rados_ioctx_destroy(ioctx
);
544 rados_shutdown(cluster
);
548 rbdfs_checkname(const char *checkname
)
550 const char *extra
[] = {"@", "/"};
551 std::string
strCheckName(checkname
);
553 if (strCheckName
.empty())
556 unsigned int sz
= sizeof(extra
) / sizeof(const char*);
557 for (unsigned int i
= 0; i
< sz
; i
++)
559 std::string
ex(extra
[i
]);
560 if (std::string::npos
!= strCheckName
.find(ex
))
567 // return -errno on error. fi->fh is not set until open time
570 rbdfs_create(const char *path
, mode_t mode
, struct fuse_file_info
*fi
)
573 int order
= imageorder
;
575 r
= rbdfs_checkname(path
+1);
581 r
= rbd_create2(ioctx
, path
+1, imagesize
, imagefeatures
, &order
);
586 rbdfs_rename(const char *path
, const char *destname
587 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
594 r
= rbdfs_checkname(destname
+1);
600 if (strcmp(path
, "/") == 0)
603 return rbd_rename(ioctx
, path
+1, destname
+1);
607 rbdfs_utimens(const char *path
, const struct timespec tv
[2]
608 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
609 , struct fuse_file_info
*fi
613 // called on create; not relevant
618 rbdfs_unlink(const char *path
)
620 int fd
= find_openrbd(path
+1);
622 struct rbd_openimage
*rbd
= &opentbl
[fd
];
623 rbd_close(rbd
->image
);
625 free(rbd
->image_name
);
626 rbd
->rbd_stat
.valid
= 0;
628 return rbd_remove(ioctx
, path
+1);
633 rbdfs_truncate(const char *path
, off_t size
634 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
635 , struct fuse_file_info
*fi
641 struct rbd_openimage
*rbd
;
643 if ((fd
= open_rbd_image(path
+1)) < 0)
647 fprintf(stderr
, "truncate %s to %" PRIdMAX
" (0x%" PRIxMAX
")\n",
649 r
= rbd_resize(rbd
->image
, size
);
653 r
= rbd_stat(rbd
->image
, &(rbd
->rbd_stat
.rbd_info
),
654 sizeof(rbd_image_info_t
));
661 * set an xattr on path, with name/value, length size.
662 * Presumably flags are from Linux, as in XATTR_CREATE or
663 * XATTR_REPLACE (both "set", but fail if exist vs fail if not exist.
665 * We accept xattrs only on the root node.
667 * All values converted with strtoull, so can be expressed in any base
670 struct rbdfuse_attr
{
674 { (char*) "user.rbdfuse.imagesize", &imagesize
},
675 { (char*) "user.rbdfuse.imageorder", &imageorder
},
676 { (char*) "user.rbdfuse.imagefeatures", &imagefeatures
},
681 rbdfs_setxattr(const char *path
, const char *name
, const char *value
,
689 struct rbdfuse_attr
*ap
;
690 if (strcmp(path
, "/") != 0)
693 for (ap
= attrs
; ap
->attrname
!= NULL
; ap
++) {
694 if (strcmp(name
, ap
->attrname
) == 0) {
695 *ap
->attrvalp
= strtoull(value
, NULL
, 0);
696 fprintf(stderr
, "rbd-fuse: %s set to 0x%" PRIx64
"\n",
697 ap
->attrname
, *ap
->attrvalp
);
705 rbdfs_getxattr(const char *path
, const char *name
, char *value
,
712 struct rbdfuse_attr
*ap
;
714 // allow gets on other files; ls likes to ask for things like
717 for (ap
= attrs
; ap
->attrname
!= NULL
; ap
++) {
718 if (strcmp(name
, ap
->attrname
) == 0) {
719 sprintf(buf
, "%" PRIu64
, *ap
->attrvalp
);
720 if (value
!= NULL
&& size
>= strlen(buf
))
722 fprintf(stderr
, "rbd-fuse: get %s\n", ap
->attrname
);
723 return (strlen(buf
));
730 rbdfs_listxattr(const char *path
, char *list
, size_t len
)
732 struct rbdfuse_attr
*ap
;
733 size_t required_len
= 0;
735 if (strcmp(path
, "/") != 0)
738 for (ap
= attrs
; ap
->attrname
!= NULL
; ap
++)
739 required_len
+= strlen(ap
->attrname
) + 1;
740 if (len
>= required_len
) {
741 for (ap
= attrs
; ap
->attrname
!= NULL
; ap
++) {
742 sprintf(list
, "%s", ap
->attrname
);
743 list
+= strlen(ap
->attrname
) + 1;
749 const static struct fuse_operations rbdfs_oper
= {
750 getattr
: rbdfs_getattr
,
752 #if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
757 unlink
: rbdfs_unlink
,
760 rename
: rbdfs_rename
,
764 truncate
: rbdfs_truncate
,
765 #if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
771 statfs
: rbdfs_statfs
,
775 setxattr
: rbdfs_setxattr
,
776 getxattr
: rbdfs_getxattr
,
777 listxattr
: rbdfs_listxattr
,
779 opendir
: rbdfs_opendir
,
780 readdir
: rbdfs_readdir
,
781 releasedir
: rbdfs_releasedir
,
784 destroy
: rbdfs_destroy
,
786 create
: rbdfs_create
,
787 #if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
792 utimens
: rbdfs_utimens
,
793 /* skip unimplemented */
802 KEY_RADOS_POOLNAME_LONG
,
803 KEY_RADOS_NSPACENAME
,
804 KEY_RADOS_NSPACENAME_LONG
,
806 KEY_RBD_IMAGENAME_LONG
809 static struct fuse_opt rbdfs_opts
[] = {
810 FUSE_OPT_KEY("-h", KEY_HELP
),
811 FUSE_OPT_KEY("--help", KEY_HELP
),
812 FUSE_OPT_KEY("-V", KEY_VERSION
),
813 FUSE_OPT_KEY("--version", KEY_VERSION
),
814 {"-p %s", offsetof(struct rbd_options
, pool_name
), KEY_RADOS_POOLNAME
},
815 {"--poolname=%s", offsetof(struct rbd_options
, pool_name
),
816 KEY_RADOS_POOLNAME_LONG
},
817 {"-s %s", offsetof(struct rbd_options
, nspace_name
), KEY_RADOS_NSPACENAME
},
818 {"--namespace=%s", offsetof(struct rbd_options
, nspace_name
),
819 KEY_RADOS_NSPACENAME_LONG
},
820 {"-r %s", offsetof(struct rbd_options
, image_name
), KEY_RBD_IMAGENAME
},
821 {"--image=%s", offsetof(struct rbd_options
, image_name
),
822 KEY_RBD_IMAGENAME_LONG
},
826 static void usage(const char *progname
)
829 "Usage: %s mountpoint [options]\n"
832 " -h --help print help\n"
833 " -V --version print version\n"
834 " -c --conf ceph configuration file [/etc/ceph/ceph.conf]\n"
835 " -p --poolname rados pool name [rbd]\n"
836 " -s --namespace rados namespace name []\n"
837 " -r --image RBD image name\n"
841 static int rbdfs_opt_proc(void *data
, const char *arg
, int key
,
842 struct fuse_args
*outargs
)
844 if (key
== KEY_HELP
) {
845 usage(outargs
->argv
[0]);
846 fuse_opt_add_arg(outargs
, "-ho");
847 fuse_main(outargs
->argc
, outargs
->argv
, &rbdfs_oper
, NULL
);
851 if (key
== KEY_VERSION
) {
852 fuse_opt_add_arg(outargs
, "--version");
853 fuse_main(outargs
->argc
, outargs
->argv
, &rbdfs_oper
, NULL
);
857 if (key
== KEY_RADOS_POOLNAME
) {
858 if (rbd_options
.pool_name
!= NULL
) {
859 free(rbd_options
.pool_name
);
860 rbd_options
.pool_name
= NULL
;
862 rbd_options
.pool_name
= strdup(arg
+2);
866 if (key
== KEY_RADOS_NSPACENAME
) {
867 if (rbd_options
.nspace_name
!= NULL
) {
868 free(rbd_options
.nspace_name
);
869 rbd_options
.nspace_name
= NULL
;
871 rbd_options
.nspace_name
= strdup(arg
+2);
875 if (key
== KEY_RBD_IMAGENAME
) {
876 if (rbd_options
.image_name
!= NULL
) {
877 free(rbd_options
.image_name
);
878 rbd_options
.image_name
= NULL
;
880 rbd_options
.image_name
= strdup(arg
+2);
888 simple_err(const char *msg
, int err
)
890 fprintf(stderr
, "%s: %s\n", msg
, strerror(-err
));
895 connect_to_cluster(rados_t
*pcluster
)
898 global_init_postfork_start(g_ceph_context
);
899 common_init_finish(g_ceph_context
);
900 global_init_postfork_finish(g_ceph_context
);
902 r
= rados_create_with_context(pcluster
, g_ceph_context
);
904 simple_err("Could not create cluster handle", r
);
908 r
= rados_connect(*pcluster
);
910 simple_err("Error connecting to cluster", r
);
911 rados_shutdown(*pcluster
);
918 int main(int argc
, const char *argv
[])
920 memset(&rbd_image_data
, 0, sizeof(rbd_image_data
));
922 // librados will filter out -f/-d options from command-line
923 std::map
<std::string
, bool> filter_args
= {
927 std::vector
<const char*> arg_vector
;
928 for (auto idx
= 0; idx
< argc
; ++idx
) {
929 auto it
= filter_args
.find(argv
[idx
]);
930 if (it
!= filter_args
.end()) {
933 arg_vector
.push_back(argv
[idx
]);
936 auto cct
= global_init(NULL
, arg_vector
, CEPH_ENTITY_TYPE_CLIENT
,
937 CODE_ENVIRONMENT_DAEMON
,
938 CINIT_FLAG_UNPRIVILEGED_DAEMON_DEFAULTS
);
939 g_ceph_context
->_conf
.set_val_or_die("pid_file", "");
940 g_ceph_context
->_conf
.set_val_or_die("daemonize", "true");
942 if (global_init_prefork(g_ceph_context
) < 0) {
943 fprintf(stderr
, "Failed to initialize librados\n");
947 for (auto& it
: filter_args
) {
949 arg_vector
.push_back(it
.first
.c_str());
953 struct fuse_args args
= FUSE_ARGS_INIT((int)arg_vector
.size(),
954 (char**)&arg_vector
.front());
955 if (fuse_opt_parse(&args
, &rbd_options
, rbdfs_opts
,
956 rbdfs_opt_proc
) == -1) {
960 return fuse_main(args
.argc
, args
.argv
, &rbdfs_oper
, NULL
);