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