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>
42 #include "storage_utils.h"
46 #include <sys/mkdev.h>
49 lxc_log_define(lvm
, lxc
);
51 struct lvcreate_args
{
58 /* snapshot specific arguments */
59 const char *source_lv
;
62 static int lvm_destroy_exec_wrapper(void *data
)
64 struct lvcreate_args
*args
= data
;
66 (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1);
67 execlp("lvremove", "lvremove", "-f", args
->lv
, (char *)NULL
);
72 static int lvm_create_exec_wrapper(void *data
)
74 struct lvcreate_args
*args
= data
;
76 (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1);
78 execlp("lvcreate", "lvcreate", "-qq", "--thinpool", args
->thinpool
,
79 "-V", args
->size
, args
->vg
, "-n", args
->lv
,
82 execlp("lvcreate", "lvcreate", "-qq", "-L", args
->size
, args
->vg
, "-n",
83 args
->lv
, (char *)NULL
);
88 static int lvm_snapshot_exec_wrapper(void *data
)
90 struct lvcreate_args
*args
= data
;
92 (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1);
94 execlp("lvcreate", "lvcreate", "-s", "-n", args
->lv
,
95 args
->source_lv
, (char *)NULL
);
97 execlp("lvcreate", "lvcreate", "-s", "-L", args
->size
, "-n",
98 args
->lv
, args
->source_lv
, (char *)NULL
);
103 /* The path must be "/dev/<vg>/<lv>". The volume group <vg> must be an existing
104 * volume group, and the logical volume <lv> must not yet exist.
105 * This function will attempt to create "/dev/<vg>/<lv> of size <size>. If
106 * thinpool is specified, we'll check for it's existence and if it's a valid
107 * thin pool, and if so, we'll create the requested logical volume from that
110 static int do_lvm_create(const char *path
, uint64_t size
, const char *thinpool
)
113 char *pathdup
, *vg
, *lv
;
114 char cmd_output
[PATH_MAX
];
117 struct lvcreate_args cmd_args
= {0};
119 ret
= snprintf(sz
, 24, "%" PRIu64
"b", size
);
120 if (ret
< 0 || ret
>= 24) {
121 ERROR("Failed to create string: %d", ret
);
125 pathdup
= strdup(path
);
127 ERROR("Failed to duplicate string \"%s\"", path
);
131 lv
= strrchr(pathdup
, '/');
133 ERROR("Failed to detect \"/\" in string \"%s\"", pathdup
);
139 TRACE("Parsed logical volume \"%s\"", lv
);
141 vg
= strrchr(pathdup
, '/');
143 ERROR("Failed to detect \"/\" in string \"%s\"", pathdup
);
148 TRACE("Parsed volume group \"%s\"", vg
);
151 len
= strlen(pathdup
) + strlen(thinpool
) + 2;
154 ret
= snprintf(tp
, len
, "%s/%s", pathdup
, thinpool
);
155 if (ret
< 0 || ret
>= len
) {
156 ERROR("Failed to create string: %d", ret
);
161 ret
= lvm_is_thin_pool(tp
);
162 TRACE("got %d for thin pool at path: %s", ret
, tp
);
164 ERROR("Failed to detect whether \"%s\" is a thinpool", tp
);
168 TRACE("Detected that \"%s\" is not a thinpool", tp
);
171 TRACE("Detected \"%s\" is a thinpool", tp
);
175 cmd_args
.thinpool
= tp
;
179 TRACE("Creating new lvm storage volume \"%s\" on volume group \"%s\" "
180 "of size \"%s\"", lv
, vg
, sz
);
181 ret
= run_command(cmd_output
, sizeof(cmd_output
),
182 lvm_create_exec_wrapper
, (void *)&cmd_args
);
184 ERROR("Failed to create logical volume \"%s\": %s", lv
,
189 TRACE("Created new lvm storage volume \"%s\" on volume group \"%s\" "
190 "of size \"%s\"", lv
, vg
, sz
);
196 /* Look at "/sys/dev/block/maj:min/dm/uuid". If it contains the hardcoded LVM
197 * prefix "LVM-" then this is an lvm2 LV.
199 bool lvm_detect(const char *path
)
204 char devp
[PATH_MAX
], buf
[4];
206 if (!strncmp(path
, "lvm:", 4))
209 ret
= stat(path
, &statbuf
);
213 if (!S_ISBLK(statbuf
.st_mode
))
216 ret
= snprintf(devp
, PATH_MAX
, "/sys/dev/block/%d:%d/dm/uuid",
217 major(statbuf
.st_rdev
), minor(statbuf
.st_rdev
));
218 if (ret
< 0 || ret
>= PATH_MAX
) {
219 ERROR("Failed to create string");
223 fd
= open(devp
, O_RDONLY
);
227 ret
= read(fd
, buf
, sizeof(buf
));
229 if (ret
!= sizeof(buf
))
232 if (strncmp(buf
, "LVM-", 4))
238 int lvm_mount(struct lxc_storage
*bdev
)
242 if (strcmp(bdev
->type
, "lvm"))
245 if (!bdev
->src
|| !bdev
->dest
)
248 src
= lxc_storage_get_path(bdev
->src
, bdev
->type
);
250 /* If we might pass in data sometime, then we'll have to enrich
251 * mount_unknown_fs().
253 return mount_unknown_fs(src
, bdev
->dest
, bdev
->mntopts
);
256 int lvm_umount(struct lxc_storage
*bdev
)
258 if (strcmp(bdev
->type
, "lvm"))
261 if (!bdev
->src
|| !bdev
->dest
)
264 return umount(bdev
->dest
);
267 int lvm_compare_lv_attr(const char *path
, int pos
, const char expected
)
269 struct lxc_popen_FILE
*f
;
275 const char *lvscmd
= "lvs --unbuffered --noheadings -o lv_attr %s 2>/dev/null";
277 len
= strlen(lvscmd
) + strlen(path
) + 1;
280 ret
= snprintf(cmd
, len
, lvscmd
, path
);
281 if (ret
< 0 || (size_t)ret
>= len
)
286 SYSERROR("popen failed");
291 if (!fgets(output
, 12, f
->f
))
294 status
= lxc_pclose(f
);
295 /* Assume either vg or lvs do not exist, default comparison to false. */
296 if (ret
|| WEXITSTATUS(status
))
299 len
= strlen(output
);
300 while (start
< len
&& output
[start
] == ' ')
303 if (start
+ pos
< len
&& output
[start
+ pos
] == expected
)
309 int lvm_is_thin_volume(const char *path
)
311 return lvm_compare_lv_attr(path
, 6, 't');
314 int lvm_is_thin_pool(const char *path
)
316 return lvm_compare_lv_attr(path
, 0, 't');
319 static int lvm_snapshot_create_new_uuid_wrapper(void *data
)
321 struct lvcreate_args
*args
= data
;
323 if (strcmp(args
->fstype
, "xfs") == 0)
324 execlp("xfs_admin", "xfs_admin", "-U", "generate", args
->lv
, (char *)NULL
);
326 if (strcmp(args
->fstype
, "btrfs") == 0)
327 execlp("btrfstune", "btrfstune", "-f", "-u", args
->lv
, (char *)NULL
);
332 static int lvm_snapshot(struct lxc_storage
*orig
, const char *path
, uint64_t size
)
338 char cmd_output
[PATH_MAX
];
341 struct lvcreate_args cmd_args
= {0};
343 ret
= snprintf(sz
, 24, "%" PRIu64
"b", size
);
344 if (ret
< 0 || ret
>= 24) {
345 ERROR("Failed to create string");
349 pathdup
= strdup(path
);
351 ERROR("Failed to duplicate string \"%s\"", path
);
355 lv
= strrchr(pathdup
, '/');
357 ERROR("Failed to detect \"/\" in string \"%s\"", pathdup
);
364 TRACE("Parsed logical volume \"%s\"", lv
);
366 /* Check if the original logical volume is backed by a thinpool, in
367 * which case we cannot specify a size that's different from the
370 origsrc
= lxc_storage_get_path(orig
->src
, "lvm");
371 ret
= lvm_is_thin_volume(origsrc
);
376 cmd_args
.thinpool
= origsrc
;
380 cmd_args
.source_lv
= origsrc
;
382 TRACE("Creating new lvm snapshot \"%s\" of \"%s\" with size \"%s\"", lv
,
384 ret
= run_command(cmd_output
, sizeof(cmd_output
),
385 lvm_snapshot_exec_wrapper
, (void *)&cmd_args
);
387 ERROR("Failed to create logical volume \"%s\": %s", lv
, cmd_output
);
392 if (detect_fs(orig
, fstype
, 100) < 0) {
393 INFO("Failed to detect filesystem type for \"%s\"", origsrc
);
401 cmd_args
.lv
= pathdup
;
402 cmd_args
.fstype
= fstype
;
403 ret
= run_command(cmd_output
, sizeof(cmd_output
),
404 lvm_snapshot_create_new_uuid_wrapper
, (void *)&cmd_args
);
406 ERROR("Failed to create new uuid for volume \"%s\": %s", pathdup
, cmd_output
);
415 int lvm_clonepaths(struct lxc_storage
*orig
, struct lxc_storage
*new,
416 const char *oldname
, const char *cname
, const char *oldpath
,
417 const char *lxcpath
, int snap
, uint64_t newsize
,
418 struct lxc_conf
*conf
)
423 if (!orig
->src
|| !orig
->dest
)
426 if (strcmp(orig
->type
, "lvm") && snap
) {
427 ERROR("LVM snapshot from \"%s\" storage driver is not supported",
432 if (strcmp(orig
->type
, "lvm")) {
433 vg
= lxc_global_config_value("lxc.bdev.lvm.vg");
434 new->src
= lxc_string_join(
436 (const char *[]){"lvm:", "dev", vg
, cname
, NULL
},
442 src
= lxc_storage_get_path(orig
->src
, orig
->type
);
446 ERROR("Failed to duplicate string \"%s\"", src
);
450 slider
= strrchr(dup
, '/');
452 ERROR("Failed to detect \"/\" in string \"%s\"", dup
);
459 new->src
= lxc_string_join(
461 (const char *[]){"lvm:", *slider
== '/' ? ++slider
: slider
,
467 ERROR("Failed to create string");
472 new->mntopts
= strdup(orig
->mntopts
);
474 ERROR("Failed to duplicate string \"%s\"", orig
->mntopts
);
479 len
= strlen(lxcpath
) + strlen(cname
) + strlen("rootfs") + 3;
480 new->dest
= malloc(len
);
482 ERROR("Failed to allocate memory");
486 ret
= snprintf(new->dest
, len
, "%s/%s/rootfs", lxcpath
, cname
);
487 if (ret
< 0 || ret
>= len
) {
488 ERROR("Failed to create string");
492 ret
= mkdir_p(new->dest
, 0755);
494 SYSERROR("Failed to create directory \"%s\"", new->dest
);
501 bool lvm_create_clone(struct lxc_conf
*conf
, struct lxc_storage
*orig
,
502 struct lxc_storage
*new, uint64_t newsize
)
506 const char *thinpool
;
507 struct rsync_data data
;
508 const char *cmd_args
[2];
509 char cmd_output
[PATH_MAX
] = {0};
510 char fstype
[100] = "ext4";
511 uint64_t size
= newsize
;
513 if (is_blktype(orig
)) {
515 if (!newsize
&& blk_getsize(orig
, &size
) < 0) {
516 ERROR("Failed to detect size of logical volume \"%s\"",
521 /* detect filesystem */
522 if (detect_fs(orig
, fstype
, 100) < 0) {
523 INFO("Failed to detect filesystem type for \"%s\"", orig
->src
);
526 } else if (!newsize
) {
527 size
= DEFAULT_FS_SIZE
;
530 src
= lxc_storage_get_path(new->src
, "lvm");
531 thinpool
= lxc_global_config_value("lxc.bdev.lvm.thin_pool");
533 ret
= do_lvm_create(src
, size
, thinpool
);
535 ERROR("Failed to create lvm storage volume \"%s\"", src
);
539 cmd_args
[0] = fstype
;
541 ret
= run_command(cmd_output
, sizeof(cmd_output
),
542 do_mkfs_exec_wrapper
, (void *)cmd_args
);
544 ERROR("Failed to create new filesystem \"%s\" for lvm storage "
545 "volume \"%s\": %s", fstype
, src
, cmd_output
);
551 ret
= run_command(cmd_output
, sizeof(cmd_output
),
552 lxc_storage_rsync_exec_wrapper
, (void *)&data
);
554 ERROR("Failed to rsync from \"%s\" to \"%s\"", orig
->dest
,
559 TRACE("Created lvm storage volume \"%s\"", new->dest
);
563 bool lvm_create_snapshot(struct lxc_conf
*conf
, struct lxc_storage
*orig
,
564 struct lxc_storage
*new, uint64_t newsize
)
568 uint64_t size
= newsize
;
570 if (is_blktype(orig
)) {
571 if (!newsize
&& blk_getsize(orig
, &size
) < 0) {
572 ERROR("Failed to detect size of logical volume \"%s\"",
576 } else if (!newsize
) {
577 size
= DEFAULT_FS_SIZE
;
580 newsrc
= lxc_storage_get_path(new->src
, "lvm");
582 ret
= lvm_snapshot(orig
, newsrc
, size
);
584 ERROR("Failed to create lvm \"%s\" snapshot of \"%s\"",
585 new->src
, orig
->src
);
589 TRACE("Created lvm snapshot \"%s\" from \"%s\"", new->dest
, orig
->dest
);
593 int lvm_destroy(struct lxc_storage
*orig
)
596 char cmd_output
[PATH_MAX
];
597 struct lvcreate_args cmd_args
= {0};
599 cmd_args
.lv
= lxc_storage_get_path(orig
->src
, "lvm");
600 ret
= run_command(cmd_output
, sizeof(cmd_output
),
601 lvm_destroy_exec_wrapper
, (void *)&cmd_args
);
603 ERROR("Failed to destroy logical volume \"%s\": %s", orig
->src
,
608 TRACE("Destroyed logical volume \"%s\"", orig
->src
);
612 int lvm_create(struct lxc_storage
*bdev
, const char *dest
, const char *n
,
613 struct bdev_specs
*specs
)
615 const char *vg
, *thinpool
, *fstype
, *lv
= n
;
618 const char *cmd_args
[2];
619 char cmd_output
[PATH_MAX
];
626 vg
= lxc_global_config_value("lxc.bdev.lvm.vg");
628 thinpool
= specs
->lvm
.thinpool
;
630 thinpool
= lxc_global_config_value("lxc.bdev.lvm.thin_pool");
636 len
= strlen(vg
) + strlen(lv
) + 4 + 7;
637 bdev
->src
= malloc(len
);
639 ERROR("Failed to allocate memory");
643 ret
= snprintf(bdev
->src
, len
, "lvm:/dev/%s/%s", vg
, lv
);
644 if (ret
< 0 || ret
>= len
) {
645 ERROR("Failed to create string");
649 /* size is in bytes */
652 sz
= DEFAULT_FS_SIZE
;
654 ret
= do_lvm_create(bdev
->src
+ 4, sz
, thinpool
);
656 ERROR("Error creating new logical volume \"%s\" of size "
657 "\"%" PRIu64
" bytes\"", bdev
->src
, sz
);
661 fstype
= specs
->fstype
;
663 fstype
= DEFAULT_FSTYPE
;
665 cmd_args
[0] = fstype
;
666 cmd_args
[1] = lxc_storage_get_path(bdev
->src
, bdev
->type
);
667 ret
= run_command(cmd_output
, sizeof(cmd_output
), do_mkfs_exec_wrapper
,
670 ERROR("Failed to create new logical volume \"%s\": %s",
671 bdev
->src
, cmd_output
);
675 bdev
->dest
= strdup(dest
);
677 ERROR("Failed to duplicate string \"%s\"", dest
);
681 ret
= mkdir_p(bdev
->dest
, 0755);
683 SYSERROR("Failed to create directory \"%s\"", bdev
->dest
);
687 TRACE("Created new logical volume \"%s\"", bdev
->dest
);