]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/bdev/lxclvm.c
storage: prefix all btrfs paths
[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,
50 const char *oldpath, const char *lxcpath);
51
52 /*
53 * LVM ops
54 */
55
56 /*
57 * path must be '/dev/$vg/$lv', $vg must be an existing VG, and $lv must not
58 * yet exist. This function will attempt to create /dev/$vg/$lv of size
59 * $size. If thinpool is specified, we'll check for it's existence and if
60 * it's
61 * a valid thin pool, and if so, we'll create the requested lv from that
62 * thin
63 * pool.
64 */
65 static int do_lvm_create(const char *path, uint64_t size,
66 const char *thinpool)
67{
68 int ret, pid, len;
69 char sz[24], *pathdup, *vg, *lv, *tp = NULL;
70
71 if ((pid = fork()) < 0) {
72 SYSERROR("failed fork");
73 return -1;
74 }
75 if (pid > 0)
76 return wait_for_pid(pid);
77
78 // specify bytes to lvcreate
79 ret = snprintf(sz, 24, "%"PRIu64"b", size);
80 if (ret < 0 || ret >= 24)
81 exit(EXIT_FAILURE);
82
83 pathdup = strdup(path);
84 if (!pathdup)
85 exit(EXIT_FAILURE);
86
87 lv = strrchr(pathdup, '/');
88 if (!lv)
89 exit(EXIT_FAILURE);
90
91 *lv = '\0';
92 lv++;
93
94 vg = strrchr(pathdup, '/');
95 if (!vg)
96 exit(EXIT_FAILURE);
97 vg++;
98
99 if (thinpool) {
100 len = strlen(pathdup) + strlen(thinpool) + 2;
101 tp = alloca(len);
102
103 ret = snprintf(tp, len, "%s/%s", pathdup, thinpool);
104 if (ret < 0 || ret >= len)
105 exit(EXIT_FAILURE);
106
107 ret = lvm_is_thin_pool(tp);
108 INFO("got %d for thin pool at path: %s", ret, tp);
109 if (ret < 0)
110 exit(EXIT_FAILURE);
111
112 if (!ret)
113 tp = NULL;
114 }
115
79816ab3 116 (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1);
2b9cbd53
CB
117 if (!tp)
118 execlp("lvcreate", "lvcreate", "-L", sz, vg, "-n", lv, (char *)NULL);
119 else
120 execlp("lvcreate", "lvcreate", "--thinpool", tp, "-V", sz, vg, "-n", lv, (char *)NULL);
121
122 SYSERROR("execlp");
123 exit(EXIT_FAILURE);
124}
125
126
127/*
128 * Look at /sys/dev/block/maj:min/dm/uuid. If it contains the hardcoded LVM
129 * prefix "LVM-", then this is an lvm2 LV
130 */
131int lvm_detect(const char *path)
132{
133 char devp[MAXPATHLEN], buf[4];
134 FILE *fout;
135 int ret;
136 struct stat statbuf;
137
f7ac4459
CB
138 if (!strncmp(path, "lvm:", 4))
139 return 1;
2b9cbd53
CB
140
141 ret = stat(path, &statbuf);
142 if (ret != 0)
143 return 0;
144 if (!S_ISBLK(statbuf.st_mode))
145 return 0;
146
147 ret = snprintf(devp, MAXPATHLEN, "/sys/dev/block/%d:%d/dm/uuid",
148 major(statbuf.st_rdev), minor(statbuf.st_rdev));
149 if (ret < 0 || ret >= MAXPATHLEN) {
150 ERROR("lvm uuid pathname too long");
151 return 0;
152 }
153 fout = fopen(devp, "r");
154 if (!fout)
155 return 0;
156 ret = fread(buf, 1, 4, fout);
157 fclose(fout);
158 if (ret != 4 || strncmp(buf, "LVM-", 4) != 0)
159 return 0;
160 return 1;
161}
162
163int lvm_mount(struct bdev *bdev)
164{
165 if (strcmp(bdev->type, "lvm"))
166 return -22;
167 if (!bdev->src || !bdev->dest)
168 return -22;
169 /* if we might pass in data sometime, then we'll have to enrich
170 * mount_unknown_fs */
171 return mount_unknown_fs(bdev->src, bdev->dest, bdev->mntopts);
172}
173
174int lvm_umount(struct bdev *bdev)
175{
176 if (strcmp(bdev->type, "lvm"))
177 return -22;
178 if (!bdev->src || !bdev->dest)
179 return -22;
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");
301 len = strlen("/dev/") + strlen(vg) + strlen(cname) + 2;
302 if ((new->src = malloc(len)) == NULL)
303 return -1;
304 ret = snprintf(new->src, len, "/dev/%s/%s", vg, cname);
305 if (ret < 0 || ret >= len)
306 return -1;
307 } else {
308 new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath);
309 if (!new->src)
310 return -1;
311 }
312
313 if (orig->mntopts) {
314 new->mntopts = strdup(orig->mntopts);
315 if (!new->mntopts)
316 return -1;
317 }
318
319 len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 3;
320 new->dest = malloc(len);
321 if (!new->dest)
322 return -1;
323 ret = snprintf(new->dest, len, "%s/%s/rootfs", lxcpath, cname);
324 if (ret < 0 || ret >= len)
325 return -1;
326 if (mkdir_p(new->dest, 0755) < 0)
327 return -1;
328
329 if (is_blktype(orig)) {
330 if (!newsize && blk_getsize(orig, &size) < 0) {
331 ERROR("Error getting size of %s", orig->src);
332 return -1;
333 }
334 if (detect_fs(orig, fstype, 100) < 0) {
335 INFO("could not find fstype for %s, using ext3", orig->src);
336 return -1;
337 }
338 } else {
339 sprintf(fstype, "ext3");
340 if (!newsize)
341 size = DEFAULT_FS_SIZE;
342 }
343
344 if (snap) {
345 if (lvm_snapshot(orig->src, new->src, size) < 0) {
346 ERROR("could not create %s snapshot of %s", new->src, orig->src);
347 return -1;
348 }
349 } else {
350 if (do_lvm_create(new->src, size, lxc_global_config_value("lxc.bdev.lvm.thin_pool")) < 0) {
351 ERROR("Error creating new lvm blockdev");
352 return -1;
353 }
a5b18cb1
CB
354
355 cmd_args[0] = fstype;
356 cmd_args[1] = new->src;
357 // create an fs in the loopback file
358 ret = run_command(cmd_output, sizeof(cmd_output),
359 do_mkfs_exec_wrapper, (void *)cmd_args);
360 if (ret < 0)
2b9cbd53 361 return -1;
2b9cbd53
CB
362 }
363
364 return 0;
365}
366
367int lvm_destroy(struct bdev *orig)
368{
369 pid_t pid;
370
371 if ((pid = fork()) < 0)
372 return -1;
373 if (!pid) {
79816ab3 374 (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1);
acf47e1b 375 execlp("lvremove", "lvremove", "-f", orig->src, (char *)NULL);
2b9cbd53
CB
376 exit(EXIT_FAILURE);
377 }
378 return wait_for_pid(pid);
379}
380
381int lvm_create(struct bdev *bdev, const char *dest, const char *n,
382 struct bdev_specs *specs)
383{
384 const char *vg, *thinpool, *fstype, *lv = n;
385 uint64_t sz;
386 int ret, len;
a5b18cb1
CB
387 const char *cmd_args[2];
388 char cmd_output[MAXPATHLEN];
2b9cbd53
CB
389
390 if (!specs)
391 return -1;
392
393 vg = specs->lvm.vg;
394 if (!vg)
395 vg = lxc_global_config_value("lxc.bdev.lvm.vg");
396
397 thinpool = specs->lvm.thinpool;
398 if (!thinpool)
399 thinpool = lxc_global_config_value("lxc.bdev.lvm.thin_pool");
400
401 /* /dev/$vg/$lv */
402 if (specs->lvm.lv)
403 lv = specs->lvm.lv;
404
405 len = strlen(vg) + strlen(lv) + 7;
406 bdev->src = malloc(len);
407 if (!bdev->src)
408 return -1;
409
410 ret = snprintf(bdev->src, len, "/dev/%s/%s", vg, lv);
411 if (ret < 0 || ret >= len)
412 return -1;
413
414 // fssize is in bytes.
415 sz = specs->fssize;
416 if (!sz)
417 sz = DEFAULT_FS_SIZE;
418
419 if (do_lvm_create(bdev->src, sz, thinpool) < 0) {
420 ERROR("Error creating new lvm blockdev %s size %"PRIu64" bytes", bdev->src, sz);
421 return -1;
422 }
423
424 fstype = specs->fstype;
425 if (!fstype)
426 fstype = DEFAULT_FSTYPE;
a5b18cb1
CB
427
428 cmd_args[0] = fstype;
429 cmd_args[1] = bdev->src;
430 ret = run_command(cmd_output, sizeof(cmd_output), do_mkfs_exec_wrapper,
431 (void *)cmd_args);
432 if (ret < 0)
2b9cbd53 433 return -1;
a5b18cb1 434
2b9cbd53
CB
435 if (!(bdev->dest = strdup(dest)))
436 return -1;
437
438 if (mkdir_p(bdev->dest, 0755) < 0) {
439 ERROR("Error creating %s", bdev->dest);
440 return -1;
441 }
442
443 return 0;
444}