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