2 * lxc: linux Container library
4 * (C) Copyright IBM Corp. 2007, 2008
7 * Daniel Lezcano <daniel.lezcano at free.fr>
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.
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.
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
27 #define __STDC_FORMAT_MACROS
33 #include <sys/sysmacros.h>
40 #include "memory_utils.h"
43 #include "storage_utils.h"
47 #include <sys/mkdev.h>
50 lxc_log_define(lvm
, lxc
);
52 struct lvcreate_args
{
60 /* snapshot specific arguments */
61 const char *source_lv
;
64 static int lvm_destroy_exec_wrapper(void *data
)
66 struct lvcreate_args
*args
= data
;
68 (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1);
69 execlp("lvremove", "lvremove", "-f", args
->lv
, (char *)NULL
);
74 static int lvm_create_exec_wrapper(void *data
)
76 struct lvcreate_args
*args
= data
;
78 (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1);
81 execlp("lvcreate", "lvcreate", "-Wy", "--yes", "--thinpool", args
->thinpool
,
82 "-V", args
->size
, args
->vg
, "-n", args
->lv
, (char *)NULL
);
84 execlp("lvcreate", "lvcreate", "-qq", "--thinpool", args
->thinpool
,
85 "-V", args
->size
, args
->vg
, "-n", args
->lv
, (char *)NULL
);
88 execlp("lvcreate", "lvcreate", "-Wy", "--yes", "-L", args
->size
, args
->vg
, "-n",
89 args
->lv
, (char *)NULL
);
91 execlp("lvcreate", "lvcreate", "-qq", "-L", args
->size
, args
->vg
, "-n",
92 args
->lv
, (char *)NULL
);
97 static int lvm_snapshot_exec_wrapper(void *data
)
99 struct lvcreate_args
*args
= data
;
101 (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1);
103 execlp("lvcreate", "lvcreate", "-s", "-n", args
->lv
,
104 args
->source_lv
, (char *)NULL
);
106 execlp("lvcreate", "lvcreate", "-s", "-L", args
->size
, "-n",
107 args
->lv
, args
->source_lv
, (char *)NULL
);
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
119 static int do_lvm_create(const char *path
, uint64_t size
, const char *thinpool
)
122 char *pathdup
, *vg
, *lv
;
123 char cmd_output
[PATH_MAX
];
125 __do_free
char *tp
= NULL
;
126 struct lvcreate_args cmd_args
= {0};
128 ret
= snprintf(sz
, 24, "%" PRIu64
"b", size
);
129 if (ret
< 0 || ret
>= 24) {
130 ERROR("Failed to create string: %d", ret
);
134 pathdup
= strdup(path
);
136 ERROR("Failed to duplicate string \"%s\"", path
);
140 lv
= strrchr(pathdup
, '/');
142 ERROR("Failed to detect \"/\" in string \"%s\"", pathdup
);
148 TRACE("Parsed logical volume \"%s\"", lv
);
150 vg
= strrchr(pathdup
, '/');
152 ERROR("Failed to detect \"/\" in string \"%s\"", pathdup
);
157 TRACE("Parsed volume group \"%s\"", vg
);
160 len
= strlen(pathdup
) + strlen(thinpool
) + 2;
161 tp
= must_realloc(NULL
, len
);
163 ret
= snprintf(tp
, len
, "%s/%s", pathdup
, thinpool
);
164 if (ret
< 0 || ret
>= len
) {
165 ERROR("Failed to create string: %d", ret
);
170 ret
= lvm_is_thin_pool(tp
);
171 TRACE("got %d for thin pool at path: %s", ret
, tp
);
173 ERROR("Failed to detect whether \"%s\" is a thinpool", tp
);
177 TRACE("Detected that \"%s\" is not a thinpool", tp
);
180 TRACE("Detected \"%s\" is a thinpool", tp
);
184 cmd_args
.thinpool
= tp
;
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
);
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.
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
);
204 ERROR("Failed to create logical volume \"%s\": %s", lv
,
209 TRACE("Created new lvm storage volume \"%s\" on volume group \"%s\" "
210 "of size \"%s\"", lv
, vg
, sz
);
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.
219 bool lvm_detect(const char *path
)
224 char devp
[PATH_MAX
], buf
[4];
226 if (!strncmp(path
, "lvm:", 4))
229 ret
= stat(path
, &statbuf
);
233 if (!S_ISBLK(statbuf
.st_mode
))
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");
243 fd
= open(devp
, O_RDONLY
);
247 ret
= read(fd
, buf
, sizeof(buf
));
249 if (ret
!= sizeof(buf
))
252 if (strncmp(buf
, "LVM-", 4))
258 int lvm_mount(struct lxc_storage
*bdev
)
262 if (strcmp(bdev
->type
, "lvm"))
265 if (!bdev
->src
|| !bdev
->dest
)
268 src
= lxc_storage_get_path(bdev
->src
, bdev
->type
);
270 /* If we might pass in data sometime, then we'll have to enrich
271 * mount_unknown_fs().
273 return mount_unknown_fs(src
, bdev
->dest
, bdev
->mntopts
);
276 int lvm_umount(struct lxc_storage
*bdev
)
278 if (strcmp(bdev
->type
, "lvm"))
281 if (!bdev
->src
|| !bdev
->dest
)
284 return umount(bdev
->dest
);
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
)
290 __do_free
char *cmd
= NULL
;
291 struct lxc_popen_FILE
*f
;
297 len
= strlen(__LVSCMD
) + strlen(path
) + 1;
298 cmd
= must_realloc(NULL
, len
);
300 ret
= snprintf(cmd
, len
, __LVSCMD
, path
);
301 if (ret
< 0 || (size_t)ret
>= len
)
306 SYSERROR("popen failed");
311 if (!fgets(output
, 12, f
->f
))
314 status
= lxc_pclose(f
);
315 /* Assume either vg or lvs do not exist, default comparison to false. */
316 if (ret
|| WEXITSTATUS(status
))
319 len
= strlen(output
);
320 while (start
< len
&& output
[start
] == ' ')
323 if (start
+ pos
< len
&& output
[start
+ pos
] == expected
)
329 int lvm_is_thin_volume(const char *path
)
331 return lvm_compare_lv_attr(path
, 6, 't');
334 int lvm_is_thin_pool(const char *path
)
336 return lvm_compare_lv_attr(path
, 0, 't');
339 static int lvm_snapshot_create_new_uuid_wrapper(void *data
)
341 struct lvcreate_args
*args
= data
;
343 if (strcmp(args
->fstype
, "xfs") == 0)
344 execlp("xfs_admin", "xfs_admin", "-U", "generate", args
->lv
, (char *)NULL
);
346 if (strcmp(args
->fstype
, "btrfs") == 0)
347 execlp("btrfstune", "btrfstune", "-f", "-u", args
->lv
, (char *)NULL
);
352 static int lvm_snapshot(struct lxc_storage
*orig
, const char *path
, uint64_t size
)
358 char cmd_output
[PATH_MAX
];
361 struct lvcreate_args cmd_args
= {0};
363 ret
= snprintf(sz
, 24, "%" PRIu64
"b", size
);
364 if (ret
< 0 || ret
>= 24) {
365 ERROR("Failed to create string");
369 pathdup
= strdup(path
);
371 ERROR("Failed to duplicate string \"%s\"", path
);
375 lv
= strrchr(pathdup
, '/');
377 ERROR("Failed to detect \"/\" in string \"%s\"", pathdup
);
384 TRACE("Parsed logical volume \"%s\"", lv
);
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
390 origsrc
= lxc_storage_get_path(orig
->src
, "lvm");
391 ret
= lvm_is_thin_volume(origsrc
);
396 cmd_args
.thinpool
= origsrc
;
400 cmd_args
.source_lv
= origsrc
;
402 TRACE("Creating new lvm snapshot \"%s\" of \"%s\" with size \"%s\"", lv
,
404 ret
= run_command(cmd_output
, sizeof(cmd_output
),
405 lvm_snapshot_exec_wrapper
, (void *)&cmd_args
);
407 ERROR("Failed to create logical volume \"%s\": %s", lv
, cmd_output
);
412 if (detect_fs(orig
, fstype
, 100) < 0) {
413 INFO("Failed to detect filesystem type for \"%s\"", origsrc
);
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
);
426 ERROR("Failed to create new uuid for volume \"%s\": %s", pathdup
, cmd_output
);
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
)
443 if (!orig
->src
|| !orig
->dest
)
446 if (strcmp(orig
->type
, "lvm") && snap
) {
447 ERROR("LVM snapshot from \"%s\" storage driver is not supported",
452 if (strcmp(orig
->type
, "lvm")) {
453 vg
= lxc_global_config_value("lxc.bdev.lvm.vg");
454 new->src
= lxc_string_join(
456 (const char *[]){"lvm:", "dev", vg
, cname
, NULL
},
462 src
= lxc_storage_get_path(orig
->src
, orig
->type
);
466 ERROR("Failed to duplicate string \"%s\"", src
);
470 slider
= strrchr(dup
, '/');
472 ERROR("Failed to detect \"/\" in string \"%s\"", dup
);
479 new->src
= lxc_string_join(
481 (const char *[]){"lvm:", *slider
== '/' ? ++slider
: slider
,
487 ERROR("Failed to create string");
492 new->mntopts
= strdup(orig
->mntopts
);
494 ERROR("Failed to duplicate string \"%s\"", orig
->mntopts
);
499 len
= strlen(lxcpath
) + strlen(cname
) + strlen("rootfs") + 3;
500 new->dest
= malloc(len
);
502 ERROR("Failed to allocate memory");
506 ret
= snprintf(new->dest
, len
, "%s/%s/rootfs", lxcpath
, cname
);
507 if (ret
< 0 || ret
>= len
) {
508 ERROR("Failed to create string");
512 ret
= mkdir_p(new->dest
, 0755);
514 SYSERROR("Failed to create directory \"%s\"", new->dest
);
521 bool lvm_create_clone(struct lxc_conf
*conf
, struct lxc_storage
*orig
,
522 struct lxc_storage
*new, uint64_t newsize
)
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
;
533 if (is_blktype(orig
)) {
535 if (!newsize
&& blk_getsize(orig
, &size
) < 0) {
536 ERROR("Failed to detect size of logical volume \"%s\"",
541 /* detect filesystem */
542 if (detect_fs(orig
, fstype
, 100) < 0) {
543 INFO("Failed to detect filesystem type for \"%s\"", orig
->src
);
546 } else if (!newsize
) {
547 size
= DEFAULT_FS_SIZE
;
550 src
= lxc_storage_get_path(new->src
, "lvm");
551 thinpool
= lxc_global_config_value("lxc.bdev.lvm.thin_pool");
553 ret
= do_lvm_create(src
, size
, thinpool
);
555 ERROR("Failed to create lvm storage volume \"%s\"", src
);
559 cmd_args
[0] = fstype
;
561 ret
= run_command(cmd_output
, sizeof(cmd_output
),
562 do_mkfs_exec_wrapper
, (void *)cmd_args
);
564 ERROR("Failed to create new filesystem \"%s\" for lvm storage "
565 "volume \"%s\": %s", fstype
, src
, cmd_output
);
571 ret
= run_command(cmd_output
, sizeof(cmd_output
),
572 lxc_storage_rsync_exec_wrapper
, (void *)&data
);
574 ERROR("Failed to rsync from \"%s\" to \"%s\"", orig
->dest
,
579 TRACE("Created lvm storage volume \"%s\"", new->dest
);
583 bool lvm_create_snapshot(struct lxc_conf
*conf
, struct lxc_storage
*orig
,
584 struct lxc_storage
*new, uint64_t newsize
)
588 uint64_t size
= newsize
;
590 if (is_blktype(orig
)) {
591 if (!newsize
&& blk_getsize(orig
, &size
) < 0) {
592 ERROR("Failed to detect size of logical volume \"%s\"",
596 } else if (!newsize
) {
597 size
= DEFAULT_FS_SIZE
;
600 newsrc
= lxc_storage_get_path(new->src
, "lvm");
602 ret
= lvm_snapshot(orig
, newsrc
, size
);
604 ERROR("Failed to create lvm \"%s\" snapshot of \"%s\"",
605 new->src
, orig
->src
);
609 TRACE("Created lvm snapshot \"%s\" from \"%s\"", new->dest
, orig
->dest
);
613 int lvm_destroy(struct lxc_storage
*orig
)
616 char cmd_output
[PATH_MAX
];
617 struct lvcreate_args cmd_args
= {0};
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
);
623 ERROR("Failed to destroy logical volume \"%s\": %s", orig
->src
,
628 TRACE("Destroyed logical volume \"%s\"", orig
->src
);
632 int lvm_create(struct lxc_storage
*bdev
, const char *dest
, const char *n
,
633 struct bdev_specs
*specs
)
635 const char *vg
, *thinpool
, *fstype
, *lv
= n
;
638 const char *cmd_args
[2];
639 char cmd_output
[PATH_MAX
];
646 vg
= lxc_global_config_value("lxc.bdev.lvm.vg");
648 thinpool
= specs
->lvm
.thinpool
;
650 thinpool
= lxc_global_config_value("lxc.bdev.lvm.thin_pool");
656 len
= strlen(vg
) + strlen(lv
) + 4 + 7;
657 bdev
->src
= malloc(len
);
659 ERROR("Failed to allocate memory");
663 ret
= snprintf(bdev
->src
, len
, "lvm:/dev/%s/%s", vg
, lv
);
664 if (ret
< 0 || ret
>= len
) {
665 ERROR("Failed to create string");
669 /* size is in bytes */
672 sz
= DEFAULT_FS_SIZE
;
674 ret
= do_lvm_create(bdev
->src
+ 4, sz
, thinpool
);
676 ERROR("Error creating new logical volume \"%s\" of size "
677 "\"%" PRIu64
" bytes\"", bdev
->src
, sz
);
681 fstype
= specs
->fstype
;
683 fstype
= DEFAULT_FSTYPE
;
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
,
690 ERROR("Failed to create new logical volume \"%s\": %s",
691 bdev
->src
, cmd_output
);
695 bdev
->dest
= strdup(dest
);
697 ERROR("Failed to duplicate string \"%s\"", dest
);
701 ret
= mkdir_p(bdev
->dest
, 0755);
703 SYSERROR("Failed to create directory \"%s\"", bdev
->dest
);
707 TRACE("Created new logical volume \"%s\"", bdev
->dest
);