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