]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/storage/lvm.c
fd: only add valid fd to mainloop
[mirror_lxc.git] / src / lxc / storage / lvm.c
1 /*
2 * lxc: linux Container library
3 *
4 * (C) Copyright IBM Corp. 2007, 2008
5 *
6 * Authors:
7 * Daniel Lezcano <daniel.lezcano at free.fr>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 #ifndef _GNU_SOURCE
25 #define _GNU_SOURCE 1
26 #endif
27 #define __STDC_FORMAT_MACROS
28 #include <inttypes.h>
29 #include <stdint.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/sysmacros.h>
34 #include <sys/wait.h>
35 #include <unistd.h>
36
37 #include "config.h"
38 #include "log.h"
39 #include "lvm.h"
40 #include "memory_utils.h"
41 #include "rsync.h"
42 #include "storage.h"
43 #include "storage_utils.h"
44 #include "utils.h"
45
46 #ifdef MAJOR_IN_MKDEV
47 #include <sys/mkdev.h>
48 #endif
49
50 lxc_log_define(lvm, lxc);
51
52 struct lvcreate_args {
53 const char *size;
54 const char *vg;
55 const char *lv;
56 const char *thinpool;
57 const char *fstype;
58 bool sigwipe;
59
60 /* snapshot specific arguments */
61 const char *source_lv;
62 };
63
64 static int lvm_destroy_exec_wrapper(void *data)
65 {
66 struct lvcreate_args *args = data;
67
68 (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1);
69 execlp("lvremove", "lvremove", "-f", args->lv, (char *)NULL);
70
71 return -1;
72 }
73
74 static int lvm_create_exec_wrapper(void *data)
75 {
76 struct lvcreate_args *args = data;
77
78 (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1);
79 if (args->thinpool)
80 if(args->sigwipe)
81 execlp("lvcreate", "lvcreate", "-Wy", "--yes", "--thinpool", args->thinpool,
82 "-V", args->size, args->vg, "-n", args->lv, (char *)NULL);
83 else
84 execlp("lvcreate", "lvcreate", "-qq", "--thinpool", args->thinpool,
85 "-V", args->size, args->vg, "-n", args->lv, (char *)NULL);
86 else
87 if(args->sigwipe)
88 execlp("lvcreate", "lvcreate", "-Wy", "--yes", "-L", args->size, args->vg, "-n",
89 args->lv, (char *)NULL);
90 else
91 execlp("lvcreate", "lvcreate", "-qq", "-L", args->size, args->vg, "-n",
92 args->lv, (char *)NULL);
93
94 return -1;
95 }
96
97 static int lvm_snapshot_exec_wrapper(void *data)
98 {
99 struct lvcreate_args *args = data;
100
101 (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1);
102 if (args->thinpool)
103 execlp("lvcreate", "lvcreate", "-s", "-n", args->lv,
104 args->source_lv, (char *)NULL);
105 else
106 execlp("lvcreate", "lvcreate", "-s", "-L", args->size, "-n",
107 args->lv, args->source_lv, (char *)NULL);
108
109 return -1;
110 }
111
112 /* The path must be "/dev/<vg>/<lv>". The volume group <vg> must be an existing
113 * volume group, and the logical volume <lv> must not yet exist.
114 * This function will attempt to create "/dev/<vg>/<lv> of size <size>. If
115 * thinpool is specified, we'll check for it's existence and if it's a valid
116 * thin pool, and if so, we'll create the requested logical volume from that
117 * thin pool.
118 */
119 static int do_lvm_create(const char *path, uint64_t size, const char *thinpool)
120 {
121 int len, ret;
122 char *pathdup, *vg, *lv;
123 char cmd_output[PATH_MAX];
124 char sz[24];
125 __do_free char *tp = NULL;
126 struct lvcreate_args cmd_args = {0};
127
128 ret = snprintf(sz, 24, "%" PRIu64 "b", size);
129 if (ret < 0 || ret >= 24) {
130 ERROR("Failed to create string: %d", ret);
131 return -1;
132 }
133
134 pathdup = strdup(path);
135 if (!pathdup) {
136 ERROR("Failed to duplicate string \"%s\"", path);
137 return -1;
138 }
139
140 lv = strrchr(pathdup, '/');
141 if (!lv) {
142 ERROR("Failed to detect \"/\" in string \"%s\"", pathdup);
143 free(pathdup);
144 return -1;
145 }
146 *lv = '\0';
147 lv++;
148 TRACE("Parsed logical volume \"%s\"", lv);
149
150 vg = strrchr(pathdup, '/');
151 if (!vg) {
152 ERROR("Failed to detect \"/\" in string \"%s\"", pathdup);
153 free(pathdup);
154 return -1;
155 }
156 vg++;
157 TRACE("Parsed volume group \"%s\"", vg);
158
159 if (thinpool) {
160 len = strlen(pathdup) + strlen(thinpool) + 2;
161 tp = must_realloc(NULL, len);
162
163 ret = snprintf(tp, len, "%s/%s", pathdup, thinpool);
164 if (ret < 0 || ret >= len) {
165 ERROR("Failed to create string: %d", ret);
166 free(pathdup);
167 return -1;
168 }
169
170 ret = lvm_is_thin_pool(tp);
171 TRACE("got %d for thin pool at path: %s", ret, tp);
172 if (ret < 0) {
173 ERROR("Failed to detect whether \"%s\" is a thinpool", tp);
174 free(pathdup);
175 return -1;
176 } else if (!ret) {
177 TRACE("Detected that \"%s\" is not a thinpool", tp);
178 tp = NULL;
179 } else {
180 TRACE("Detected \"%s\" is a thinpool", tp);
181 }
182 }
183
184 cmd_args.thinpool = tp;
185 cmd_args.vg = vg;
186 cmd_args.lv = lv;
187 cmd_args.size = sz;
188 cmd_args.sigwipe = true;
189 TRACE("Creating new lvm storage volume \"%s\" on volume group \"%s\" "
190 "of size \"%s\"", lv, vg, sz);
191 ret = run_command_status(cmd_output, sizeof(cmd_output),
192 lvm_create_exec_wrapper, (void *)&cmd_args);
193
194 /* If lvcreate is old and doesn't support signature wiping, try again without it.
195 * Test for exit code EINVALID_CMD_LINE(3) of lvcreate command.
196 */
197 if (WIFEXITED(ret) && WEXITSTATUS(ret) == 3) {
198 cmd_args.sigwipe = false;
199 ret = run_command(cmd_output, sizeof(cmd_output),
200 lvm_create_exec_wrapper, (void *)&cmd_args);
201 }
202
203 if (ret != 0) {
204 ERROR("Failed to create logical volume \"%s\": %s", lv,
205 cmd_output);
206 free(pathdup);
207 return -1;
208 }
209 TRACE("Created new lvm storage volume \"%s\" on volume group \"%s\" "
210 "of size \"%s\"", lv, vg, sz);
211
212 free(pathdup);
213 return ret;
214 }
215
216 /* Look at "/sys/dev/block/maj:min/dm/uuid". If it contains the hardcoded LVM
217 * prefix "LVM-" then this is an lvm2 LV.
218 */
219 bool lvm_detect(const char *path)
220 {
221 int fd;
222 ssize_t ret;
223 struct stat statbuf;
224 char devp[PATH_MAX], buf[4];
225
226 if (!strncmp(path, "lvm:", 4))
227 return true;
228
229 ret = stat(path, &statbuf);
230 if (ret < 0)
231 return false;
232
233 if (!S_ISBLK(statbuf.st_mode))
234 return false;
235
236 ret = snprintf(devp, PATH_MAX, "/sys/dev/block/%d:%d/dm/uuid",
237 major(statbuf.st_rdev), minor(statbuf.st_rdev));
238 if (ret < 0 || ret >= PATH_MAX) {
239 ERROR("Failed to create string");
240 return false;
241 }
242
243 fd = open(devp, O_RDONLY);
244 if (fd < 0)
245 return false;
246
247 ret = read(fd, buf, sizeof(buf));
248 close(fd);
249 if (ret != sizeof(buf))
250 return false;
251
252 if (strncmp(buf, "LVM-", 4))
253 return false;
254
255 return true;
256 }
257
258 int lvm_mount(struct lxc_storage *bdev)
259 {
260 const char *src;
261
262 if (strcmp(bdev->type, "lvm"))
263 return -22;
264
265 if (!bdev->src || !bdev->dest)
266 return -22;
267
268 src = lxc_storage_get_path(bdev->src, bdev->type);
269
270 /* If we might pass in data sometime, then we'll have to enrich
271 * mount_unknown_fs().
272 */
273 return mount_unknown_fs(src, bdev->dest, bdev->mntopts);
274 }
275
276 int lvm_umount(struct lxc_storage *bdev)
277 {
278 if (strcmp(bdev->type, "lvm"))
279 return -22;
280
281 if (!bdev->src || !bdev->dest)
282 return -22;
283
284 return umount(bdev->dest);
285 }
286
287 #define __LVSCMD "lvs --unbuffered --noheadings -o lv_attr %s 2>/dev/null"
288 int lvm_compare_lv_attr(const char *path, int pos, const char expected)
289 {
290 __do_free char *cmd = NULL;
291 struct lxc_popen_FILE *f;
292 int ret, status;
293 size_t len;
294 char output[12];
295 int start = 0;
296
297 len = strlen(__LVSCMD) + strlen(path) + 1;
298 cmd = must_realloc(NULL, len);
299
300 ret = snprintf(cmd, len, __LVSCMD, path);
301 if (ret < 0 || (size_t)ret >= len)
302 return -1;
303
304 f = lxc_popen(cmd);
305 if (!f) {
306 SYSERROR("popen failed");
307 return -1;
308 }
309
310 ret = 0;
311 if (!fgets(output, 12, f->f))
312 ret = 1;
313
314 status = lxc_pclose(f);
315 /* Assume either vg or lvs do not exist, default comparison to false. */
316 if (ret || WEXITSTATUS(status))
317 return 0;
318
319 len = strlen(output);
320 while (start < len && output[start] == ' ')
321 start++;
322
323 if (start + pos < len && output[start + pos] == expected)
324 return 1;
325
326 return 0;
327 }
328
329 int lvm_is_thin_volume(const char *path)
330 {
331 return lvm_compare_lv_attr(path, 6, 't');
332 }
333
334 int lvm_is_thin_pool(const char *path)
335 {
336 return lvm_compare_lv_attr(path, 0, 't');
337 }
338
339 static int lvm_snapshot_create_new_uuid_wrapper(void *data)
340 {
341 struct lvcreate_args *args = data;
342
343 if (strcmp(args->fstype, "xfs") == 0)
344 execlp("xfs_admin", "xfs_admin", "-U", "generate", args->lv, (char *)NULL);
345
346 if (strcmp(args->fstype, "btrfs") == 0)
347 execlp("btrfstune", "btrfstune", "-f", "-u", args->lv, (char *)NULL);
348
349 return 0;
350 }
351
352 static int lvm_snapshot(struct lxc_storage *orig, const char *path, uint64_t size)
353 {
354 int ret;
355 char *lv, *pathdup;
356 char sz[24];
357 char fstype[100];
358 char cmd_output[PATH_MAX];
359 char repairchar;
360 const char *origsrc;
361 struct lvcreate_args cmd_args = {0};
362
363 ret = snprintf(sz, 24, "%" PRIu64 "b", size);
364 if (ret < 0 || ret >= 24) {
365 ERROR("Failed to create string");
366 return -1;
367 }
368
369 pathdup = strdup(path);
370 if (!pathdup) {
371 ERROR("Failed to duplicate string \"%s\"", path);
372 return -1;
373 }
374
375 lv = strrchr(pathdup, '/');
376 if (!lv) {
377 ERROR("Failed to detect \"/\" in string \"%s\"", pathdup);
378 free(pathdup);
379 return -1;
380 }
381 repairchar = *lv;
382 *lv = '\0';
383 lv++;
384 TRACE("Parsed logical volume \"%s\"", lv);
385
386 /* Check if the original logical volume is backed by a thinpool, in
387 * which case we cannot specify a size that's different from the
388 * original size.
389 */
390 origsrc = lxc_storage_get_path(orig->src, "lvm");
391 ret = lvm_is_thin_volume(origsrc);
392 if (ret < 0) {
393 free(pathdup);
394 return -1;
395 } else if (ret) {
396 cmd_args.thinpool = origsrc;
397 }
398
399 cmd_args.lv = lv;
400 cmd_args.source_lv = origsrc;
401 cmd_args.size = sz;
402 TRACE("Creating new lvm snapshot \"%s\" of \"%s\" with size \"%s\"", lv,
403 origsrc, sz);
404 ret = run_command(cmd_output, sizeof(cmd_output),
405 lvm_snapshot_exec_wrapper, (void *)&cmd_args);
406 if (ret < 0) {
407 ERROR("Failed to create logical volume \"%s\": %s", lv, cmd_output);
408 free(pathdup);
409 return -1;
410 }
411
412 if (detect_fs(orig, fstype, 100) < 0) {
413 INFO("Failed to detect filesystem type for \"%s\"", origsrc);
414 free(pathdup);
415 return -1;
416 }
417
418 /* repair path */
419 lv--;
420 *lv = repairchar;
421 cmd_args.lv = pathdup;
422 cmd_args.fstype = fstype;
423 ret = run_command(cmd_output, sizeof(cmd_output),
424 lvm_snapshot_create_new_uuid_wrapper, (void *)&cmd_args);
425 if (ret < 0) {
426 ERROR("Failed to create new uuid for volume \"%s\": %s", pathdup, cmd_output);
427 free(pathdup);
428 return -1;
429 }
430
431 free(pathdup);
432 return 0;
433 }
434
435 int lvm_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
436 const char *oldname, const char *cname, const char *oldpath,
437 const char *lxcpath, int snap, uint64_t newsize,
438 struct lxc_conf *conf)
439 {
440 int len, ret;
441 const char *vg;
442
443 if (!orig->src || !orig->dest)
444 return -1;
445
446 if (strcmp(orig->type, "lvm") && snap) {
447 ERROR("LVM snapshot from \"%s\" storage driver is not supported",
448 orig->type);
449 return -1;
450 }
451
452 if (strcmp(orig->type, "lvm")) {
453 vg = lxc_global_config_value("lxc.bdev.lvm.vg");
454 new->src = lxc_string_join(
455 "/",
456 (const char *[]){"lvm:", "dev", vg, cname, NULL},
457 false);
458 } else {
459 const char *src;
460 char *dup, *slider;
461
462 src = lxc_storage_get_path(orig->src, orig->type);
463
464 dup = strdup(src);
465 if (!dup) {
466 ERROR("Failed to duplicate string \"%s\"", src);
467 return -1;
468 }
469
470 slider = strrchr(dup, '/');
471 if (!slider) {
472 ERROR("Failed to detect \"/\" in string \"%s\"", dup);
473 free(dup);
474 return -1;
475 }
476 *slider = '\0';
477 slider = dup;
478
479 new->src = lxc_string_join(
480 "/",
481 (const char *[]){"lvm:", *slider == '/' ? ++slider : slider,
482 cname, NULL},
483 false);
484 free(dup);
485 }
486 if (!new->src) {
487 ERROR("Failed to create string");
488 return -1;
489 }
490
491 if (orig->mntopts) {
492 new->mntopts = strdup(orig->mntopts);
493 if (!new->mntopts) {
494 ERROR("Failed to duplicate string \"%s\"", orig->mntopts);
495 return -1;
496 }
497 }
498
499 len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 3;
500 new->dest = malloc(len);
501 if (!new->dest) {
502 ERROR("Failed to allocate memory");
503 return -1;
504 }
505
506 ret = snprintf(new->dest, len, "%s/%s/rootfs", lxcpath, cname);
507 if (ret < 0 || ret >= len) {
508 ERROR("Failed to create string");
509 return -1;
510 }
511
512 ret = mkdir_p(new->dest, 0755);
513 if (ret < 0) {
514 SYSERROR("Failed to create directory \"%s\"", new->dest);
515 return -1;
516 }
517
518 return 0;
519 }
520
521 bool lvm_create_clone(struct lxc_conf *conf, struct lxc_storage *orig,
522 struct lxc_storage *new, uint64_t newsize)
523 {
524 int ret;
525 const char *src;
526 const char *thinpool;
527 struct rsync_data data;
528 const char *cmd_args[2];
529 char cmd_output[PATH_MAX] = {0};
530 char fstype[100] = "ext4";
531 uint64_t size = newsize;
532
533 if (is_blktype(orig)) {
534 /* detect size */
535 if (!newsize && blk_getsize(orig, &size) < 0) {
536 ERROR("Failed to detect size of logical volume \"%s\"",
537 orig->src);
538 return false;
539 }
540
541 /* detect filesystem */
542 if (detect_fs(orig, fstype, 100) < 0) {
543 INFO("Failed to detect filesystem type for \"%s\"", orig->src);
544 return false;
545 }
546 } else if (!newsize) {
547 size = DEFAULT_FS_SIZE;
548 }
549
550 src = lxc_storage_get_path(new->src, "lvm");
551 thinpool = lxc_global_config_value("lxc.bdev.lvm.thin_pool");
552
553 ret = do_lvm_create(src, size, thinpool);
554 if (ret < 0) {
555 ERROR("Failed to create lvm storage volume \"%s\"", src);
556 return false;
557 }
558
559 cmd_args[0] = fstype;
560 cmd_args[1] = src;
561 ret = run_command(cmd_output, sizeof(cmd_output),
562 do_mkfs_exec_wrapper, (void *)cmd_args);
563 if (ret < 0) {
564 ERROR("Failed to create new filesystem \"%s\" for lvm storage "
565 "volume \"%s\": %s", fstype, src, cmd_output);
566 return false;
567 }
568
569 data.orig = orig;
570 data.new = new;
571 ret = run_command(cmd_output, sizeof(cmd_output),
572 lxc_storage_rsync_exec_wrapper, (void *)&data);
573 if (ret < 0) {
574 ERROR("Failed to rsync from \"%s\" to \"%s\"", orig->dest,
575 new->dest);
576 return false;
577 }
578
579 TRACE("Created lvm storage volume \"%s\"", new->dest);
580 return true;
581 }
582
583 bool lvm_create_snapshot(struct lxc_conf *conf, struct lxc_storage *orig,
584 struct lxc_storage *new, uint64_t newsize)
585 {
586 int ret;
587 const char *newsrc;
588 uint64_t size = newsize;
589
590 if (is_blktype(orig)) {
591 if (!newsize && blk_getsize(orig, &size) < 0) {
592 ERROR("Failed to detect size of logical volume \"%s\"",
593 orig->src);
594 return -1;
595 }
596 } else if (!newsize) {
597 size = DEFAULT_FS_SIZE;
598 }
599
600 newsrc = lxc_storage_get_path(new->src, "lvm");
601
602 ret = lvm_snapshot(orig, newsrc, size);
603 if (ret < 0) {
604 ERROR("Failed to create lvm \"%s\" snapshot of \"%s\"",
605 new->src, orig->src);
606 return false;
607 }
608
609 TRACE("Created lvm snapshot \"%s\" from \"%s\"", new->dest, orig->dest);
610 return true;
611 }
612
613 int lvm_destroy(struct lxc_storage *orig)
614 {
615 int ret;
616 char cmd_output[PATH_MAX];
617 struct lvcreate_args cmd_args = {0};
618
619 cmd_args.lv = lxc_storage_get_path(orig->src, "lvm");
620 ret = run_command(cmd_output, sizeof(cmd_output),
621 lvm_destroy_exec_wrapper, (void *)&cmd_args);
622 if (ret < 0) {
623 ERROR("Failed to destroy logical volume \"%s\": %s", orig->src,
624 cmd_output);
625 return -1;
626 }
627
628 TRACE("Destroyed logical volume \"%s\"", orig->src);
629 return 0;
630 }
631
632 int lvm_create(struct lxc_storage *bdev, const char *dest, const char *n,
633 struct bdev_specs *specs)
634 {
635 const char *vg, *thinpool, *fstype, *lv = n;
636 uint64_t sz;
637 int ret, len;
638 const char *cmd_args[2];
639 char cmd_output[PATH_MAX];
640
641 if (!specs)
642 return -1;
643
644 vg = specs->lvm.vg;
645 if (!vg)
646 vg = lxc_global_config_value("lxc.bdev.lvm.vg");
647
648 thinpool = specs->lvm.thinpool;
649 if (!thinpool)
650 thinpool = lxc_global_config_value("lxc.bdev.lvm.thin_pool");
651
652 /* /dev/$vg/$lv */
653 if (specs->lvm.lv)
654 lv = specs->lvm.lv;
655
656 len = strlen(vg) + strlen(lv) + 4 + 7;
657 bdev->src = malloc(len);
658 if (!bdev->src) {
659 ERROR("Failed to allocate memory");
660 return -1;
661 }
662
663 ret = snprintf(bdev->src, len, "lvm:/dev/%s/%s", vg, lv);
664 if (ret < 0 || ret >= len) {
665 ERROR("Failed to create string");
666 return -1;
667 }
668
669 /* size is in bytes */
670 sz = specs->fssize;
671 if (!sz)
672 sz = DEFAULT_FS_SIZE;
673
674 ret = do_lvm_create(bdev->src + 4, sz, thinpool);
675 if (ret < 0) {
676 ERROR("Error creating new logical volume \"%s\" of size "
677 "\"%" PRIu64 " bytes\"", bdev->src, sz);
678 return -1;
679 }
680
681 fstype = specs->fstype;
682 if (!fstype)
683 fstype = DEFAULT_FSTYPE;
684
685 cmd_args[0] = fstype;
686 cmd_args[1] = lxc_storage_get_path(bdev->src, bdev->type);
687 ret = run_command(cmd_output, sizeof(cmd_output), do_mkfs_exec_wrapper,
688 (void *)cmd_args);
689 if (ret < 0) {
690 ERROR("Failed to create new logical volume \"%s\": %s",
691 bdev->src, cmd_output);
692 return -1;
693 }
694
695 bdev->dest = strdup(dest);
696 if (!bdev->dest) {
697 ERROR("Failed to duplicate string \"%s\"", dest);
698 return -1;
699 }
700
701 ret = mkdir_p(bdev->dest, 0755);
702 if (ret < 0) {
703 SYSERROR("Failed to create directory \"%s\"", bdev->dest);
704 return -1;
705 }
706
707 TRACE("Created new logical volume \"%s\"", bdev->dest);
708 return 0;
709 }