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