chiark / gitweb /
btrfs: make btrfs_subvol_snapshot() parameters a flags field
[elogind.git] / src / shared / btrfs-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2014 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdlib.h>
23 #include <sys/vfs.h>
24 #include <sys/stat.h>
25
26 #ifdef HAVE_LINUX_BTRFS_H
27 #include <linux/btrfs.h>
28 #endif
29
30 #include "missing.h"
31 #include "util.h"
32 #include "path-util.h"
33 #include "macro.h"
34 #include "copy.h"
35 #include "selinux-util.h"
36 #include "smack-util.h"
37 #include "fileio.h"
38 #include "btrfs-ctree.h"
39 #include "btrfs-util.h"
40
41 static int validate_subvolume_name(const char *name) {
42
43         if (!filename_is_valid(name))
44                 return -EINVAL;
45
46         if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
47                 return -E2BIG;
48
49         return 0;
50 }
51
52 static int open_parent(const char *path, int flags) {
53         _cleanup_free_ char *parent = NULL;
54         int r, fd;
55
56         assert(path);
57
58         r = path_get_parent(path, &parent);
59         if (r < 0)
60                 return r;
61
62         fd = open(parent, flags);
63         if (fd < 0)
64                 return -errno;
65
66         return fd;
67 }
68
69 static int extract_subvolume_name(const char *path, const char **subvolume) {
70         const char *fn;
71         int r;
72
73         assert(path);
74         assert(subvolume);
75
76         fn = basename(path);
77
78         r = validate_subvolume_name(fn);
79         if (r < 0)
80                 return r;
81
82         *subvolume = fn;
83         return 0;
84 }
85
86 int btrfs_is_snapshot(int fd) {
87         struct stat st;
88         struct statfs sfs;
89
90         /* On btrfs subvolumes always have the inode 256 */
91
92         if (fstat(fd, &st) < 0)
93                 return -errno;
94
95         if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
96                 return 0;
97
98         if (fstatfs(fd, &sfs) < 0)
99                 return -errno;
100
101         return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
102 }
103
104 int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
105         struct btrfs_ioctl_vol_args_v2 args = {
106                 .flags = flags & BTRFS_SNAPSHOT_READ_ONLY ? BTRFS_SUBVOL_RDONLY : 0,
107         };
108         _cleanup_close_ int new_fd = -1;
109         const char *subvolume;
110         int r;
111
112         assert(new_path);
113
114         r = btrfs_is_snapshot(old_fd);
115         if (r < 0)
116                 return r;
117         if (r == 0) {
118                 if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY))
119                         return -EISDIR;
120
121                 r = btrfs_subvol_make(new_path);
122                 if (r < 0)
123                         return r;
124
125                 r = copy_directory_fd(old_fd, new_path, true);
126                 if (r < 0) {
127                         btrfs_subvol_remove(new_path, false);
128                         return r;
129                 }
130
131                 if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
132                         r = btrfs_subvol_set_read_only(new_path, true);
133                         if (r < 0) {
134                                 btrfs_subvol_remove(new_path, false);
135                                 return r;
136                         }
137                 }
138
139                 return 0;
140         }
141
142         r = extract_subvolume_name(new_path, &subvolume);
143         if (r < 0)
144                 return r;
145
146         new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
147         if (new_fd < 0)
148                 return new_fd;
149
150         strncpy(args.name, subvolume, sizeof(args.name)-1);
151         args.fd = old_fd;
152
153         if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &args) < 0)
154                 return -errno;
155
156         return 0;
157 }
158
159 int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) {
160         _cleanup_close_ int old_fd = -1;
161
162         assert(old_path);
163         assert(new_path);
164
165         old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
166         if (old_fd < 0)
167                 return -errno;
168
169         return btrfs_subvol_snapshot_fd(old_fd, new_path, flags);
170 }
171
172 int btrfs_subvol_make(const char *path) {
173         struct btrfs_ioctl_vol_args args = {};
174         _cleanup_close_ int fd = -1;
175         const char *subvolume;
176         int r;
177
178         assert(path);
179
180         r = extract_subvolume_name(path, &subvolume);
181         if (r < 0)
182                 return r;
183
184         fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
185         if (fd < 0)
186                 return fd;
187
188         strncpy(args.name, subvolume, sizeof(args.name)-1);
189
190         if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
191                 return -errno;
192
193         return 0;
194 }
195
196 int btrfs_subvol_make_label(const char *path) {
197         int r;
198
199         assert(path);
200
201         r = mac_selinux_create_file_prepare(path, S_IFDIR);
202         if (r < 0)
203                 return r;
204
205         r = btrfs_subvol_make(path);
206         mac_selinux_create_file_clear();
207
208         if (r < 0)
209                 return r;
210
211         return mac_smack_fix(path, false, false);
212 }
213
214 int btrfs_subvol_set_read_only_fd(int fd, bool b) {
215         uint64_t flags, nflags;
216         struct stat st;
217
218         assert(fd >= 0);
219
220         if (fstat(fd, &st) < 0)
221                 return -errno;
222
223         if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
224                 return -EINVAL;
225
226         if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
227                 return -errno;
228
229         if (b)
230                 nflags = flags | BTRFS_SUBVOL_RDONLY;
231         else
232                 nflags = flags & ~BTRFS_SUBVOL_RDONLY;
233
234         if (flags == nflags)
235                 return 0;
236
237         if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
238                 return -errno;
239
240         return 0;
241 }
242
243 int btrfs_subvol_set_read_only(const char *path, bool b) {
244         _cleanup_close_ int fd = -1;
245
246         fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
247         if (fd < 0)
248                 return -errno;
249
250         return btrfs_subvol_set_read_only_fd(fd, b);
251 }
252
253 int btrfs_subvol_get_read_only_fd(int fd) {
254         uint64_t flags;
255
256         if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
257                 return -errno;
258
259         return !!(flags & BTRFS_SUBVOL_RDONLY);
260 }
261
262 int btrfs_reflink(int infd, int outfd) {
263         int r;
264
265         assert(infd >= 0);
266         assert(outfd >= 0);
267
268         r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
269         if (r < 0)
270                 return -errno;
271
272         return 0;
273 }
274
275 int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) {
276         struct btrfs_ioctl_clone_range_args args = {
277                 .src_fd = infd,
278                 .src_offset = in_offset,
279                 .src_length = sz,
280                 .dest_offset = out_offset,
281         };
282         int r;
283
284         assert(infd >= 0);
285         assert(outfd >= 0);
286         assert(sz > 0);
287
288         r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args);
289         if (r < 0)
290                 return -errno;
291
292         return 0;
293 }
294
295 int btrfs_get_block_device_fd(int fd, dev_t *dev) {
296         struct btrfs_ioctl_fs_info_args fsi = {};
297         uint64_t id;
298
299         assert(fd >= 0);
300         assert(dev);
301
302         if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
303                 return -errno;
304
305         /* We won't do this for btrfs RAID */
306         if (fsi.num_devices != 1)
307                 return 0;
308
309         for (id = 1; id <= fsi.max_id; id++) {
310                 struct btrfs_ioctl_dev_info_args di = {
311                         .devid = id,
312                 };
313                 struct stat st;
314
315                 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
316                         if (errno == ENODEV)
317                                 continue;
318
319                         return -errno;
320                 }
321
322                 if (stat((char*) di.path, &st) < 0)
323                         return -errno;
324
325                 if (!S_ISBLK(st.st_mode))
326                         return -ENODEV;
327
328                 if (major(st.st_rdev) == 0)
329                         return -ENODEV;
330
331                 *dev = st.st_rdev;
332                 return 1;
333         }
334
335         return -ENODEV;
336 }
337
338 int btrfs_get_block_device(const char *path, dev_t *dev) {
339         _cleanup_close_ int fd = -1;
340
341         assert(path);
342         assert(dev);
343
344         fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
345         if (fd < 0)
346                 return -errno;
347
348         return btrfs_get_block_device_fd(fd, dev);
349 }
350
351 int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
352         struct btrfs_ioctl_ino_lookup_args args = {
353                 .objectid = BTRFS_FIRST_FREE_OBJECTID
354         };
355
356         assert(fd >= 0);
357         assert(ret);
358
359         if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
360                 return -errno;
361
362         *ret = args.treeid;
363         return 0;
364 }
365
366 static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) {
367         assert(args);
368
369         /* the objectid, type, offset together make up the btrfs key,
370          * which is considered a single 136byte integer when
371          * comparing. This call increases the counter by one, dealing
372          * with the overflow between the overflows */
373
374         if (args->key.min_offset < (uint64_t) -1) {
375                 args->key.min_offset++;
376                 return true;
377         }
378
379         if (args->key.min_type < (uint8_t) -1) {
380                 args->key.min_type++;
381                 args->key.min_offset = 0;
382                 return true;
383         }
384
385         if (args->key.min_objectid < (uint64_t) -1) {
386                 args->key.min_objectid++;
387                 args->key.min_offset = 0;
388                 args->key.min_type = 0;
389                 return true;
390         }
391
392         return 0;
393 }
394
395 static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) {
396         assert(args);
397         assert(h);
398
399         args->key.min_objectid = h->objectid;
400         args->key.min_type = h->type;
401         args->key.min_offset = h->offset;
402 }
403
404 static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
405         assert(args);
406
407         /* Compare min and max */
408
409         if (args->key.min_objectid < args->key.max_objectid)
410                 return -1;
411         if (args->key.min_objectid > args->key.max_objectid)
412                 return 1;
413
414         if (args->key.min_type < args->key.max_type)
415                 return -1;
416         if (args->key.min_type > args->key.max_type)
417                 return 1;
418
419         if (args->key.min_offset < args->key.max_offset)
420                 return -1;
421         if (args->key.min_offset > args->key.max_offset)
422                 return 1;
423
424         return 0;
425 }
426
427 #define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args)                  \
428         for ((i) = 0,                                                   \
429              (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \
430              (i) < (args).key.nr_items;                                 \
431              (i)++,                                                     \
432              (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len))
433
434 #define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh)                              \
435         ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header)))
436
437 int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
438         struct btrfs_ioctl_search_args args = {
439                 /* Tree of tree roots */
440                 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
441
442                 /* Look precisely for the subvolume items */
443                 .key.min_type = BTRFS_ROOT_ITEM_KEY,
444                 .key.max_type = BTRFS_ROOT_ITEM_KEY,
445
446                 .key.min_offset = 0,
447                 .key.max_offset = (uint64_t) -1,
448
449                 /* No restrictions on the other components */
450                 .key.min_transid = 0,
451                 .key.max_transid = (uint64_t) -1,
452         };
453
454         uint64_t subvol_id;
455         bool found = false;
456         int r;
457
458         assert(fd >= 0);
459         assert(ret);
460
461         r = btrfs_subvol_get_id_fd(fd, &subvol_id);
462         if (r < 0)
463                 return r;
464
465         args.key.min_objectid = args.key.max_objectid = subvol_id;
466
467         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
468                 const struct btrfs_ioctl_search_header *sh;
469                 unsigned i;
470
471                 args.key.nr_items = 256;
472                 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
473                         return -errno;
474
475                 if (args.key.nr_items <= 0)
476                         break;
477
478                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
479
480                         const struct btrfs_root_item *ri;
481
482                         /* Make sure we start the next search at least from this entry */
483                         btrfs_ioctl_search_args_set(&args, sh);
484
485                         if (sh->objectid != subvol_id)
486                                 continue;
487                         if (sh->type != BTRFS_ROOT_ITEM_KEY)
488                                 continue;
489
490                         /* Older versions of the struct lacked the otime setting */
491                         if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
492                                 continue;
493
494                         ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
495
496                         ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
497                                 (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
498
499                         ret->subvol_id = subvol_id;
500                         ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
501
502                         assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
503                         memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
504                         memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
505
506                         found = true;
507                         goto finish;
508                 }
509
510                 /* Increase search key by one, to read the next item, if we can. */
511                 if (!btrfs_ioctl_search_args_inc(&args))
512                         break;
513         }
514
515 finish:
516         if (!found)
517                 return -ENODATA;
518
519         return 0;
520 }
521
522 int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
523
524         struct btrfs_ioctl_search_args args = {
525                 /* Tree of quota items */
526                 .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
527
528                 /* The object ID is always 0 */
529                 .key.min_objectid = 0,
530                 .key.max_objectid = 0,
531
532                 /* Look precisely for the quota items */
533                 .key.min_type = BTRFS_QGROUP_STATUS_KEY,
534                 .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
535
536                 /* No restrictions on the other components */
537                 .key.min_transid = 0,
538                 .key.max_transid = (uint64_t) -1,
539         };
540
541         uint64_t subvol_id;
542         bool found_info = false, found_limit = false;
543         int r;
544
545         assert(fd >= 0);
546         assert(ret);
547
548         r = btrfs_subvol_get_id_fd(fd, &subvol_id);
549         if (r < 0)
550                 return r;
551
552         args.key.min_offset = args.key.max_offset = subvol_id;
553
554         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
555                 const struct btrfs_ioctl_search_header *sh;
556                 unsigned i;
557
558                 args.key.nr_items = 256;
559                 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
560                         return -errno;
561
562                 if (args.key.nr_items <= 0)
563                         break;
564
565                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
566
567                         /* Make sure we start the next search at least from this entry */
568                         btrfs_ioctl_search_args_set(&args, sh);
569
570                         if (sh->objectid != 0)
571                                 continue;
572                         if (sh->offset != subvol_id)
573                                 continue;
574
575                         if (sh->type == BTRFS_QGROUP_INFO_KEY) {
576                                 const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
577
578                                 ret->referenced = le64toh(qii->rfer);
579                                 ret->exclusive = le64toh(qii->excl);
580
581                                 found_info = true;
582
583                         } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
584                                 const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
585
586                                 ret->referenced_max = le64toh(qli->max_rfer);
587                                 ret->exclusive_max = le64toh(qli->max_excl);
588
589                                 if (ret->referenced_max == 0)
590                                         ret->referenced_max = (uint64_t) -1;
591                                 if (ret->exclusive_max == 0)
592                                         ret->exclusive_max = (uint64_t) -1;
593
594                                 found_limit = true;
595                         }
596
597                         if (found_info && found_limit)
598                                 goto finish;
599                 }
600
601                 /* Increase search key by one, to read the next item, if we can. */
602                 if (!btrfs_ioctl_search_args_inc(&args))
603                         break;
604         }
605
606 finish:
607         if (!found_limit && !found_info)
608                 return -ENODATA;
609
610         if (!found_info) {
611                 ret->referenced = (uint64_t) -1;
612                 ret->exclusive = (uint64_t) -1;
613         }
614
615         if (!found_limit) {
616                 ret->referenced_max = (uint64_t) -1;
617                 ret->exclusive_max = (uint64_t) -1;
618         }
619
620         return 0;
621 }
622
623 int btrfs_defrag_fd(int fd) {
624         assert(fd >= 0);
625
626         if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
627                 return -errno;
628
629         return 0;
630 }
631
632 int btrfs_defrag(const char *p) {
633         _cleanup_close_ int fd = -1;
634
635         fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
636         if (fd < 0)
637                 return -errno;
638
639         return btrfs_defrag_fd(fd);
640 }
641
642 int btrfs_quota_enable_fd(int fd, bool b) {
643         struct btrfs_ioctl_quota_ctl_args args = {
644                 .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE,
645         };
646
647         assert(fd >= 0);
648
649         if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0)
650                 return -errno;
651
652         return 0;
653 }
654
655 int btrfs_quota_enable(const char *path, bool b) {
656         _cleanup_close_ int fd = -1;
657
658         fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
659         if (fd < 0)
660                 return -errno;
661
662         return btrfs_quota_enable_fd(fd, b);
663 }
664
665 int btrfs_quota_limit_fd(int fd, uint64_t referenced_max) {
666         struct btrfs_ioctl_qgroup_limit_args args = {
667                 .lim.max_rfer =
668                         referenced_max == (uint64_t) -1 ? 0 :
669                         referenced_max == 0 ? 1 : referenced_max,
670                 .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER,
671         };
672
673         assert(fd >= 0);
674
675         if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0)
676                 return -errno;
677
678         return 0;
679 }
680
681 int btrfs_quota_limit(const char *path, uint64_t referenced_max) {
682         _cleanup_close_ int fd = -1;
683
684         fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
685         if (fd < 0)
686                 return -errno;
687
688         return btrfs_quota_limit_fd(fd, referenced_max);
689 }
690
691 int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
692         struct btrfs_ioctl_vol_args args = {};
693         _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL;
694         _cleanup_close_ int loop_fd = -1, backing_fd = -1;
695         struct stat st;
696         dev_t dev = 0;
697         int r;
698
699         /* btrfs cannot handle file systems < 16M, hence use this as minimum */
700         if (new_size < 16*1024*1024)
701                 new_size = 16*1024*1024;
702
703         r = btrfs_get_block_device_fd(fd, &dev);
704         if (r < 0)
705                 return r;
706         if (r == 0)
707                 return -ENODEV;
708
709         if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0)
710                 return -ENOMEM;
711         r = read_one_line_file(p, &backing);
712         if (r == -ENOENT)
713                 return -ENODEV;
714         if (r < 0)
715                 return r;
716         if (isempty(backing) || !path_is_absolute(backing))
717                 return -ENODEV;
718
719         backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY);
720         if (backing_fd < 0)
721                 return -errno;
722
723         if (fstat(backing_fd, &st) < 0)
724                 return -errno;
725         if (!S_ISREG(st.st_mode))
726                 return -ENODEV;
727
728         if (new_size == (uint64_t) st.st_size)
729                 return 0;
730
731         if (grow_only && new_size < (uint64_t) st.st_size)
732                 return -EINVAL;
733
734         if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0)
735                 return -ENOMEM;
736         loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY);
737         if (loop_fd < 0)
738                 return -errno;
739
740         if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name))
741                 return -EINVAL;
742
743         if (new_size < (uint64_t) st.st_size) {
744                 /* Decrease size: first decrease btrfs size, then shorten loopback */
745                 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
746                         return -errno;
747         }
748
749         if (ftruncate(backing_fd, new_size) < 0)
750                 return -errno;
751
752         if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0)
753                 return -errno;
754
755         if (new_size > (uint64_t) st.st_size) {
756                 /* Increase size: first enlarge loopback, then increase btrfs size */
757                 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
758                         return -errno;
759         }
760
761         /* Make sure the free disk space is correctly updated for both file systems */
762         (void) fsync(fd);
763         (void) fsync(backing_fd);
764
765         return 1;
766 }
767
768 int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) {
769         _cleanup_close_ int fd = -1;
770
771         fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
772         if (fd < 0)
773                 return -errno;
774
775         return btrfs_resize_loopback_fd(fd, new_size, grow_only);
776 }
777
778 static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol_id, bool recursive) {
779         struct btrfs_ioctl_search_args args = {
780                 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
781
782                 .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
783                 .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
784
785                 .key.min_type = BTRFS_ROOT_BACKREF_KEY,
786                 .key.max_type = BTRFS_ROOT_BACKREF_KEY,
787
788                 .key.min_transid = 0,
789                 .key.max_transid = (uint64_t) -1,
790         };
791
792         struct btrfs_ioctl_vol_args vol_args = {};
793         _cleanup_close_ int subvol_fd = -1;
794         int r;
795
796         assert(fd >= 0);
797         assert(subvolume);
798
799         /* First, try to remove the subvolume. If it happens to be
800          * already empty, this will just work. */
801         strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
802         if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) >= 0)
803                 return 0;
804         if (!recursive || errno != ENOTEMPTY)
805                 return -errno;
806
807         /* OK, the subvolume is not empty, let's look for child
808          * subvolumes, and remove them, first */
809         subvol_fd = openat(fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
810         if (subvol_fd < 0)
811                 return -errno;
812
813         if (subvol_id == 0) {
814                 r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id);
815                 if (r < 0)
816                         return r;
817         }
818
819         args.key.min_offset = args.key.max_offset = subvol_id;
820
821         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
822                 const struct btrfs_ioctl_search_header *sh;
823                 unsigned i;
824
825                 args.key.nr_items = 256;
826                 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
827                         return -errno;
828
829                 if (args.key.nr_items <= 0)
830                         break;
831
832                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
833                         _cleanup_free_ char *p = NULL;
834                         const struct btrfs_root_ref *ref;
835                         struct btrfs_ioctl_ino_lookup_args ino_args;
836
837                         btrfs_ioctl_search_args_set(&args, sh);
838
839                         if (sh->type != BTRFS_ROOT_BACKREF_KEY)
840                                 continue;
841                         if (sh->offset != subvol_id)
842                                 continue;
843
844                         ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
845
846                         p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
847                         if (!p)
848                                 return -ENOMEM;
849
850                         zero(ino_args);
851                         ino_args.treeid = subvol_id;
852                         ino_args.objectid = ref->dirid;
853
854                         if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
855                                 return -errno;
856
857                         if (isempty(ino_args.name))
858                                 /* Subvolume is in the top-level
859                                  * directory of the subvolume. */
860                                 r = subvol_remove_children(subvol_fd, p, sh->objectid, recursive);
861                         else {
862                                 _cleanup_close_ int child_fd = -1;
863
864                                 /* Subvolume is somewhere further down,
865                                  * hence we need to open the
866                                  * containing directory first */
867
868                                 child_fd = openat(subvol_fd, ino_args.name, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
869                                 if (child_fd < 0)
870                                         return -errno;
871
872                                 r = subvol_remove_children(child_fd, p, sh->objectid, recursive);
873                         }
874                         if (r < 0)
875                                 return r;
876                 }
877
878                 /* Increase search key by one, to read the next item, if we can. */
879                 if (!btrfs_ioctl_search_args_inc(&args))
880                         break;
881         }
882
883         /* OK, the child subvolumes should all be gone now, let's try
884          * again to remove the subvolume */
885         if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) < 0)
886                 return -errno;
887
888         return 0;
889 }
890
891 int btrfs_subvol_remove(const char *path, bool recursive) {
892         _cleanup_close_ int fd = -1;
893         const char *subvolume;
894         int r;
895
896         assert(path);
897
898         r = extract_subvolume_name(path, &subvolume);
899         if (r < 0)
900                 return r;
901
902         fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
903         if (fd < 0)
904                 return fd;
905
906         return subvol_remove_children(fd, subvolume, 0, recursive);
907 }
908
909 int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive) {
910         return subvol_remove_children(fd, subvolume, 0, recursive);
911 }