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