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