]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/bdev/lxclvm.c
storage: add arg to create_snapshot()
[mirror_lxc.git] / src / lxc / bdev / lxclvm.c
CommitLineData
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
25#define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */
26#include <inttypes.h> /* Required for PRIu64 to work. */
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"
f2d5a09d 39#include "storage_utils.h"
2b9cbd53
CB
40#include "utils.h"
41
af6824fc
ST
42/* major()/minor() */
43#ifdef MAJOR_IN_MKDEV
44# include <sys/mkdev.h>
45#endif
af6824fc 46
2b9cbd53
CB
47lxc_log_define(lxclvm, lxc);
48
49extern char *dir_new_path(char *src, const char *oldname, const char *name,
02b5e381 50 const char *oldpath, const char *lxcpath);
2b9cbd53 51
02b5e381
CB
52/* Path must be '/dev/$vg/$lv', $vg must be an existing VG, and $lv must not yet
53 * exist. This function will attempt to create /dev/$vg/$lv of size $size. If
54 * thinpool is specified, we'll check for it's existence and if it's a valid
55 * thin pool, and if so, we'll create the requested lv from that thin pool.
56 */
57static int do_lvm_create(const char *path, uint64_t size, const char *thinpool)
2b9cbd53
CB
58{
59 int ret, pid, len;
60 char sz[24], *pathdup, *vg, *lv, *tp = NULL;
61
62 if ((pid = fork()) < 0) {
63 SYSERROR("failed fork");
64 return -1;
65 }
66 if (pid > 0)
67 return wait_for_pid(pid);
68
69 // specify bytes to lvcreate
70 ret = snprintf(sz, 24, "%"PRIu64"b", size);
71 if (ret < 0 || ret >= 24)
72 exit(EXIT_FAILURE);
73
74 pathdup = strdup(path);
75 if (!pathdup)
76 exit(EXIT_FAILURE);
77
78 lv = strrchr(pathdup, '/');
79 if (!lv)
80 exit(EXIT_FAILURE);
81
82 *lv = '\0';
83 lv++;
84
85 vg = strrchr(pathdup, '/');
86 if (!vg)
87 exit(EXIT_FAILURE);
88 vg++;
89
90 if (thinpool) {
91 len = strlen(pathdup) + strlen(thinpool) + 2;
92 tp = alloca(len);
93
94 ret = snprintf(tp, len, "%s/%s", pathdup, thinpool);
95 if (ret < 0 || ret >= len)
96 exit(EXIT_FAILURE);
97
98 ret = lvm_is_thin_pool(tp);
99 INFO("got %d for thin pool at path: %s", ret, tp);
100 if (ret < 0)
101 exit(EXIT_FAILURE);
102
103 if (!ret)
104 tp = NULL;
105 }
106
79816ab3 107 (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1);
2b9cbd53
CB
108 if (!tp)
109 execlp("lvcreate", "lvcreate", "-L", sz, vg, "-n", lv, (char *)NULL);
110 else
111 execlp("lvcreate", "lvcreate", "--thinpool", tp, "-V", sz, vg, "-n", lv, (char *)NULL);
112
113 SYSERROR("execlp");
114 exit(EXIT_FAILURE);
115}
116
117
118/*
119 * Look at /sys/dev/block/maj:min/dm/uuid. If it contains the hardcoded LVM
120 * prefix "LVM-", then this is an lvm2 LV
121 */
122int lvm_detect(const char *path)
123{
124 char devp[MAXPATHLEN], buf[4];
125 FILE *fout;
126 int ret;
127 struct stat statbuf;
128
f7ac4459
CB
129 if (!strncmp(path, "lvm:", 4))
130 return 1;
2b9cbd53
CB
131
132 ret = stat(path, &statbuf);
133 if (ret != 0)
134 return 0;
135 if (!S_ISBLK(statbuf.st_mode))
136 return 0;
137
138 ret = snprintf(devp, MAXPATHLEN, "/sys/dev/block/%d:%d/dm/uuid",
139 major(statbuf.st_rdev), minor(statbuf.st_rdev));
140 if (ret < 0 || ret >= MAXPATHLEN) {
141 ERROR("lvm uuid pathname too long");
142 return 0;
143 }
144 fout = fopen(devp, "r");
145 if (!fout)
146 return 0;
147 ret = fread(buf, 1, 4, fout);
148 fclose(fout);
149 if (ret != 4 || strncmp(buf, "LVM-", 4) != 0)
150 return 0;
151 return 1;
152}
153
154int lvm_mount(struct bdev *bdev)
155{
11274f69
CB
156 char *src;
157
2b9cbd53
CB
158 if (strcmp(bdev->type, "lvm"))
159 return -22;
11274f69 160
2b9cbd53
CB
161 if (!bdev->src || !bdev->dest)
162 return -22;
11274f69
CB
163
164 src = lxc_storage_get_path(bdev->src, bdev->type);
165
166 /* If we might pass in data sometime, then we'll have to enrich
167 * mount_unknown_fs().
168 */
169 return mount_unknown_fs(src, bdev->dest, bdev->mntopts);
2b9cbd53
CB
170}
171
172int lvm_umount(struct bdev *bdev)
173{
174 if (strcmp(bdev->type, "lvm"))
175 return -22;
11274f69 176
2b9cbd53
CB
177 if (!bdev->src || !bdev->dest)
178 return -22;
11274f69 179
2b9cbd53
CB
180 return umount(bdev->dest);
181}
182
183int lvm_compare_lv_attr(const char *path, int pos, const char expected)
184{
185 struct lxc_popen_FILE *f;
186 int ret, len, status, start=0;
187 char *cmd, output[12];
188 const char *lvscmd = "lvs --unbuffered --noheadings -o lv_attr %s 2>/dev/null";
189
190 len = strlen(lvscmd) + strlen(path) - 1;
191 cmd = alloca(len);
192
193 ret = snprintf(cmd, len, lvscmd, path);
194 if (ret < 0 || ret >= len)
195 return -1;
196
197 f = lxc_popen(cmd);
198
199 if (f == NULL) {
200 SYSERROR("popen failed");
201 return -1;
202 }
203
204 ret = fgets(output, 12, f->f) == NULL;
205
206 status = lxc_pclose(f);
207
208 if (ret || WEXITSTATUS(status))
209 // Assume either vg or lvs do not exist, default
210 // comparison to false.
211 return 0;
212
213 len = strlen(output);
214 while(start < len && output[start] == ' ') start++;
215
216 if (start + pos < len && output[start + pos] == expected)
217 return 1;
218
219 return 0;
220}
221
222int lvm_is_thin_volume(const char *path)
223{
224 return lvm_compare_lv_attr(path, 6, 't');
225}
226
227int lvm_is_thin_pool(const char *path)
228{
229 return lvm_compare_lv_attr(path, 0, 't');
230}
231
232int lvm_snapshot(const char *orig, const char *path, uint64_t size)
233{
234 int ret, pid;
235 char sz[24], *pathdup, *lv;
236
237 if ((pid = fork()) < 0) {
238 SYSERROR("failed fork");
239 return -1;
240 }
241 if (pid > 0)
242 return wait_for_pid(pid);
243
244 // specify bytes to lvcreate
245 ret = snprintf(sz, 24, "%"PRIu64"b", size);
246 if (ret < 0 || ret >= 24)
247 exit(EXIT_FAILURE);
248
249 pathdup = strdup(path);
250 if (!pathdup)
251 exit(EXIT_FAILURE);
252 lv = strrchr(pathdup, '/');
253 if (!lv) {
254 free(pathdup);
255 exit(EXIT_FAILURE);
256 }
257 *lv = '\0';
258 lv++;
259
260 // check if the original lv is backed by a thin pool, in which case we
261 // cannot specify a size that's different from the original size.
262 ret = lvm_is_thin_volume(orig);
263 if (ret == -1) {
264 free(pathdup);
265 return -1;
266 }
267
79816ab3 268 (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1);
2b9cbd53
CB
269 if (!ret) {
270 ret = execlp("lvcreate", "lvcreate", "-s", "-L", sz, "-n", lv, orig, (char *)NULL);
271 } else {
272 ret = execlp("lvcreate", "lvcreate", "-s", "-n", lv, orig, (char *)NULL);
273 }
274
275 free(pathdup);
276 exit(EXIT_FAILURE);
277}
278
279int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname,
280 const char *cname, const char *oldpath, const char *lxcpath, int snap,
281 uint64_t newsize, struct lxc_conf *conf)
282{
283 char fstype[100];
284 uint64_t size = newsize;
285 int len, ret;
a5b18cb1
CB
286 const char *cmd_args[2];
287 char cmd_output[MAXPATHLEN];
2b9cbd53
CB
288
289 if (!orig->src || !orig->dest)
290 return -1;
291
292 if (strcmp(orig->type, "lvm")) {
293 const char *vg;
294
295 if (snap) {
296 ERROR("LVM snapshot from %s backing store is not supported",
297 orig->type);
298 return -1;
299 }
300 vg = lxc_global_config_value("lxc.bdev.lvm.vg");
01e5fa07
CB
301 if (!vg) {
302 ERROR("The \"lxc.bdev.lvm.vg\" key is not set");
303 return -1;
304 }
305
11274f69
CB
306 len = strlen("/dev/") + strlen(vg) + strlen(cname) + 4 + 2;
307 new->src = malloc(len);
505dd987 308 if (!new->src)
2b9cbd53 309 return -1;
11274f69
CB
310
311 ret = snprintf(new->src, len, "lvm:/dev/%s/%s", vg, cname);
2b9cbd53
CB
312 if (ret < 0 || ret >= len)
313 return -1;
314 } else {
315 new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath);
316 if (!new->src)
317 return -1;
318 }
319
320 if (orig->mntopts) {
321 new->mntopts = strdup(orig->mntopts);
322 if (!new->mntopts)
323 return -1;
324 }
325
326 len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 3;
327 new->dest = malloc(len);
328 if (!new->dest)
329 return -1;
330 ret = snprintf(new->dest, len, "%s/%s/rootfs", lxcpath, cname);
331 if (ret < 0 || ret >= len)
332 return -1;
333 if (mkdir_p(new->dest, 0755) < 0)
334 return -1;
335
336 if (is_blktype(orig)) {
337 if (!newsize && blk_getsize(orig, &size) < 0) {
338 ERROR("Error getting size of %s", orig->src);
339 return -1;
340 }
341 if (detect_fs(orig, fstype, 100) < 0) {
342 INFO("could not find fstype for %s, using ext3", orig->src);
343 return -1;
344 }
345 } else {
346 sprintf(fstype, "ext3");
347 if (!newsize)
348 size = DEFAULT_FS_SIZE;
349 }
350
351 if (snap) {
11274f69
CB
352 char *newsrc, *origsrc;
353
354 origsrc = lxc_storage_get_path(orig->src, "lvm");
355 newsrc = lxc_storage_get_path(new->src, "lvm");
356
357 if (lvm_snapshot(origsrc, newsrc, size) < 0) {
2b9cbd53
CB
358 ERROR("could not create %s snapshot of %s", new->src, orig->src);
359 return -1;
360 }
361 } else {
11274f69
CB
362 char *src;
363
364 src = lxc_storage_get_path(new->src, "lvm");
365 if (do_lvm_create(src, size, lxc_global_config_value("lxc.bdev.lvm.thin_pool")) < 0) {
2b9cbd53
CB
366 ERROR("Error creating new lvm blockdev");
367 return -1;
368 }
a5b18cb1
CB
369
370 cmd_args[0] = fstype;
11274f69 371 cmd_args[1] = src;
a5b18cb1
CB
372 // create an fs in the loopback file
373 ret = run_command(cmd_output, sizeof(cmd_output),
374 do_mkfs_exec_wrapper, (void *)cmd_args);
375 if (ret < 0)
2b9cbd53 376 return -1;
2b9cbd53
CB
377 }
378
379 return 0;
380}
381
382int lvm_destroy(struct bdev *orig)
383{
11274f69
CB
384 char *src;
385
2b9cbd53
CB
386 pid_t pid;
387
388 if ((pid = fork()) < 0)
389 return -1;
11274f69 390
2b9cbd53 391 if (!pid) {
79816ab3 392 (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1);
11274f69
CB
393 src = lxc_storage_get_path(orig->src, "lvm");
394 execlp("lvremove", "lvremove", "-f", src, (char *)NULL);
2b9cbd53
CB
395 exit(EXIT_FAILURE);
396 }
11274f69 397
2b9cbd53
CB
398 return wait_for_pid(pid);
399}
400
401int lvm_create(struct bdev *bdev, const char *dest, const char *n,
402 struct bdev_specs *specs)
403{
404 const char *vg, *thinpool, *fstype, *lv = n;
405 uint64_t sz;
406 int ret, len;
a5b18cb1
CB
407 const char *cmd_args[2];
408 char cmd_output[MAXPATHLEN];
2b9cbd53
CB
409
410 if (!specs)
411 return -1;
412
413 vg = specs->lvm.vg;
414 if (!vg)
415 vg = lxc_global_config_value("lxc.bdev.lvm.vg");
416
417 thinpool = specs->lvm.thinpool;
418 if (!thinpool)
419 thinpool = lxc_global_config_value("lxc.bdev.lvm.thin_pool");
420
421 /* /dev/$vg/$lv */
422 if (specs->lvm.lv)
423 lv = specs->lvm.lv;
424
11274f69 425 len = strlen(vg) + strlen(lv) + 4 + 7;
2b9cbd53
CB
426 bdev->src = malloc(len);
427 if (!bdev->src)
428 return -1;
429
11274f69 430 ret = snprintf(bdev->src, len, "lvm:/dev/%s/%s", vg, lv);
2b9cbd53
CB
431 if (ret < 0 || ret >= len)
432 return -1;
433
434 // fssize is in bytes.
435 sz = specs->fssize;
436 if (!sz)
437 sz = DEFAULT_FS_SIZE;
438
11274f69 439 if (do_lvm_create(bdev->src + 4, sz, thinpool) < 0) {
2b9cbd53
CB
440 ERROR("Error creating new lvm blockdev %s size %"PRIu64" bytes", bdev->src, sz);
441 return -1;
442 }
443
444 fstype = specs->fstype;
445 if (!fstype)
446 fstype = DEFAULT_FSTYPE;
a5b18cb1
CB
447
448 cmd_args[0] = fstype;
11274f69 449 cmd_args[1] = bdev->src + 4;
a5b18cb1
CB
450 ret = run_command(cmd_output, sizeof(cmd_output), do_mkfs_exec_wrapper,
451 (void *)cmd_args);
452 if (ret < 0)
2b9cbd53 453 return -1;
a5b18cb1 454
2b9cbd53
CB
455 if (!(bdev->dest = strdup(dest)))
456 return -1;
457
458 if (mkdir_p(bdev->dest, 0755) < 0) {
459 ERROR("Error creating %s", bdev->dest);
460 return -1;
461 }
462
463 return 0;
464}