]> git.proxmox.com Git - ceph.git/blame - ceph/src/mount/mount.ceph.c
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / mount / mount.ceph.c
CommitLineData
7c673cae
FG
1#include <stdio.h>
2#include <string.h>
3#include <stdlib.h>
4#include <errno.h>
5#include <sys/mount.h>
eafe8130
TL
6#include <stdbool.h>
7#include <sys/mman.h>
8#include <wait.h>
9#include <cap-ng.h>
7c673cae
FG
10
11#include "common/module.h"
12#include "common/secret.h"
13#include "include/addr_parsing.h"
eafe8130 14#include "mount.ceph.h"
7c673cae
FG
15
16#ifndef MS_RELATIME
17# define MS_RELATIME (1<<21)
18#endif
19
eafe8130
TL
20bool verboseflag = false;
21bool skip_mtab_flag = false;
f67539c2 22bool v2_addrs = false;
7c673cae
FG
23static const char * const EMPTY_STRING = "";
24
25/* TODO duplicates logic from kernel */
26#define CEPH_AUTH_NAME_DEFAULT "guest"
27
28#include "mtab.c"
29
eafe8130
TL
30struct ceph_mount_info {
31 unsigned long cmi_flags;
32 char *cmi_name;
33 char *cmi_path;
34 char *cmi_mons;
35 char *cmi_conf;
36 char *cmi_opts;
37 int cmi_opts_len;
38 char cmi_secret[SECRET_BUFSIZE];
39};
40
7c673cae
FG
41static void block_signals (int how)
42{
43 sigset_t sigs;
44
45 sigfillset (&sigs);
46 sigdelset(&sigs, SIGTRAP);
47 sigdelset(&sigs, SIGSEGV);
48 sigprocmask (how, &sigs, (sigset_t *) 0);
49}
50
eafe8130
TL
51void mount_ceph_debug(const char *fmt, ...)
52{
53 if (verboseflag) {
54 va_list args;
55
56 va_start(args, fmt);
57 vprintf(fmt, args);
58 va_end(args);
59 }
60}
61
62static int parse_src(const char *orig_str, struct ceph_mount_info *cmi)
7c673cae 63{
eafe8130 64 size_t len;
7c673cae 65 char *mount_path;
7c673cae 66
eafe8130 67 mount_path = strstr(orig_str, ":/");
7c673cae 68 if (!mount_path) {
eafe8130
TL
69 fprintf(stderr, "source mount path was not specified\n");
70 return -EINVAL;
7c673cae 71 }
eafe8130
TL
72
73 len = mount_path - orig_str;
74 if (len != 0) {
75 cmi->cmi_mons = strndup(orig_str, len);
76 if (!cmi->cmi_mons)
77 return -ENOMEM;
7c673cae
FG
78 }
79
7c673cae 80 mount_path++;
eafe8130
TL
81 cmi->cmi_path = strdup(mount_path);
82 if (!cmi->cmi_path)
83 return -ENOMEM;
84 return 0;
85}
7c673cae 86
eafe8130
TL
87static char *finalize_src(struct ceph_mount_info *cmi)
88{
89 int pos, len;
90 char *src;
7c673cae 91
eafe8130
TL
92 src = resolve_addrs(cmi->cmi_mons);
93 if (!src)
7c673cae 94 return NULL;
7c673cae
FG
95
96 len = strlen(src);
97 pos = safe_cat(&src, &len, len, ":");
eafe8130 98 safe_cat(&src, &len, pos, cmi->cmi_path);
7c673cae 99
7c673cae
FG
100 return src;
101}
102
eafe8130
TL
103static int
104drop_capabilities()
105{
106 capng_setpid(getpid());
107 capng_clear(CAPNG_SELECT_BOTH);
108 if (capng_update(CAPNG_ADD, CAPNG_PERMITTED, CAP_DAC_READ_SEARCH)) {
109 fprintf(stderr, "Unable to update permitted capability set.\n");
110 return EX_SYSERR;
111 }
112 if (capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_READ_SEARCH)) {
113 fprintf(stderr, "Unable to update effective capability set.\n");
114 return EX_SYSERR;
115 }
116 if (capng_apply(CAPNG_SELECT_BOTH)) {
117 fprintf(stderr, "Unable to apply new capability set.\n");
118 return EX_SYSERR;
119 }
120 return 0;
121}
122
123/*
124 * Attempt to fetch info from the local config file, if one is present. Since
125 * this involves activity that may be dangerous for a privileged task, we
126 * fork(), have the child drop privileges and do the processing and then hand
127 * back the results via memory shared with the parent.
128 */
129static int fetch_config_info(struct ceph_mount_info *cmi)
130{
131 int ret = 0;
132 pid_t pid;
133 struct ceph_config_info *cci;
134
135 /* Don't do anything if we already have requisite info */
136 if (cmi->cmi_secret[0] && cmi->cmi_mons)
137 return 0;
138
139 cci = mmap((void *)0, sizeof(*cci), PROT_READ | PROT_WRITE,
140 MAP_ANONYMOUS | MAP_SHARED, -1, 0);
141 if (cci == MAP_FAILED) {
142 mount_ceph_debug("Unable to allocate memory: %s\n",
143 strerror(errno));
144 return EX_SYSERR;
145 }
146
147 pid = fork();
148 if (pid < 0) {
149 mount_ceph_debug("fork() failure: %s\n", strerror(errno));
150 ret = EX_SYSERR;
151 goto out;
152 }
153
154 if (pid == 0) {
155 /* child */
156 ret = drop_capabilities();
157 if (ret)
158 exit(1);
f67539c2 159 mount_ceph_get_config_info(cmi->cmi_conf, cmi->cmi_name, v2_addrs, cci);
eafe8130
TL
160 exit(0);
161 } else {
162 /* parent */
163 pid = wait(&ret);
164 if (!WIFEXITED(ret)) {
165 mount_ceph_debug("Child process terminated abnormally.\n");
166 ret = EX_SYSERR;
167 goto out;
168 }
169 ret = WEXITSTATUS(ret);
170 if (ret) {
171 mount_ceph_debug("Child exited with status %d\n", ret);
172 ret = EX_SYSERR;
173 goto out;
174 }
175
176 /*
177 * Copy values from MAP_SHARED buffer to cmi if we didn't
178 * already find anything and we got something from the child.
179 */
180 size_t len;
181 if (!cmi->cmi_secret[0] && cci->cci_secret[0]) {
182
183 len = strnlen(cci->cci_secret, SECRET_BUFSIZE);
184 if (len < SECRET_BUFSIZE) {
185 memcpy(cmi->cmi_secret, cci->cci_secret, len + 1);
186 } else {
187 mount_ceph_debug("secret is too long (len=%zu max=%zu)!\n", len, SECRET_BUFSIZE);
188 }
189 }
190 if (!cmi->cmi_mons && cci->cci_mons[0]) {
191 len = strnlen(cci->cci_mons, MON_LIST_BUFSIZE);
192 if (len < MON_LIST_BUFSIZE)
193 cmi->cmi_mons = strndup(cci->cci_mons, len + 1);
194 }
195 }
196out:
197 munmap(cci, sizeof(*cci));
198 return ret;
199}
200
7c673cae 201/*
11fdf7f2 202 * this one is partially based on parse_options() from cifs.mount.c
7c673cae 203 */
eafe8130 204static int parse_options(const char *data, struct ceph_mount_info *cmi)
7c673cae
FG
205{
206 char * next_keyword = NULL;
7c673cae
FG
207 int pos = 0;
208 char *name = NULL;
209 int name_len = 0;
210 int name_pos = 0;
7c673cae 211
f67539c2
TL
212 if (data == EMPTY_STRING)
213 goto out;
214
eafe8130 215 mount_ceph_debug("parsing options: %s\n", data);
7c673cae
FG
216
217 do {
218 char * value = NULL;
eafe8130
TL
219 bool skip = true;
220
7c673cae
FG
221 /* check if ends with trailing comma */
222 if(*data == 0)
223 break;
224 next_keyword = strchr(data,',');
9f95a23c 225
7c673cae
FG
226 /* temporarily null terminate end of keyword=value pair */
227 if(next_keyword)
228 *next_keyword++ = 0;
229
230 /* temporarily null terminate keyword to make keyword and value distinct */
231 if ((value = strchr(data, '=')) != NULL) {
232 *value = '\0';
233 value++;
234 }
235
9f95a23c 236 if (strcmp(data, "ro") == 0) {
eafe8130 237 cmi->cmi_flags |= MS_RDONLY;
9f95a23c 238 } else if (strcmp(data, "rw") == 0) {
eafe8130 239 cmi->cmi_flags &= ~MS_RDONLY;
9f95a23c 240 } else if (strcmp(data, "nosuid") == 0) {
eafe8130 241 cmi->cmi_flags |= MS_NOSUID;
9f95a23c 242 } else if (strcmp(data, "suid") == 0) {
eafe8130 243 cmi->cmi_flags &= ~MS_NOSUID;
9f95a23c 244 } else if (strcmp(data, "dev") == 0) {
eafe8130 245 cmi->cmi_flags &= ~MS_NODEV;
9f95a23c 246 } else if (strcmp(data, "nodev") == 0) {
eafe8130 247 cmi->cmi_flags |= MS_NODEV;
9f95a23c 248 } else if (strcmp(data, "noexec") == 0) {
eafe8130 249 cmi->cmi_flags |= MS_NOEXEC;
9f95a23c 250 } else if (strcmp(data, "exec") == 0) {
eafe8130 251 cmi->cmi_flags &= ~MS_NOEXEC;
9f95a23c 252 } else if (strcmp(data, "sync") == 0) {
eafe8130 253 cmi->cmi_flags |= MS_SYNCHRONOUS;
9f95a23c 254 } else if (strcmp(data, "remount") == 0) {
eafe8130 255 cmi->cmi_flags |= MS_REMOUNT;
9f95a23c 256 } else if (strcmp(data, "mandlock") == 0) {
eafe8130 257 cmi->cmi_flags |= MS_MANDLOCK;
9f95a23c
TL
258 } else if ((strcmp(data, "nobrl") == 0) ||
259 (strcmp(data, "nolock") == 0)) {
eafe8130 260 cmi->cmi_flags &= ~MS_MANDLOCK;
9f95a23c 261 } else if (strcmp(data, "noatime") == 0) {
eafe8130 262 cmi->cmi_flags |= MS_NOATIME;
9f95a23c 263 } else if (strcmp(data, "nodiratime") == 0) {
eafe8130 264 cmi->cmi_flags |= MS_NODIRATIME;
9f95a23c 265 } else if (strcmp(data, "relatime") == 0) {
eafe8130 266 cmi->cmi_flags |= MS_RELATIME;
9f95a23c 267 } else if (strcmp(data, "strictatime") == 0) {
eafe8130 268 cmi->cmi_flags |= MS_STRICTATIME;
9f95a23c 269 } else if (strcmp(data, "noauto") == 0) {
eafe8130 270 /* ignore */
9f95a23c 271 } else if (strcmp(data, "_netdev") == 0) {
eafe8130 272 /* ignore */
9f95a23c 273 } else if (strcmp(data, "nofail") == 0) {
eafe8130 274 /* ignore */
9f95a23c
TL
275 } else if (strcmp(data, "fs") == 0) {
276 if (!value || !*value) {
277 fprintf(stderr, "mount option fs requires a value.\n");
278 return -EINVAL;
279 }
280 data = "mds_namespace";
281 skip = false;
282 } else if (strcmp(data, "secretfile") == 0) {
eafe8130
TL
283 int ret;
284
7c673cae 285 if (!value || !*value) {
eafe8130
TL
286 fprintf(stderr, "keyword secretfile found, but no secret file specified\n");
287 return -EINVAL;
7c673cae 288 }
eafe8130
TL
289 ret = read_secret_from_file(value, cmi->cmi_secret, sizeof(cmi->cmi_secret));
290 if (ret < 0) {
291 fprintf(stderr, "error reading secret file: %d\n", ret);
292 return ret;
7c673cae 293 }
9f95a23c 294 } else if (strcmp(data, "secret") == 0) {
eafe8130
TL
295 size_t len;
296
7c673cae 297 if (!value || !*value) {
eafe8130
TL
298 fprintf(stderr, "mount option secret requires a value.\n");
299 return -EINVAL;
7c673cae
FG
300 }
301
eafe8130
TL
302 len = strnlen(value, sizeof(cmi->cmi_secret)) + 1;
303 if (len <= sizeof(cmi->cmi_secret))
304 memcpy(cmi->cmi_secret, value, len);
9f95a23c 305 } else if (strcmp(data, "conf") == 0) {
7c673cae 306 if (!value || !*value) {
eafe8130
TL
307 fprintf(stderr, "mount option conf requires a value.\n");
308 return -EINVAL;
7c673cae 309 }
eafe8130
TL
310 /* keep pointer to value */
311 cmi->cmi_conf = strdup(value);
312 if (!cmi->cmi_conf)
313 return -ENOMEM;
9f95a23c 314 } else if (strcmp(data, "name") == 0) {
eafe8130
TL
315 if (!value || !*value) {
316 fprintf(stderr, "mount option name requires a value.\n");
317 return -EINVAL;
7c673cae 318 }
eafe8130
TL
319 /* keep pointer to value */
320 name = value;
321 skip = false;
f67539c2
TL
322 } else if (strcmp(data, "ms_mode") == 0) {
323 if (!value || !*value) {
324 fprintf(stderr, "mount option ms_mode requires a value.\n");
325 return -EINVAL;
326 }
327 /* Only legacy ms_mode needs v1 addrs */
328 v2_addrs = strcmp(value, "legacy");
329 skip = false;
7c673cae 330 } else {
f67539c2 331 /* unrecognized mount options, passing to kernel */
eafe8130 332 skip = false;
7c673cae
FG
333 }
334
335 /* Copy (possibly modified) option to out */
336 if (!skip) {
337 if (pos)
eafe8130 338 pos = safe_cat(&cmi->cmi_opts, &cmi->cmi_opts_len, pos, ",");
7c673cae
FG
339
340 if (value) {
eafe8130
TL
341 pos = safe_cat(&cmi->cmi_opts, &cmi->cmi_opts_len, pos, data);
342 pos = safe_cat(&cmi->cmi_opts, &cmi->cmi_opts_len, pos, "=");
343 pos = safe_cat(&cmi->cmi_opts, &cmi->cmi_opts_len, pos, value);
7c673cae 344 } else {
eafe8130 345 pos = safe_cat(&cmi->cmi_opts, &cmi->cmi_opts_len, pos, data);
7c673cae 346 }
7c673cae
FG
347 }
348 data = next_keyword;
349 } while (data);
350
f67539c2 351out:
eafe8130
TL
352 name_pos = safe_cat(&cmi->cmi_name, &name_len, name_pos, "client.");
353 name_pos = safe_cat(&cmi->cmi_name, &name_len, name_pos,
354 name ? name : CEPH_AUTH_NAME_DEFAULT);
7c673cae 355
f67539c2
TL
356 if (cmi->cmi_opts)
357 mount_ceph_debug("mount.ceph: options \"%s\" will pass to kernel.\n",
358 cmi->cmi_opts);
359
eafe8130
TL
360 if (!cmi->cmi_opts) {
361 cmi->cmi_opts = strdup(EMPTY_STRING);
362 if (!cmi->cmi_opts)
363 return -ENOMEM;
364 }
365 return 0;
7c673cae
FG
366}
367
368
369static int parse_arguments(int argc, char *const *const argv,
370 const char **src, const char **node, const char **opts)
371{
372 int i;
373
374 if (argc < 2) {
375 // There were no arguments. Just show the usage.
376 return 1;
377 }
378 if ((!strcmp(argv[1], "-h")) || (!strcmp(argv[1], "--help"))) {
379 // The user asked for help.
380 return 1;
381 }
382
383 // The first two arguments are positional
384 if (argc < 3)
385 return -EINVAL;
386 *src = argv[1];
387 *node = argv[2];
388
389 // Parse the remaining options
390 *opts = EMPTY_STRING;
391 for (i = 3; i < argc; ++i) {
392 if (!strcmp("-h", argv[i]))
393 return 1;
394 else if (!strcmp("-n", argv[i]))
eafe8130 395 skip_mtab_flag = true;
7c673cae 396 else if (!strcmp("-v", argv[i]))
eafe8130 397 verboseflag = true;
7c673cae
FG
398 else if (!strcmp("-o", argv[i])) {
399 ++i;
400 if (i >= argc) {
eafe8130 401 fprintf(stderr, "Option -o requires an argument.\n\n");
7c673cae
FG
402 return -EINVAL;
403 }
404 *opts = argv[i];
405 }
406 else {
eafe8130 407 fprintf(stderr, "Can't understand option: '%s'\n\n", argv[i]);
7c673cae
FG
408 return -EINVAL;
409 }
410 }
411 return 0;
412}
413
414/* modprobe failing doesn't necessarily prevent from working, so this
415 returns void */
416static void modprobe(void)
417{
418 int r;
419
420 r = module_load("ceph", NULL);
421 if (r)
422 printf("failed to load ceph kernel module (%d)\n", r);
423}
424
425static void usage(const char *prog_name)
426{
427 printf("usage: %s [src] [mount-point] [-n] [-v] [-o ceph-options]\n",
428 prog_name);
429 printf("options:\n");
430 printf("\t-h: Print this help\n");
431 printf("\t-n: Do not update /etc/mtab\n");
432 printf("\t-v: Verbose\n");
433 printf("\tceph-options: refer to mount.ceph(8)\n");
434 printf("\n");
435}
436
eafe8130
TL
437/*
438 * The structure itself lives on the stack, so don't free it. Just the
439 * pointers inside.
440 */
441static void ceph_mount_info_free(struct ceph_mount_info *cmi)
442{
443 free(cmi->cmi_opts);
444 free(cmi->cmi_name);
445 free(cmi->cmi_path);
446 free(cmi->cmi_mons);
447 free(cmi->cmi_conf);
448}
449
92f5a8d4 450static int append_key_or_secret_option(struct ceph_mount_info *cmi)
eafe8130 451{
92f5a8d4 452 int pos = strlen(cmi->cmi_opts);
eafe8130 453
92f5a8d4
TL
454 if (!cmi->cmi_secret[0] && !is_kernel_secret(cmi->cmi_name))
455 return 0;
eafe8130 456
92f5a8d4
TL
457 if (pos)
458 pos = safe_cat(&cmi->cmi_opts, &cmi->cmi_opts_len, pos, ",");
459
460 /* when parsing kernel options (-o remount) we get '<hidden>' as the secret */
461 if (cmi->cmi_secret[0] && (strcmp(cmi->cmi_secret, "<hidden>") != 0)) {
462 int ret = set_kernel_secret(cmi->cmi_secret, cmi->cmi_name);
463 if (ret < 0) {
464 if (ret == -ENODEV || ret == -ENOSYS) {
465 /* old kernel; fall back to secret= in options */
466 pos = safe_cat(&cmi->cmi_opts,
467 &cmi->cmi_opts_len, pos,
468 "secret=");
469 pos = safe_cat(&cmi->cmi_opts,
470 &cmi->cmi_opts_len, pos,
471 cmi->cmi_secret);
472 return 0;
473 }
474 fprintf(stderr, "adding ceph secret key to kernel failed: %s\n",
475 strerror(-ret));
eafe8130 476 return ret;
92f5a8d4 477 }
eafe8130 478 }
92f5a8d4
TL
479
480 pos = safe_cat(&cmi->cmi_opts, &cmi->cmi_opts_len, pos, "key=");
481 pos = safe_cat(&cmi->cmi_opts, &cmi->cmi_opts_len, pos, cmi->cmi_name);
482
eafe8130
TL
483 return 0;
484}
485
7c673cae
FG
486int main(int argc, char *argv[])
487{
488 const char *src, *node, *opts;
489 char *rsrc = NULL;
eafe8130
TL
490 int retval;
491 struct ceph_mount_info cmi = { 0 };
7c673cae
FG
492
493 retval = parse_arguments(argc, argv, &src, &node, &opts);
494 if (retval) {
495 usage(argv[0]);
eafe8130
TL
496 retval = (retval > 0) ? 0 : EX_USAGE;
497 goto out;
7c673cae
FG
498 }
499
eafe8130
TL
500 retval = parse_options(opts, &cmi);
501 if (retval) {
502 fprintf(stderr, "failed to parse ceph_options: %d\n", retval);
503 retval = EX_USAGE;
504 goto out;
505 }
506
507 retval = parse_src(src, &cmi);
508 if (retval) {
509 fprintf(stderr, "unable to parse mount source: %d\n", retval);
510 retval = EX_USAGE;
511 goto out;
512 }
513
514 /* We don't care if this errors out, since this is best-effort */
515 fetch_config_info(&cmi);
516
517 if (!cmi.cmi_mons) {
518 fprintf(stderr, "unable to determine mon addresses\n");
519 retval = EX_USAGE;
520 goto out;
521 }
522
523 rsrc = finalize_src(&cmi);
7c673cae 524 if (!rsrc) {
eafe8130
TL
525 fprintf(stderr, "failed to resolve source\n");
526 retval = EX_USAGE;
527 goto out;
7c673cae
FG
528 }
529
eafe8130 530 /* Ensure the ceph key_type is available */
7c673cae
FG
531 modprobe();
532
92f5a8d4 533 retval = append_key_or_secret_option(&cmi);
eafe8130 534 if (retval) {
92f5a8d4 535 fprintf(stderr, "couldn't append secret option: %d\n", retval);
eafe8130
TL
536 retval = EX_USAGE;
537 goto out;
7c673cae
FG
538 }
539
540 block_signals(SIG_BLOCK);
541
eafe8130
TL
542 if (mount(rsrc, node, "ceph", cmi.cmi_flags, cmi.cmi_opts)) {
543 retval = EX_FAIL;
7c673cae
FG
544 switch (errno) {
545 case ENODEV:
eafe8130 546 fprintf(stderr, "mount error: ceph filesystem not supported by the system\n");
7c673cae 547 break;
92f5a8d4
TL
548 case EHOSTUNREACH:
549 fprintf(stderr, "mount error: no mds server is up or the cluster is laggy\n");
550 break;
7c673cae 551 default:
eafe8130 552 fprintf(stderr, "mount error %d = %s\n",errno,strerror(errno));
7c673cae
FG
553 }
554 } else {
555 if (!skip_mtab_flag) {
eafe8130 556 update_mtab_entry(rsrc, node, "ceph", cmi.cmi_opts, cmi.cmi_flags, 0, 0);
7c673cae
FG
557 }
558 }
559
560 block_signals(SIG_UNBLOCK);
eafe8130
TL
561out:
562 ceph_mount_info_free(&cmi);
7c673cae 563 free(rsrc);
eafe8130 564 return retval;
7c673cae
FG
565}
566