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