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