4 #include "include/int_types.h"
14 #include <sys/types.h>
22 #if defined(__FreeBSD__)
23 #include <sys/param.h>
26 #include "include/compat.h"
27 #include "include/rbd/librbd.h"
28 #include "include/ceph_assert.h"
30 #include "common/ceph_argparse.h"
31 #include "common/ceph_context.h"
32 #include "include/ceph_fuse.h"
34 #include "global/global_init.h"
35 #include "global/global_context.h"
37 static int gotrados
= 0;
40 char *mount_image_name
;
44 std::mutex readdir_lock
;
48 rbd_image_info_t rbd_info
;
59 struct rbd_image
*next
;
61 struct rbd_image_data
{
62 struct rbd_image
*images
;
63 rbd_image_spec_t
*image_specs
;
64 size_t image_spec_count
;
66 struct rbd_image_data rbd_image_data
;
68 struct rbd_openimage
{
71 struct rbd_stat rbd_stat
;
73 #define MAX_RBD_IMAGES 128
74 struct rbd_openimage opentbl
[MAX_RBD_IMAGES
];
76 struct rbd_options rbd_options
= {(char*) "rbd", (char*) "", NULL
};
78 #define rbdsize(fd) opentbl[fd].rbd_stat.rbd_info.size
79 #define rbdblksize(fd) opentbl[fd].rbd_stat.rbd_info.obj_size
80 #define rbdblkcnt(fd) opentbl[fd].rbd_stat.rbd_info.num_objs
82 uint64_t imagesize
= 1024ULL * 1024 * 1024;
83 uint64_t imageorder
= 22ULL;
84 uint64_t imagefeatures
= 1ULL;
86 // Minimize calls to rbd_list: marks bracketing of opendir/<ops>/releasedir
90 int connect_to_cluster(rados_t
*pcluster
);
91 void enumerate_images(struct rbd_image_data
*data
);
92 int open_rbd_image(const char *image_name
);
93 int find_openrbd(const char *path
);
95 void simple_err(const char *msg
, int err
);
98 enumerate_images(struct rbd_image_data
*data
)
100 struct rbd_image
**head
= &data
->images
;
101 struct rbd_image
*im
, *next
;
105 for (im
= *head
; im
!= NULL
;) {
111 rbd_image_spec_list_cleanup(data
->image_specs
,
112 data
->image_spec_count
);
113 free(data
->image_specs
);
114 data
->image_specs
= NULL
;
115 data
->image_spec_count
= 0;
119 ret
= rbd_list2(ioctx
, data
->image_specs
,
120 &data
->image_spec_count
);
121 if (ret
== -ERANGE
) {
122 data
->image_specs
= static_cast<rbd_image_spec_t
*>(
123 realloc(data
->image_specs
,
124 sizeof(rbd_image_spec_t
) * data
->image_spec_count
));
125 } else if (ret
< 0) {
126 simple_err("Failed to list images", ret
);
132 if (*nspace_name
!= '\0') {
133 fprintf(stderr
, "pool/namespace %s/%s: ", pool_name
, nspace_name
);
135 fprintf(stderr
, "pool %s: ", pool_name
);
137 for (size_t idx
= 0; idx
< data
->image_spec_count
; ++idx
) {
138 if ((mount_image_name
== NULL
) ||
139 ((strlen(mount_image_name
) > 0) &&
140 (strcmp(data
->image_specs
[idx
].name
, mount_image_name
) == 0))) {
141 fprintf(stderr
, "%s, ", data
->image_specs
[idx
].name
);
142 im
= static_cast<rbd_image
*>(malloc(sizeof(*im
)));
143 im
->image_name
= data
->image_specs
[idx
].name
;
148 fprintf(stderr
, "\n");
152 find_openrbd(const char *path
)
156 /* find in opentbl[] entry if already open */
157 for (i
= 0; i
< MAX_RBD_IMAGES
; i
++) {
158 if ((opentbl
[i
].image_name
!= NULL
) &&
159 (strcmp(opentbl
[i
].image_name
, path
) == 0)) {
167 open_rbd_image(const char *image_name
)
169 struct rbd_image
*im
;
170 struct rbd_openimage
*rbd
= NULL
;
173 if (image_name
== (char *)NULL
)
176 // relies on caller to keep rbd_image_data up to date
177 for (im
= rbd_image_data
.images
; im
!= NULL
; im
= im
->next
) {
178 if (strcmp(im
->image_name
, image_name
) == 0) {
185 /* find in opentbl[] entry if already open */
186 if ((fd
= find_openrbd(image_name
)) != -1) {
190 // allocate an opentbl[] and open the image
191 for (i
= 0; i
< MAX_RBD_IMAGES
; i
++) {
192 if (opentbl
[i
].image
== NULL
) {
195 rbd
->image_name
= strdup(image_name
);
199 if (i
== MAX_RBD_IMAGES
|| !rbd
)
201 int ret
= rbd_open(ioctx
, rbd
->image_name
, &(rbd
->image
), NULL
);
203 simple_err("open_rbd_image: can't open: ", ret
);
207 rbd_stat(rbd
->image
, &(rbd
->rbd_stat
.rbd_info
),
208 sizeof(rbd_image_info_t
));
209 rbd
->rbd_stat
.valid
= 1;
214 iter_images(void *cookie
,
215 void (*iter
)(void *cookie
, const char *image
))
217 struct rbd_image
*im
;
221 for (im
= rbd_image_data
.images
; im
!= NULL
; im
= im
->next
)
222 iter(cookie
, im
->image_name
);
223 readdir_lock
.unlock();
226 static void count_images_cb(void *cookie
, const char *image
)
228 (*((unsigned int *)cookie
))++;
231 static int count_images(void)
233 unsigned int count
= 0;
236 enumerate_images(&rbd_image_data
);
237 readdir_lock
.unlock();
239 iter_images(&count
, count_images_cb
);
245 static int rbdfs_getattr(const char *path
, struct stat
*stbuf
246 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
247 , struct fuse_file_info
*fi
260 memset(stbuf
, 0, sizeof(struct stat
));
262 if (strcmp(path
, "/") == 0) {
265 stbuf
->st_mode
= S_IFDIR
+ 0755;
266 stbuf
->st_nlink
= 2+count_images();
267 stbuf
->st_uid
= getuid();
268 stbuf
->st_gid
= getgid();
269 stbuf
->st_size
= 1024;
270 stbuf
->st_blksize
= 1024;
271 stbuf
->st_blocks
= 1;
272 stbuf
->st_atime
= now
;
273 stbuf
->st_mtime
= now
;
274 stbuf
->st_ctime
= now
;
281 enumerate_images(&rbd_image_data
);
282 readdir_lock
.unlock();
284 fd
= open_rbd_image(path
+ 1);
289 stbuf
->st_mode
= S_IFREG
| 0666;
291 stbuf
->st_uid
= getuid();
292 stbuf
->st_gid
= getgid();
293 stbuf
->st_size
= rbdsize(fd
);
294 stbuf
->st_blksize
= rbdblksize(fd
);
295 stbuf
->st_blocks
= rbdblkcnt(fd
);
296 stbuf
->st_atime
= now
;
297 stbuf
->st_mtime
= now
;
298 stbuf
->st_ctime
= now
;
304 static int rbdfs_open(const char *path
, struct fuse_file_info
*fi
)
315 enumerate_images(&rbd_image_data
);
316 readdir_lock
.unlock();
317 fd
= open_rbd_image(path
+ 1);
325 static int rbdfs_read(const char *path
, char *buf
, size_t size
,
326 off_t offset
, struct fuse_file_info
*fi
)
329 struct rbd_openimage
*rbd
;
334 rbd
= &opentbl
[fi
->fh
];
339 ret
= rbd_read(rbd
->image
, offset
, size
, buf
);
352 static int rbdfs_write(const char *path
, const char *buf
, size_t size
,
353 off_t offset
, struct fuse_file_info
*fi
)
356 struct rbd_openimage
*rbd
;
361 rbd
= &opentbl
[fi
->fh
];
366 if ((size_t)(offset
+ size
) > rbdsize(fi
->fh
)) {
368 fprintf(stderr
, "rbdfs_write resizing %s to 0x%" PRIxMAX
"\n",
370 r
= rbd_resize(rbd
->image
, offset
+size
);
374 r
= rbd_stat(rbd
->image
, &(rbd
->rbd_stat
.rbd_info
),
375 sizeof(rbd_image_info_t
));
379 ret
= rbd_write(rbd
->image
, offset
, size
, buf
);
392 static void rbdfs_statfs_image_cb(void *num
, const char *image
)
396 ((uint64_t *)num
)[0]++;
398 fd
= open_rbd_image(image
);
400 ((uint64_t *)num
)[1] += rbdsize(fd
);
403 static int rbdfs_statfs(const char *path
, struct statvfs
*buf
)
413 enumerate_images(&rbd_image_data
);
414 readdir_lock
.unlock();
415 iter_images(num
, rbdfs_statfs_image_cb
);
417 #define RBDFS_BSIZE 4096
418 buf
->f_bsize
= RBDFS_BSIZE
;
419 buf
->f_frsize
= RBDFS_BSIZE
;
420 buf
->f_blocks
= num
[1] / RBDFS_BSIZE
;
423 buf
->f_files
= num
[0];
428 buf
->f_namemax
= PATH_MAX
;
433 static int rbdfs_fsync(const char *path
, int datasync
,
434 struct fuse_file_info
*fi
)
438 rbd_flush(opentbl
[fi
->fh
].image
);
442 static int rbdfs_opendir(const char *path
, struct fuse_file_info
*fi
)
444 // only one directory, so global "in_opendir" flag should be fine
447 enumerate_images(&rbd_image_data
);
448 readdir_lock
.unlock();
452 struct rbdfs_readdir_info
{
454 fuse_fill_dir_t filler
;
457 static void rbdfs_readdir_cb(void *_info
, const char *name
)
459 struct rbdfs_readdir_info
*info
= (struct rbdfs_readdir_info
*) _info
;
461 filler_compat(info
->filler
, info
->buf
, name
, NULL
, 0);
464 static int rbdfs_readdir(const char *path
, void *buf
, fuse_fill_dir_t filler
,
465 off_t offset
, struct fuse_file_info
*fi
466 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
467 , enum fuse_readdir_flags
471 struct rbdfs_readdir_info info
= { buf
, filler
};
476 fprintf(stderr
, "in readdir, but not inside opendir?\n");
478 if (strcmp(path
, "/") != 0)
481 filler_compat(filler
, buf
, ".", NULL
, 0);
482 filler_compat(filler
, buf
, "..", NULL
, 0);
483 iter_images(&info
, rbdfs_readdir_cb
);
487 static int rbdfs_releasedir(const char *path
, struct fuse_file_info
*fi
)
489 // see opendir comments
492 readdir_lock
.unlock();
497 rbdfs_init(struct fuse_conn_info
*conn
498 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
499 , struct fuse_config
*cfg
505 // init cannot fail, so if we fail here, gotrados remains at 0,
506 // causing other operations to fail immediately with ENXIO
508 ret
= connect_to_cluster(&cluster
);
512 pool_name
= rbd_options
.pool_name
;
513 nspace_name
= rbd_options
.nspace_name
;
514 mount_image_name
= rbd_options
.image_name
;
515 ret
= rados_ioctx_create(cluster
, pool_name
, &ioctx
);
518 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8) && FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
519 conn
->want
|= FUSE_CAP_BIG_WRITES
;
521 rados_ioctx_set_namespace(ioctx
, nspace_name
);
524 // init's return value shows up in fuse_context.private_data,
525 // also to void (*destroy)(void *); useful?
530 rbdfs_destroy(void *unused
)
534 for (int i
= 0; i
< MAX_RBD_IMAGES
; ++i
) {
535 if (opentbl
[i
].image
) {
536 rbd_close(opentbl
[i
].image
);
537 opentbl
[i
].image
= NULL
;
540 rados_ioctx_destroy(ioctx
);
541 rados_shutdown(cluster
);
545 rbdfs_checkname(const char *checkname
)
547 const char *extra
[] = {"@", "/"};
548 std::string
strCheckName(checkname
);
550 if (strCheckName
.empty())
553 unsigned int sz
= sizeof(extra
) / sizeof(const char*);
554 for (unsigned int i
= 0; i
< sz
; i
++)
556 std::string
ex(extra
[i
]);
557 if (std::string::npos
!= strCheckName
.find(ex
))
564 // return -errno on error. fi->fh is not set until open time
567 rbdfs_create(const char *path
, mode_t mode
, struct fuse_file_info
*fi
)
570 int order
= imageorder
;
572 r
= rbdfs_checkname(path
+1);
578 r
= rbd_create2(ioctx
, path
+1, imagesize
, imagefeatures
, &order
);
583 rbdfs_rename(const char *path
, const char *destname
584 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
591 r
= rbdfs_checkname(destname
+1);
597 if (strcmp(path
, "/") == 0)
600 return rbd_rename(ioctx
, path
+1, destname
+1);
604 rbdfs_utimens(const char *path
, const struct timespec tv
[2]
605 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
606 , struct fuse_file_info
*fi
610 // called on create; not relevant
615 rbdfs_unlink(const char *path
)
617 int fd
= find_openrbd(path
+1);
619 struct rbd_openimage
*rbd
= &opentbl
[fd
];
620 rbd_close(rbd
->image
);
622 free(rbd
->image_name
);
623 rbd
->rbd_stat
.valid
= 0;
625 return rbd_remove(ioctx
, path
+1);
630 rbdfs_truncate(const char *path
, off_t size
631 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
632 , struct fuse_file_info
*fi
638 struct rbd_openimage
*rbd
;
640 if ((fd
= open_rbd_image(path
+1)) < 0)
644 fprintf(stderr
, "truncate %s to %" PRIdMAX
" (0x%" PRIxMAX
")\n",
646 r
= rbd_resize(rbd
->image
, size
);
650 r
= rbd_stat(rbd
->image
, &(rbd
->rbd_stat
.rbd_info
),
651 sizeof(rbd_image_info_t
));
658 * set an xattr on path, with name/value, length size.
659 * Presumably flags are from Linux, as in XATTR_CREATE or
660 * XATTR_REPLACE (both "set", but fail if exist vs fail if not exist.
662 * We accept xattrs only on the root node.
664 * All values converted with strtoull, so can be expressed in any base
667 struct rbdfuse_attr
{
671 { (char*) "user.rbdfuse.imagesize", &imagesize
},
672 { (char*) "user.rbdfuse.imageorder", &imageorder
},
673 { (char*) "user.rbdfuse.imagefeatures", &imagefeatures
},
678 rbdfs_setxattr(const char *path
, const char *name
, const char *value
,
686 struct rbdfuse_attr
*ap
;
687 if (strcmp(path
, "/") != 0)
690 for (ap
= attrs
; ap
->attrname
!= NULL
; ap
++) {
691 if (strcmp(name
, ap
->attrname
) == 0) {
692 *ap
->attrvalp
= strtoull(value
, NULL
, 0);
693 fprintf(stderr
, "rbd-fuse: %s set to 0x%" PRIx64
"\n",
694 ap
->attrname
, *ap
->attrvalp
);
702 rbdfs_getxattr(const char *path
, const char *name
, char *value
,
709 struct rbdfuse_attr
*ap
;
711 // allow gets on other files; ls likes to ask for things like
714 for (ap
= attrs
; ap
->attrname
!= NULL
; ap
++) {
715 if (strcmp(name
, ap
->attrname
) == 0) {
716 sprintf(buf
, "%" PRIu64
, *ap
->attrvalp
);
717 if (value
!= NULL
&& size
>= strlen(buf
))
719 fprintf(stderr
, "rbd-fuse: get %s\n", ap
->attrname
);
720 return (strlen(buf
));
727 rbdfs_listxattr(const char *path
, char *list
, size_t len
)
729 struct rbdfuse_attr
*ap
;
730 size_t required_len
= 0;
732 if (strcmp(path
, "/") != 0)
735 for (ap
= attrs
; ap
->attrname
!= NULL
; ap
++)
736 required_len
+= strlen(ap
->attrname
) + 1;
737 if (len
>= required_len
) {
738 for (ap
= attrs
; ap
->attrname
!= NULL
; ap
++) {
739 sprintf(list
, "%s", ap
->attrname
);
740 list
+= strlen(ap
->attrname
) + 1;
746 const static struct fuse_operations rbdfs_oper
= {
747 getattr
: rbdfs_getattr
,
749 #if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
754 unlink
: rbdfs_unlink
,
757 rename
: rbdfs_rename
,
761 truncate
: rbdfs_truncate
,
762 #if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
768 statfs
: rbdfs_statfs
,
772 setxattr
: rbdfs_setxattr
,
773 getxattr
: rbdfs_getxattr
,
774 listxattr
: rbdfs_listxattr
,
776 opendir
: rbdfs_opendir
,
777 readdir
: rbdfs_readdir
,
778 releasedir
: rbdfs_releasedir
,
781 destroy
: rbdfs_destroy
,
783 create
: rbdfs_create
,
784 #if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
789 utimens
: rbdfs_utimens
,
790 /* skip unimplemented */
799 KEY_RADOS_POOLNAME_LONG
,
800 KEY_RADOS_NSPACENAME
,
801 KEY_RADOS_NSPACENAME_LONG
,
803 KEY_RBD_IMAGENAME_LONG
806 static struct fuse_opt rbdfs_opts
[] = {
807 FUSE_OPT_KEY("-h", KEY_HELP
),
808 FUSE_OPT_KEY("--help", KEY_HELP
),
809 FUSE_OPT_KEY("-V", KEY_VERSION
),
810 FUSE_OPT_KEY("--version", KEY_VERSION
),
811 {"-p %s", offsetof(struct rbd_options
, pool_name
), KEY_RADOS_POOLNAME
},
812 {"--poolname=%s", offsetof(struct rbd_options
, pool_name
),
813 KEY_RADOS_POOLNAME_LONG
},
814 {"-s %s", offsetof(struct rbd_options
, nspace_name
), KEY_RADOS_NSPACENAME
},
815 {"--namespace=%s", offsetof(struct rbd_options
, nspace_name
),
816 KEY_RADOS_NSPACENAME_LONG
},
817 {"-r %s", offsetof(struct rbd_options
, image_name
), KEY_RBD_IMAGENAME
},
818 {"--image=%s", offsetof(struct rbd_options
, image_name
),
819 KEY_RBD_IMAGENAME_LONG
},
823 static void usage(const char *progname
)
826 "Usage: %s mountpoint [options]\n"
829 " -h --help print help\n"
830 " -V --version print version\n"
831 " -c --conf ceph configuration file [/etc/ceph/ceph.conf]\n"
832 " -p --poolname rados pool name [rbd]\n"
833 " -s --namespace rados namespace name []\n"
834 " -r --image RBD image name\n"
838 static int rbdfs_opt_proc(void *data
, const char *arg
, int key
,
839 struct fuse_args
*outargs
)
841 if (key
== KEY_HELP
) {
842 usage(outargs
->argv
[0]);
843 fuse_opt_add_arg(outargs
, "-ho");
844 fuse_main(outargs
->argc
, outargs
->argv
, &rbdfs_oper
, NULL
);
848 if (key
== KEY_VERSION
) {
849 fuse_opt_add_arg(outargs
, "--version");
850 fuse_main(outargs
->argc
, outargs
->argv
, &rbdfs_oper
, NULL
);
854 if (key
== KEY_RADOS_POOLNAME
) {
855 if (rbd_options
.pool_name
!= NULL
) {
856 free(rbd_options
.pool_name
);
857 rbd_options
.pool_name
= NULL
;
859 rbd_options
.pool_name
= strdup(arg
+2);
863 if (key
== KEY_RADOS_NSPACENAME
) {
864 if (rbd_options
.nspace_name
!= NULL
) {
865 free(rbd_options
.nspace_name
);
866 rbd_options
.nspace_name
= NULL
;
868 rbd_options
.nspace_name
= strdup(arg
+2);
872 if (key
== KEY_RBD_IMAGENAME
) {
873 if (rbd_options
.image_name
!= NULL
) {
874 free(rbd_options
.image_name
);
875 rbd_options
.image_name
= NULL
;
877 rbd_options
.image_name
= strdup(arg
+2);
885 simple_err(const char *msg
, int err
)
887 fprintf(stderr
, "%s: %s\n", msg
, strerror(-err
));
892 connect_to_cluster(rados_t
*pcluster
)
895 global_init_postfork_start(g_ceph_context
);
896 common_init_finish(g_ceph_context
);
897 global_init_postfork_finish(g_ceph_context
);
899 r
= rados_create_with_context(pcluster
, g_ceph_context
);
901 simple_err("Could not create cluster handle", r
);
905 r
= rados_connect(*pcluster
);
907 simple_err("Error connecting to cluster", r
);
908 rados_shutdown(*pcluster
);
915 int main(int argc
, const char *argv
[])
917 memset(&rbd_image_data
, 0, sizeof(rbd_image_data
));
919 // librados will filter out -f/-d options from command-line
920 std::map
<std::string
, bool> filter_args
= {
924 std::vector
<const char*> arg_vector
;
925 for (auto idx
= 0; idx
< argc
; ++idx
) {
926 auto it
= filter_args
.find(argv
[idx
]);
927 if (it
!= filter_args
.end()) {
930 arg_vector
.push_back(argv
[idx
]);
933 auto cct
= global_init(NULL
, arg_vector
, CEPH_ENTITY_TYPE_CLIENT
,
934 CODE_ENVIRONMENT_DAEMON
,
935 CINIT_FLAG_UNPRIVILEGED_DAEMON_DEFAULTS
);
936 g_ceph_context
->_conf
.set_val_or_die("pid_file", "");
937 g_ceph_context
->_conf
.set_val_or_die("daemonize", "true");
939 if (global_init_prefork(g_ceph_context
) < 0) {
940 fprintf(stderr
, "Failed to initialize librados\n");
944 for (auto& it
: filter_args
) {
946 arg_vector
.push_back(it
.first
.c_str());
950 struct fuse_args args
= FUSE_ARGS_INIT((int)arg_vector
.size(),
951 (char**)&arg_vector
.front());
952 if (fuse_opt_parse(&args
, &rbd_options
, rbdfs_opts
,
953 rbdfs_opt_proc
) == -1) {
957 return fuse_main(args
.argc
, args
.argv
, &rbdfs_oper
, NULL
);