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