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