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