From: Matt Benjamin Date: Thu, 1 Jan 2015 18:47:20 +0000 (-0500) Subject: Build rbd-fuse as a C++ unit (matching its existing linkage). X-Git-Tag: v0.93~265^2~22 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=b677a86fb22adfbe3beb6c152e5e6a47bdf27c27;p=ceph.git Build rbd-fuse as a C++ unit (matching its existing linkage). Since rbd-fuse is linking C++ libraries, link it with the C++ runtime as we already do for ceph-fuse. Signed-off-by: Matt Benjamin --- diff --git a/src/Makefile.am b/src/Makefile.am index 303e9bd50c2..b9064aea0d2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -98,7 +98,7 @@ ceph_fuse_SOURCES = ceph_fuse.cc ceph_fuse_LDADD = $(LIBCLIENT_FUSE) $(CEPH_GLOBAL) bin_PROGRAMS += ceph-fuse -rbd_fuse_SOURCES = rbd_fuse/rbd-fuse.c +rbd_fuse_SOURCES = rbd_fuse/rbd-fuse.cc rbd_fuse_LDADD = -lfuse $(LIBRBD) $(LIBRADOS) $(CEPH_GLOBAL) bin_PROGRAMS += rbd-fuse endif # WITH_FUSE diff --git a/src/rbd_fuse/rbd-fuse.c b/src/rbd_fuse/rbd-fuse.c deleted file mode 100644 index 4192c961380..00000000000 --- a/src/rbd_fuse/rbd-fuse.c +++ /dev/null @@ -1,793 +0,0 @@ -/* - * rbd-fuse - */ -#define FUSE_USE_VERSION 30 - -#include "include/int_types.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "include/rbd/librbd.h" - -static int gotrados = 0; -char *pool_name; -char *mount_image_name; -rados_t cluster; -rados_ioctx_t ioctx; - -static pthread_mutex_t readdir_lock; - -struct rbd_stat { - u_char valid; - rbd_image_info_t rbd_info; -}; - -struct rbd_options { - char *ceph_config; - char *pool_name; - char *image_name; -}; - -struct rbd_image { - char *image_name; - struct rbd_image *next; -}; -struct rbd_image_data { - struct rbd_image *images; - void *buf; -}; -struct rbd_image_data rbd_image_data; - -struct rbd_openimage { - char *image_name; - rbd_image_t image; - struct rbd_stat rbd_stat; -}; -#define MAX_RBD_IMAGES 128 -struct rbd_openimage opentbl[MAX_RBD_IMAGES]; - -struct rbd_options rbd_options = {"/etc/ceph/ceph.conf", "rbd", NULL}; - -#define rbdsize(fd) opentbl[fd].rbd_stat.rbd_info.size -#define rbdblksize(fd) opentbl[fd].rbd_stat.rbd_info.obj_size -#define rbdblkcnt(fd) opentbl[fd].rbd_stat.rbd_info.num_objs - -uint64_t imagesize = 1024ULL * 1024 * 1024; -uint64_t imageorder = 22ULL; -uint64_t imagefeatures = 1ULL; - -// Minimize calls to rbd_list: marks bracketing of opendir//releasedir -int in_opendir; - -/* prototypes */ -int connect_to_cluster(rados_t *pcluster); -void enumerate_images(struct rbd_image_data *data); -int open_rbd_image(const char *image_name); -int find_openrbd(const char *path); - -void simple_err(const char *msg, int err); - -void -enumerate_images(struct rbd_image_data *data) -{ - struct rbd_image **head = &data->images; - char *ibuf = NULL; - size_t ibuf_len = 0; - struct rbd_image *im, *next; - char *ip; - int ret; - - if (*head != NULL) { - for (im = *head; im != NULL;) { - next = im->next; - free(im); - im = next; - } - *head = NULL; - free(data->buf); - data->buf = NULL; - } - - ret = rbd_list(ioctx, ibuf, &ibuf_len); - if (ret == -ERANGE) { - assert(ibuf_len > 0); - ibuf = malloc(ibuf_len); - if (!ibuf) { - simple_err("Failed to get ibuf", -ENOMEM); - return; - } - } else if (ret < 0) { - simple_err("Failed to get ibuf_len", ret); - return; - } - - ret = rbd_list(ioctx, ibuf, &ibuf_len); - if (ret < 0) { - simple_err("Failed to populate ibuf", ret); - free(ibuf); - return; - } - assert(ret == (int)ibuf_len); - - fprintf(stderr, "pool %s: ", pool_name); - for (ip = ibuf; ip < &ibuf[ibuf_len]; ip += strlen(ip) + 1) { - if ((mount_image_name == NULL) || - ((strlen(mount_image_name) > 0) && - (strcmp(ip, mount_image_name) == 0))) { - fprintf(stderr, "%s, ", ip); - im = malloc(sizeof(*im)); - im->image_name = ip; - im->next = *head; - *head = im; - } - } - fprintf(stderr, "\n"); - data->buf = ibuf; -} - -int -find_openrbd(const char *path) -{ - int i; - - /* find in opentbl[] entry if already open */ - for (i = 0; i < MAX_RBD_IMAGES; i++) { - if ((opentbl[i].image_name != NULL) && - (strcmp(opentbl[i].image_name, path) == 0)) { - return i; - } - } - return -1; -} - -int -open_rbd_image(const char *image_name) -{ - struct rbd_image *im; - struct rbd_openimage *rbd = NULL; - int fd; - - if (image_name == (char *)NULL) - return -1; - - // relies on caller to keep rbd_image_data up to date - for (im = rbd_image_data.images; im != NULL; im = im->next) { - if (strcmp(im->image_name, image_name) == 0) { - break; - } - } - if (im == NULL) - return -1; - - /* find in opentbl[] entry if already open */ - if ((fd = find_openrbd(image_name)) != -1) { - rbd = &opentbl[fd]; - } else { - int i; - // allocate an opentbl[] and open the image - for (i = 0; i < MAX_RBD_IMAGES; i++) { - if (opentbl[i].image == NULL) { - fd = i; - rbd = &opentbl[fd]; - rbd->image_name = strdup(image_name); - break; - } - } - if (i == MAX_RBD_IMAGES || !rbd) - return -1; - int ret = rbd_open(ioctx, rbd->image_name, &(rbd->image), NULL); - if (ret < 0) { - simple_err("open_rbd_image: can't open: ", ret); - return ret; - } - } - rbd_stat(rbd->image, &(rbd->rbd_stat.rbd_info), - sizeof(rbd_image_info_t)); - rbd->rbd_stat.valid = 1; - return fd; -} - -static void -iter_images(void *cookie, - void (*iter)(void *cookie, const char *image)) -{ - struct rbd_image *im; - - pthread_mutex_lock(&readdir_lock); - - for (im = rbd_image_data.images; im != NULL; im = im->next) - iter(cookie, im->image_name); - pthread_mutex_unlock(&readdir_lock); -} - -static void count_images_cb(void *cookie, const char *image) -{ - (*((unsigned int *)cookie))++; -} - -static int count_images(void) -{ - unsigned int count = 0; - - pthread_mutex_lock(&readdir_lock); - enumerate_images(&rbd_image_data); - pthread_mutex_unlock(&readdir_lock); - - iter_images(&count, count_images_cb); - return count; -} - -static int rbdfs_getattr(const char *path, struct stat *stbuf) -{ - int fd; - time_t now; - - if (!gotrados) - return -ENXIO; - - if (path[0] == 0) - return -ENOENT; - - memset(stbuf, 0, sizeof(struct stat)); - - if (strcmp(path, "/") == 0) { - - now = time(NULL); - stbuf->st_mode = S_IFDIR + 0755; - stbuf->st_nlink = 2+count_images(); - stbuf->st_uid = getuid(); - stbuf->st_gid = getgid(); - stbuf->st_size = 1024; - stbuf->st_blksize = 1024; - stbuf->st_blocks = 1; - stbuf->st_atime = now; - stbuf->st_mtime = now; - stbuf->st_ctime = now; - - return 0; - } - - if (!in_opendir) { - pthread_mutex_lock(&readdir_lock); - enumerate_images(&rbd_image_data); - pthread_mutex_unlock(&readdir_lock); - } - fd = open_rbd_image(path + 1); - if (fd < 0) - return -ENOENT; - - now = time(NULL); - stbuf->st_mode = S_IFREG | 0666; - stbuf->st_nlink = 1; - stbuf->st_uid = getuid(); - stbuf->st_gid = getgid(); - stbuf->st_size = rbdsize(fd); - stbuf->st_blksize = rbdblksize(fd); - stbuf->st_blocks = rbdblkcnt(fd); - stbuf->st_atime = now; - stbuf->st_mtime = now; - stbuf->st_ctime = now; - - return 0; -} - - -static int rbdfs_open(const char *path, struct fuse_file_info *fi) -{ - int fd; - - if (!gotrados) - return -ENXIO; - - if (path[0] == 0) - return -ENOENT; - - pthread_mutex_lock(&readdir_lock); - enumerate_images(&rbd_image_data); - pthread_mutex_unlock(&readdir_lock); - fd = open_rbd_image(path + 1); - if (fd < 0) - return -ENOENT; - - fi->fh = fd; - return 0; -} - -static int rbdfs_read(const char *path, char *buf, size_t size, - off_t offset, struct fuse_file_info *fi) -{ - size_t numread; - struct rbd_openimage *rbd; - - if (!gotrados) - return -ENXIO; - - rbd = &opentbl[fi->fh]; - numread = 0; - while (size > 0) { - ssize_t ret; - - ret = rbd_read(rbd->image, offset, size, buf); - - if (ret <= 0) - break; - buf += ret; - size -= ret; - offset += ret; - numread += ret; - } - - return numread; -} - -static int rbdfs_write(const char *path, const char *buf, size_t size, - off_t offset, struct fuse_file_info *fi) -{ - size_t numwritten; - struct rbd_openimage *rbd; - - if (!gotrados) - return -ENXIO; - - rbd = &opentbl[fi->fh]; - numwritten = 0; - while (size > 0) { - ssize_t ret; - - if (offset + size > rbdsize(fi->fh)) { - int r; - fprintf(stderr, "rbdfs_write resizing %s to 0x%"PRIxMAX"\n", - path, offset+size); - r = rbd_resize(rbd->image, offset+size); - if (r < 0) - return r; - - r = rbd_stat(rbd->image, &(rbd->rbd_stat.rbd_info), - sizeof(rbd_image_info_t)); - if (r < 0) - return r; - } - ret = rbd_write(rbd->image, offset, size, buf); - - if (ret < 0) - break; - buf += ret; - size -= ret; - offset += ret; - numwritten += ret; - } - - return numwritten; -} - -static void rbdfs_statfs_image_cb(void *num, const char *image) -{ - int fd; - - ((uint64_t *)num)[0]++; - - fd = open_rbd_image(image); - if (fd >= 0) - ((uint64_t *)num)[1] += rbdsize(fd); -} - -static int rbdfs_statfs(const char *path, struct statvfs *buf) -{ - uint64_t num[2]; - - if (!gotrados) - return -ENXIO; - - num[0] = 1; - num[1] = 0; - pthread_mutex_lock(&readdir_lock); - enumerate_images(&rbd_image_data); - pthread_mutex_unlock(&readdir_lock); - iter_images(num, rbdfs_statfs_image_cb); - -#define RBDFS_BSIZE 4096 - buf->f_bsize = RBDFS_BSIZE; - buf->f_frsize = RBDFS_BSIZE; - buf->f_blocks = num[1] / RBDFS_BSIZE; - buf->f_bfree = 0; - buf->f_bavail = 0; - buf->f_files = num[0]; - buf->f_ffree = 0; - buf->f_favail = 0; - buf->f_fsid = 0; - buf->f_flag = 0; - buf->f_namemax = PATH_MAX; - - return 0; -} - -static int rbdfs_fsync(const char *path, int datasync, - struct fuse_file_info *fi) -{ - if (!gotrados) - return -ENXIO; - rbd_flush(opentbl[fi->fh].image); - return 0; -} - -static int rbdfs_opendir(const char *path, struct fuse_file_info *fi) -{ - // only one directory, so global "in_opendir" flag should be fine - pthread_mutex_lock(&readdir_lock); - in_opendir++; - enumerate_images(&rbd_image_data); - pthread_mutex_unlock(&readdir_lock); - return 0; -} - -struct rbdfs_readdir_info { - void *buf; - fuse_fill_dir_t filler; -}; - -static void rbdfs_readdir_cb(void *_info, const char *name) -{ - struct rbdfs_readdir_info *info = _info; - - info->filler(info->buf, name, NULL, 0); -} - -static int rbdfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi) -{ - struct rbdfs_readdir_info info = { buf, filler }; - - if (!gotrados) - return -ENXIO; - if (!in_opendir) - fprintf(stderr, "in readdir, but not inside opendir?\n"); - - if (strcmp(path, "/") != 0) - return -ENOENT; - - filler(buf, ".", NULL, 0); - filler(buf, "..", NULL, 0); - iter_images(&info, rbdfs_readdir_cb); - - return 0; -} -static int rbdfs_releasedir(const char *path, struct fuse_file_info *fi) -{ - // see opendir comments - pthread_mutex_lock(&readdir_lock); - in_opendir--; - pthread_mutex_unlock(&readdir_lock); - return 0; -} - -void * -rbdfs_init(struct fuse_conn_info *conn) -{ - int ret; - - // init cannot fail, so if we fail here, gotrados remains at 0, - // causing other operations to fail immediately with ENXIO - - ret = connect_to_cluster(&cluster); - if (ret < 0) - exit(90); - - pool_name = rbd_options.pool_name; - mount_image_name = rbd_options.image_name; - ret = rados_ioctx_create(cluster, pool_name, &ioctx); - if (ret < 0) - exit(91); -#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8) - conn->want |= FUSE_CAP_BIG_WRITES; -#endif - gotrados = 1; - - // init's return value shows up in fuse_context.private_data, - // also to void (*destroy)(void *); useful? - return NULL; -} - -// return -errno on error. fi->fh is not set until open time - -int -rbdfs_create(const char *path, mode_t mode, struct fuse_file_info *fi) -{ - int r; - int order = imageorder; - - r = rbd_create2(ioctx, path+1, imagesize, imagefeatures, &order); - return r; -} - -int -rbdfs_utime(const char *path, struct utimbuf *utime) -{ - // called on create; not relevant - return 0; -} - -int -rbdfs_unlink(const char *path) -{ - int fd = find_openrbd(path+1); - if (fd != -1) { - struct rbd_openimage *rbd = &opentbl[fd]; - rbd_close(rbd->image); - rbd->image = 0; - free(rbd->image_name); - rbd->rbd_stat.valid = 0; - } - return rbd_remove(ioctx, path+1); -} - - -int -rbdfs_truncate(const char *path, off_t size) -{ - int fd; - int r; - struct rbd_openimage *rbd; - - if ((fd = open_rbd_image(path+1)) < 0) - return -ENOENT; - - rbd = &opentbl[fd]; - fprintf(stderr, "truncate %s to %"PRIdMAX" (0x%"PRIxMAX")\n", path, size, size); - r = rbd_resize(rbd->image, size); - if (r < 0) - return r; - - r = rbd_stat(rbd->image, &(rbd->rbd_stat.rbd_info), - sizeof(rbd_image_info_t)); - if (r < 0) - return r; - return 0; -} - -/** - * set an xattr on path, with name/value, length size. - * Presumably flags are from Linux, as in XATTR_CREATE or - * XATTR_REPLACE (both "set", but fail if exist vs fail if not exist. - * - * We accept xattrs only on the root node. - * - * All values converted with strtoull, so can be expressed in any base - */ - -struct rbdfuse_attr { - char *attrname; - uint64_t *attrvalp; -} attrs[] = { - { "user.rbdfuse.imagesize", &imagesize }, - { "user.rbdfuse.imageorder", &imageorder }, - { "user.rbdfuse.imagefeatures", &imagefeatures }, - { NULL, NULL } -}; - -int -rbdfs_setxattr(const char *path, const char *name, const char *value, - size_t size, int flags) -{ - struct rbdfuse_attr *ap; - if (strcmp(path, "/") != 0) - return -EINVAL; - - for (ap = attrs; ap->attrname != NULL; ap++) { - if (strcmp(name, ap->attrname) == 0) { - *ap->attrvalp = strtoull(value, NULL, 0); - fprintf(stderr, "rbd-fuse: %s set to 0x%"PRIx64"\n", - ap->attrname, *ap->attrvalp); - return 0; - } - } - return -EINVAL; -} - -int -rbdfs_getxattr(const char *path, const char *name, char *value, - size_t size) -{ - struct rbdfuse_attr *ap; - char buf[128]; - // allow gets on other files; ls likes to ask for things like - // security.* - - for (ap = attrs; ap->attrname != NULL; ap++) { - if (strcmp(name, ap->attrname) == 0) { - sprintf(buf, "%"PRIu64, *ap->attrvalp); - if (value != NULL && size >= strlen(buf)) - strcpy(value, buf); - fprintf(stderr, "rbd-fuse: get %s\n", ap->attrname); - return (strlen(buf)); - } - } - return 0; -} - -int -rbdfs_listxattr(const char *path, char *list, size_t len) -{ - struct rbdfuse_attr *ap; - size_t required_len = 0; - - if (strcmp(path, "/") != 0) - return -EINVAL; - - for (ap = attrs; ap->attrname != NULL; ap++) - required_len += strlen(ap->attrname) + 1; - if (len >= required_len) { - for (ap = attrs; ap->attrname != NULL; ap++) { - sprintf(list, "%s", ap->attrname); - list += strlen(ap->attrname) + 1; - } - } - return required_len; -} - -static struct fuse_operations rbdfs_oper = { - .create = rbdfs_create, - .fsync = rbdfs_fsync, - .getattr = rbdfs_getattr, - .getxattr = rbdfs_getxattr, - .init = rbdfs_init, - .listxattr = rbdfs_listxattr, - .open = rbdfs_open, - .opendir = rbdfs_opendir, - .read = rbdfs_read, - .readdir = rbdfs_readdir, - .releasedir = rbdfs_releasedir, - .setxattr = rbdfs_setxattr, - .statfs = rbdfs_statfs, - .truncate = rbdfs_truncate, - .unlink = rbdfs_unlink, - .utime = rbdfs_utime, - .write = rbdfs_write, -}; - -enum { - KEY_HELP, - KEY_VERSION, - KEY_CEPH_CONFIG, - KEY_CEPH_CONFIG_LONG, - KEY_RADOS_POOLNAME, - KEY_RADOS_POOLNAME_LONG, - KEY_RBD_IMAGENAME, - KEY_RBD_IMAGENAME_LONG -}; - -static struct fuse_opt rbdfs_opts[] = { - FUSE_OPT_KEY("-h", KEY_HELP), - FUSE_OPT_KEY("--help", KEY_HELP), - FUSE_OPT_KEY("-V", KEY_VERSION), - FUSE_OPT_KEY("--version", KEY_VERSION), - {"-c %s", offsetof(struct rbd_options, ceph_config), KEY_CEPH_CONFIG}, - {"--configfile=%s", offsetof(struct rbd_options, ceph_config), - KEY_CEPH_CONFIG_LONG}, - {"-p %s", offsetof(struct rbd_options, pool_name), KEY_RADOS_POOLNAME}, - {"--poolname=%s", offsetof(struct rbd_options, pool_name), - KEY_RADOS_POOLNAME_LONG}, - {"-r %s", offsetof(struct rbd_options, image_name), KEY_RBD_IMAGENAME}, - {"--image=%s", offsetof(struct rbd_options, image_name), - KEY_RBD_IMAGENAME_LONG}, -}; - -static void usage(const char *progname) -{ - fprintf(stderr, -"Usage: %s mountpoint [options]\n" -"\n" -"General options:\n" -" -h --help print help\n" -" -V --version print version\n" -" -c --configfile ceph configuration file [/etc/ceph/ceph.conf]\n" -" -p --poolname rados pool name [rbd]\n" -" -r --image RBD image name\n" -"\n", progname); -} - -static int rbdfs_opt_proc(void *data, const char *arg, int key, - struct fuse_args *outargs) -{ - if (key == KEY_HELP) { - usage(outargs->argv[0]); - fuse_opt_add_arg(outargs, "-ho"); - fuse_main(outargs->argc, outargs->argv, &rbdfs_oper, NULL); - exit(1); - } - - if (key == KEY_VERSION) { - fuse_opt_add_arg(outargs, "--version"); - fuse_main(outargs->argc, outargs->argv, &rbdfs_oper, NULL); - exit(0); - } - - if (key == KEY_CEPH_CONFIG) { - if (rbd_options.ceph_config != NULL) { - free(rbd_options.ceph_config); - rbd_options.ceph_config = NULL; - } - rbd_options.ceph_config = strdup(arg+2); - return 0; - } - - if (key == KEY_RADOS_POOLNAME) { - if (rbd_options.pool_name != NULL) { - free(rbd_options.pool_name); - rbd_options.pool_name = NULL; - } - rbd_options.pool_name = strdup(arg+2); - return 0; - } - - if (key == KEY_RBD_IMAGENAME) { - if (rbd_options.image_name!= NULL) { - free(rbd_options.image_name); - rbd_options.image_name = NULL; - } - rbd_options.image_name = strdup(arg+2); - return 0; - } - - return 1; -} - -void -simple_err(const char *msg, int err) -{ - fprintf(stderr, "%s: %s\n", msg, strerror(-err)); - return; -} - -int -connect_to_cluster(rados_t *pcluster) -{ - int r; - - r = rados_create(pcluster, NULL); - if (r < 0) { - simple_err("Could not create cluster handle", r); - return r; - } - rados_conf_parse_env(*pcluster, NULL); - r = rados_conf_read_file(*pcluster, rbd_options.ceph_config); - if (r < 0) { - simple_err("Error reading Ceph config file", r); - goto failed_shutdown; - } - r = rados_connect(*pcluster); - if (r < 0) { - simple_err("Error connecting to cluster", r); - goto failed_shutdown; - } - - return 0; - -failed_shutdown: - rados_shutdown(*pcluster); - return r; -} - -int main(int argc, char *argv[]) -{ - struct fuse_args args = FUSE_ARGS_INIT(argc, argv); - - if (fuse_opt_parse(&args, &rbd_options, rbdfs_opts, rbdfs_opt_proc) == -1) { - exit(1); - } - - pthread_mutex_init(&readdir_lock, NULL); - - return fuse_main(args.argc, args.argv, &rbdfs_oper, NULL); -} diff --git a/src/rbd_fuse/rbd-fuse.cc b/src/rbd_fuse/rbd-fuse.cc new file mode 100644 index 00000000000..56e01975c7a --- /dev/null +++ b/src/rbd_fuse/rbd-fuse.cc @@ -0,0 +1,815 @@ +/* + * rbd-fuse + */ +#define FUSE_USE_VERSION 30 + +#include "include/int_types.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "include/rbd/librbd.h" + +static int gotrados = 0; +char *pool_name; +char *mount_image_name; +rados_t cluster; +rados_ioctx_t ioctx; + +static pthread_mutex_t readdir_lock; + +struct rbd_stat { + u_char valid; + rbd_image_info_t rbd_info; +}; + +struct rbd_options { + char *ceph_config; + char *pool_name; + char *image_name; +}; + +struct rbd_image { + char *image_name; + struct rbd_image *next; +}; +struct rbd_image_data { + struct rbd_image *images; + void *buf; +}; +struct rbd_image_data rbd_image_data; + +struct rbd_openimage { + char *image_name; + rbd_image_t image; + struct rbd_stat rbd_stat; +}; +#define MAX_RBD_IMAGES 128 +struct rbd_openimage opentbl[MAX_RBD_IMAGES]; + +struct rbd_options rbd_options = {(char*) "/etc/ceph/ceph.conf", (char*) "rbd", + NULL}; + +#define rbdsize(fd) opentbl[fd].rbd_stat.rbd_info.size +#define rbdblksize(fd) opentbl[fd].rbd_stat.rbd_info.obj_size +#define rbdblkcnt(fd) opentbl[fd].rbd_stat.rbd_info.num_objs + +uint64_t imagesize = 1024ULL * 1024 * 1024; +uint64_t imageorder = 22ULL; +uint64_t imagefeatures = 1ULL; + +// Minimize calls to rbd_list: marks bracketing of opendir//releasedir +int in_opendir; + +/* prototypes */ +int connect_to_cluster(rados_t *pcluster); +void enumerate_images(struct rbd_image_data *data); +int open_rbd_image(const char *image_name); +int find_openrbd(const char *path); + +void simple_err(const char *msg, int err); + +void +enumerate_images(struct rbd_image_data *data) +{ + struct rbd_image **head = &data->images; + char *ibuf = NULL; + size_t ibuf_len = 0; + struct rbd_image *im, *next; + char *ip; + int ret; + + if (*head != NULL) { + for (im = *head; im != NULL;) { + next = im->next; + free(im); + im = next; + } + *head = NULL; + free(data->buf); + data->buf = NULL; + } + + ret = rbd_list(ioctx, ibuf, &ibuf_len); + if (ret == -ERANGE) { + assert(ibuf_len > 0); + ibuf = (char*) malloc(ibuf_len); + if (!ibuf) { + simple_err("Failed to get ibuf", -ENOMEM); + return; + } + } else if (ret < 0) { + simple_err("Failed to get ibuf_len", ret); + return; + } + + ret = rbd_list(ioctx, ibuf, &ibuf_len); + if (ret < 0) { + simple_err("Failed to populate ibuf", ret); + free(ibuf); + return; + } + assert(ret == (int)ibuf_len); + + fprintf(stderr, "pool %s: ", pool_name); + for (ip = ibuf; ip < &ibuf[ibuf_len]; ip += strlen(ip) + 1) { + if ((mount_image_name == NULL) || + ((strlen(mount_image_name) > 0) && + (strcmp(ip, mount_image_name) == 0))) { + fprintf(stderr, "%s, ", ip); + im = (rbd_image*) malloc(sizeof(*im)); + im->image_name = ip; + im->next = *head; + *head = im; + } + } + fprintf(stderr, "\n"); + data->buf = ibuf; +} + +int +find_openrbd(const char *path) +{ + int i; + + /* find in opentbl[] entry if already open */ + for (i = 0; i < MAX_RBD_IMAGES; i++) { + if ((opentbl[i].image_name != NULL) && + (strcmp(opentbl[i].image_name, path) == 0)) { + return i; + } + } + return -1; +} + +int +open_rbd_image(const char *image_name) +{ + struct rbd_image *im; + struct rbd_openimage *rbd = NULL; + int fd; + + if (image_name == (char *)NULL) + return -1; + + // relies on caller to keep rbd_image_data up to date + for (im = rbd_image_data.images; im != NULL; im = im->next) { + if (strcmp(im->image_name, image_name) == 0) { + break; + } + } + if (im == NULL) + return -1; + + /* find in opentbl[] entry if already open */ + if ((fd = find_openrbd(image_name)) != -1) { + rbd = &opentbl[fd]; + } else { + int i; + // allocate an opentbl[] and open the image + for (i = 0; i < MAX_RBD_IMAGES; i++) { + if (opentbl[i].image == NULL) { + fd = i; + rbd = &opentbl[fd]; + rbd->image_name = strdup(image_name); + break; + } + } + if (i == MAX_RBD_IMAGES || !rbd) + return -1; + int ret = rbd_open(ioctx, rbd->image_name, &(rbd->image), NULL); + if (ret < 0) { + simple_err("open_rbd_image: can't open: ", ret); + return ret; + } + } + rbd_stat(rbd->image, &(rbd->rbd_stat.rbd_info), + sizeof(rbd_image_info_t)); + rbd->rbd_stat.valid = 1; + return fd; +} + +static void +iter_images(void *cookie, + void (*iter)(void *cookie, const char *image)) +{ + struct rbd_image *im; + + pthread_mutex_lock(&readdir_lock); + + for (im = rbd_image_data.images; im != NULL; im = im->next) + iter(cookie, im->image_name); + pthread_mutex_unlock(&readdir_lock); +} + +static void count_images_cb(void *cookie, const char *image) +{ + (*((unsigned int *)cookie))++; +} + +static int count_images(void) +{ + unsigned int count = 0; + + pthread_mutex_lock(&readdir_lock); + enumerate_images(&rbd_image_data); + pthread_mutex_unlock(&readdir_lock); + + iter_images(&count, count_images_cb); + return count; +} + +extern "C" { + +static int rbdfs_getattr(const char *path, struct stat *stbuf) +{ + int fd; + time_t now; + + if (!gotrados) + return -ENXIO; + + if (path[0] == 0) + return -ENOENT; + + memset(stbuf, 0, sizeof(struct stat)); + + if (strcmp(path, "/") == 0) { + + now = time(NULL); + stbuf->st_mode = S_IFDIR + 0755; + stbuf->st_nlink = 2+count_images(); + stbuf->st_uid = getuid(); + stbuf->st_gid = getgid(); + stbuf->st_size = 1024; + stbuf->st_blksize = 1024; + stbuf->st_blocks = 1; + stbuf->st_atime = now; + stbuf->st_mtime = now; + stbuf->st_ctime = now; + + return 0; + } + + if (!in_opendir) { + pthread_mutex_lock(&readdir_lock); + enumerate_images(&rbd_image_data); + pthread_mutex_unlock(&readdir_lock); + } + fd = open_rbd_image(path + 1); + if (fd < 0) + return -ENOENT; + + now = time(NULL); + stbuf->st_mode = S_IFREG | 0666; + stbuf->st_nlink = 1; + stbuf->st_uid = getuid(); + stbuf->st_gid = getgid(); + stbuf->st_size = rbdsize(fd); + stbuf->st_blksize = rbdblksize(fd); + stbuf->st_blocks = rbdblkcnt(fd); + stbuf->st_atime = now; + stbuf->st_mtime = now; + stbuf->st_ctime = now; + + return 0; +} + + +static int rbdfs_open(const char *path, struct fuse_file_info *fi) +{ + int fd; + + if (!gotrados) + return -ENXIO; + + if (path[0] == 0) + return -ENOENT; + + pthread_mutex_lock(&readdir_lock); + enumerate_images(&rbd_image_data); + pthread_mutex_unlock(&readdir_lock); + fd = open_rbd_image(path + 1); + if (fd < 0) + return -ENOENT; + + fi->fh = fd; + return 0; +} + +static int rbdfs_read(const char *path, char *buf, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + size_t numread; + struct rbd_openimage *rbd; + + if (!gotrados) + return -ENXIO; + + rbd = &opentbl[fi->fh]; + numread = 0; + while (size > 0) { + ssize_t ret; + + ret = rbd_read(rbd->image, offset, size, buf); + + if (ret <= 0) + break; + buf += ret; + size -= ret; + offset += ret; + numread += ret; + } + + return numread; +} + +static int rbdfs_write(const char *path, const char *buf, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + size_t numwritten; + struct rbd_openimage *rbd; + + if (!gotrados) + return -ENXIO; + + rbd = &opentbl[fi->fh]; + numwritten = 0; + while (size > 0) { + ssize_t ret; + + if (offset + size > rbdsize(fi->fh)) { + int r; + fprintf(stderr, "rbdfs_write resizing %s to 0x%"PRIxMAX"\n", + path, offset+size); + r = rbd_resize(rbd->image, offset+size); + if (r < 0) + return r; + + r = rbd_stat(rbd->image, &(rbd->rbd_stat.rbd_info), + sizeof(rbd_image_info_t)); + if (r < 0) + return r; + } + ret = rbd_write(rbd->image, offset, size, buf); + + if (ret < 0) + break; + buf += ret; + size -= ret; + offset += ret; + numwritten += ret; + } + + return numwritten; +} + +static void rbdfs_statfs_image_cb(void *num, const char *image) +{ + int fd; + + ((uint64_t *)num)[0]++; + + fd = open_rbd_image(image); + if (fd >= 0) + ((uint64_t *)num)[1] += rbdsize(fd); +} + +static int rbdfs_statfs(const char *path, struct statvfs *buf) +{ + uint64_t num[2]; + + if (!gotrados) + return -ENXIO; + + num[0] = 1; + num[1] = 0; + pthread_mutex_lock(&readdir_lock); + enumerate_images(&rbd_image_data); + pthread_mutex_unlock(&readdir_lock); + iter_images(num, rbdfs_statfs_image_cb); + +#define RBDFS_BSIZE 4096 + buf->f_bsize = RBDFS_BSIZE; + buf->f_frsize = RBDFS_BSIZE; + buf->f_blocks = num[1] / RBDFS_BSIZE; + buf->f_bfree = 0; + buf->f_bavail = 0; + buf->f_files = num[0]; + buf->f_ffree = 0; + buf->f_favail = 0; + buf->f_fsid = 0; + buf->f_flag = 0; + buf->f_namemax = PATH_MAX; + + return 0; +} + +static int rbdfs_fsync(const char *path, int datasync, + struct fuse_file_info *fi) +{ + if (!gotrados) + return -ENXIO; + rbd_flush(opentbl[fi->fh].image); + return 0; +} + +static int rbdfs_opendir(const char *path, struct fuse_file_info *fi) +{ + // only one directory, so global "in_opendir" flag should be fine + pthread_mutex_lock(&readdir_lock); + in_opendir++; + enumerate_images(&rbd_image_data); + pthread_mutex_unlock(&readdir_lock); + return 0; +} + +struct rbdfs_readdir_info { + void *buf; + fuse_fill_dir_t filler; +}; + +static void rbdfs_readdir_cb(void *_info, const char *name) +{ + struct rbdfs_readdir_info *info = (struct rbdfs_readdir_info*) _info; + + info->filler(info->buf, name, NULL, 0); +} + +static int rbdfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *fi) +{ + struct rbdfs_readdir_info info = { buf, filler }; + + if (!gotrados) + return -ENXIO; + if (!in_opendir) + fprintf(stderr, "in readdir, but not inside opendir?\n"); + + if (strcmp(path, "/") != 0) + return -ENOENT; + + filler(buf, ".", NULL, 0); + filler(buf, "..", NULL, 0); + iter_images(&info, rbdfs_readdir_cb); + + return 0; +} +static int rbdfs_releasedir(const char *path, struct fuse_file_info *fi) +{ + // see opendir comments + pthread_mutex_lock(&readdir_lock); + in_opendir--; + pthread_mutex_unlock(&readdir_lock); + return 0; +} + +void * +rbdfs_init(struct fuse_conn_info *conn) +{ + int ret; + + // init cannot fail, so if we fail here, gotrados remains at 0, + // causing other operations to fail immediately with ENXIO + + ret = connect_to_cluster(&cluster); + if (ret < 0) + exit(90); + + pool_name = rbd_options.pool_name; + mount_image_name = rbd_options.image_name; + ret = rados_ioctx_create(cluster, pool_name, &ioctx); + if (ret < 0) + exit(91); +#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8) + conn->want |= FUSE_CAP_BIG_WRITES; +#endif + gotrados = 1; + + // init's return value shows up in fuse_context.private_data, + // also to void (*destroy)(void *); useful? + return NULL; +} + +// return -errno on error. fi->fh is not set until open time + +int +rbdfs_create(const char *path, mode_t mode, struct fuse_file_info *fi) +{ + int r; + int order = imageorder; + + r = rbd_create2(ioctx, path+1, imagesize, imagefeatures, &order); + return r; +} + +int +rbdfs_utime(const char *path, struct utimbuf *utime) +{ + // called on create; not relevant + return 0; +} + +int +rbdfs_unlink(const char *path) +{ + int fd = find_openrbd(path+1); + if (fd != -1) { + struct rbd_openimage *rbd = &opentbl[fd]; + rbd_close(rbd->image); + rbd->image = 0; + free(rbd->image_name); + rbd->rbd_stat.valid = 0; + } + return rbd_remove(ioctx, path+1); +} + + +int +rbdfs_truncate(const char *path, off_t size) +{ + int fd; + int r; + struct rbd_openimage *rbd; + + if ((fd = open_rbd_image(path+1)) < 0) + return -ENOENT; + + rbd = &opentbl[fd]; + fprintf(stderr, "truncate %s to %"PRIdMAX" (0x%"PRIxMAX")\n", path, size, size); + r = rbd_resize(rbd->image, size); + if (r < 0) + return r; + + r = rbd_stat(rbd->image, &(rbd->rbd_stat.rbd_info), + sizeof(rbd_image_info_t)); + if (r < 0) + return r; + return 0; +} + +/** + * set an xattr on path, with name/value, length size. + * Presumably flags are from Linux, as in XATTR_CREATE or + * XATTR_REPLACE (both "set", but fail if exist vs fail if not exist. + * + * We accept xattrs only on the root node. + * + * All values converted with strtoull, so can be expressed in any base + */ + +struct rbdfuse_attr { + char *attrname; + uint64_t *attrvalp; +} attrs[] = { + { (char*) "user.rbdfuse.imagesize", &imagesize }, + { (char*) "user.rbdfuse.imageorder", &imageorder }, + { (char*) "user.rbdfuse.imagefeatures", &imagefeatures }, + { NULL, NULL } +}; + +int +rbdfs_setxattr(const char *path, const char *name, const char *value, + size_t size, int flags) +{ + struct rbdfuse_attr *ap; + if (strcmp(path, "/") != 0) + return -EINVAL; + + for (ap = attrs; ap->attrname != NULL; ap++) { + if (strcmp(name, ap->attrname) == 0) { + *ap->attrvalp = strtoull(value, NULL, 0); + fprintf(stderr, "rbd-fuse: %s set to 0x%"PRIx64"\n", + ap->attrname, *ap->attrvalp); + return 0; + } + } + return -EINVAL; +} + +int +rbdfs_getxattr(const char *path, const char *name, char *value, + size_t size) +{ + struct rbdfuse_attr *ap; + char buf[128]; + // allow gets on other files; ls likes to ask for things like + // security.* + + for (ap = attrs; ap->attrname != NULL; ap++) { + if (strcmp(name, ap->attrname) == 0) { + sprintf(buf, "%"PRIu64, *ap->attrvalp); + if (value != NULL && size >= strlen(buf)) + strcpy(value, buf); + fprintf(stderr, "rbd-fuse: get %s\n", ap->attrname); + return (strlen(buf)); + } + } + return 0; +} + +int +rbdfs_listxattr(const char *path, char *list, size_t len) +{ + struct rbdfuse_attr *ap; + size_t required_len = 0; + + if (strcmp(path, "/") != 0) + return -EINVAL; + + for (ap = attrs; ap->attrname != NULL; ap++) + required_len += strlen(ap->attrname) + 1; + if (len >= required_len) { + for (ap = attrs; ap->attrname != NULL; ap++) { + sprintf(list, "%s", ap->attrname); + list += strlen(ap->attrname) + 1; + } + } + return required_len; +} + +const static struct fuse_operations rbdfs_oper = { + getattr: rbdfs_getattr, + readlink: 0, + getdir: 0, + mknod: 0, + mkdir: 0, + unlink: rbdfs_unlink, + rmdir: 0, + symlink: 0, + rename: 0, + link: 0, + chmod: 0, + chown: 0, + truncate: rbdfs_truncate, + utime: rbdfs_utime, + open: rbdfs_open, + read: rbdfs_read, + write: rbdfs_write, + statfs: rbdfs_statfs, + flush: 0, + release: 0, + fsync: rbdfs_fsync, + setxattr: rbdfs_setxattr, + getxattr: rbdfs_getxattr, + listxattr: rbdfs_listxattr, + removexattr: 0, + opendir: rbdfs_opendir, + readdir: rbdfs_readdir, + releasedir: rbdfs_releasedir, + fsyncdir: 0, + init: rbdfs_init, + destroy: 0, + access: 0, + create: rbdfs_create, + /* skip unimplemented */ +}; + +} /* extern "C" */ + +enum { + KEY_HELP, + KEY_VERSION, + KEY_CEPH_CONFIG, + KEY_CEPH_CONFIG_LONG, + KEY_RADOS_POOLNAME, + KEY_RADOS_POOLNAME_LONG, + KEY_RBD_IMAGENAME, + KEY_RBD_IMAGENAME_LONG +}; + +static struct fuse_opt rbdfs_opts[] = { + FUSE_OPT_KEY("-h", KEY_HELP), + FUSE_OPT_KEY("--help", KEY_HELP), + FUSE_OPT_KEY("-V", KEY_VERSION), + FUSE_OPT_KEY("--version", KEY_VERSION), + {"-c %s", offsetof(struct rbd_options, ceph_config), KEY_CEPH_CONFIG}, + {"--configfile=%s", offsetof(struct rbd_options, ceph_config), + KEY_CEPH_CONFIG_LONG}, + {"-p %s", offsetof(struct rbd_options, pool_name), KEY_RADOS_POOLNAME}, + {"--poolname=%s", offsetof(struct rbd_options, pool_name), + KEY_RADOS_POOLNAME_LONG}, + {"-r %s", offsetof(struct rbd_options, image_name), KEY_RBD_IMAGENAME}, + {"--image=%s", offsetof(struct rbd_options, image_name), + KEY_RBD_IMAGENAME_LONG}, +}; + +static void usage(const char *progname) +{ + fprintf(stderr, +"Usage: %s mountpoint [options]\n" +"\n" +"General options:\n" +" -h --help print help\n" +" -V --version print version\n" +" -c --configfile ceph configuration file [/etc/ceph/ceph.conf]\n" +" -p --poolname rados pool name [rbd]\n" +" -r --image RBD image name\n" +"\n", progname); +} + +static int rbdfs_opt_proc(void *data, const char *arg, int key, + struct fuse_args *outargs) +{ + if (key == KEY_HELP) { + usage(outargs->argv[0]); + fuse_opt_add_arg(outargs, "-ho"); + fuse_main(outargs->argc, outargs->argv, &rbdfs_oper, NULL); + exit(1); + } + + if (key == KEY_VERSION) { + fuse_opt_add_arg(outargs, "--version"); + fuse_main(outargs->argc, outargs->argv, &rbdfs_oper, NULL); + exit(0); + } + + if (key == KEY_CEPH_CONFIG) { + if (rbd_options.ceph_config != NULL) { + free(rbd_options.ceph_config); + rbd_options.ceph_config = NULL; + } + rbd_options.ceph_config = strdup(arg+2); + return 0; + } + + if (key == KEY_RADOS_POOLNAME) { + if (rbd_options.pool_name != NULL) { + free(rbd_options.pool_name); + rbd_options.pool_name = NULL; + } + rbd_options.pool_name = strdup(arg+2); + return 0; + } + + if (key == KEY_RBD_IMAGENAME) { + if (rbd_options.image_name!= NULL) { + free(rbd_options.image_name); + rbd_options.image_name = NULL; + } + rbd_options.image_name = strdup(arg+2); + return 0; + } + + return 1; +} + +void +simple_err(const char *msg, int err) +{ + fprintf(stderr, "%s: %s\n", msg, strerror(-err)); + return; +} + +int +connect_to_cluster(rados_t *pcluster) +{ + int r; + + r = rados_create(pcluster, NULL); + if (r < 0) { + simple_err("Could not create cluster handle", r); + return r; + } + rados_conf_parse_env(*pcluster, NULL); + r = rados_conf_read_file(*pcluster, rbd_options.ceph_config); + if (r < 0) { + simple_err("Error reading Ceph config file", r); + goto failed_shutdown; + } + r = rados_connect(*pcluster); + if (r < 0) { + simple_err("Error connecting to cluster", r); + goto failed_shutdown; + } + + return 0; + +failed_shutdown: + rados_shutdown(*pcluster); + return r; +} + +int main(int argc, char *argv[]) +{ + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); + + if (fuse_opt_parse(&args, &rbd_options, rbdfs_opts, rbdfs_opt_proc) == -1) { + exit(1); + } + + pthread_mutex_init(&readdir_lock, NULL); + + return fuse_main(args.argc, args.argv, &rbdfs_oper, NULL); +}