]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/storage/btrfs.c
build: add src/include to build and simplify header inclusions
[mirror_lxc.git] / src / lxc / storage / btrfs.c
CommitLineData
cc73685d 1/* SPDX-License-Identifier: LGPL-2.1+ */
3c16d0cb 2
d38dd64a
CB
3#ifndef _GNU_SOURCE
4#define _GNU_SOURCE 1
5#endif
988be9c1
CB
6#include <errno.h>
7#include <fcntl.h>
8#include <grp.h>
9#include <libgen.h>
3c16d0cb
CB
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
988be9c1
CB
13#include <sys/ioctl.h>
14#include <sys/stat.h>
15#include <sys/types.h>
6e0fa6a0 16#include <sys/vfs.h>
d38dd64a
CB
17#include <sys/wait.h>
18#include <unistd.h>
3c16d0cb 19
28d832c4 20#include "btrfs.h"
d38dd64a
CB
21#include "config.h"
22#include "log.h"
240a130f 23#include "memory_utils.h"
28d832c4
CB
24#include "rsync.h"
25#include "storage.h"
26#include "utils.h"
3c16d0cb 27
9de31d5a 28#ifndef HAVE_STRLCPY
58db1a61 29#include "strlcpy.h"
9de31d5a
CB
30#endif
31
3afafe55 32#ifndef HAVE_STRLCAT
58db1a61 33#include "strlcat.h"
3afafe55
CB
34#endif
35
10bc1861 36lxc_log_define(btrfs, lxc);
3c16d0cb
CB
37
38/*
39 * Return the full path of objid under dirid. Let's say dirid is
40 * /lxc/c1/rootfs, and objid is /lxc/c1/rootfs/a/b/c. Then we will
41 * return a/b/c. If instead objid is for /lxc/c1/rootfs/a, we will
42 * simply return a.
43 */
44char *get_btrfs_subvol_path(int fd, u64 dir_id, u64 objid, char *name,
fca23691 45 u16 name_len)
3c16d0cb
CB
46{
47 struct btrfs_ioctl_ino_lookup_args args;
6d1400b5 48 int ret;
25aced9f 49 size_t len, retlen;
3c16d0cb
CB
50 char *retpath;
51
52 memset(&args, 0, sizeof(args));
53 args.treeid = dir_id;
54 args.objectid = objid;
55
56 ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
3c16d0cb 57 if (ret) {
6d1400b5 58 SYSERROR("Failed to lookup path for %llu %llu %s",
59 (unsigned long long) dir_id,
60 (unsigned long long) objid,
61 name);
3c16d0cb
CB
62 return NULL;
63 } else
fca23691 64 INFO("Got path for %llu %llu - %s",
65 (unsigned long long) objid, (unsigned long long) dir_id,
66 name);
3c16d0cb
CB
67
68 if (args.name[0]) {
69 /*
70 * we're in a subdirectory of ref_tree, the kernel ioctl
71 * puts a / in there for us
72 */
73 len = strlen(args.name) + name_len + 2;
74 retpath = malloc(len);
75 if (!retpath)
76 return NULL;
2df5f6bf 77
cbb9c7c7 78 (void)strlcpy(retpath, args.name, len);
2df5f6bf 79 (void)strlcat(retpath, "/", len);
25aced9f
DJ
80
81 retlen = strlcat(retpath, name, len);
82 if (retlen >= len) {
fca23691 83 ERROR("Failed to append name - %s", name);
25aced9f
DJ
84 free(retpath);
85 return NULL;
86 }
3c16d0cb
CB
87 } else {
88 /* we're at the root of ref_tree */
89 len = name_len + 1;
90 retpath = malloc(len);
91 if (!retpath)
92 return NULL;
25aced9f 93
3c16d0cb 94 *retpath = '\0';
25aced9f
DJ
95
96 retlen = strlcat(retpath, name, len);
97 if (retlen >= len) {
fca23691 98 ERROR("Failed to append name - %s", name);
25aced9f
DJ
99 free(retpath);
100 return NULL;
101 }
3c16d0cb 102 }
25aced9f 103
3c16d0cb
CB
104 return retpath;
105}
106
3c16d0cb
CB
107int btrfs_list_get_path_rootid(int fd, u64 *treeid)
108{
109 int ret;
110 struct btrfs_ioctl_ino_lookup_args args;
111
112 memset(&args, 0, sizeof(args));
113 args.objectid = BTRFS_FIRST_FREE_OBJECTID;
114
115 ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
116 if (ret < 0) {
fca23691 117 SYSWARN("Can't perform the search");
3c16d0cb
CB
118 return ret;
119 }
fca23691 120
3c16d0cb
CB
121 *treeid = args.treeid;
122 return 0;
123}
124
125bool is_btrfs_fs(const char *path)
126{
127 int fd, ret;
128 struct btrfs_ioctl_space_args sargs;
129
1a0e70ac 130 /* Make sure this is a btrfs filesystem. */
3c16d0cb
CB
131 fd = open(path, O_RDONLY);
132 if (fd < 0)
133 return false;
fca23691 134
3c16d0cb
CB
135 sargs.space_slots = 0;
136 sargs.total_spaces = 0;
137 ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, &sargs);
138 close(fd);
139 if (ret < 0)
140 return false;
141
142 return true;
143}
144
6e0fa6a0
CB
145/*
146 * Taken from btrfs toolsuite. Test if path is a subvolume.
147 * return 0; path exists but it is not a subvolume
148 * return 1; path exists and it is a subvolume
149 * return < 0; error
150 */
151int is_btrfs_subvol(const char *path)
152{
153 struct stat st;
154 struct statfs stfs;
155 int ret;
156
157 ret = stat(path, &st);
158 if (ret < 0)
159 return -errno;
160
161 if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode))
162 return 0;
163
164 ret = statfs(path, &stfs);
165 if (ret < 0)
166 return -errno;
167
168 return stfs.f_type == BTRFS_SUPER_MAGIC;
169}
170
3d2ae1e2 171bool btrfs_detect(const char *path)
3c16d0cb
CB
172{
173 struct stat st;
174 int ret;
175
f7ac4459 176 if (!strncmp(path, "btrfs:", 6))
3d2ae1e2 177 return true;
f7ac4459 178
3c16d0cb 179 if (!is_btrfs_fs(path))
3d2ae1e2 180 return false;
3c16d0cb 181
f7ac4459 182 /* make sure it's a subvolume */
3c16d0cb
CB
183 ret = stat(path, &st);
184 if (ret < 0)
3d2ae1e2 185 return false;
3c16d0cb
CB
186
187 if (st.st_ino == 256 && S_ISDIR(st.st_mode))
3d2ae1e2 188 return true;
3c16d0cb 189
3d2ae1e2 190 return false;
3c16d0cb
CB
191}
192
10bc1861 193int btrfs_mount(struct lxc_storage *bdev)
3c16d0cb 194{
a08bfbe3
CB
195 unsigned long mntflags = 0;
196 char *mntdata = NULL;
41dc7155 197 const char *src;
3c16d0cb
CB
198 int ret;
199
200 if (strcmp(bdev->type, "btrfs"))
201 return -22;
3f88da0e 202
3c16d0cb
CB
203 if (!bdev->src || !bdev->dest)
204 return -22;
205
d94eb390 206 if (parse_mntopts_legacy(bdev->mntopts, &mntflags, &mntdata) < 0) {
3c16d0cb
CB
207 free(mntdata);
208 return -22;
209 }
210
3f88da0e
CB
211 src = lxc_storage_get_path(bdev->src, "btrfs");
212
213 ret = mount(src, bdev->dest, "bind", MS_BIND | MS_REC | mntflags, mntdata);
3c16d0cb
CB
214 free(mntdata);
215 return ret;
216}
217
10bc1861 218int btrfs_umount(struct lxc_storage *bdev)
3c16d0cb
CB
219{
220 if (strcmp(bdev->type, "btrfs"))
221 return -22;
3f88da0e 222
3c16d0cb
CB
223 if (!bdev->src || !bdev->dest)
224 return -22;
3f88da0e 225
3c16d0cb
CB
226 return umount(bdev->dest);
227}
228
229static int btrfs_subvolume_create(const char *path)
230{
9edcd5ab 231 int ret, saved_errno;
9de31d5a 232 size_t retlen;
9edcd5ab
CB
233 struct btrfs_ioctl_vol_args args;
234 char *p, *newfull;
235 int fd = -1;
3c16d0cb 236
9edcd5ab 237 newfull = strdup(path);
3c16d0cb 238 if (!newfull) {
9edcd5ab
CB
239 errno = ENOMEM;
240 return -ENOMEM;
3c16d0cb
CB
241 }
242
243 p = strrchr(newfull, '/');
244 if (!p) {
3c16d0cb 245 free(newfull);
9edcd5ab
CB
246 errno = EINVAL;
247 return -EINVAL;
3c16d0cb
CB
248 }
249 *p = '\0';
250
251 fd = open(newfull, O_RDONLY);
252 if (fd < 0) {
dd49809f 253 free(newfull);
9edcd5ab 254 return -errno;
3c16d0cb
CB
255 }
256
257 memset(&args, 0, sizeof(args));
9de31d5a 258 retlen = strlcpy(args.name, p + 1, BTRFS_SUBVOL_NAME_MAX);
4b812aca
CB
259 if (retlen >= BTRFS_SUBVOL_NAME_MAX) {
260 free(newfull);
261 close(fd);
9de31d5a 262 return -E2BIG;
4b812aca 263 }
9edcd5ab 264
3c16d0cb 265 ret = ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args);
9edcd5ab 266 saved_errno = errno;
3c16d0cb 267
3c16d0cb 268 close(fd);
dd49809f 269 free(newfull);
9edcd5ab 270 errno = saved_errno;
3c16d0cb
CB
271 return ret;
272}
273
988be9c1 274int btrfs_same_fs(const char *orig, const char *new)
3c16d0cb
CB
275{
276 int fd_orig = -1, fd_new = -1, ret = -1;
277 struct btrfs_ioctl_fs_info_args orig_args, new_args;
278
279 fd_orig = open(orig, O_RDONLY);
280 if (fd_orig < 0) {
fca23691 281 SYSERROR("Failed to open original rootfs %s", orig);
3c16d0cb
CB
282 goto out;
283 }
fca23691 284
3c16d0cb
CB
285 ret = ioctl(fd_orig, BTRFS_IOC_FS_INFO, &orig_args);
286 if (ret < 0) {
287 SYSERROR("BTRFS_IOC_FS_INFO %s", orig);
288 goto out;
289 }
290
291 fd_new = open(new, O_RDONLY);
292 if (fd_new < 0) {
fca23691 293 SYSERROR("Failed to open new container dir %s", new);
3c16d0cb
CB
294 ret = -1;
295 goto out;
296 }
fca23691 297
3c16d0cb
CB
298 ret = ioctl(fd_new, BTRFS_IOC_FS_INFO, &new_args);
299 if (ret < 0) {
300 SYSERROR("BTRFS_IOC_FS_INFO %s", new);
301 goto out;
302 }
303
304 if (strncmp(orig_args.fsid, new_args.fsid, BTRFS_FSID_SIZE) != 0) {
305 ret = -1;
306 goto out;
307 }
fca23691 308
3c16d0cb 309 ret = 0;
fca23691 310
3c16d0cb
CB
311out:
312 if (fd_new != -1)
313 close(fd_new);
fca23691 314
3c16d0cb
CB
315 if (fd_orig != -1)
316 close(fd_orig);
fca23691 317
3c16d0cb
CB
318 return ret;
319}
320
988be9c1 321int btrfs_snapshot(const char *orig, const char *new)
3c16d0cb 322{
9de31d5a 323 size_t retlen;
9edcd5ab
CB
324 struct btrfs_ioctl_vol_args_v2 args;
325 char *newdir, *newname;
326 char *newfull = NULL;
327 int saved_errno = -1;
3c16d0cb 328 int fd = -1, fddst = -1, ret = -1;
3c16d0cb
CB
329
330 newfull = strdup(new);
9edcd5ab 331 if (!newfull)
3c16d0cb 332 goto out;
9edcd5ab
CB
333
334 ret = rmdir(newfull);
335 if (ret < 0 && errno != ENOENT)
3c16d0cb 336 goto out;
9edcd5ab 337
3c16d0cb 338 newname = basename(newfull);
3c16d0cb 339 fd = open(orig, O_RDONLY);
9edcd5ab 340 if (fd < 0)
3c16d0cb 341 goto out;
9edcd5ab
CB
342
343 newdir = dirname(newfull);
3c16d0cb 344 fddst = open(newdir, O_RDONLY);
9edcd5ab 345 if (fddst < 0)
3c16d0cb 346 goto out;
3c16d0cb
CB
347
348 memset(&args, 0, sizeof(args));
37aa4851 349 args.fd = fd;
9de31d5a
CB
350 retlen = strlcpy(args.name, newname, BTRFS_SUBVOL_NAME_MAX);
351 if (retlen >= BTRFS_SUBVOL_NAME_MAX)
352 goto out;
9edcd5ab 353
3c16d0cb 354 ret = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
9edcd5ab 355 saved_errno = errno;
3c16d0cb
CB
356
357out:
358 if (fddst != -1)
359 close(fddst);
fca23691 360
3c16d0cb
CB
361 if (fd != -1)
362 close(fd);
fca23691 363
3c16d0cb 364 free(newfull);
9edcd5ab
CB
365
366 if (saved_errno >= 0)
367 errno = saved_errno;
fca23691 368
3c16d0cb
CB
369 return ret;
370}
371
e4a894d7 372int btrfs_snapshot_wrapper(void *data)
3c16d0cb 373{
41dc7155 374 const char *src;
3c16d0cb 375 struct rsync_data_char *arg = data;
3f88da0e 376
8917c382 377 (void)lxc_drop_groups();
b58214ac 378
3c16d0cb
CB
379 if (setgid(0) < 0) {
380 ERROR("Failed to setgid to 0");
381 return -1;
382 }
fca23691 383
3c16d0cb
CB
384 if (setuid(0) < 0) {
385 ERROR("Failed to setuid to 0");
386 return -1;
387 }
3f88da0e
CB
388
389 src = lxc_storage_get_path(arg->src, "btrfs");
3f88da0e 390 return btrfs_snapshot(src, arg->dest);
3c16d0cb
CB
391}
392
10bc1861
CB
393int btrfs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
394 const char *oldname, const char *cname,
395 const char *oldpath, const char *lxcpath, int snap,
396 uint64_t newsize, struct lxc_conf *conf)
3c16d0cb 397{
41dc7155 398 const char *src;
3f88da0e 399
3c16d0cb
CB
400 if (!orig->dest || !orig->src)
401 return -1;
402
0801b280
CB
403 if (strcmp(orig->type, "btrfs") && snap) {
404 ERROR("btrfs snapshot from %s backing store is not supported",
405 orig->type);
406 return -1;
3c16d0cb
CB
407 }
408
0801b280
CB
409 new->src = lxc_string_join(
410 "/",
411 (const char *[]){"btrfs:", *lxcpath != '/' ? lxcpath : ++lxcpath,
412 cname, "rootfs", NULL},
413 false);
9edcd5ab
CB
414 if (!new->src) {
415 ERROR("Failed to create new rootfs path");
0801b280 416 return -1;
9edcd5ab 417 }
b196516b 418 TRACE("Constructed new rootfs path \"%s\"", new->src);
0801b280 419
3f88da0e
CB
420 src = lxc_storage_get_path(new->src, "btrfs");
421 new->dest = strdup(src);
9edcd5ab
CB
422 if (!new->dest) {
423 ERROR("Failed to duplicate string \"%s\"", src);
3c16d0cb 424 return -1;
9edcd5ab 425 }
3c16d0cb 426
9edcd5ab
CB
427 if (orig->mntopts) {
428 new->mntopts = strdup(orig->mntopts);
429 if (!new->mntopts) {
430 ERROR("Failed to duplicate string \"%s\"",
431 orig->mntopts);
432 return -1;
433 }
434 }
3c16d0cb 435
b196516b
CB
436 return 0;
437}
45dd65e7 438
10bc1861
CB
439bool btrfs_create_clone(struct lxc_conf *conf, struct lxc_storage *orig,
440 struct lxc_storage *new, uint64_t newsize)
b196516b 441{
dd49809f
CB
442 int ret;
443 struct rsync_data data = {0, 0};
339de297 444 char cmd_output[PATH_MAX] = {0};
3c16d0cb 445
9edcd5ab 446 ret = rmdir(new->dest);
b196516b
CB
447 if (ret < 0 && errno != ENOENT)
448 return false;
3c16d0cb 449
9edcd5ab 450 ret = btrfs_subvolume_create(new->dest);
b196516b 451 if (ret < 0) {
9edcd5ab 452 SYSERROR("Failed to create btrfs subvolume \"%s\"", new->dest);
b196516b
CB
453 return false;
454 }
455
dd49809f
CB
456 /* rsync the contents from source to target */
457 data.orig = orig;
458 data.new = new;
fca23691 459
e0010464 460 if (am_guest_unpriv()) {
5c05427a
CB
461 ret = userns_exec_full(conf, lxc_storage_rsync_exec_wrapper,
462 &data, "lxc_storage_rsync_exec_wrapper");
b196516b 463 if (ret < 0) {
dd49809f
CB
464 ERROR("Failed to rsync from \"%s\" into \"%s\"",
465 orig->dest, new->dest);
b196516b
CB
466 return false;
467 }
dd49809f 468
b196516b
CB
469 return true;
470 }
471
dd49809f 472 ret = run_command(cmd_output, sizeof(cmd_output),
17a367d8 473 lxc_storage_rsync_exec_wrapper, (void *)&data);
b196516b 474 if (ret < 0) {
dd49809f
CB
475 ERROR("Failed to rsync from \"%s\" into \"%s\": %s", orig->dest,
476 new->dest, cmd_output);
b196516b
CB
477 return false;
478 }
479
b196516b
CB
480 return true;
481}
482
10bc1861
CB
483bool btrfs_create_snapshot(struct lxc_conf *conf, struct lxc_storage *orig,
484 struct lxc_storage *new, uint64_t newsize)
b196516b
CB
485{
486 int ret;
487
488 ret = rmdir(new->dest);
489 if (ret < 0 && errno != ENOENT)
490 return false;
491
e0010464 492 if (am_guest_unpriv()) {
b196516b
CB
493 struct rsync_data_char args;
494
4aaf9b81 495 args.src = orig->src;
b196516b
CB
496 args.dest = new->dest;
497
498 ret = userns_exec_1(conf, btrfs_snapshot_wrapper, &args,
4aaf9b81 499 "btrfs_snapshot_wrapper");
b196516b
CB
500 if (ret < 0) {
501 ERROR("Failed to run \"btrfs_snapshot_wrapper\"");
502 return false;
503 }
504
505 TRACE("Created btrfs snapshot \"%s\" from \"%s\"", new->dest,
506 orig->dest);
507 return true;
508 }
509
1c7222c0 510 ret = btrfs_snapshot(orig->src, new->dest);
b196516b
CB
511 if (ret < 0) {
512 SYSERROR("Failed to create btrfs snapshot \"%s\" from \"%s\"",
fca23691 513 new->dest, orig->dest);
b196516b
CB
514 return false;
515 }
516
517 TRACE("Created btrfs snapshot \"%s\" from \"%s\"", new->dest, orig->dest);
518 return true;
3c16d0cb
CB
519}
520
521static int btrfs_do_destroy_subvol(const char *path)
522{
523 int ret, fd = -1;
9de31d5a 524 size_t retlen;
3c16d0cb
CB
525 struct btrfs_ioctl_vol_args args;
526 char *p, *newfull = strdup(path);
527
528 if (!newfull) {
fca23691 529 ERROR("Out of memory");
3c16d0cb
CB
530 return -1;
531 }
532
533 p = strrchr(newfull, '/');
534 if (!p) {
fca23691 535 ERROR("Invalid path: %s", path);
3c16d0cb
CB
536 free(newfull);
537 return -1;
538 }
539 *p = '\0';
540
541 fd = open(newfull, O_RDONLY);
542 if (fd < 0) {
fca23691 543 SYSERROR("Failed to open %s", newfull);
3c16d0cb
CB
544 free(newfull);
545 return -1;
546 }
547
548 memset(&args, 0, sizeof(args));
9de31d5a
CB
549 retlen = strlcpy(args.name, p+1, BTRFS_SUBVOL_NAME_MAX);
550 if (retlen >= BTRFS_SUBVOL_NAME_MAX) {
551 free(newfull);
eb091d00 552 close(fd);
9de31d5a
CB
553 return -E2BIG;
554 }
555
3c16d0cb 556 ret = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
fca23691 557 INFO("IOCTL for destroying snapshot returned %d for %s", ret, path);
3c16d0cb
CB
558 if (ret < 0 && errno == EPERM)
559 ERROR("Is the rootfs mounted with -o user_subvol_rm_allowed?");
560
561 free(newfull);
562 close(fd);
563 return ret;
564}
565
566static int get_btrfs_tree_idx(struct my_btrfs_tree *tree, u64 id)
567{
568 int i;
fca23691 569
3c16d0cb
CB
570 if (!tree)
571 return -1;
fca23691 572
573 for (i = 0; i < tree->num; i++)
3c16d0cb
CB
574 if (tree->nodes[i].objid == id)
575 return i;
fca23691 576
3c16d0cb
CB
577 return -1;
578}
579
580static struct my_btrfs_tree *create_my_btrfs_tree(u64 id, const char *path,
581 int name_len)
582{
583 struct my_btrfs_tree *tree;
584
585 tree = malloc(sizeof(struct my_btrfs_tree));
586 if (!tree)
587 return NULL;
fca23691 588
3c16d0cb
CB
589 tree->nodes = malloc(sizeof(struct mytree_node));
590 if (!tree->nodes) {
591 free(tree);
592 return NULL;
593 }
fca23691 594
3c16d0cb
CB
595 tree->num = 1;
596 tree->nodes[0].dirname = NULL;
597 tree->nodes[0].name = strdup(path);
598 if (!tree->nodes[0].name) {
599 free(tree->nodes);
600 free(tree);
601 return NULL;
602 }
fca23691 603
3c16d0cb
CB
604 tree->nodes[0].parentid = 0;
605 tree->nodes[0].objid = id;
606 return tree;
607}
608
609static bool update_tree_node(struct mytree_node *n, u64 id, u64 parent,
fca23691 610 char *name, u16 name_len, char *dirname)
3c16d0cb
CB
611{
612 if (id)
613 n->objid = id;
9de31d5a 614
3c16d0cb
CB
615 if (parent)
616 n->parentid = parent;
9de31d5a 617
3c16d0cb
CB
618 if (name) {
619 n->name = malloc(name_len + 1);
620 if (!n->name)
621 return false;
9de31d5a 622
cbb9c7c7 623 (void)strlcpy(n->name, name, name_len + 1);
3c16d0cb 624 }
9de31d5a 625
3c16d0cb 626 if (dirname) {
cbb9c7c7
DJ
627 size_t len;
628
629 len = strlen(dirname);
630 n->dirname = malloc(len + 1);
3c16d0cb
CB
631 if (!n->dirname) {
632 free(n->name);
633 return false;
634 }
9de31d5a 635
cbb9c7c7 636 (void)strlcpy(n->dirname, dirname, len + 1);
3c16d0cb 637 }
fca23691 638
3c16d0cb
CB
639 return true;
640}
641
642static bool add_btrfs_tree_node(struct my_btrfs_tree *tree, u64 id, u64 parent,
fca23691 643 char *name, u16 name_len, char *dirname)
3c16d0cb
CB
644{
645 struct mytree_node *tmp;
646
647 int i = get_btrfs_tree_idx(tree, id);
648 if (i != -1)
649 return update_tree_node(&tree->nodes[i], id, parent, name,
650 name_len, dirname);
651
652 tmp = realloc(tree->nodes, (tree->num+1) * sizeof(struct mytree_node));
653 if (!tmp)
654 return false;
fca23691 655
3c16d0cb
CB
656 tree->nodes = tmp;
657 memset(&tree->nodes[tree->num], 0, sizeof(struct mytree_node));
fca23691 658
3c16d0cb
CB
659 if (!update_tree_node(&tree->nodes[tree->num], id, parent, name,
660 name_len, dirname))
661 return false;
fca23691 662
3c16d0cb
CB
663 tree->num++;
664 return true;
665}
666
667static void free_btrfs_tree(struct my_btrfs_tree *tree)
668{
669 int i;
fca23691 670
3c16d0cb
CB
671 if (!tree)
672 return;
fca23691 673
3c16d0cb
CB
674 for (i = 0; i < tree->num; i++) {
675 free(tree->nodes[i].name);
676 free(tree->nodes[i].dirname);
677 }
fca23691 678
3c16d0cb
CB
679 free(tree->nodes);
680 free(tree);
681}
682
683/*
684 * Given a @tree of subvolumes under @path, ask btrfs to remove each
685 * subvolume
686 */
687static bool do_remove_btrfs_children(struct my_btrfs_tree *tree, u64 root_id,
688 const char *path)
689{
76c00d39 690 int i, ret;
3c16d0cb
CB
691 char *newpath;
692 size_t len;
693
694 for (i = 0; i < tree->num; i++) {
695 if (tree->nodes[i].parentid == root_id) {
696 if (!tree->nodes[i].dirname) {
fca23691 697 WARN("Odd condition: child objid with no name under %s", path);
3c16d0cb
CB
698 continue;
699 }
fca23691 700
3c16d0cb
CB
701 len = strlen(path) + strlen(tree->nodes[i].dirname) + 2;
702 newpath = malloc(len);
703 if (!newpath) {
704 ERROR("Out of memory");
705 return false;
706 }
fca23691 707
76c00d39
CB
708 ret = snprintf(newpath, len, "%s/%s", path, tree->nodes[i].dirname);
709 if (ret < 0 || ret >= len) {
710 free(newpath);
711 return false;
712 }
fca23691 713
3c16d0cb 714 if (!do_remove_btrfs_children(tree, tree->nodes[i].objid, newpath)) {
fca23691 715 ERROR("Failed to prune %s", tree->nodes[i].name);
3c16d0cb
CB
716 free(newpath);
717 return false;
718 }
fca23691 719
3c16d0cb 720 if (btrfs_do_destroy_subvol(newpath) != 0) {
fca23691 721 ERROR("Failed to remove %s", newpath);
3c16d0cb
CB
722 free(newpath);
723 return false;
724 }
fca23691 725
3c16d0cb
CB
726 free(newpath);
727 }
728 }
fca23691 729
3c16d0cb
CB
730 return true;
731}
732
8408a9cc 733static int btrfs_lxc_rm_rf(const char *path)
3c16d0cb
CB
734{
735 u64 root_id;
736 int fd;
737 struct btrfs_ioctl_search_args args;
738 struct btrfs_ioctl_search_key *sk = &args.key;
21e019c2 739 struct btrfs_ioctl_search_header sh;
3c16d0cb
CB
740 struct btrfs_root_ref *ref;
741 struct my_btrfs_tree *tree;
58232f8c 742 int ret, e, i;
3c16d0cb 743 unsigned long off = 0;
fca23691 744 u16 name_len;
21e019c2 745 u64 dir_id;
3c16d0cb
CB
746
747 fd = open(path, O_RDONLY);
748 if (fd < 0) {
fca23691 749 ERROR("Failed to open %s", path);
3c16d0cb
CB
750 return -1;
751 }
752
753 if (btrfs_list_get_path_rootid(fd, &root_id)) {
58232f8c 754 e = errno;
3c16d0cb 755 close(fd);
58232f8c 756 if (e == EPERM || e == EACCES) {
3c16d0cb
CB
757 WARN("Will simply try removing");
758 goto ignore_search;
759 }
760
761 return -1;
762 }
763
764 tree = create_my_btrfs_tree(root_id, path, strlen(path));
765 if (!tree) {
fca23691 766 ERROR("Out of memory");
3c16d0cb
CB
767 close(fd);
768 return -1;
769 }
fca23691 770
3c16d0cb
CB
771 /* Walk all subvols looking for any under this id */
772 memset(&args, 0, sizeof(args));
773
774 /* search in the tree of tree roots */
775 sk->tree_id = 1;
3c16d0cb
CB
776 sk->max_type = BTRFS_ROOT_REF_KEY;
777 sk->min_type = BTRFS_ROOT_ITEM_KEY;
778 sk->min_objectid = 0;
779 sk->max_objectid = (u64)-1;
780 sk->max_offset = (u64)-1;
781 sk->min_offset = 0;
782 sk->max_transid = (u64)-1;
783 sk->nr_items = 4096;
784
51a8a74c 785 for (;;) {
3c16d0cb 786 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
58232f8c 787 e = errno;
3c16d0cb
CB
788 if (ret < 0) {
789 close(fd);
3c16d0cb 790 free_btrfs_tree(tree);
58232f8c 791 if (e == EPERM || e == EACCES) {
fca23691 792 WARN("Can't perform the search under %s. "
793 "Will simply try removing", path);
8ba6dc5e
OF
794 goto ignore_search;
795 }
796
fca23691 797 ERROR("Can't perform the search under %s", path);
3c16d0cb
CB
798 return -1;
799 }
fca23691 800
3c16d0cb
CB
801 if (sk->nr_items == 0)
802 break;
803
804 off = 0;
805 for (i = 0; i < sk->nr_items; i++) {
21e019c2
TT
806 memcpy(&sh, args.buf + off, sizeof(sh));
807 off += sizeof(sh);
fca23691 808
3c16d0cb
CB
809 /*
810 * A backref key with the name and dirid of the parent
6226464d 811 * comes followed by the root ref key which has the
3c16d0cb
CB
812 * name of the child subvol in question.
813 */
21e019c2 814 if (sh.objectid != root_id && sh.type == BTRFS_ROOT_BACKREF_KEY) {
240a130f
CB
815 __do_free char *name = NULL, *tmppath = NULL;
816 char *tmp;
317e6a83 817
3c16d0cb 818 ref = (struct btrfs_root_ref *)(args.buf + off);
21e019c2 819 name_len = btrfs_stack_root_ref_name_len(ref);
317e6a83
CB
820 tmp = (char *)(ref + 1);
821
822 name = malloc(name_len + 1);
823 if (!name) {
824 ERROR("Out of memory");
825 free_btrfs_tree(tree);
317e6a83 826 close(fd);
ae9ad030 827 return -ENOMEM;
317e6a83
CB
828 }
829
830 memcpy(name, tmp, name_len);
831 name[name_len] = '\0';
21e019c2
TT
832 dir_id = btrfs_stack_root_ref_dirid(ref);
833 tmppath = get_btrfs_subvol_path(fd, sh.offset,
834 dir_id, name, name_len);
fca23691 835
21e019c2
TT
836 if (!add_btrfs_tree_node(tree, sh.objectid,
837 sh.offset, name,
3c16d0cb
CB
838 name_len, tmppath)) {
839 ERROR("Out of memory");
840 free_btrfs_tree(tree);
3c16d0cb
CB
841 close(fd);
842 return -1;
843 }
3c16d0cb 844 }
fca23691 845
21e019c2 846 off += sh.len;
3c16d0cb
CB
847
848 /*
849 * record the mins in sk so we can make sure the
850 * next search doesn't repeat this root
851 */
21e019c2
TT
852 sk->min_objectid = sh.objectid;
853 sk->min_type = sh.type;
854 sk->min_offset = sh.offset;
3c16d0cb 855 }
fca23691 856
3c16d0cb
CB
857 sk->nr_items = 4096;
858 sk->min_offset++;
fca23691 859
3c16d0cb
CB
860 if (!sk->min_offset)
861 sk->min_type++;
862 else
863 continue;
864
865 if (sk->min_type > BTRFS_ROOT_BACKREF_KEY) {
866 sk->min_type = BTRFS_ROOT_ITEM_KEY;
867 sk->min_objectid++;
fca23691 868 } else {
3c16d0cb 869 continue;
fca23691 870 }
3c16d0cb
CB
871
872 if (sk->min_objectid >= sk->max_objectid)
873 break;
874 }
fca23691 875
3c16d0cb
CB
876 close(fd);
877
878 /* now actually remove them */
3c16d0cb
CB
879 if (!do_remove_btrfs_children(tree, root_id, path)) {
880 free_btrfs_tree(tree);
fca23691 881 ERROR("Failed to prune");
3c16d0cb
CB
882 return -1;
883 }
884
885 free_btrfs_tree(tree);
fca23691 886
3c16d0cb
CB
887 /* All child subvols have been removed, now remove this one */
888ignore_search:
889 return btrfs_do_destroy_subvol(path);
890}
891
892bool btrfs_try_remove_subvol(const char *path)
893{
894 if (!btrfs_detect(path))
895 return false;
3f88da0e 896
8408a9cc 897 return btrfs_lxc_rm_rf(path) == 0;
3c16d0cb
CB
898}
899
10bc1861 900int btrfs_destroy(struct lxc_storage *orig)
3c16d0cb 901{
41dc7155 902 const char *src;
3f88da0e
CB
903
904 src = lxc_storage_get_path(orig->src, "btrfs");
905
8408a9cc 906 return btrfs_lxc_rm_rf(src);
3c16d0cb
CB
907}
908
10bc1861 909int btrfs_create(struct lxc_storage *bdev, const char *dest, const char *n,
facdf925 910 struct bdev_specs *specs, const struct lxc_conf *conf)
3c16d0cb 911{
3f88da0e
CB
912 int ret;
913 size_t len;
914
fca23691 915
3f88da0e
CB
916 len = strlen(dest) + 1;
917 /* strlen("btrfs:") */
918 len += 6;
fca23691 919
3f88da0e 920 bdev->src = malloc(len);
dd49809f
CB
921 if (!bdev->src) {
922 ERROR("Failed to allocate memory");
3f88da0e 923 return -1;
dd49809f 924 }
3f88da0e
CB
925
926 ret = snprintf(bdev->src, len, "btrfs:%s", dest);
dd49809f
CB
927 if (ret < 0 || (size_t)ret >= len) {
928 ERROR("Failed to create string");
3f88da0e 929 return -1;
dd49809f 930 }
3f88da0e 931
3c16d0cb 932 bdev->dest = strdup(dest);
dd49809f
CB
933 if (!bdev->dest) {
934 ERROR("Failed to duplicate string \"%s\"", dest);
3c16d0cb 935 return -1;
dd49809f 936 }
3f88da0e 937
dd49809f 938 ret = btrfs_subvolume_create(bdev->dest);
fca23691 939 if (ret < 0)
dd49809f 940 SYSERROR("Failed to create btrfs subvolume \"%s\"", bdev->dest);
dd49809f
CB
941
942 return ret;
3c16d0cb 943}