]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /* |
2 | * rbd-fuse | |
3 | */ | |
4 | #define FUSE_USE_VERSION 30 | |
5 | ||
6 | #include "include/int_types.h" | |
7 | ||
8 | #include <stdio.h> | |
9 | #include <stdlib.h> | |
10 | #include <stddef.h> | |
11 | #include <dirent.h> | |
12 | #include <errno.h> | |
13 | #include <fcntl.h> | |
14 | #include <fuse.h> | |
15 | #include <pthread.h> | |
16 | #include <string.h> | |
17 | #include <sys/types.h> | |
18 | #include <unistd.h> | |
19 | #include <getopt.h> | |
20 | #include <assert.h> | |
21 | #include <string> | |
d2e6a577 | 22 | #include <mutex> |
7c673cae FG |
23 | |
24 | #if defined(__FreeBSD__) | |
25 | #include <sys/param.h> | |
26 | #endif | |
27 | ||
28 | #include "include/compat.h" | |
29 | #include "include/rbd/librbd.h" | |
7c673cae FG |
30 | |
31 | static int gotrados = 0; | |
32 | char *pool_name; | |
33 | char *mount_image_name; | |
34 | rados_t cluster; | |
35 | rados_ioctx_t ioctx; | |
36 | ||
d2e6a577 | 37 | std::mutex readdir_lock; |
7c673cae FG |
38 | |
39 | struct rbd_stat { | |
40 | u_char valid; | |
41 | rbd_image_info_t rbd_info; | |
42 | }; | |
43 | ||
44 | struct rbd_options { | |
45 | char *ceph_config; | |
46 | char *pool_name; | |
47 | char *image_name; | |
48 | }; | |
49 | ||
50 | struct rbd_image { | |
51 | char *image_name; | |
52 | struct rbd_image *next; | |
53 | }; | |
54 | struct rbd_image_data { | |
55 | struct rbd_image *images; | |
56 | void *buf; | |
57 | }; | |
58 | struct rbd_image_data rbd_image_data; | |
59 | ||
60 | struct rbd_openimage { | |
61 | char *image_name; | |
62 | rbd_image_t image; | |
63 | struct rbd_stat rbd_stat; | |
64 | }; | |
65 | #define MAX_RBD_IMAGES 128 | |
66 | struct rbd_openimage opentbl[MAX_RBD_IMAGES]; | |
67 | ||
68 | struct rbd_options rbd_options = {(char*) "/etc/ceph/ceph.conf", (char*) "rbd", | |
69 | NULL}; | |
70 | ||
71 | #define rbdsize(fd) opentbl[fd].rbd_stat.rbd_info.size | |
72 | #define rbdblksize(fd) opentbl[fd].rbd_stat.rbd_info.obj_size | |
73 | #define rbdblkcnt(fd) opentbl[fd].rbd_stat.rbd_info.num_objs | |
74 | ||
75 | uint64_t imagesize = 1024ULL * 1024 * 1024; | |
76 | uint64_t imageorder = 22ULL; | |
77 | uint64_t imagefeatures = 1ULL; | |
78 | ||
79 | // Minimize calls to rbd_list: marks bracketing of opendir/<ops>/releasedir | |
80 | int in_opendir; | |
81 | ||
82 | /* prototypes */ | |
83 | int connect_to_cluster(rados_t *pcluster); | |
84 | void enumerate_images(struct rbd_image_data *data); | |
85 | int open_rbd_image(const char *image_name); | |
86 | int find_openrbd(const char *path); | |
87 | ||
88 | void simple_err(const char *msg, int err); | |
89 | ||
90 | void | |
91 | enumerate_images(struct rbd_image_data *data) | |
92 | { | |
93 | struct rbd_image **head = &data->images; | |
94 | char *ibuf = NULL; | |
95 | size_t ibuf_len = 0; | |
96 | struct rbd_image *im, *next; | |
97 | char *ip; | |
98 | int ret; | |
99 | ||
100 | if (*head != NULL) { | |
101 | for (im = *head; im != NULL;) { | |
102 | next = im->next; | |
103 | free(im); | |
104 | im = next; | |
105 | } | |
106 | *head = NULL; | |
107 | free(data->buf); | |
108 | data->buf = NULL; | |
109 | } | |
110 | ||
111 | ret = rbd_list(ioctx, ibuf, &ibuf_len); | |
112 | if (ret == -ERANGE) { | |
113 | assert(ibuf_len > 0); | |
114 | ibuf = (char*) malloc(ibuf_len); | |
115 | if (!ibuf) { | |
116 | simple_err("Failed to get ibuf", -ENOMEM); | |
117 | return; | |
118 | } | |
119 | } else if (ret < 0) { | |
120 | simple_err("Failed to get ibuf_len", ret); | |
121 | return; | |
122 | } | |
123 | ||
124 | ret = rbd_list(ioctx, ibuf, &ibuf_len); | |
125 | if (ret < 0) { | |
126 | simple_err("Failed to populate ibuf", ret); | |
127 | free(ibuf); | |
128 | return; | |
129 | } | |
130 | assert(ret == (int)ibuf_len); | |
131 | ||
132 | fprintf(stderr, "pool %s: ", pool_name); | |
133 | for (ip = ibuf; ip < &ibuf[ibuf_len]; ip += strlen(ip) + 1) { | |
134 | if ((mount_image_name == NULL) || | |
135 | ((strlen(mount_image_name) > 0) && | |
136 | (strcmp(ip, mount_image_name) == 0))) { | |
137 | fprintf(stderr, "%s, ", ip); | |
138 | im = static_cast<rbd_image*>(malloc(sizeof(*im))); | |
139 | im->image_name = ip; | |
140 | im->next = *head; | |
141 | *head = im; | |
142 | } | |
143 | } | |
144 | fprintf(stderr, "\n"); | |
145 | data->buf = ibuf; | |
146 | } | |
147 | ||
148 | int | |
149 | find_openrbd(const char *path) | |
150 | { | |
151 | int i; | |
152 | ||
153 | /* find in opentbl[] entry if already open */ | |
154 | for (i = 0; i < MAX_RBD_IMAGES; i++) { | |
155 | if ((opentbl[i].image_name != NULL) && | |
156 | (strcmp(opentbl[i].image_name, path) == 0)) { | |
157 | return i; | |
158 | } | |
159 | } | |
160 | return -1; | |
161 | } | |
162 | ||
163 | int | |
164 | open_rbd_image(const char *image_name) | |
165 | { | |
166 | struct rbd_image *im; | |
167 | struct rbd_openimage *rbd = NULL; | |
168 | int fd; | |
169 | ||
170 | if (image_name == (char *)NULL) | |
171 | return -1; | |
172 | ||
173 | // relies on caller to keep rbd_image_data up to date | |
174 | for (im = rbd_image_data.images; im != NULL; im = im->next) { | |
175 | if (strcmp(im->image_name, image_name) == 0) { | |
176 | break; | |
177 | } | |
178 | } | |
179 | if (im == NULL) | |
180 | return -1; | |
181 | ||
182 | /* find in opentbl[] entry if already open */ | |
183 | if ((fd = find_openrbd(image_name)) != -1) { | |
184 | rbd = &opentbl[fd]; | |
185 | } else { | |
186 | int i; | |
187 | // allocate an opentbl[] and open the image | |
188 | for (i = 0; i < MAX_RBD_IMAGES; i++) { | |
189 | if (opentbl[i].image == NULL) { | |
190 | fd = i; | |
191 | rbd = &opentbl[fd]; | |
192 | rbd->image_name = strdup(image_name); | |
193 | break; | |
194 | } | |
195 | } | |
196 | if (i == MAX_RBD_IMAGES || !rbd) | |
197 | return -1; | |
198 | int ret = rbd_open(ioctx, rbd->image_name, &(rbd->image), NULL); | |
199 | if (ret < 0) { | |
200 | simple_err("open_rbd_image: can't open: ", ret); | |
201 | return ret; | |
202 | } | |
203 | } | |
204 | rbd_stat(rbd->image, &(rbd->rbd_stat.rbd_info), | |
205 | sizeof(rbd_image_info_t)); | |
206 | rbd->rbd_stat.valid = 1; | |
207 | return fd; | |
208 | } | |
209 | ||
210 | static void | |
211 | iter_images(void *cookie, | |
212 | void (*iter)(void *cookie, const char *image)) | |
213 | { | |
214 | struct rbd_image *im; | |
215 | ||
d2e6a577 FG |
216 | readdir_lock.lock(); |
217 | ||
7c673cae FG |
218 | for (im = rbd_image_data.images; im != NULL; im = im->next) |
219 | iter(cookie, im->image_name); | |
d2e6a577 | 220 | readdir_lock.unlock(); |
7c673cae FG |
221 | } |
222 | ||
223 | static void count_images_cb(void *cookie, const char *image) | |
224 | { | |
225 | (*((unsigned int *)cookie))++; | |
226 | } | |
227 | ||
228 | static int count_images(void) | |
229 | { | |
230 | unsigned int count = 0; | |
231 | ||
d2e6a577 | 232 | readdir_lock.lock(); |
7c673cae | 233 | enumerate_images(&rbd_image_data); |
d2e6a577 | 234 | readdir_lock.unlock(); |
7c673cae FG |
235 | |
236 | iter_images(&count, count_images_cb); | |
237 | return count; | |
238 | } | |
239 | ||
240 | extern "C" { | |
241 | ||
242 | static int rbdfs_getattr(const char *path, struct stat *stbuf) | |
243 | { | |
244 | int fd; | |
245 | time_t now; | |
246 | ||
247 | if (!gotrados) | |
248 | return -ENXIO; | |
249 | ||
250 | if (path[0] == 0) | |
251 | return -ENOENT; | |
252 | ||
253 | memset(stbuf, 0, sizeof(struct stat)); | |
254 | ||
255 | if (strcmp(path, "/") == 0) { | |
256 | ||
257 | now = time(NULL); | |
258 | stbuf->st_mode = S_IFDIR + 0755; | |
259 | stbuf->st_nlink = 2+count_images(); | |
260 | stbuf->st_uid = getuid(); | |
261 | stbuf->st_gid = getgid(); | |
262 | stbuf->st_size = 1024; | |
263 | stbuf->st_blksize = 1024; | |
264 | stbuf->st_blocks = 1; | |
265 | stbuf->st_atime = now; | |
266 | stbuf->st_mtime = now; | |
267 | stbuf->st_ctime = now; | |
268 | ||
269 | return 0; | |
270 | } | |
271 | ||
272 | if (!in_opendir) { | |
d2e6a577 | 273 | readdir_lock.lock(); |
7c673cae | 274 | enumerate_images(&rbd_image_data); |
d2e6a577 | 275 | readdir_lock.unlock(); |
7c673cae FG |
276 | } |
277 | fd = open_rbd_image(path + 1); | |
278 | if (fd < 0) | |
279 | return -ENOENT; | |
280 | ||
281 | now = time(NULL); | |
282 | stbuf->st_mode = S_IFREG | 0666; | |
283 | stbuf->st_nlink = 1; | |
284 | stbuf->st_uid = getuid(); | |
285 | stbuf->st_gid = getgid(); | |
286 | stbuf->st_size = rbdsize(fd); | |
287 | stbuf->st_blksize = rbdblksize(fd); | |
288 | stbuf->st_blocks = rbdblkcnt(fd); | |
289 | stbuf->st_atime = now; | |
290 | stbuf->st_mtime = now; | |
291 | stbuf->st_ctime = now; | |
292 | ||
293 | return 0; | |
294 | } | |
295 | ||
296 | ||
297 | static int rbdfs_open(const char *path, struct fuse_file_info *fi) | |
298 | { | |
299 | int fd; | |
300 | ||
301 | if (!gotrados) | |
302 | return -ENXIO; | |
303 | ||
304 | if (path[0] == 0) | |
305 | return -ENOENT; | |
306 | ||
d2e6a577 | 307 | readdir_lock.lock(); |
7c673cae | 308 | enumerate_images(&rbd_image_data); |
d2e6a577 | 309 | readdir_lock.unlock(); |
7c673cae FG |
310 | fd = open_rbd_image(path + 1); |
311 | if (fd < 0) | |
312 | return -ENOENT; | |
313 | ||
314 | fi->fh = fd; | |
315 | return 0; | |
316 | } | |
317 | ||
318 | static int rbdfs_read(const char *path, char *buf, size_t size, | |
319 | off_t offset, struct fuse_file_info *fi) | |
320 | { | |
321 | size_t numread; | |
322 | struct rbd_openimage *rbd; | |
323 | ||
324 | if (!gotrados) | |
325 | return -ENXIO; | |
326 | ||
327 | rbd = &opentbl[fi->fh]; | |
328 | numread = 0; | |
329 | while (size > 0) { | |
330 | ssize_t ret; | |
331 | ||
332 | ret = rbd_read(rbd->image, offset, size, buf); | |
333 | ||
334 | if (ret <= 0) | |
335 | break; | |
336 | buf += ret; | |
337 | size -= ret; | |
338 | offset += ret; | |
339 | numread += ret; | |
340 | } | |
341 | ||
342 | return numread; | |
343 | } | |
344 | ||
345 | static int rbdfs_write(const char *path, const char *buf, size_t size, | |
346 | off_t offset, struct fuse_file_info *fi) | |
347 | { | |
348 | size_t numwritten; | |
349 | struct rbd_openimage *rbd; | |
350 | ||
351 | if (!gotrados) | |
352 | return -ENXIO; | |
353 | ||
354 | rbd = &opentbl[fi->fh]; | |
355 | numwritten = 0; | |
356 | while (size > 0) { | |
357 | ssize_t ret; | |
358 | ||
359 | if ((size_t)(offset + size) > rbdsize(fi->fh)) { | |
360 | int r; | |
361 | fprintf(stderr, "rbdfs_write resizing %s to 0x%" PRIxMAX "\n", | |
362 | path, offset+size); | |
363 | r = rbd_resize(rbd->image, offset+size); | |
364 | if (r < 0) | |
365 | return r; | |
366 | ||
367 | r = rbd_stat(rbd->image, &(rbd->rbd_stat.rbd_info), | |
368 | sizeof(rbd_image_info_t)); | |
369 | if (r < 0) | |
370 | return r; | |
371 | } | |
372 | ret = rbd_write(rbd->image, offset, size, buf); | |
373 | ||
374 | if (ret < 0) | |
375 | break; | |
376 | buf += ret; | |
377 | size -= ret; | |
378 | offset += ret; | |
379 | numwritten += ret; | |
380 | } | |
381 | ||
382 | return numwritten; | |
383 | } | |
384 | ||
385 | static void rbdfs_statfs_image_cb(void *num, const char *image) | |
386 | { | |
387 | int fd; | |
388 | ||
389 | ((uint64_t *)num)[0]++; | |
390 | ||
391 | fd = open_rbd_image(image); | |
392 | if (fd >= 0) | |
393 | ((uint64_t *)num)[1] += rbdsize(fd); | |
394 | } | |
395 | ||
396 | static int rbdfs_statfs(const char *path, struct statvfs *buf) | |
397 | { | |
398 | uint64_t num[2]; | |
399 | ||
400 | if (!gotrados) | |
401 | return -ENXIO; | |
402 | ||
403 | num[0] = 1; | |
404 | num[1] = 0; | |
d2e6a577 | 405 | readdir_lock.lock(); |
7c673cae | 406 | enumerate_images(&rbd_image_data); |
d2e6a577 | 407 | readdir_lock.unlock(); |
7c673cae FG |
408 | iter_images(num, rbdfs_statfs_image_cb); |
409 | ||
410 | #define RBDFS_BSIZE 4096 | |
411 | buf->f_bsize = RBDFS_BSIZE; | |
412 | buf->f_frsize = RBDFS_BSIZE; | |
413 | buf->f_blocks = num[1] / RBDFS_BSIZE; | |
414 | buf->f_bfree = 0; | |
415 | buf->f_bavail = 0; | |
416 | buf->f_files = num[0]; | |
417 | buf->f_ffree = 0; | |
418 | buf->f_favail = 0; | |
419 | buf->f_fsid = 0; | |
420 | buf->f_flag = 0; | |
421 | buf->f_namemax = PATH_MAX; | |
422 | ||
423 | return 0; | |
424 | } | |
425 | ||
426 | static int rbdfs_fsync(const char *path, int datasync, | |
427 | struct fuse_file_info *fi) | |
428 | { | |
429 | if (!gotrados) | |
430 | return -ENXIO; | |
431 | rbd_flush(opentbl[fi->fh].image); | |
432 | return 0; | |
433 | } | |
434 | ||
435 | static int rbdfs_opendir(const char *path, struct fuse_file_info *fi) | |
436 | { | |
437 | // only one directory, so global "in_opendir" flag should be fine | |
d2e6a577 | 438 | readdir_lock.lock(); |
7c673cae FG |
439 | in_opendir++; |
440 | enumerate_images(&rbd_image_data); | |
d2e6a577 | 441 | readdir_lock.unlock(); |
7c673cae FG |
442 | return 0; |
443 | } | |
444 | ||
445 | struct rbdfs_readdir_info { | |
446 | void *buf; | |
447 | fuse_fill_dir_t filler; | |
448 | }; | |
449 | ||
450 | static void rbdfs_readdir_cb(void *_info, const char *name) | |
451 | { | |
452 | struct rbdfs_readdir_info *info = (struct rbdfs_readdir_info*) _info; | |
453 | ||
454 | info->filler(info->buf, name, NULL, 0); | |
455 | } | |
456 | ||
457 | static int rbdfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, | |
458 | off_t offset, struct fuse_file_info *fi) | |
459 | { | |
460 | struct rbdfs_readdir_info info = { buf, filler }; | |
461 | ||
462 | if (!gotrados) | |
463 | return -ENXIO; | |
464 | if (!in_opendir) | |
465 | fprintf(stderr, "in readdir, but not inside opendir?\n"); | |
466 | ||
467 | if (strcmp(path, "/") != 0) | |
468 | return -ENOENT; | |
469 | ||
470 | filler(buf, ".", NULL, 0); | |
471 | filler(buf, "..", NULL, 0); | |
472 | iter_images(&info, rbdfs_readdir_cb); | |
473 | ||
474 | return 0; | |
475 | } | |
476 | static int rbdfs_releasedir(const char *path, struct fuse_file_info *fi) | |
477 | { | |
478 | // see opendir comments | |
d2e6a577 | 479 | readdir_lock.lock(); |
7c673cae | 480 | in_opendir--; |
d2e6a577 | 481 | readdir_lock.unlock(); |
7c673cae FG |
482 | return 0; |
483 | } | |
484 | ||
485 | void * | |
486 | rbdfs_init(struct fuse_conn_info *conn) | |
487 | { | |
488 | int ret; | |
489 | ||
490 | // init cannot fail, so if we fail here, gotrados remains at 0, | |
491 | // causing other operations to fail immediately with ENXIO | |
492 | ||
493 | ret = connect_to_cluster(&cluster); | |
494 | if (ret < 0) | |
495 | exit(90); | |
496 | ||
497 | pool_name = rbd_options.pool_name; | |
498 | mount_image_name = rbd_options.image_name; | |
499 | ret = rados_ioctx_create(cluster, pool_name, &ioctx); | |
500 | if (ret < 0) | |
501 | exit(91); | |
502 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8) | |
503 | conn->want |= FUSE_CAP_BIG_WRITES; | |
504 | #endif | |
505 | gotrados = 1; | |
506 | ||
507 | // init's return value shows up in fuse_context.private_data, | |
508 | // also to void (*destroy)(void *); useful? | |
509 | return NULL; | |
510 | } | |
511 | ||
512 | void | |
513 | rbdfs_destroy(void *unused) | |
514 | { | |
515 | if (!gotrados) | |
516 | return; | |
517 | for (int i = 0; i < MAX_RBD_IMAGES; ++i) { | |
518 | if (opentbl[i].image) { | |
519 | rbd_close(opentbl[i].image); | |
520 | opentbl[i].image = NULL; | |
521 | } | |
522 | } | |
523 | rados_ioctx_destroy(ioctx); | |
524 | rados_shutdown(cluster); | |
525 | } | |
526 | ||
527 | int | |
528 | rbdfs_checkname(const char *checkname) | |
529 | { | |
530 | const char *extra[] = {"@", "/"}; | |
531 | std::string strCheckName(checkname); | |
532 | ||
533 | if (strCheckName.empty()) | |
534 | return -EINVAL; | |
535 | ||
536 | unsigned int sz = sizeof(extra) / sizeof(const char*); | |
537 | for (unsigned int i = 0; i < sz; i++) | |
538 | { | |
539 | std::string ex(extra[i]); | |
540 | if (std::string::npos != strCheckName.find(ex)) | |
541 | return -EINVAL; | |
542 | } | |
543 | ||
544 | return 0; | |
545 | } | |
546 | ||
547 | // return -errno on error. fi->fh is not set until open time | |
548 | ||
549 | int | |
550 | rbdfs_create(const char *path, mode_t mode, struct fuse_file_info *fi) | |
551 | { | |
552 | int r; | |
553 | int order = imageorder; | |
554 | ||
555 | r = rbdfs_checkname(path+1); | |
556 | if (r != 0) | |
557 | { | |
558 | return r; | |
559 | } | |
560 | ||
561 | r = rbd_create2(ioctx, path+1, imagesize, imagefeatures, &order); | |
562 | return r; | |
563 | } | |
564 | ||
565 | int | |
566 | rbdfs_rename(const char *path, const char *destname) | |
567 | { | |
568 | int r; | |
569 | ||
570 | r = rbdfs_checkname(destname+1); | |
571 | if (r != 0) | |
572 | { | |
573 | return r; | |
574 | } | |
575 | ||
576 | if (strcmp(path, "/") == 0) | |
577 | return -EINVAL; | |
578 | ||
579 | return rbd_rename(ioctx, path+1, destname+1); | |
580 | } | |
581 | ||
582 | int | |
583 | rbdfs_utime(const char *path, struct utimbuf *utime) | |
584 | { | |
585 | // called on create; not relevant | |
586 | return 0; | |
587 | } | |
588 | ||
589 | int | |
590 | rbdfs_unlink(const char *path) | |
591 | { | |
592 | int fd = find_openrbd(path+1); | |
593 | if (fd != -1) { | |
594 | struct rbd_openimage *rbd = &opentbl[fd]; | |
595 | rbd_close(rbd->image); | |
596 | rbd->image = 0; | |
597 | free(rbd->image_name); | |
598 | rbd->rbd_stat.valid = 0; | |
599 | } | |
600 | return rbd_remove(ioctx, path+1); | |
601 | } | |
602 | ||
603 | ||
604 | int | |
605 | rbdfs_truncate(const char *path, off_t size) | |
606 | { | |
607 | int fd; | |
608 | int r; | |
609 | struct rbd_openimage *rbd; | |
610 | ||
611 | if ((fd = open_rbd_image(path+1)) < 0) | |
612 | return -ENOENT; | |
613 | ||
614 | rbd = &opentbl[fd]; | |
615 | fprintf(stderr, "truncate %s to %" PRIdMAX " (0x%" PRIxMAX ")\n", | |
616 | path, size, size); | |
617 | r = rbd_resize(rbd->image, size); | |
618 | if (r < 0) | |
619 | return r; | |
620 | ||
621 | r = rbd_stat(rbd->image, &(rbd->rbd_stat.rbd_info), | |
622 | sizeof(rbd_image_info_t)); | |
623 | if (r < 0) | |
624 | return r; | |
625 | return 0; | |
626 | } | |
627 | ||
628 | /** | |
629 | * set an xattr on path, with name/value, length size. | |
630 | * Presumably flags are from Linux, as in XATTR_CREATE or | |
631 | * XATTR_REPLACE (both "set", but fail if exist vs fail if not exist. | |
632 | * | |
633 | * We accept xattrs only on the root node. | |
634 | * | |
635 | * All values converted with strtoull, so can be expressed in any base | |
636 | */ | |
637 | ||
638 | struct rbdfuse_attr { | |
639 | char *attrname; | |
640 | uint64_t *attrvalp; | |
641 | } attrs[] = { | |
642 | { (char*) "user.rbdfuse.imagesize", &imagesize }, | |
643 | { (char*) "user.rbdfuse.imageorder", &imageorder }, | |
644 | { (char*) "user.rbdfuse.imagefeatures", &imagefeatures }, | |
645 | { NULL, NULL } | |
646 | }; | |
647 | ||
648 | int | |
649 | rbdfs_setxattr(const char *path, const char *name, const char *value, | |
650 | size_t size, | |
651 | int flags | |
652 | #if defined(DARWIN) | |
653 | ,uint32_t pos | |
654 | #endif | |
655 | ) | |
656 | { | |
657 | struct rbdfuse_attr *ap; | |
658 | if (strcmp(path, "/") != 0) | |
659 | return -EINVAL; | |
660 | ||
661 | for (ap = attrs; ap->attrname != NULL; ap++) { | |
662 | if (strcmp(name, ap->attrname) == 0) { | |
663 | *ap->attrvalp = strtoull(value, NULL, 0); | |
664 | fprintf(stderr, "rbd-fuse: %s set to 0x%" PRIx64 "\n", | |
665 | ap->attrname, *ap->attrvalp); | |
666 | return 0; | |
667 | } | |
668 | } | |
669 | return -EINVAL; | |
670 | } | |
671 | ||
672 | int | |
673 | rbdfs_getxattr(const char *path, const char *name, char *value, | |
674 | size_t size | |
675 | #if defined(DARWIN) | |
676 | ,uint32_t position | |
677 | #endif | |
678 | ) | |
679 | { | |
680 | struct rbdfuse_attr *ap; | |
681 | char buf[128]; | |
682 | // allow gets on other files; ls likes to ask for things like | |
683 | // security.* | |
684 | ||
685 | for (ap = attrs; ap->attrname != NULL; ap++) { | |
686 | if (strcmp(name, ap->attrname) == 0) { | |
687 | sprintf(buf, "%" PRIu64, *ap->attrvalp); | |
688 | if (value != NULL && size >= strlen(buf)) | |
689 | strcpy(value, buf); | |
690 | fprintf(stderr, "rbd-fuse: get %s\n", ap->attrname); | |
691 | return (strlen(buf)); | |
692 | } | |
693 | } | |
694 | return 0; | |
695 | } | |
696 | ||
697 | int | |
698 | rbdfs_listxattr(const char *path, char *list, size_t len) | |
699 | { | |
700 | struct rbdfuse_attr *ap; | |
701 | size_t required_len = 0; | |
702 | ||
703 | if (strcmp(path, "/") != 0) | |
704 | return -EINVAL; | |
705 | ||
706 | for (ap = attrs; ap->attrname != NULL; ap++) | |
707 | required_len += strlen(ap->attrname) + 1; | |
708 | if (len >= required_len) { | |
709 | for (ap = attrs; ap->attrname != NULL; ap++) { | |
710 | sprintf(list, "%s", ap->attrname); | |
711 | list += strlen(ap->attrname) + 1; | |
712 | } | |
713 | } | |
714 | return required_len; | |
715 | } | |
716 | ||
717 | const static struct fuse_operations rbdfs_oper = { | |
718 | getattr: rbdfs_getattr, | |
719 | readlink: 0, | |
720 | getdir: 0, | |
721 | mknod: 0, | |
722 | mkdir: 0, | |
723 | unlink: rbdfs_unlink, | |
724 | rmdir: 0, | |
725 | symlink: 0, | |
726 | rename: rbdfs_rename, | |
727 | link: 0, | |
728 | chmod: 0, | |
729 | chown: 0, | |
730 | truncate: rbdfs_truncate, | |
731 | utime: rbdfs_utime, | |
732 | open: rbdfs_open, | |
733 | read: rbdfs_read, | |
734 | write: rbdfs_write, | |
735 | statfs: rbdfs_statfs, | |
736 | flush: 0, | |
737 | release: 0, | |
738 | fsync: rbdfs_fsync, | |
739 | setxattr: rbdfs_setxattr, | |
740 | getxattr: rbdfs_getxattr, | |
741 | listxattr: rbdfs_listxattr, | |
742 | removexattr: 0, | |
743 | opendir: rbdfs_opendir, | |
744 | readdir: rbdfs_readdir, | |
745 | releasedir: rbdfs_releasedir, | |
746 | fsyncdir: 0, | |
747 | init: rbdfs_init, | |
748 | destroy: rbdfs_destroy, | |
749 | access: 0, | |
750 | create: rbdfs_create, | |
751 | /* skip unimplemented */ | |
752 | }; | |
753 | ||
754 | } /* extern "C" */ | |
755 | ||
756 | enum { | |
757 | KEY_HELP, | |
758 | KEY_VERSION, | |
759 | KEY_CEPH_CONFIG, | |
760 | KEY_CEPH_CONFIG_LONG, | |
761 | KEY_RADOS_POOLNAME, | |
762 | KEY_RADOS_POOLNAME_LONG, | |
763 | KEY_RBD_IMAGENAME, | |
764 | KEY_RBD_IMAGENAME_LONG | |
765 | }; | |
766 | ||
767 | static struct fuse_opt rbdfs_opts[] = { | |
768 | FUSE_OPT_KEY("-h", KEY_HELP), | |
769 | FUSE_OPT_KEY("--help", KEY_HELP), | |
770 | FUSE_OPT_KEY("-V", KEY_VERSION), | |
771 | FUSE_OPT_KEY("--version", KEY_VERSION), | |
772 | {"-c %s", offsetof(struct rbd_options, ceph_config), KEY_CEPH_CONFIG}, | |
773 | {"--configfile=%s", offsetof(struct rbd_options, ceph_config), | |
774 | KEY_CEPH_CONFIG_LONG}, | |
775 | {"-p %s", offsetof(struct rbd_options, pool_name), KEY_RADOS_POOLNAME}, | |
776 | {"--poolname=%s", offsetof(struct rbd_options, pool_name), | |
777 | KEY_RADOS_POOLNAME_LONG}, | |
778 | {"-r %s", offsetof(struct rbd_options, image_name), KEY_RBD_IMAGENAME}, | |
779 | {"--image=%s", offsetof(struct rbd_options, image_name), | |
780 | KEY_RBD_IMAGENAME_LONG}, | |
781 | }; | |
782 | ||
783 | static void usage(const char *progname) | |
784 | { | |
785 | fprintf(stderr, | |
786 | "Usage: %s mountpoint [options]\n" | |
787 | "\n" | |
788 | "General options:\n" | |
789 | " -h --help print help\n" | |
790 | " -V --version print version\n" | |
791 | " -c --configfile ceph configuration file [/etc/ceph/ceph.conf]\n" | |
792 | " -p --poolname rados pool name [rbd]\n" | |
793 | " -r --image RBD image name\n" | |
794 | "\n", progname); | |
795 | } | |
796 | ||
797 | static int rbdfs_opt_proc(void *data, const char *arg, int key, | |
798 | struct fuse_args *outargs) | |
799 | { | |
800 | if (key == KEY_HELP) { | |
801 | usage(outargs->argv[0]); | |
802 | fuse_opt_add_arg(outargs, "-ho"); | |
803 | fuse_main(outargs->argc, outargs->argv, &rbdfs_oper, NULL); | |
804 | exit(1); | |
805 | } | |
806 | ||
807 | if (key == KEY_VERSION) { | |
808 | fuse_opt_add_arg(outargs, "--version"); | |
809 | fuse_main(outargs->argc, outargs->argv, &rbdfs_oper, NULL); | |
810 | exit(0); | |
811 | } | |
812 | ||
813 | if (key == KEY_CEPH_CONFIG) { | |
814 | if (rbd_options.ceph_config != NULL) { | |
815 | free(rbd_options.ceph_config); | |
816 | rbd_options.ceph_config = NULL; | |
817 | } | |
818 | rbd_options.ceph_config = strdup(arg+2); | |
819 | return 0; | |
820 | } | |
821 | ||
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; | |
826 | } | |
827 | rbd_options.pool_name = strdup(arg+2); | |
828 | return 0; | |
829 | } | |
830 | ||
831 | if (key == KEY_RBD_IMAGENAME) { | |
832 | if (rbd_options.image_name!= NULL) { | |
833 | free(rbd_options.image_name); | |
834 | rbd_options.image_name = NULL; | |
835 | } | |
836 | rbd_options.image_name = strdup(arg+2); | |
837 | return 0; | |
838 | } | |
839 | ||
840 | return 1; | |
841 | } | |
842 | ||
843 | void | |
844 | simple_err(const char *msg, int err) | |
845 | { | |
846 | fprintf(stderr, "%s: %s\n", msg, strerror(-err)); | |
847 | return; | |
848 | } | |
849 | ||
850 | int | |
851 | connect_to_cluster(rados_t *pcluster) | |
852 | { | |
853 | int r; | |
854 | ||
855 | r = rados_create(pcluster, NULL); | |
856 | if (r < 0) { | |
857 | simple_err("Could not create cluster handle", r); | |
858 | return r; | |
859 | } | |
860 | rados_conf_parse_env(*pcluster, NULL); | |
861 | r = rados_conf_read_file(*pcluster, rbd_options.ceph_config); | |
862 | if (r < 0) { | |
863 | simple_err("Error reading Ceph config file", r); | |
864 | goto failed_shutdown; | |
865 | } | |
866 | r = rados_connect(*pcluster); | |
867 | if (r < 0) { | |
868 | simple_err("Error connecting to cluster", r); | |
869 | goto failed_shutdown; | |
870 | } | |
871 | ||
872 | return 0; | |
873 | ||
874 | failed_shutdown: | |
875 | rados_shutdown(*pcluster); | |
876 | return r; | |
877 | } | |
878 | ||
879 | int main(int argc, char *argv[]) | |
880 | { | |
881 | struct fuse_args args = FUSE_ARGS_INIT(argc, argv); | |
882 | ||
883 | if (fuse_opt_parse(&args, &rbd_options, rbdfs_opts, rbdfs_opt_proc) == -1) { | |
884 | exit(1); | |
885 | } | |
886 | ||
887 | return fuse_main(args.argc, args.argv, &rbdfs_oper, NULL); | |
888 | } |