]> git.proxmox.com Git - ceph.git/blob - ceph/src/mount/mount.ceph.c
update source to Ceph Pacific 16.2.2
[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 = false;
23 static 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
30 struct 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
41 static 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
51 void 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
62 static int parse_src(const char *orig_str, struct ceph_mount_info *cmi)
63 {
64 size_t len;
65 char *mount_path;
66
67 mount_path = strstr(orig_str, ":/");
68 if (!mount_path) {
69 fprintf(stderr, "source mount path was not specified\n");
70 return -EINVAL;
71 }
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;
78 }
79
80 mount_path++;
81 cmi->cmi_path = strdup(mount_path);
82 if (!cmi->cmi_path)
83 return -ENOMEM;
84 return 0;
85 }
86
87 static char *finalize_src(struct ceph_mount_info *cmi)
88 {
89 int pos, len;
90 char *src;
91
92 src = resolve_addrs(cmi->cmi_mons);
93 if (!src)
94 return NULL;
95
96 len = strlen(src);
97 pos = safe_cat(&src, &len, len, ":");
98 safe_cat(&src, &len, pos, cmi->cmi_path);
99
100 return src;
101 }
102
103 static int
104 drop_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 */
129 static 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);
159 mount_ceph_get_config_info(cmi->cmi_conf, cmi->cmi_name, v2_addrs, cci);
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 }
196 out:
197 munmap(cci, sizeof(*cci));
198 return ret;
199 }
200
201 /*
202 * this one is partially based on parse_options() from cifs.mount.c
203 */
204 static int parse_options(const char *data, struct ceph_mount_info *cmi)
205 {
206 char * next_keyword = NULL;
207 int pos = 0;
208 char *name = NULL;
209 int name_len = 0;
210 int name_pos = 0;
211
212 if (data == EMPTY_STRING)
213 goto out;
214
215 mount_ceph_debug("parsing options: %s\n", data);
216
217 do {
218 char * value = NULL;
219 bool skip = true;
220
221 /* check if ends with trailing comma */
222 if(*data == 0)
223 break;
224 next_keyword = strchr(data,',');
225
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
236 if (strcmp(data, "ro") == 0) {
237 cmi->cmi_flags |= MS_RDONLY;
238 } else if (strcmp(data, "rw") == 0) {
239 cmi->cmi_flags &= ~MS_RDONLY;
240 } else if (strcmp(data, "nosuid") == 0) {
241 cmi->cmi_flags |= MS_NOSUID;
242 } else if (strcmp(data, "suid") == 0) {
243 cmi->cmi_flags &= ~MS_NOSUID;
244 } else if (strcmp(data, "dev") == 0) {
245 cmi->cmi_flags &= ~MS_NODEV;
246 } else if (strcmp(data, "nodev") == 0) {
247 cmi->cmi_flags |= MS_NODEV;
248 } else if (strcmp(data, "noexec") == 0) {
249 cmi->cmi_flags |= MS_NOEXEC;
250 } else if (strcmp(data, "exec") == 0) {
251 cmi->cmi_flags &= ~MS_NOEXEC;
252 } else if (strcmp(data, "sync") == 0) {
253 cmi->cmi_flags |= MS_SYNCHRONOUS;
254 } else if (strcmp(data, "remount") == 0) {
255 cmi->cmi_flags |= MS_REMOUNT;
256 } else if (strcmp(data, "mandlock") == 0) {
257 cmi->cmi_flags |= MS_MANDLOCK;
258 } else if ((strcmp(data, "nobrl") == 0) ||
259 (strcmp(data, "nolock") == 0)) {
260 cmi->cmi_flags &= ~MS_MANDLOCK;
261 } else if (strcmp(data, "noatime") == 0) {
262 cmi->cmi_flags |= MS_NOATIME;
263 } else if (strcmp(data, "nodiratime") == 0) {
264 cmi->cmi_flags |= MS_NODIRATIME;
265 } else if (strcmp(data, "relatime") == 0) {
266 cmi->cmi_flags |= MS_RELATIME;
267 } else if (strcmp(data, "strictatime") == 0) {
268 cmi->cmi_flags |= MS_STRICTATIME;
269 } else if (strcmp(data, "noauto") == 0) {
270 /* ignore */
271 } else if (strcmp(data, "_netdev") == 0) {
272 /* ignore */
273 } else if (strcmp(data, "nofail") == 0) {
274 /* ignore */
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) {
283 int ret;
284
285 if (!value || !*value) {
286 fprintf(stderr, "keyword secretfile found, but no secret file specified\n");
287 return -EINVAL;
288 }
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;
293 }
294 } else if (strcmp(data, "secret") == 0) {
295 size_t len;
296
297 if (!value || !*value) {
298 fprintf(stderr, "mount option secret requires a value.\n");
299 return -EINVAL;
300 }
301
302 len = strnlen(value, sizeof(cmi->cmi_secret)) + 1;
303 if (len <= sizeof(cmi->cmi_secret))
304 memcpy(cmi->cmi_secret, value, len);
305 } else if (strcmp(data, "conf") == 0) {
306 if (!value || !*value) {
307 fprintf(stderr, "mount option conf requires a value.\n");
308 return -EINVAL;
309 }
310 /* keep pointer to value */
311 cmi->cmi_conf = strdup(value);
312 if (!cmi->cmi_conf)
313 return -ENOMEM;
314 } else if (strcmp(data, "name") == 0) {
315 if (!value || !*value) {
316 fprintf(stderr, "mount option name requires a value.\n");
317 return -EINVAL;
318 }
319 /* keep pointer to value */
320 name = value;
321 skip = false;
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;
330 } else {
331 /* unrecognized mount options, passing to kernel */
332 skip = false;
333 }
334
335 /* Copy (possibly modified) option to out */
336 if (!skip) {
337 if (pos)
338 pos = safe_cat(&cmi->cmi_opts, &cmi->cmi_opts_len, pos, ",");
339
340 if (value) {
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);
344 } else {
345 pos = safe_cat(&cmi->cmi_opts, &cmi->cmi_opts_len, pos, data);
346 }
347 }
348 data = next_keyword;
349 } while (data);
350
351 out:
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);
355
356 if (cmi->cmi_opts)
357 mount_ceph_debug("mount.ceph: options \"%s\" will pass to kernel.\n",
358 cmi->cmi_opts);
359
360 if (!cmi->cmi_opts) {
361 cmi->cmi_opts = strdup(EMPTY_STRING);
362 if (!cmi->cmi_opts)
363 return -ENOMEM;
364 }
365 return 0;
366 }
367
368
369 static 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]))
395 skip_mtab_flag = true;
396 else if (!strcmp("-v", argv[i]))
397 verboseflag = true;
398 else if (!strcmp("-o", argv[i])) {
399 ++i;
400 if (i >= argc) {
401 fprintf(stderr, "Option -o requires an argument.\n\n");
402 return -EINVAL;
403 }
404 *opts = argv[i];
405 }
406 else {
407 fprintf(stderr, "Can't understand option: '%s'\n\n", argv[i]);
408 return -EINVAL;
409 }
410 }
411 return 0;
412 }
413
414 /* modprobe failing doesn't necessarily prevent from working, so this
415 returns void */
416 static 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
425 static 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
437 /*
438 * The structure itself lives on the stack, so don't free it. Just the
439 * pointers inside.
440 */
441 static 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
450 static int append_key_or_secret_option(struct ceph_mount_info *cmi)
451 {
452 int pos = strlen(cmi->cmi_opts);
453
454 if (!cmi->cmi_secret[0] && !is_kernel_secret(cmi->cmi_name))
455 return 0;
456
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));
476 return ret;
477 }
478 }
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
483 return 0;
484 }
485
486 int main(int argc, char *argv[])
487 {
488 const char *src, *node, *opts;
489 char *rsrc = NULL;
490 int retval;
491 struct ceph_mount_info cmi = { 0 };
492
493 retval = parse_arguments(argc, argv, &src, &node, &opts);
494 if (retval) {
495 usage(argv[0]);
496 retval = (retval > 0) ? 0 : EX_USAGE;
497 goto out;
498 }
499
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);
524 if (!rsrc) {
525 fprintf(stderr, "failed to resolve source\n");
526 retval = EX_USAGE;
527 goto out;
528 }
529
530 /* Ensure the ceph key_type is available */
531 modprobe();
532
533 retval = append_key_or_secret_option(&cmi);
534 if (retval) {
535 fprintf(stderr, "couldn't append secret option: %d\n", retval);
536 retval = EX_USAGE;
537 goto out;
538 }
539
540 block_signals(SIG_BLOCK);
541
542 if (mount(rsrc, node, "ceph", cmi.cmi_flags, cmi.cmi_opts)) {
543 retval = EX_FAIL;
544 switch (errno) {
545 case ENODEV:
546 fprintf(stderr, "mount error: ceph filesystem not supported by the system\n");
547 break;
548 case EHOSTUNREACH:
549 fprintf(stderr, "mount error: no mds server is up or the cluster is laggy\n");
550 break;
551 default:
552 fprintf(stderr, "mount error %d = %s\n",errno,strerror(errno));
553 }
554 } else {
555 if (!skip_mtab_flag) {
556 update_mtab_entry(rsrc, node, "ceph", cmi.cmi_opts, cmi.cmi_flags, 0, 0);
557 }
558 }
559
560 block_signals(SIG_UNBLOCK);
561 out:
562 ceph_mount_info_free(&cmi);
563 free(rsrc);
564 return retval;
565 }
566