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"
36 #include "global/global_init.h"
37 #include "global/global_context.h"
39 static int gotrados
= 0;
42 char *mount_image_name
;
46 std::mutex readdir_lock
;
50 rbd_image_info_t rbd_info
;
61 struct rbd_image
*next
;
63 struct rbd_image_data
{
64 struct rbd_image
*images
;
65 rbd_image_spec_t
*image_specs
;
66 size_t image_spec_count
;
68 struct rbd_image_data rbd_image_data
;
70 struct rbd_openimage
{
73 struct rbd_stat rbd_stat
;
75 #define MAX_RBD_IMAGES 128
76 struct rbd_openimage opentbl
[MAX_RBD_IMAGES
];
78 struct rbd_options rbd_options
= {(char*) "rbd", (char*) "", NULL
};
80 #define rbdsize(fd) opentbl[fd].rbd_stat.rbd_info.size
81 #define rbdblksize(fd) opentbl[fd].rbd_stat.rbd_info.obj_size
82 #define rbdblkcnt(fd) opentbl[fd].rbd_stat.rbd_info.num_objs
84 uint64_t imagesize
= 1024ULL * 1024 * 1024;
85 uint64_t imageorder
= 22ULL;
86 uint64_t imagefeatures
= 1ULL;
88 // Minimize calls to rbd_list: marks bracketing of opendir/<ops>/releasedir
92 int connect_to_cluster(rados_t
*pcluster
);
93 void enumerate_images(struct rbd_image_data
*data
);
94 int open_rbd_image(const char *image_name
);
95 int find_openrbd(const char *path
);
97 void simple_err(const char *msg
, int err
);
100 enumerate_images(struct rbd_image_data
*data
)
102 struct rbd_image
**head
= &data
->images
;
103 struct rbd_image
*im
, *next
;
107 for (im
= *head
; im
!= NULL
;) {
113 rbd_image_spec_list_cleanup(data
->image_specs
,
114 data
->image_spec_count
);
115 free(data
->image_specs
);
116 data
->image_specs
= NULL
;
117 data
->image_spec_count
= 0;
121 ret
= rbd_list2(ioctx
, data
->image_specs
,
122 &data
->image_spec_count
);
123 if (ret
== -ERANGE
) {
124 data
->image_specs
= static_cast<rbd_image_spec_t
*>(
125 realloc(data
->image_specs
,
126 sizeof(rbd_image_spec_t
) * data
->image_spec_count
));
127 } else if (ret
< 0) {
128 simple_err("Failed to list images", ret
);
134 if (*nspace_name
!= '\0') {
135 fprintf(stderr
, "pool/namespace %s/%s: ", pool_name
, nspace_name
);
137 fprintf(stderr
, "pool %s: ", pool_name
);
139 for (size_t idx
= 0; idx
< data
->image_spec_count
; ++idx
) {
140 if ((mount_image_name
== NULL
) ||
141 ((strlen(mount_image_name
) > 0) &&
142 (strcmp(data
->image_specs
[idx
].name
, mount_image_name
) == 0))) {
143 fprintf(stderr
, "%s, ", data
->image_specs
[idx
].name
);
144 im
= static_cast<rbd_image
*>(malloc(sizeof(*im
)));
145 im
->image_name
= data
->image_specs
[idx
].name
;
150 fprintf(stderr
, "\n");
154 find_openrbd(const char *path
)
158 /* find in opentbl[] entry if already open */
159 for (i
= 0; i
< MAX_RBD_IMAGES
; i
++) {
160 if ((opentbl
[i
].image_name
!= NULL
) &&
161 (strcmp(opentbl
[i
].image_name
, path
) == 0)) {
169 open_rbd_image(const char *image_name
)
171 struct rbd_image
*im
;
172 struct rbd_openimage
*rbd
= NULL
;
175 if (image_name
== (char *)NULL
)
178 // relies on caller to keep rbd_image_data up to date
179 for (im
= rbd_image_data
.images
; im
!= NULL
; im
= im
->next
) {
180 if (strcmp(im
->image_name
, image_name
) == 0) {
187 /* find in opentbl[] entry if already open */
188 if ((fd
= find_openrbd(image_name
)) != -1) {
192 // allocate an opentbl[] and open the image
193 for (i
= 0; i
< MAX_RBD_IMAGES
; i
++) {
194 if (opentbl
[i
].image
== NULL
) {
197 rbd
->image_name
= strdup(image_name
);
201 if (i
== MAX_RBD_IMAGES
|| !rbd
)
203 int ret
= rbd_open(ioctx
, rbd
->image_name
, &(rbd
->image
), NULL
);
205 simple_err("open_rbd_image: can't open: ", ret
);
209 rbd_stat(rbd
->image
, &(rbd
->rbd_stat
.rbd_info
),
210 sizeof(rbd_image_info_t
));
211 rbd
->rbd_stat
.valid
= 1;
216 iter_images(void *cookie
,
217 void (*iter
)(void *cookie
, const char *image
))
219 struct rbd_image
*im
;
223 for (im
= rbd_image_data
.images
; im
!= NULL
; im
= im
->next
)
224 iter(cookie
, im
->image_name
);
225 readdir_lock
.unlock();
228 static void count_images_cb(void *cookie
, const char *image
)
230 (*((unsigned int *)cookie
))++;
233 static int count_images(void)
235 unsigned int count
= 0;
238 enumerate_images(&rbd_image_data
);
239 readdir_lock
.unlock();
241 iter_images(&count
, count_images_cb
);
247 static int rbdfs_getattr(const char *path
, struct stat
*stbuf
)
258 memset(stbuf
, 0, sizeof(struct stat
));
260 if (strcmp(path
, "/") == 0) {
263 stbuf
->st_mode
= S_IFDIR
+ 0755;
264 stbuf
->st_nlink
= 2+count_images();
265 stbuf
->st_uid
= getuid();
266 stbuf
->st_gid
= getgid();
267 stbuf
->st_size
= 1024;
268 stbuf
->st_blksize
= 1024;
269 stbuf
->st_blocks
= 1;
270 stbuf
->st_atime
= now
;
271 stbuf
->st_mtime
= now
;
272 stbuf
->st_ctime
= now
;
279 enumerate_images(&rbd_image_data
);
280 readdir_lock
.unlock();
282 fd
= open_rbd_image(path
+ 1);
287 stbuf
->st_mode
= S_IFREG
| 0666;
289 stbuf
->st_uid
= getuid();
290 stbuf
->st_gid
= getgid();
291 stbuf
->st_size
= rbdsize(fd
);
292 stbuf
->st_blksize
= rbdblksize(fd
);
293 stbuf
->st_blocks
= rbdblkcnt(fd
);
294 stbuf
->st_atime
= now
;
295 stbuf
->st_mtime
= now
;
296 stbuf
->st_ctime
= now
;
302 static int rbdfs_open(const char *path
, struct fuse_file_info
*fi
)
313 enumerate_images(&rbd_image_data
);
314 readdir_lock
.unlock();
315 fd
= open_rbd_image(path
+ 1);
323 static int rbdfs_read(const char *path
, char *buf
, size_t size
,
324 off_t offset
, struct fuse_file_info
*fi
)
327 struct rbd_openimage
*rbd
;
332 rbd
= &opentbl
[fi
->fh
];
337 ret
= rbd_read(rbd
->image
, offset
, size
, buf
);
350 static int rbdfs_write(const char *path
, const char *buf
, size_t size
,
351 off_t offset
, struct fuse_file_info
*fi
)
354 struct rbd_openimage
*rbd
;
359 rbd
= &opentbl
[fi
->fh
];
364 if ((size_t)(offset
+ size
) > rbdsize(fi
->fh
)) {
366 fprintf(stderr
, "rbdfs_write resizing %s to 0x%" PRIxMAX
"\n",
368 r
= rbd_resize(rbd
->image
, offset
+size
);
372 r
= rbd_stat(rbd
->image
, &(rbd
->rbd_stat
.rbd_info
),
373 sizeof(rbd_image_info_t
));
377 ret
= rbd_write(rbd
->image
, offset
, size
, buf
);
390 static void rbdfs_statfs_image_cb(void *num
, const char *image
)
394 ((uint64_t *)num
)[0]++;
396 fd
= open_rbd_image(image
);
398 ((uint64_t *)num
)[1] += rbdsize(fd
);
401 static int rbdfs_statfs(const char *path
, struct statvfs
*buf
)
411 enumerate_images(&rbd_image_data
);
412 readdir_lock
.unlock();
413 iter_images(num
, rbdfs_statfs_image_cb
);
415 #define RBDFS_BSIZE 4096
416 buf
->f_bsize
= RBDFS_BSIZE
;
417 buf
->f_frsize
= RBDFS_BSIZE
;
418 buf
->f_blocks
= num
[1] / RBDFS_BSIZE
;
421 buf
->f_files
= num
[0];
426 buf
->f_namemax
= PATH_MAX
;
431 static int rbdfs_fsync(const char *path
, int datasync
,
432 struct fuse_file_info
*fi
)
436 rbd_flush(opentbl
[fi
->fh
].image
);
440 static int rbdfs_opendir(const char *path
, struct fuse_file_info
*fi
)
442 // only one directory, so global "in_opendir" flag should be fine
445 enumerate_images(&rbd_image_data
);
446 readdir_lock
.unlock();
450 struct rbdfs_readdir_info
{
452 fuse_fill_dir_t filler
;
455 static void rbdfs_readdir_cb(void *_info
, const char *name
)
457 struct rbdfs_readdir_info
*info
= (struct rbdfs_readdir_info
*) _info
;
459 info
->filler(info
->buf
, name
, NULL
, 0);
462 static int rbdfs_readdir(const char *path
, void *buf
, fuse_fill_dir_t filler
,
463 off_t offset
, struct fuse_file_info
*fi
)
465 struct rbdfs_readdir_info info
= { buf
, filler
};
470 fprintf(stderr
, "in readdir, but not inside opendir?\n");
472 if (strcmp(path
, "/") != 0)
475 filler(buf
, ".", NULL
, 0);
476 filler(buf
, "..", NULL
, 0);
477 iter_images(&info
, rbdfs_readdir_cb
);
481 static int rbdfs_releasedir(const char *path
, struct fuse_file_info
*fi
)
483 // see opendir comments
486 readdir_lock
.unlock();
491 rbdfs_init(struct fuse_conn_info
*conn
)
495 // init cannot fail, so if we fail here, gotrados remains at 0,
496 // causing other operations to fail immediately with ENXIO
498 ret
= connect_to_cluster(&cluster
);
502 pool_name
= rbd_options
.pool_name
;
503 nspace_name
= rbd_options
.nspace_name
;
504 mount_image_name
= rbd_options
.image_name
;
505 ret
= rados_ioctx_create(cluster
, pool_name
, &ioctx
);
508 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
509 conn
->want
|= FUSE_CAP_BIG_WRITES
;
511 rados_ioctx_set_namespace(ioctx
, nspace_name
);
514 // init's return value shows up in fuse_context.private_data,
515 // also to void (*destroy)(void *); useful?
520 rbdfs_destroy(void *unused
)
524 for (int i
= 0; i
< MAX_RBD_IMAGES
; ++i
) {
525 if (opentbl
[i
].image
) {
526 rbd_close(opentbl
[i
].image
);
527 opentbl
[i
].image
= NULL
;
530 rados_ioctx_destroy(ioctx
);
531 rados_shutdown(cluster
);
535 rbdfs_checkname(const char *checkname
)
537 const char *extra
[] = {"@", "/"};
538 std::string
strCheckName(checkname
);
540 if (strCheckName
.empty())
543 unsigned int sz
= sizeof(extra
) / sizeof(const char*);
544 for (unsigned int i
= 0; i
< sz
; i
++)
546 std::string
ex(extra
[i
]);
547 if (std::string::npos
!= strCheckName
.find(ex
))
554 // return -errno on error. fi->fh is not set until open time
557 rbdfs_create(const char *path
, mode_t mode
, struct fuse_file_info
*fi
)
560 int order
= imageorder
;
562 r
= rbdfs_checkname(path
+1);
568 r
= rbd_create2(ioctx
, path
+1, imagesize
, imagefeatures
, &order
);
573 rbdfs_rename(const char *path
, const char *destname
)
577 r
= rbdfs_checkname(destname
+1);
583 if (strcmp(path
, "/") == 0)
586 return rbd_rename(ioctx
, path
+1, destname
+1);
590 rbdfs_utime(const char *path
, struct utimbuf
*utime
)
592 // called on create; not relevant
597 rbdfs_unlink(const char *path
)
599 int fd
= find_openrbd(path
+1);
601 struct rbd_openimage
*rbd
= &opentbl
[fd
];
602 rbd_close(rbd
->image
);
604 free(rbd
->image_name
);
605 rbd
->rbd_stat
.valid
= 0;
607 return rbd_remove(ioctx
, path
+1);
612 rbdfs_truncate(const char *path
, off_t size
)
616 struct rbd_openimage
*rbd
;
618 if ((fd
= open_rbd_image(path
+1)) < 0)
622 fprintf(stderr
, "truncate %s to %" PRIdMAX
" (0x%" PRIxMAX
")\n",
624 r
= rbd_resize(rbd
->image
, size
);
628 r
= rbd_stat(rbd
->image
, &(rbd
->rbd_stat
.rbd_info
),
629 sizeof(rbd_image_info_t
));
636 * set an xattr on path, with name/value, length size.
637 * Presumably flags are from Linux, as in XATTR_CREATE or
638 * XATTR_REPLACE (both "set", but fail if exist vs fail if not exist.
640 * We accept xattrs only on the root node.
642 * All values converted with strtoull, so can be expressed in any base
645 struct rbdfuse_attr
{
649 { (char*) "user.rbdfuse.imagesize", &imagesize
},
650 { (char*) "user.rbdfuse.imageorder", &imageorder
},
651 { (char*) "user.rbdfuse.imagefeatures", &imagefeatures
},
656 rbdfs_setxattr(const char *path
, const char *name
, const char *value
,
664 struct rbdfuse_attr
*ap
;
665 if (strcmp(path
, "/") != 0)
668 for (ap
= attrs
; ap
->attrname
!= NULL
; ap
++) {
669 if (strcmp(name
, ap
->attrname
) == 0) {
670 *ap
->attrvalp
= strtoull(value
, NULL
, 0);
671 fprintf(stderr
, "rbd-fuse: %s set to 0x%" PRIx64
"\n",
672 ap
->attrname
, *ap
->attrvalp
);
680 rbdfs_getxattr(const char *path
, const char *name
, char *value
,
687 struct rbdfuse_attr
*ap
;
689 // allow gets on other files; ls likes to ask for things like
692 for (ap
= attrs
; ap
->attrname
!= NULL
; ap
++) {
693 if (strcmp(name
, ap
->attrname
) == 0) {
694 sprintf(buf
, "%" PRIu64
, *ap
->attrvalp
);
695 if (value
!= NULL
&& size
>= strlen(buf
))
697 fprintf(stderr
, "rbd-fuse: get %s\n", ap
->attrname
);
698 return (strlen(buf
));
705 rbdfs_listxattr(const char *path
, char *list
, size_t len
)
707 struct rbdfuse_attr
*ap
;
708 size_t required_len
= 0;
710 if (strcmp(path
, "/") != 0)
713 for (ap
= attrs
; ap
->attrname
!= NULL
; ap
++)
714 required_len
+= strlen(ap
->attrname
) + 1;
715 if (len
>= required_len
) {
716 for (ap
= attrs
; ap
->attrname
!= NULL
; ap
++) {
717 sprintf(list
, "%s", ap
->attrname
);
718 list
+= strlen(ap
->attrname
) + 1;
724 const static struct fuse_operations rbdfs_oper
= {
725 getattr
: rbdfs_getattr
,
730 unlink
: rbdfs_unlink
,
733 rename
: rbdfs_rename
,
737 truncate
: rbdfs_truncate
,
742 statfs
: rbdfs_statfs
,
746 setxattr
: rbdfs_setxattr
,
747 getxattr
: rbdfs_getxattr
,
748 listxattr
: rbdfs_listxattr
,
750 opendir
: rbdfs_opendir
,
751 readdir
: rbdfs_readdir
,
752 releasedir
: rbdfs_releasedir
,
755 destroy
: rbdfs_destroy
,
757 create
: rbdfs_create
,
758 /* skip unimplemented */
767 KEY_RADOS_POOLNAME_LONG
,
768 KEY_RADOS_NSPACENAME
,
769 KEY_RADOS_NSPACENAME_LONG
,
771 KEY_RBD_IMAGENAME_LONG
774 static struct fuse_opt rbdfs_opts
[] = {
775 FUSE_OPT_KEY("-h", KEY_HELP
),
776 FUSE_OPT_KEY("--help", KEY_HELP
),
777 FUSE_OPT_KEY("-V", KEY_VERSION
),
778 FUSE_OPT_KEY("--version", KEY_VERSION
),
779 {"-p %s", offsetof(struct rbd_options
, pool_name
), KEY_RADOS_POOLNAME
},
780 {"--poolname=%s", offsetof(struct rbd_options
, pool_name
),
781 KEY_RADOS_POOLNAME_LONG
},
782 {"-s %s", offsetof(struct rbd_options
, nspace_name
), KEY_RADOS_NSPACENAME
},
783 {"--namespace=%s", offsetof(struct rbd_options
, nspace_name
),
784 KEY_RADOS_NSPACENAME_LONG
},
785 {"-r %s", offsetof(struct rbd_options
, image_name
), KEY_RBD_IMAGENAME
},
786 {"--image=%s", offsetof(struct rbd_options
, image_name
),
787 KEY_RBD_IMAGENAME_LONG
},
791 static void usage(const char *progname
)
794 "Usage: %s mountpoint [options]\n"
797 " -h --help print help\n"
798 " -V --version print version\n"
799 " -c --conf ceph configuration file [/etc/ceph/ceph.conf]\n"
800 " -p --poolname rados pool name [rbd]\n"
801 " -s --namespace rados namespace name []\n"
802 " -r --image RBD image name\n"
806 static int rbdfs_opt_proc(void *data
, const char *arg
, int key
,
807 struct fuse_args
*outargs
)
809 if (key
== KEY_HELP
) {
810 usage(outargs
->argv
[0]);
811 fuse_opt_add_arg(outargs
, "-ho");
812 fuse_main(outargs
->argc
, outargs
->argv
, &rbdfs_oper
, NULL
);
816 if (key
== KEY_VERSION
) {
817 fuse_opt_add_arg(outargs
, "--version");
818 fuse_main(outargs
->argc
, outargs
->argv
, &rbdfs_oper
, NULL
);
822 if (key
== KEY_RADOS_POOLNAME
) {
823 if (rbd_options
.pool_name
!= NULL
) {
824 free(rbd_options
.pool_name
);
825 rbd_options
.pool_name
= NULL
;
827 rbd_options
.pool_name
= strdup(arg
+2);
831 if (key
== KEY_RADOS_NSPACENAME
) {
832 if (rbd_options
.nspace_name
!= NULL
) {
833 free(rbd_options
.nspace_name
);
834 rbd_options
.nspace_name
= NULL
;
836 rbd_options
.nspace_name
= strdup(arg
+2);
840 if (key
== KEY_RBD_IMAGENAME
) {
841 if (rbd_options
.image_name
!= NULL
) {
842 free(rbd_options
.image_name
);
843 rbd_options
.image_name
= NULL
;
845 rbd_options
.image_name
= strdup(arg
+2);
853 simple_err(const char *msg
, int err
)
855 fprintf(stderr
, "%s: %s\n", msg
, strerror(-err
));
860 connect_to_cluster(rados_t
*pcluster
)
863 global_init_postfork_start(g_ceph_context
);
864 common_init_finish(g_ceph_context
);
865 global_init_postfork_finish(g_ceph_context
);
867 r
= rados_create_with_context(pcluster
, g_ceph_context
);
869 simple_err("Could not create cluster handle", r
);
873 r
= rados_connect(*pcluster
);
875 simple_err("Error connecting to cluster", r
);
876 rados_shutdown(*pcluster
);
883 int main(int argc
, const char *argv
[])
885 memset(&rbd_image_data
, 0, sizeof(rbd_image_data
));
887 // librados will filter out -f/-d options from command-line
888 std::map
<std::string
, bool> filter_args
= {
892 std::vector
<const char*> arg_vector
;
893 for (auto idx
= 0; idx
< argc
; ++idx
) {
894 auto it
= filter_args
.find(argv
[idx
]);
895 if (it
!= filter_args
.end()) {
898 arg_vector
.push_back(argv
[idx
]);
901 auto cct
= global_init(NULL
, arg_vector
, CEPH_ENTITY_TYPE_CLIENT
,
902 CODE_ENVIRONMENT_DAEMON
,
903 CINIT_FLAG_UNPRIVILEGED_DAEMON_DEFAULTS
);
904 g_ceph_context
->_conf
.set_val_or_die("pid_file", "");
905 g_ceph_context
->_conf
.set_val_or_die("daemonize", "true");
907 if (global_init_prefork(g_ceph_context
) < 0) {
908 fprintf(stderr
, "Failed to initialize librados\n");
912 for (auto& it
: filter_args
) {
914 arg_vector
.push_back(it
.first
.c_str());
918 struct fuse_args args
= FUSE_ARGS_INIT((int)arg_vector
.size(),
919 (char**)&arg_vector
.front());
920 if (fuse_opt_parse(&args
, &rbd_options
, rbdfs_opts
,
921 rbdfs_opt_proc
) == -1) {
925 return fuse_main(args
.argc
, args
.argv
, &rbdfs_oper
, NULL
);