]> git.proxmox.com Git - ceph.git/blob - ceph/src/mount/mount.ceph.c
032c22e6c23f00c09f3122f42d7fca518a1c65de
[ceph.git] / ceph / src / mount / mount.ceph.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <errno.h>
5 #include <sys/mount.h>
6 #include <stdbool.h>
7 #include <sys/mman.h>
8 #include <wait.h>
9 #include <cap-ng.h>
10
11 #include "common/module.h"
12 #include "common/secret.h"
13 #include "include/addr_parsing.h"
14 #include "mount.ceph.h"
15
16 #ifndef MS_RELATIME
17 # define MS_RELATIME (1<<21)
18 #endif
19
20 bool verboseflag = false;
21 bool skip_mtab_flag = false;
22 bool v2_addrs = true;
23 bool no_fallback = false;
24 bool ms_mode_specified = false;
25 bool mon_addr_specified = false;
26 static const char * const EMPTY_STRING = "";
27
28 /* TODO duplicates logic from kernel */
29 #define CEPH_AUTH_NAME_DEFAULT "guest"
30
31 /* path to sysfs for ceph */
32 #define CEPH_SYS_FS_PATH "/sys/module/ceph/"
33 #define CEPH_SYS_FS_PARAM_PATH CEPH_SYS_FS_PATH"/parameters"
34
35 /*
36 * mount support hint from kernel -- we only need to check
37 * v2 support for catching bugs.
38 */
39 #define CEPH_V2_MOUNT_SUPPORT_PATH CEPH_SYS_FS_PARAM_PATH"/mount_syntax_v2"
40
41 #define CEPH_DEFAULT_V2_MS_MODE "prefer-crc"
42
43 #include "mtab.c"
44
45 enum mount_dev_format {
46 MOUNT_DEV_FORMAT_OLD = 0,
47 MOUNT_DEV_FORMAT_NEW = 1,
48 };
49
50 struct ceph_mount_info {
51 unsigned long cmi_flags;
52 char *cmi_name;
53 char *cmi_fsname;
54 char *cmi_fsid;
55 char *cmi_path;
56 char *cmi_mons;
57 char *cmi_conf;
58 char *cmi_opts;
59 int cmi_opts_len;
60 char cmi_secret[SECRET_BUFSIZE];
61
62 /* mount dev syntax format */
63 enum mount_dev_format format;
64 };
65
66 static void mon_addr_as_resolve_param(char *mon_addr)
67 {
68 for (; *mon_addr; ++mon_addr)
69 if (*mon_addr == '/')
70 *mon_addr = ',';
71 }
72
73 static void resolved_mon_addr_as_mount_opt(char *mon_addr)
74 {
75 for (; *mon_addr; ++mon_addr)
76 if (*mon_addr == ',')
77 *mon_addr = '/';
78 }
79
80 static void resolved_mon_addr_as_mount_dev(char *mon_addr)
81 {
82 for (; *mon_addr; ++mon_addr)
83 if (*mon_addr == '/')
84 *mon_addr = ',';
85 }
86
87 static void block_signals (int how)
88 {
89 sigset_t sigs;
90
91 sigfillset (&sigs);
92 sigdelset(&sigs, SIGTRAP);
93 sigdelset(&sigs, SIGSEGV);
94 sigprocmask (how, &sigs, (sigset_t *) 0);
95 }
96
97 void mount_ceph_debug(const char *fmt, ...)
98 {
99 if (verboseflag) {
100 va_list args;
101
102 va_start(args, fmt);
103 vprintf(fmt, args);
104 va_end(args);
105 }
106 }
107
108 /*
109 * append a key value pair option to option string.
110 */
111 static void append_opt(const char *key, const char *value,
112 struct ceph_mount_info *cmi, int *pos)
113 {
114 if (*pos != 0)
115 *pos = safe_cat(&cmi->cmi_opts, &cmi->cmi_opts_len, *pos, ",");
116
117 if (value) {
118 *pos = safe_cat(&cmi->cmi_opts, &cmi->cmi_opts_len, *pos, key);
119 *pos = safe_cat(&cmi->cmi_opts, &cmi->cmi_opts_len, *pos, "=");
120 *pos = safe_cat(&cmi->cmi_opts, &cmi->cmi_opts_len, *pos, value);
121 } else {
122 *pos = safe_cat(&cmi->cmi_opts, &cmi->cmi_opts_len, *pos, key);
123 }
124 }
125
126 /*
127 * remove a key value pair from option string. caller should ensure that the
128 * key value pair is separated by "=".
129 */
130 static int remove_opt(struct ceph_mount_info *cmi, const char *key, char **value)
131 {
132 char *key_start = strstr(cmi->cmi_opts, key);
133 if (!key_start) {
134 return -ENOENT;
135 }
136
137 /* key present -- try to split */
138 char *key_sep = strstr(key_start, "=");
139 if (!key_sep) {
140 return -ENOENT;
141 }
142
143 if (strncmp(key, key_start, key_sep - key_start) != 0) {
144 return -ENOENT;
145 }
146
147 ++key_sep;
148 char *value_end = strstr(key_sep, ",");
149 if (!value_end)
150 value_end = key_sep + strlen(key_sep);
151
152 if (value_end != key_sep && value) {
153 size_t len1 = value_end - key_sep;
154 *value = strndup(key_sep, len1+1);
155 if (!*value)
156 return -ENOMEM;
157 (*value)[len1] = '\0';
158 }
159
160 /* purge it */
161 size_t len2 = strlen(value_end);
162 if (len2) {
163 ++value_end;
164 memmove(key_start, value_end, len2);
165 } else {
166 /* last kv pair - swallow the comma */
167 --key_start;
168 *key_start = '\0';
169 }
170
171 return 0;
172 }
173
174 static void record_name(const char *name, struct ceph_mount_info *cmi)
175 {
176 int name_pos = 0;
177 int name_len = 0;
178
179 name_pos = safe_cat(&cmi->cmi_name, &name_len, name_pos, name);
180 }
181
182 /*
183 * parse old device string of format: <mon_addr>:/<path>
184 */
185 static int parse_old_dev(const char *dev_str, struct ceph_mount_info *cmi,
186 int *opt_pos)
187 {
188 size_t len;
189 char *mount_path;
190
191 mount_path = strstr(dev_str, ":/");
192 if (!mount_path) {
193 fprintf(stderr, "source mount path was not specified\n");
194 return -EINVAL;
195 }
196
197 len = mount_path - dev_str;
198 if (len != 0) {
199 free(cmi->cmi_mons);
200 /* overrides mon_addr passed via mount option (if any) */
201 cmi->cmi_mons = strndup(dev_str, len);
202 if (!cmi->cmi_mons)
203 return -ENOMEM;
204 mon_addr_specified = true;
205 } else {
206 /* reset mon_addr=<> mount option */
207 mon_addr_specified = false;
208 }
209
210 mount_path++;
211 cmi->cmi_path = strdup(mount_path);
212 if (!cmi->cmi_path)
213 return -ENOMEM;
214 if (!cmi->cmi_name)
215 record_name(CEPH_AUTH_NAME_DEFAULT, cmi);
216
217 cmi->format = MOUNT_DEV_FORMAT_OLD;
218 return 0;
219 }
220
221 /*
222 * parse new device string of format: name@<fsid>.fs_name=/path
223 */
224 static int parse_new_dev(const char *dev_str, struct ceph_mount_info *cmi,
225 int *opt_pos)
226 {
227 size_t len;
228 char *name;
229 char *name_end;
230 char *dot;
231 char *fs_name;
232
233 name_end = strstr(dev_str, "@");
234 if (!name_end) {
235 mount_ceph_debug("invalid new device string format\n");
236 return -ENODEV;
237 }
238
239 len = name_end - dev_str;
240 if (!len) {
241 fprintf(stderr, "missing <name> in device\n");
242 return -EINVAL;
243 }
244
245 name = (char *)alloca(len+1);
246 memcpy(name, dev_str, len);
247 name[len] = '\0';
248
249 if (cmi->cmi_name && strcmp(cmi->cmi_name, name)) {
250 fprintf(stderr, "mismatching ceph user in mount option and device string\n");
251 return -EINVAL;
252 }
253
254 /* record name and store in option string */
255 if (!cmi->cmi_name) {
256 record_name(name, cmi);
257 append_opt("name", name, cmi, opt_pos);
258 }
259
260 ++name_end;
261 /* check if an fsid is included in the device string */
262 dot = strstr(name_end, ".");
263 if (!dot) {
264 fprintf(stderr, "invalid device string format\n");
265 return -EINVAL;
266 }
267 len = dot - name_end;
268 if (len) {
269 /* check if this _looks_ like a UUID */
270 if (len != CLUSTER_FSID_LEN - 1) {
271 fprintf(stderr, "invalid device string format\n");
272 return -EINVAL;
273 }
274
275 cmi->cmi_fsid = strndup(name_end, len);
276 if (!cmi->cmi_fsid)
277 return -ENOMEM;
278 }
279
280 ++dot;
281 fs_name = strstr(dot, "=");
282 if (!fs_name) {
283 fprintf(stderr, "invalid device string format\n");
284 return -EINVAL;
285 }
286 len = fs_name - dot;
287 if (!len) {
288 fprintf(stderr, "missing <fs_name> in device\n");
289 return -EINVAL;
290 }
291 cmi->cmi_fsname = strndup(dot, len);
292 if (!cmi->cmi_fsname)
293 return -ENOMEM;
294
295 ++fs_name;
296 if (strlen(fs_name)) {
297 cmi->cmi_path = strdup(fs_name);
298 if (!cmi->cmi_path)
299 return -ENOMEM;
300 }
301
302 /* new-style dev - force using v2 addrs first */
303 if (!ms_mode_specified && !mon_addr_specified)
304 append_opt("ms_mode", CEPH_DEFAULT_V2_MS_MODE, cmi,
305 opt_pos);
306
307 cmi->format = MOUNT_DEV_FORMAT_NEW;
308 return 0;
309 }
310
311 static int parse_dev(const char *dev_str, struct ceph_mount_info *cmi,
312 int *opt_pos)
313 {
314 int ret;
315
316 ret = parse_new_dev(dev_str, cmi, opt_pos);
317 if (ret < 0 && ret != -ENODEV)
318 return -EINVAL;
319 if (ret)
320 ret = parse_old_dev(dev_str, cmi, opt_pos);
321 if (ret < 0)
322 fprintf(stderr, "error parsing device string\n");
323 return ret;
324 }
325
326 /* resolve monitor host and optionally record in option string.
327 * use opt_pos to determine if the caller wants to record the
328 * resolved address in mount option (c.f., mount_old_device_format).
329 */
330 static int finalize_src(struct ceph_mount_info *cmi, int *opt_pos,
331 char **resolved_addr)
332 {
333 char *src;
334 size_t len = strlen(cmi->cmi_mons);
335 char *addr = alloca(len+1);
336
337 memcpy(addr, cmi->cmi_mons, len+1);
338 mon_addr_as_resolve_param(addr);
339
340 src = resolve_addrs(addr);
341 if (!src)
342 return -1;
343
344 mount_ceph_debug("mount.ceph: resolved to: \"%s\"\n", src);
345 if (opt_pos) {
346 resolved_mon_addr_as_mount_opt(src);
347 append_opt("mon_addr", src, cmi, opt_pos);
348 } else if (resolved_addr) {
349 *resolved_addr = strdup(src);
350 }
351 free(src);
352 return 0;
353 }
354
355 static int
356 drop_capabilities()
357 {
358 capng_setpid(getpid());
359 capng_clear(CAPNG_SELECT_BOTH);
360 if (capng_update(CAPNG_ADD, CAPNG_PERMITTED, CAP_DAC_READ_SEARCH)) {
361 fprintf(stderr, "Unable to update permitted capability set.\n");
362 return EX_SYSERR;
363 }
364 if (capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_READ_SEARCH)) {
365 fprintf(stderr, "Unable to update effective capability set.\n");
366 return EX_SYSERR;
367 }
368 if (capng_apply(CAPNG_SELECT_BOTH)) {
369 fprintf(stderr, "Unable to apply new capability set.\n");
370 return EX_SYSERR;
371 }
372 return 0;
373 }
374
375 /*
376 * Attempt to fetch info from the local config file, if one is present. Since
377 * this involves activity that may be dangerous for a privileged task, we
378 * fork(), have the child drop privileges and do the processing and then hand
379 * back the results via memory shared with the parent.
380 */
381 static int fetch_config_info(struct ceph_mount_info *cmi)
382 {
383 int ret = 0;
384 pid_t pid;
385 struct ceph_config_info *cci;
386
387 /* Don't do anything if we already have requisite info */
388 if (cmi->cmi_secret[0] && cmi->cmi_mons && cmi->cmi_fsid)
389 return 0;
390
391 cci = mmap((void *)0, sizeof(*cci), PROT_READ | PROT_WRITE,
392 MAP_ANONYMOUS | MAP_SHARED, -1, 0);
393 if (cci == MAP_FAILED) {
394 mount_ceph_debug("Unable to allocate memory: %s\n",
395 strerror(errno));
396 return EX_SYSERR;
397 }
398
399 pid = fork();
400 if (pid < 0) {
401 mount_ceph_debug("fork() failure: %s\n", strerror(errno));
402 ret = EX_SYSERR;
403 goto out;
404 }
405
406 if (pid == 0) {
407 char *entity_name = NULL;
408 int name_pos = 0;
409 int name_len = 0;
410
411 /* child */
412 ret = drop_capabilities();
413 if (ret)
414 exit(1);
415
416 name_pos = safe_cat(&entity_name, &name_len, name_pos, "client.");
417 name_pos = safe_cat(&entity_name, &name_len, name_pos, cmi->cmi_name);
418 mount_ceph_get_config_info(cmi->cmi_conf, entity_name, v2_addrs, cci);
419 free(entity_name);
420 exit(0);
421 } else {
422 /* parent */
423 pid = wait(&ret);
424 if (!WIFEXITED(ret)) {
425 mount_ceph_debug("Child process terminated abnormally.\n");
426 ret = EX_SYSERR;
427 goto out;
428 }
429 ret = WEXITSTATUS(ret);
430 if (ret) {
431 mount_ceph_debug("Child exited with status %d\n", ret);
432 ret = EX_SYSERR;
433 goto out;
434 }
435
436 /*
437 * Copy values from MAP_SHARED buffer to cmi if we didn't
438 * already find anything and we got something from the child.
439 */
440 size_t len;
441 if (!cmi->cmi_secret[0] && cci->cci_secret[0]) {
442
443 len = strnlen(cci->cci_secret, SECRET_BUFSIZE);
444 if (len < SECRET_BUFSIZE) {
445 memcpy(cmi->cmi_secret, cci->cci_secret, len + 1);
446 } else {
447 mount_ceph_debug("secret is too long (len=%zu max=%zu)!\n", len, SECRET_BUFSIZE);
448 }
449 }
450 if (!cmi->cmi_mons && cci->cci_mons[0]) {
451 len = strnlen(cci->cci_mons, MON_LIST_BUFSIZE);
452 if (len < MON_LIST_BUFSIZE)
453 cmi->cmi_mons = strndup(cci->cci_mons, len + 1);
454 }
455 if (!cmi->cmi_fsid) {
456 len = strnlen(cci->cci_fsid, CLUSTER_FSID_LEN);
457 if (len < CLUSTER_FSID_LEN)
458 cmi->cmi_fsid = strndup(cci->cci_fsid, len + 1);
459 }
460 }
461 out:
462 munmap(cci, sizeof(*cci));
463 return ret;
464 }
465
466 /*
467 * this one is partially based on parse_options() from cifs.mount.c
468 */
469 static int parse_options(const char *data, struct ceph_mount_info *cmi,
470 int *opt_pos)
471 {
472 char * next_keyword = NULL;
473 char *name = NULL;
474
475 if (data == EMPTY_STRING)
476 goto out;
477
478 mount_ceph_debug("parsing options: %s\n", data);
479
480 do {
481 char * value = NULL;
482 bool skip = true;
483
484 /* check if ends with trailing comma */
485 if(*data == 0)
486 break;
487 next_keyword = strchr(data,',');
488
489 /* temporarily null terminate end of keyword=value pair */
490 if(next_keyword)
491 *next_keyword++ = 0;
492
493 /* temporarily null terminate keyword to make keyword and value distinct */
494 if ((value = strchr(data, '=')) != NULL) {
495 *value = '\0';
496 value++;
497 }
498
499 if (strcmp(data, "ro") == 0) {
500 cmi->cmi_flags |= MS_RDONLY;
501 } else if (strcmp(data, "rw") == 0) {
502 cmi->cmi_flags &= ~MS_RDONLY;
503 } else if (strcmp(data, "nosuid") == 0) {
504 cmi->cmi_flags |= MS_NOSUID;
505 } else if (strcmp(data, "suid") == 0) {
506 cmi->cmi_flags &= ~MS_NOSUID;
507 } else if (strcmp(data, "dev") == 0) {
508 cmi->cmi_flags &= ~MS_NODEV;
509 } else if (strcmp(data, "nodev") == 0) {
510 cmi->cmi_flags |= MS_NODEV;
511 } else if (strcmp(data, "noexec") == 0) {
512 cmi->cmi_flags |= MS_NOEXEC;
513 } else if (strcmp(data, "exec") == 0) {
514 cmi->cmi_flags &= ~MS_NOEXEC;
515 } else if (strcmp(data, "sync") == 0) {
516 cmi->cmi_flags |= MS_SYNCHRONOUS;
517 } else if (strcmp(data, "remount") == 0) {
518 cmi->cmi_flags |= MS_REMOUNT;
519 } else if (strcmp(data, "mandlock") == 0) {
520 cmi->cmi_flags |= MS_MANDLOCK;
521 } else if ((strcmp(data, "nobrl") == 0) ||
522 (strcmp(data, "nolock") == 0)) {
523 cmi->cmi_flags &= ~MS_MANDLOCK;
524 } else if (strcmp(data, "noatime") == 0) {
525 cmi->cmi_flags |= MS_NOATIME;
526 } else if (strcmp(data, "nodiratime") == 0) {
527 cmi->cmi_flags |= MS_NODIRATIME;
528 } else if (strcmp(data, "relatime") == 0) {
529 cmi->cmi_flags |= MS_RELATIME;
530 } else if (strcmp(data, "strictatime") == 0) {
531 cmi->cmi_flags |= MS_STRICTATIME;
532 } else if (strcmp(data, "noauto") == 0) {
533 /* ignore */
534 } else if (strcmp(data, "_netdev") == 0) {
535 /* ignore */
536 } else if (strcmp(data, "nofail") == 0) {
537 /* ignore */
538 } else if (strcmp(data, "fs") == 0) {
539 if (!value || !*value) {
540 fprintf(stderr, "mount option fs requires a value.\n");
541 return -EINVAL;
542 }
543 data = "mds_namespace";
544 skip = false;
545 } else if (strcmp(data, "nofallback") == 0) {
546 no_fallback = true;
547 } else if (strcmp(data, "secretfile") == 0) {
548 int ret;
549
550 if (!value || !*value) {
551 fprintf(stderr, "keyword secretfile found, but no secret file specified\n");
552 return -EINVAL;
553 }
554 ret = read_secret_from_file(value, cmi->cmi_secret, sizeof(cmi->cmi_secret));
555 if (ret < 0) {
556 fprintf(stderr, "error reading secret file: %d\n", ret);
557 return ret;
558 }
559 } else if (strcmp(data, "secret") == 0) {
560 size_t len;
561
562 if (!value || !*value) {
563 fprintf(stderr, "mount option secret requires a value.\n");
564 return -EINVAL;
565 }
566
567 len = strnlen(value, sizeof(cmi->cmi_secret)) + 1;
568 if (len <= sizeof(cmi->cmi_secret))
569 memcpy(cmi->cmi_secret, value, len);
570 } else if (strcmp(data, "conf") == 0) {
571 if (!value || !*value) {
572 fprintf(stderr, "mount option conf requires a value.\n");
573 return -EINVAL;
574 }
575 /* keep pointer to value */
576 cmi->cmi_conf = strdup(value);
577 if (!cmi->cmi_conf)
578 return -ENOMEM;
579 } else if (strcmp(data, "name") == 0) {
580 if (!value || !*value) {
581 fprintf(stderr, "mount option name requires a value.\n");
582 return -EINVAL;
583 }
584 /* keep pointer to value */
585 name = value;
586 skip = false;
587 } else if (strcmp(data, "ms_mode") == 0) {
588 if (!value || !*value) {
589 fprintf(stderr, "mount option ms_mode requires a value.\n");
590 return -EINVAL;
591 }
592 /* Only legacy ms_mode needs v1 addrs */
593 v2_addrs = strcmp(value, "legacy");
594 skip = false;
595 ms_mode_specified = true;
596 } else if (strcmp(data, "mon_addr") == 0) {
597 /* monitor address to use for mounting */
598 if (!value || !*value) {
599 fprintf(stderr, "mount option mon_addr requires a value.\n");
600 return -EINVAL;
601 }
602 cmi->cmi_mons = strdup(value);
603 if (!cmi->cmi_mons)
604 return -ENOMEM;
605 mon_addr_specified = true;
606 } else {
607 /* unrecognized mount options, passing to kernel */
608 skip = false;
609 }
610
611 /* Copy (possibly modified) option to out */
612 if (!skip)
613 append_opt(data, value, cmi, opt_pos);
614 data = next_keyword;
615 } while (data);
616
617 out:
618 /*
619 * set ->cmi_name conditionally -- this gets checked when parsing new
620 * device format. for old device format, ->cmi_name is set to default
621 * user name when name option is not passed in.
622 */
623 if (name)
624 record_name(name, cmi);
625 if (cmi->cmi_opts)
626 mount_ceph_debug("mount.ceph: options \"%s\".\n", cmi->cmi_opts);
627
628 if (!cmi->cmi_opts) {
629 cmi->cmi_opts = strdup(EMPTY_STRING);
630 if (!cmi->cmi_opts)
631 return -ENOMEM;
632 }
633 return 0;
634 }
635
636
637 static int parse_arguments(int argc, char *const *const argv,
638 const char **src, const char **node, const char **opts)
639 {
640 int i;
641
642 if (argc < 2) {
643 // There were no arguments. Just show the usage.
644 return 1;
645 }
646 if ((!strcmp(argv[1], "-h")) || (!strcmp(argv[1], "--help"))) {
647 // The user asked for help.
648 return 1;
649 }
650
651 // The first two arguments are positional
652 if (argc < 3)
653 return -EINVAL;
654 *src = argv[1];
655 *node = argv[2];
656
657 // Parse the remaining options
658 *opts = EMPTY_STRING;
659 for (i = 3; i < argc; ++i) {
660 if (!strcmp("-h", argv[i]))
661 return 1;
662 else if (!strcmp("-n", argv[i]))
663 skip_mtab_flag = true;
664 else if (!strcmp("-v", argv[i]))
665 verboseflag = true;
666 else if (!strcmp("-o", argv[i])) {
667 ++i;
668 if (i >= argc) {
669 fprintf(stderr, "Option -o requires an argument.\n\n");
670 return -EINVAL;
671 }
672 *opts = argv[i];
673 } else {
674 fprintf(stderr, "Can't understand option: '%s'\n\n", argv[i]);
675 return -EINVAL;
676 }
677 }
678 return 0;
679 }
680
681 /* modprobe failing doesn't necessarily prevent from working, so this
682 returns void */
683 static void modprobe(void)
684 {
685 int r;
686
687 r = module_load("ceph", NULL);
688 if (r)
689 printf("failed to load ceph kernel module (%d)\n", r);
690 }
691
692 static void usage(const char *prog_name)
693 {
694 printf("usage: %s [src] [mount-point] [-n] [-v] [-o ceph-options]\n",
695 prog_name);
696 printf("options:\n");
697 printf("\t-h: Print this help\n");
698 printf("\t-n: Do not update /etc/mtab\n");
699 printf("\t-v: Verbose\n");
700 printf("\tceph-options: refer to mount.ceph(8)\n");
701 printf("\n");
702 }
703
704 /*
705 * The structure itself lives on the stack, so don't free it. Just the
706 * pointers inside.
707 */
708 static void ceph_mount_info_free(struct ceph_mount_info *cmi)
709 {
710 free(cmi->cmi_opts);
711 free(cmi->cmi_name);
712 free(cmi->cmi_fsname);
713 free(cmi->cmi_fsid);
714 free(cmi->cmi_path);
715 free(cmi->cmi_mons);
716 free(cmi->cmi_conf);
717 }
718
719 static int mount_new_device_format(const char *node, struct ceph_mount_info *cmi)
720 {
721 int r;
722 char *rsrc = NULL;
723 int pos = 0;
724 int len = 0;
725
726 if (!cmi->cmi_fsid) {
727 fprintf(stderr, "missing ceph cluster-id");
728 return -EINVAL;
729 }
730
731 pos = safe_cat(&rsrc, &len, pos, cmi->cmi_name);
732 pos = safe_cat(&rsrc, &len, pos, "@");
733 pos = safe_cat(&rsrc, &len, pos, cmi->cmi_fsid);
734 pos = safe_cat(&rsrc, &len, pos, ".");
735 pos = safe_cat(&rsrc, &len, pos, cmi->cmi_fsname);
736 pos = safe_cat(&rsrc, &len, pos, "=");
737 if (cmi->cmi_path)
738 safe_cat(&rsrc, &len, pos, cmi->cmi_path);
739
740 mount_ceph_debug("mount.ceph: trying mount with new device syntax: %s\n",
741 rsrc);
742 if (cmi->cmi_opts)
743 mount_ceph_debug("mount.ceph: options \"%s\" will pass to kernel\n",
744 cmi->cmi_opts);
745 r = mount(rsrc, node, "ceph", cmi->cmi_flags, cmi->cmi_opts);
746 if (r)
747 r = -errno;
748 free(rsrc);
749 return r;
750 }
751
752 static int mount_old_device_format(const char *node, struct ceph_mount_info *cmi)
753 {
754 int r;
755 int len = 0;
756 int pos = 0;
757 char *mon_addr = NULL;
758 char *rsrc = NULL;
759
760 r = remove_opt(cmi, "mon_addr", &mon_addr);
761 if (r) {
762 fprintf(stderr, "failed to switch using old device format\n");
763 return -EINVAL;
764 }
765
766 /* if we reach here and still have a v2 addr, we'd need to
767 * refresh with v1 addrs, since we'll be not passing ms_mode
768 * with the old syntax.
769 */
770 if (v2_addrs && !ms_mode_specified && !mon_addr_specified) {
771 mount_ceph_debug("mount.ceph: switching to using v1 address with old syntax\n");
772 v2_addrs = false;
773 free(mon_addr);
774 free(cmi->cmi_mons);
775 mon_addr = NULL;
776 cmi->cmi_mons = NULL;
777 fetch_config_info(cmi);
778 if (!cmi->cmi_mons) {
779 fprintf(stderr, "unable to determine (v1) mon addresses\n");
780 return -EINVAL;
781 }
782 r = finalize_src(cmi, NULL, &mon_addr);
783 if (r) {
784 fprintf(stderr, "failed to resolve (v1) mon addresses\n");
785 return -EINVAL;
786 }
787 remove_opt(cmi, "ms_mode", NULL);
788 }
789
790 pos = strlen(cmi->cmi_opts);
791 if (cmi->cmi_fsname)
792 append_opt("mds_namespace", cmi->cmi_fsname, cmi, &pos);
793 if (cmi->cmi_fsid)
794 append_opt("fsid", cmi->cmi_fsid, cmi, &pos);
795
796 pos = 0;
797 resolved_mon_addr_as_mount_dev(mon_addr);
798 pos = safe_cat(&rsrc, &len, pos, mon_addr);
799 pos = safe_cat(&rsrc, &len, pos, ":");
800 if (cmi->cmi_path)
801 safe_cat(&rsrc, &len, pos, cmi->cmi_path);
802
803 mount_ceph_debug("mount.ceph: trying mount with old device syntax: %s\n",
804 rsrc);
805 if (cmi->cmi_opts)
806 mount_ceph_debug("mount.ceph: options \"%s\" will pass to kernel\n",
807 cmi->cmi_opts);
808
809 r = mount(rsrc, node, "ceph", cmi->cmi_flags, cmi->cmi_opts);
810 free(mon_addr);
811 free(rsrc);
812
813 return r;
814 }
815
816 /*
817 * check whether to fall-back to using old-style mount syntax (called
818 * when new-style mount syntax fails). this is mostly to catch any
819 * new-style (v2) implementation bugs in the kernel and is primarly
820 * used in teuthology tests.
821 */
822 static bool should_fallback()
823 {
824 int ret;
825 struct stat stbuf;
826
827 if (!no_fallback)
828 return true;
829
830 ret = stat(CEPH_V2_MOUNT_SUPPORT_PATH, &stbuf);
831 if (ret) {
832 mount_ceph_debug("mount.ceph: v2 mount support check returned %d\n",
833 errno);
834 if (errno == ENOENT)
835 mount_ceph_debug("mount.ceph: kernel does not support v2"
836 " syntax\n");
837 /* fallback on *all* errors */
838 return true;
839 }
840
841 fprintf(stderr, "mount.ceph: kernel BUG!\n");
842 return false;
843 }
844
845 static int do_mount(const char *dev, const char *node,
846 struct ceph_mount_info *cmi) {
847 int pos = 0;
848 int retval= -EINVAL;
849 bool fallback = true;
850
851 /* no v2 addresses available via config - try v1 addresses */
852 if (v2_addrs &&
853 !cmi->cmi_mons &&
854 !ms_mode_specified &&
855 !mon_addr_specified) {
856 mount_ceph_debug("mount.ceph: switching to using v1 address\n");
857 v2_addrs = false;
858 fetch_config_info(cmi);
859 remove_opt(cmi, "ms_mode", NULL);
860 }
861
862 if (!cmi->cmi_mons) {
863 fprintf(stderr, "unable to determine mon addresses\n");
864 return -EINVAL;
865 }
866
867 pos = strlen(cmi->cmi_opts);
868 retval = finalize_src(cmi, &pos, NULL);
869 if (retval) {
870 fprintf(stderr, "failed to resolve source\n");
871 return -EINVAL;
872 }
873
874 retval = -1;
875 if (cmi->format == MOUNT_DEV_FORMAT_NEW) {
876 retval = mount_new_device_format(node, cmi);
877 if (retval)
878 fallback = (should_fallback() && retval == -EINVAL && cmi->cmi_fsid);
879 }
880
881 /* pass-through or fallback to old-style mount device */
882 if (retval && fallback)
883 retval = mount_old_device_format(node, cmi);
884 if (retval) {
885 retval = EX_FAIL;
886 switch (errno) {
887 case ENODEV:
888 fprintf(stderr, "mount error: ceph filesystem not supported by the system\n");
889 break;
890 case EHOSTUNREACH:
891 fprintf(stderr, "mount error: no mds server is up or the cluster is laggy\n");
892 break;
893 default:
894 fprintf(stderr, "mount error %d = %s\n", errno, strerror(errno));
895 }
896 }
897
898 if (!retval && !skip_mtab_flag) {
899 update_mtab_entry(dev, node, "ceph", cmi->cmi_opts, cmi->cmi_flags, 0, 0);
900 }
901
902 return retval;
903 }
904
905 static int append_key_or_secret_option(struct ceph_mount_info *cmi)
906 {
907 int pos = strlen(cmi->cmi_opts);
908
909 if (!cmi->cmi_secret[0] && !is_kernel_secret(cmi->cmi_name))
910 return 0;
911
912 if (pos)
913 pos = safe_cat(&cmi->cmi_opts, &cmi->cmi_opts_len, pos, ",");
914
915 /* when parsing kernel options (-o remount) we get '<hidden>' as the secret */
916 if (cmi->cmi_secret[0] && (strcmp(cmi->cmi_secret, "<hidden>") != 0)) {
917 int ret = set_kernel_secret(cmi->cmi_secret, cmi->cmi_name);
918 if (ret < 0) {
919 if (ret == -ENODEV || ret == -ENOSYS) {
920 /* old kernel; fall back to secret= in options */
921 pos = safe_cat(&cmi->cmi_opts,
922 &cmi->cmi_opts_len, pos,
923 "secret=");
924 pos = safe_cat(&cmi->cmi_opts,
925 &cmi->cmi_opts_len, pos,
926 cmi->cmi_secret);
927 return 0;
928 }
929 fprintf(stderr, "adding ceph secret key to kernel failed: %s\n",
930 strerror(-ret));
931 return ret;
932 }
933 }
934
935 pos = safe_cat(&cmi->cmi_opts, &cmi->cmi_opts_len, pos, "key=");
936 pos = safe_cat(&cmi->cmi_opts, &cmi->cmi_opts_len, pos, cmi->cmi_name);
937
938 return 0;
939 }
940
941 int main(int argc, char *argv[])
942 {
943 int opt_pos = 0;
944 const char *dev, *node, *opts;
945 int retval;
946 struct ceph_mount_info cmi = { 0 };
947
948 retval = parse_arguments(argc, argv, &dev, &node, &opts);
949 if (retval) {
950 usage(argv[0]);
951 retval = (retval > 0) ? 0 : EX_USAGE;
952 goto out;
953 }
954
955 retval = parse_options(opts, &cmi, &opt_pos);
956 if (retval) {
957 fprintf(stderr, "failed to parse ceph_options: %d\n", retval);
958 retval = EX_USAGE;
959 goto out;
960 }
961
962 retval = parse_dev(dev, &cmi, &opt_pos);
963 if (retval) {
964 fprintf(stderr, "unable to parse mount device string: %d\n", retval);
965 retval = EX_USAGE;
966 goto out;
967 }
968
969 /*
970 * We don't care if this errors out, since this is best-effort.
971 * note that this fetches v1 or v2 addr depending on @v2_addr
972 * flag.
973 */
974 fetch_config_info(&cmi);
975
976 /* Ensure the ceph key_type is available */
977 modprobe();
978
979 retval = append_key_or_secret_option(&cmi);
980 if (retval) {
981 fprintf(stderr, "couldn't append secret option: %d\n", retval);
982 retval = EX_USAGE;
983 goto out;
984 }
985
986 block_signals(SIG_BLOCK);
987 retval = do_mount(dev, node, &cmi);
988 block_signals(SIG_UNBLOCK);
989 out:
990 ceph_mount_info_free(&cmi);
991 return retval;
992 }
993