chiark / gitweb /
units: make networkd pull in its own .busname unit
[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 /* WARNING: Be careful with file system ioctls! When we get an fd, we
42  * need to make sure it either refers to only a regular file or
43  * directory, or that it is located on btrfs, before invoking any
44  * btrfs ioctls. The ioctl numbers are reused by some device drivers
45  * (such as DRM), and hence might have bad effects when invoked on
46  * device nodes (that reference drivers) rather than fds to normal
47  * files or directories. */
48
49 static int validate_subvolume_name(const char *name) {
50
51         if (!filename_is_valid(name))
52                 return -EINVAL;
53
54         if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
55                 return -E2BIG;
56
57         return 0;
58 }
59
60 static int open_parent(const char *path, int flags) {
61         _cleanup_free_ char *parent = NULL;
62         int r, fd;
63
64         assert(path);
65
66         r = path_get_parent(path, &parent);
67         if (r < 0)
68                 return r;
69
70         fd = open(parent, flags);
71         if (fd < 0)
72                 return -errno;
73
74         return fd;
75 }
76
77 static int extract_subvolume_name(const char *path, const char **subvolume) {
78         const char *fn;
79         int r;
80
81         assert(path);
82         assert(subvolume);
83
84         fn = basename(path);
85
86         r = validate_subvolume_name(fn);
87         if (r < 0)
88                 return r;
89
90         *subvolume = fn;
91         return 0;
92 }
93
94 int btrfs_is_filesystem(int fd) {
95         struct statfs sfs;
96
97         assert(fd >= 0);
98
99         if (fstatfs(fd, &sfs) < 0)
100                 return -errno;
101
102         return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
103 }
104
105 int btrfs_is_subvol(int fd) {
106         struct stat st;
107
108         assert(fd >= 0);
109
110         /* On btrfs subvolumes always have the inode 256 */
111
112         if (fstat(fd, &st) < 0)
113                 return -errno;
114
115         if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
116                 return 0;
117
118         return btrfs_is_filesystem(fd);
119 }
120
121 int btrfs_subvol_make(const char *path) {
122         struct btrfs_ioctl_vol_args args = {};
123         _cleanup_close_ int fd = -1;
124         const char *subvolume;
125         int r;
126
127         assert(path);
128
129         r = extract_subvolume_name(path, &subvolume);
130         if (r < 0)
131                 return r;
132
133         fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
134         if (fd < 0)
135                 return fd;
136
137         strncpy(args.name, subvolume, sizeof(args.name)-1);
138
139         if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
140                 return -errno;
141
142         return 0;
143 }
144
145 int btrfs_subvol_make_label(const char *path) {
146         int r;
147
148         assert(path);
149
150         r = mac_selinux_create_file_prepare(path, S_IFDIR);
151         if (r < 0)
152                 return r;
153
154         r = btrfs_subvol_make(path);
155         mac_selinux_create_file_clear();
156
157         if (r < 0)
158                 return r;
159
160         return mac_smack_fix(path, false, false);
161 }
162
163 int btrfs_subvol_set_read_only_fd(int fd, bool b) {
164         uint64_t flags, nflags;
165         struct stat st;
166
167         assert(fd >= 0);
168
169         if (fstat(fd, &st) < 0)
170                 return -errno;
171
172         if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
173                 return -EINVAL;
174
175         if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
176                 return -errno;
177
178         if (b)
179                 nflags = flags | BTRFS_SUBVOL_RDONLY;
180         else
181                 nflags = flags & ~BTRFS_SUBVOL_RDONLY;
182
183         if (flags == nflags)
184                 return 0;
185
186         if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
187                 return -errno;
188
189         return 0;
190 }
191
192 int btrfs_subvol_set_read_only(const char *path, bool b) {
193         _cleanup_close_ int fd = -1;
194
195         fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
196         if (fd < 0)
197                 return -errno;
198
199         return btrfs_subvol_set_read_only_fd(fd, b);
200 }
201
202 int btrfs_subvol_get_read_only_fd(int fd) {
203         uint64_t flags;
204         struct stat st;
205
206         assert(fd >= 0);
207
208         if (fstat(fd, &st) < 0)
209                 return -errno;
210
211         if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
212                 return -EINVAL;
213
214         if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
215                 return -errno;
216
217         return !!(flags & BTRFS_SUBVOL_RDONLY);
218 }
219
220 int btrfs_reflink(int infd, int outfd) {
221         struct stat st;
222         int r;
223
224         assert(infd >= 0);
225         assert(outfd >= 0);
226
227         /* Make sure we invoke the ioctl on a regular file, so that no
228          * device driver accidentally gets it. */
229
230         if (fstat(outfd, &st) < 0)
231                 return -errno;
232
233         if (!S_ISREG(st.st_mode))
234                 return -EINVAL;
235
236         r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
237         if (r < 0)
238                 return -errno;
239
240         return 0;
241 }
242
243 int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) {
244         struct btrfs_ioctl_clone_range_args args = {
245                 .src_fd = infd,
246                 .src_offset = in_offset,
247                 .src_length = sz,
248                 .dest_offset = out_offset,
249         };
250         struct stat st;
251         int r;
252
253         assert(infd >= 0);
254         assert(outfd >= 0);
255         assert(sz > 0);
256
257         if (fstat(outfd, &st) < 0)
258                 return -errno;
259
260         if (!S_ISREG(st.st_mode))
261                 return -EINVAL;
262
263         r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args);
264         if (r < 0)
265                 return -errno;
266
267         return 0;
268 }
269
270 int btrfs_get_block_device_fd(int fd, dev_t *dev) {
271         struct btrfs_ioctl_fs_info_args fsi = {};
272         uint64_t id;
273         int r;
274
275         assert(fd >= 0);
276         assert(dev);
277
278         r = btrfs_is_filesystem(fd);
279         if (r < 0)
280                 return r;
281         if (!r)
282                 return -ENOTTY;
283
284         if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
285                 return -errno;
286
287         /* We won't do this for btrfs RAID */
288         if (fsi.num_devices != 1)
289                 return 0;
290
291         for (id = 1; id <= fsi.max_id; id++) {
292                 struct btrfs_ioctl_dev_info_args di = {
293                         .devid = id,
294                 };
295                 struct stat st;
296
297                 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
298                         if (errno == ENODEV)
299                                 continue;
300
301                         return -errno;
302                 }
303
304                 if (stat((char*) di.path, &st) < 0)
305                         return -errno;
306
307                 if (!S_ISBLK(st.st_mode))
308                         return -ENODEV;
309
310                 if (major(st.st_rdev) == 0)
311                         return -ENODEV;
312
313                 *dev = st.st_rdev;
314                 return 1;
315         }
316
317         return -ENODEV;
318 }
319
320 int btrfs_get_block_device(const char *path, dev_t *dev) {
321         _cleanup_close_ int fd = -1;
322
323         assert(path);
324         assert(dev);
325
326         fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
327         if (fd < 0)
328                 return -errno;
329
330         return btrfs_get_block_device_fd(fd, dev);
331 }
332
333 int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
334         struct btrfs_ioctl_ino_lookup_args args = {
335                 .objectid = BTRFS_FIRST_FREE_OBJECTID
336         };
337         int r;
338
339         assert(fd >= 0);
340         assert(ret);
341
342         r = btrfs_is_filesystem(fd);
343         if (r < 0)
344                 return r;
345         if (!r)
346                 return -ENOTTY;
347
348         if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
349                 return -errno;
350
351         *ret = args.treeid;
352         return 0;
353 }
354
355 static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) {
356         assert(args);
357
358         /* the objectid, type, offset together make up the btrfs key,
359          * which is considered a single 136byte integer when
360          * comparing. This call increases the counter by one, dealing
361          * with the overflow between the overflows */
362
363         if (args->key.min_offset < (uint64_t) -1) {
364                 args->key.min_offset++;
365                 return true;
366         }
367
368         if (args->key.min_type < (uint8_t) -1) {
369                 args->key.min_type++;
370                 args->key.min_offset = 0;
371                 return true;
372         }
373
374         if (args->key.min_objectid < (uint64_t) -1) {
375                 args->key.min_objectid++;
376                 args->key.min_offset = 0;
377                 args->key.min_type = 0;
378                 return true;
379         }
380
381         return 0;
382 }
383
384 static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) {
385         assert(args);
386         assert(h);
387
388         args->key.min_objectid = h->objectid;
389         args->key.min_type = h->type;
390         args->key.min_offset = h->offset;
391 }
392
393 static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
394         assert(args);
395
396         /* Compare min and max */
397
398         if (args->key.min_objectid < args->key.max_objectid)
399                 return -1;
400         if (args->key.min_objectid > args->key.max_objectid)
401                 return 1;
402
403         if (args->key.min_type < args->key.max_type)
404                 return -1;
405         if (args->key.min_type > args->key.max_type)
406                 return 1;
407
408         if (args->key.min_offset < args->key.max_offset)
409                 return -1;
410         if (args->key.min_offset > args->key.max_offset)
411                 return 1;
412
413         return 0;
414 }
415
416 #define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args)                  \
417         for ((i) = 0,                                                   \
418              (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \
419              (i) < (args).key.nr_items;                                 \
420              (i)++,                                                     \
421              (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len))
422
423 #define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh)                              \
424         ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header)))
425
426 int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
427         struct btrfs_ioctl_search_args args = {
428                 /* Tree of tree roots */
429                 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
430
431                 /* Look precisely for the subvolume items */
432                 .key.min_type = BTRFS_ROOT_ITEM_KEY,
433                 .key.max_type = BTRFS_ROOT_ITEM_KEY,
434
435                 .key.min_offset = 0,
436                 .key.max_offset = (uint64_t) -1,
437
438                 /* No restrictions on the other components */
439                 .key.min_transid = 0,
440                 .key.max_transid = (uint64_t) -1,
441         };
442
443         uint64_t subvol_id;
444         bool found = false;
445         int r;
446
447         assert(fd >= 0);
448         assert(ret);
449
450         r = btrfs_subvol_get_id_fd(fd, &subvol_id);
451         if (r < 0)
452                 return r;
453
454         args.key.min_objectid = args.key.max_objectid = subvol_id;
455
456         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
457                 const struct btrfs_ioctl_search_header *sh;
458                 unsigned i;
459
460                 args.key.nr_items = 256;
461                 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
462                         return -errno;
463
464                 if (args.key.nr_items <= 0)
465                         break;
466
467                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
468
469                         const struct btrfs_root_item *ri;
470
471                         /* Make sure we start the next search at least from this entry */
472                         btrfs_ioctl_search_args_set(&args, sh);
473
474                         if (sh->objectid != subvol_id)
475                                 continue;
476                         if (sh->type != BTRFS_ROOT_ITEM_KEY)
477                                 continue;
478
479                         /* Older versions of the struct lacked the otime setting */
480                         if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
481                                 continue;
482
483                         ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
484
485                         ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
486                                 (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
487
488                         ret->subvol_id = subvol_id;
489                         ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
490
491                         assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
492                         memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
493                         memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
494
495                         found = true;
496                         goto finish;
497                 }
498
499                 /* Increase search key by one, to read the next item, if we can. */
500                 if (!btrfs_ioctl_search_args_inc(&args))
501                         break;
502         }
503
504 finish:
505         if (!found)
506                 return -ENODATA;
507
508         return 0;
509 }
510
511 int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
512
513         struct btrfs_ioctl_search_args args = {
514                 /* Tree of quota items */
515                 .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
516
517                 /* The object ID is always 0 */
518                 .key.min_objectid = 0,
519                 .key.max_objectid = 0,
520
521                 /* Look precisely for the quota items */
522                 .key.min_type = BTRFS_QGROUP_STATUS_KEY,
523                 .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
524
525                 /* No restrictions on the other components */
526                 .key.min_transid = 0,
527                 .key.max_transid = (uint64_t) -1,
528         };
529
530         uint64_t subvol_id;
531         bool found_info = false, found_limit = false;
532         int r;
533
534         assert(fd >= 0);
535         assert(ret);
536
537         r = btrfs_subvol_get_id_fd(fd, &subvol_id);
538         if (r < 0)
539                 return r;
540
541         args.key.min_offset = args.key.max_offset = subvol_id;
542
543         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
544                 const struct btrfs_ioctl_search_header *sh;
545                 unsigned i;
546
547                 args.key.nr_items = 256;
548                 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
549                         return -errno;
550
551                 if (args.key.nr_items <= 0)
552                         break;
553
554                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
555
556                         /* Make sure we start the next search at least from this entry */
557                         btrfs_ioctl_search_args_set(&args, sh);
558
559                         if (sh->objectid != 0)
560                                 continue;
561                         if (sh->offset != subvol_id)
562                                 continue;
563
564                         if (sh->type == BTRFS_QGROUP_INFO_KEY) {
565                                 const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
566
567                                 ret->referenced = le64toh(qii->rfer);
568                                 ret->exclusive = le64toh(qii->excl);
569
570                                 found_info = true;
571
572                         } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
573                                 const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
574
575                                 ret->referenced_max = le64toh(qli->max_rfer);
576                                 ret->exclusive_max = le64toh(qli->max_excl);
577
578                                 if (ret->referenced_max == 0)
579                                         ret->referenced_max = (uint64_t) -1;
580                                 if (ret->exclusive_max == 0)
581                                         ret->exclusive_max = (uint64_t) -1;
582
583                                 found_limit = true;
584                         }
585
586                         if (found_info && found_limit)
587                                 goto finish;
588                 }
589
590                 /* Increase search key by one, to read the next item, if we can. */
591                 if (!btrfs_ioctl_search_args_inc(&args))
592                         break;
593         }
594
595 finish:
596         if (!found_limit && !found_info)
597                 return -ENODATA;
598
599         if (!found_info) {
600                 ret->referenced = (uint64_t) -1;
601                 ret->exclusive = (uint64_t) -1;
602         }
603
604         if (!found_limit) {
605                 ret->referenced_max = (uint64_t) -1;
606                 ret->exclusive_max = (uint64_t) -1;
607         }
608
609         return 0;
610 }
611
612 int btrfs_defrag_fd(int fd) {
613         struct stat st;
614
615         assert(fd >= 0);
616
617         if (fstat(fd, &st) < 0)
618                 return -errno;
619
620         if (!S_ISREG(st.st_mode))
621                 return -EINVAL;
622
623         if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
624                 return -errno;
625
626         return 0;
627 }
628
629 int btrfs_defrag(const char *p) {
630         _cleanup_close_ int fd = -1;
631
632         fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
633         if (fd < 0)
634                 return -errno;
635
636         return btrfs_defrag_fd(fd);
637 }
638
639 int btrfs_quota_enable_fd(int fd, bool b) {
640         struct btrfs_ioctl_quota_ctl_args args = {
641                 .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE,
642         };
643         int r;
644
645         assert(fd >= 0);
646
647         r = btrfs_is_filesystem(fd);
648         if (r < 0)
649                 return r;
650         if (!r)
651                 return -ENOTTY;
652
653         if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0)
654                 return -errno;
655
656         return 0;
657 }
658
659 int btrfs_quota_enable(const char *path, bool b) {
660         _cleanup_close_ int fd = -1;
661
662         fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
663         if (fd < 0)
664                 return -errno;
665
666         return btrfs_quota_enable_fd(fd, b);
667 }
668
669 int btrfs_quota_limit_fd(int fd, uint64_t referenced_max) {
670         struct btrfs_ioctl_qgroup_limit_args args = {
671                 .lim.max_rfer =
672                         referenced_max == (uint64_t) -1 ? 0 :
673                         referenced_max == 0 ? 1 : referenced_max,
674                 .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER,
675         };
676         int r;
677
678         assert(fd >= 0);
679
680         r = btrfs_is_filesystem(fd);
681         if (r < 0)
682                 return r;
683         if (!r)
684                 return -ENOTTY;
685
686         if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0)
687                 return -errno;
688
689         return 0;
690 }
691
692 int btrfs_quota_limit(const char *path, uint64_t referenced_max) {
693         _cleanup_close_ int fd = -1;
694
695         fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
696         if (fd < 0)
697                 return -errno;
698
699         return btrfs_quota_limit_fd(fd, referenced_max);
700 }
701
702 int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
703         struct btrfs_ioctl_vol_args args = {};
704         _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL;
705         _cleanup_close_ int loop_fd = -1, backing_fd = -1;
706         struct stat st;
707         dev_t dev = 0;
708         int r;
709
710         /* btrfs cannot handle file systems < 16M, hence use this as minimum */
711         if (new_size < 16*1024*1024)
712                 new_size = 16*1024*1024;
713
714         r = btrfs_get_block_device_fd(fd, &dev);
715         if (r < 0)
716                 return r;
717         if (r == 0)
718                 return -ENODEV;
719
720         if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0)
721                 return -ENOMEM;
722         r = read_one_line_file(p, &backing);
723         if (r == -ENOENT)
724                 return -ENODEV;
725         if (r < 0)
726                 return r;
727         if (isempty(backing) || !path_is_absolute(backing))
728                 return -ENODEV;
729
730         backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY);
731         if (backing_fd < 0)
732                 return -errno;
733
734         if (fstat(backing_fd, &st) < 0)
735                 return -errno;
736         if (!S_ISREG(st.st_mode))
737                 return -ENODEV;
738
739         if (new_size == (uint64_t) st.st_size)
740                 return 0;
741
742         if (grow_only && new_size < (uint64_t) st.st_size)
743                 return -EINVAL;
744
745         if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0)
746                 return -ENOMEM;
747         loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY);
748         if (loop_fd < 0)
749                 return -errno;
750
751         if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name))
752                 return -EINVAL;
753
754         if (new_size < (uint64_t) st.st_size) {
755                 /* Decrease size: first decrease btrfs size, then shorten loopback */
756                 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
757                         return -errno;
758         }
759
760         if (ftruncate(backing_fd, new_size) < 0)
761                 return -errno;
762
763         if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0)
764                 return -errno;
765
766         if (new_size > (uint64_t) st.st_size) {
767                 /* Increase size: first enlarge loopback, then increase btrfs size */
768                 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
769                         return -errno;
770         }
771
772         /* Make sure the free disk space is correctly updated for both file systems */
773         (void) fsync(fd);
774         (void) fsync(backing_fd);
775
776         return 1;
777 }
778
779 int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) {
780         _cleanup_close_ int fd = -1;
781
782         fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
783         if (fd < 0)
784                 return -errno;
785
786         return btrfs_resize_loopback_fd(fd, new_size, grow_only);
787 }
788
789 static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol_id, bool recursive) {
790         struct btrfs_ioctl_search_args args = {
791                 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
792
793                 .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
794                 .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
795
796                 .key.min_type = BTRFS_ROOT_BACKREF_KEY,
797                 .key.max_type = BTRFS_ROOT_BACKREF_KEY,
798
799                 .key.min_transid = 0,
800                 .key.max_transid = (uint64_t) -1,
801         };
802
803         struct btrfs_ioctl_vol_args vol_args = {};
804         _cleanup_close_ int subvol_fd = -1;
805         struct stat st;
806         bool made_writable = false;
807         int r;
808
809         assert(fd >= 0);
810         assert(subvolume);
811
812         if (fstat(fd, &st) < 0)
813                 return -errno;
814
815         if (!S_ISDIR(st.st_mode))
816                 return -EINVAL;
817
818         /* First, try to remove the subvolume. If it happens to be
819          * already empty, this will just work. */
820         strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
821         if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) >= 0)
822                 return 0;
823         if (!recursive || errno != ENOTEMPTY)
824                 return -errno;
825
826         /* OK, the subvolume is not empty, let's look for child
827          * subvolumes, and remove them, first */
828         subvol_fd = openat(fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
829         if (subvol_fd < 0)
830                 return -errno;
831
832         if (subvol_id == 0) {
833                 r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id);
834                 if (r < 0)
835                         return r;
836         }
837
838         args.key.min_offset = args.key.max_offset = subvol_id;
839
840         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
841                 const struct btrfs_ioctl_search_header *sh;
842                 unsigned i;
843
844                 args.key.nr_items = 256;
845                 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
846                         return -errno;
847
848                 if (args.key.nr_items <= 0)
849                         break;
850
851                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
852                         _cleanup_free_ char *p = NULL;
853                         const struct btrfs_root_ref *ref;
854                         struct btrfs_ioctl_ino_lookup_args ino_args;
855
856                         btrfs_ioctl_search_args_set(&args, sh);
857
858                         if (sh->type != BTRFS_ROOT_BACKREF_KEY)
859                                 continue;
860                         if (sh->offset != subvol_id)
861                                 continue;
862
863                         ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
864
865                         p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
866                         if (!p)
867                                 return -ENOMEM;
868
869                         zero(ino_args);
870                         ino_args.treeid = subvol_id;
871                         ino_args.objectid = htole64(ref->dirid);
872
873                         if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
874                                 return -errno;
875
876                         if (!made_writable) {
877                                 r = btrfs_subvol_set_read_only_fd(subvol_fd, false);
878                                 if (r < 0)
879                                         return r;
880
881                                 made_writable = true;
882                         }
883
884                         if (isempty(ino_args.name))
885                                 /* Subvolume is in the top-level
886                                  * directory of the subvolume. */
887                                 r = subvol_remove_children(subvol_fd, p, sh->objectid, recursive);
888                         else {
889                                 _cleanup_close_ int child_fd = -1;
890
891                                 /* Subvolume is somewhere further down,
892                                  * hence we need to open the
893                                  * containing directory first */
894
895                                 child_fd = openat(subvol_fd, ino_args.name, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
896                                 if (child_fd < 0)
897                                         return -errno;
898
899                                 r = subvol_remove_children(child_fd, p, sh->objectid, recursive);
900                         }
901                         if (r < 0)
902                                 return r;
903                 }
904
905                 /* Increase search key by one, to read the next item, if we can. */
906                 if (!btrfs_ioctl_search_args_inc(&args))
907                         break;
908         }
909
910         /* OK, the child subvolumes should all be gone now, let's try
911          * again to remove the subvolume */
912         if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) < 0)
913                 return -errno;
914
915         return 0;
916 }
917
918 int btrfs_subvol_remove(const char *path, bool recursive) {
919         _cleanup_close_ int fd = -1;
920         const char *subvolume;
921         int r;
922
923         assert(path);
924
925         r = extract_subvolume_name(path, &subvolume);
926         if (r < 0)
927                 return r;
928
929         fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
930         if (fd < 0)
931                 return fd;
932
933         return subvol_remove_children(fd, subvolume, 0, recursive);
934 }
935
936 int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive) {
937         return subvol_remove_children(fd, subvolume, 0, recursive);
938 }
939
940 static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolume, uint64_t subvol_id, BtrfsSnapshotFlags flags) {
941
942         struct btrfs_ioctl_search_args args = {
943                 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
944
945                 .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
946                 .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
947
948                 .key.min_type = BTRFS_ROOT_BACKREF_KEY,
949                 .key.max_type = BTRFS_ROOT_BACKREF_KEY,
950
951                 .key.min_transid = 0,
952                 .key.max_transid = (uint64_t) -1,
953         };
954
955         struct btrfs_ioctl_vol_args_v2 vol_args = {
956                 .flags = flags & BTRFS_SNAPSHOT_READ_ONLY ? BTRFS_SUBVOL_RDONLY : 0,
957                 .fd = old_fd,
958         };
959         int r;
960         _cleanup_close_ int subvolume_fd = -1;
961
962         assert(old_fd >= 0);
963         assert(new_fd >= 0);
964         assert(subvolume);
965
966         strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
967         vol_args.fd = old_fd;
968
969         if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &vol_args) < 0)
970                 return -errno;
971
972         if (!(flags & BTRFS_SNAPSHOT_RECURSIVE))
973                 return 0;
974
975         if (subvol_id == 0) {
976                 r = btrfs_subvol_get_id_fd(old_fd, &subvol_id);
977                 if (r < 0)
978                         return r;
979         }
980
981         args.key.min_offset = args.key.max_offset = subvol_id;
982
983         while (btrfs_ioctl_search_args_compare(&args) <= 0) {
984                 const struct btrfs_ioctl_search_header *sh;
985                 unsigned i;
986
987                 args.key.nr_items = 256;
988                 if (ioctl(old_fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
989                         return -errno;
990
991                 if (args.key.nr_items <= 0)
992                         break;
993
994                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
995                         _cleanup_free_ char *p = NULL, *c = NULL, *np = NULL;
996                         struct btrfs_ioctl_ino_lookup_args ino_args;
997                         const struct btrfs_root_ref *ref;
998                         _cleanup_close_ int old_child_fd = -1, new_child_fd = -1;
999
1000                         btrfs_ioctl_search_args_set(&args, sh);
1001
1002                         if (sh->type != BTRFS_ROOT_BACKREF_KEY)
1003                                 continue;
1004                         if (sh->offset != subvol_id)
1005                                 continue;
1006
1007                         ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
1008
1009                         p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
1010                         if (!p)
1011                                 return -ENOMEM;
1012
1013                         zero(ino_args);
1014                         ino_args.treeid = subvol_id;
1015                         ino_args.objectid = htole64(ref->dirid);
1016
1017                         if (ioctl(old_fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
1018                                 return -errno;
1019
1020                         /* The kernel returns an empty name if the
1021                          * subvolume is in the top-level directory,
1022                          * and otherwise appends a slash, so that we
1023                          * can just concatenate easily here, without
1024                          * adding a slash. */
1025                         c = strappend(ino_args.name, p);
1026                         if (!c)
1027                                 return -ENOMEM;
1028
1029                         old_child_fd = openat(old_fd, c, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1030                         if (old_child_fd < 0)
1031                                 return -errno;
1032
1033                         np = strjoin(subvolume, "/", ino_args.name, NULL);
1034                         if (!np)
1035                                 return -ENOMEM;
1036
1037                         new_child_fd = openat(new_fd, np, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1038                         if (new_child_fd < 0)
1039                                 return -errno;
1040
1041                         if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1042                                 /* If the snapshot is read-only we
1043                                  * need to mark it writable
1044                                  * temporarily, to put the subsnapshot
1045                                  * into place. */
1046
1047                                 if (subvolume_fd < 0) {
1048                                         subvolume_fd = openat(new_fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1049                                         if (subvolume_fd < 0)
1050                                                 return -errno;
1051                                 }
1052
1053                                 r = btrfs_subvol_set_read_only_fd(subvolume_fd, false);
1054                                 if (r < 0)
1055                                         return r;
1056                         }
1057
1058                         /* When btrfs clones the subvolumes, child
1059                          * subvolumes appear as directories. Remove
1060                          * them, so that we can create a new snapshot
1061                          * in their place */
1062                         if (unlinkat(new_child_fd, p, AT_REMOVEDIR) < 0) {
1063                                 int k = -errno;
1064
1065                                 if (flags & BTRFS_SNAPSHOT_READ_ONLY)
1066                                         (void) btrfs_subvol_set_read_only_fd(subvolume_fd, true);
1067
1068                                 return k;
1069                         }
1070
1071                         r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid, flags & ~BTRFS_SNAPSHOT_FALLBACK_COPY);
1072
1073                         /* Restore the readonly flag */
1074                         if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1075                                 int k;
1076
1077                                 k = btrfs_subvol_set_read_only_fd(subvolume_fd, true);
1078                                 if (r >= 0 && k < 0)
1079                                         return k;
1080                         }
1081
1082                         if (r < 0)
1083                                 return r;
1084                 }
1085
1086                 /* Increase search key by one, to read the next item, if we can. */
1087                 if (!btrfs_ioctl_search_args_inc(&args))
1088                         break;
1089         }
1090
1091         return 0;
1092 }
1093
1094 int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
1095         _cleanup_close_ int new_fd = -1;
1096         const char *subvolume;
1097         int r;
1098
1099         assert(old_fd >= 0);
1100         assert(new_path);
1101
1102         r = btrfs_is_subvol(old_fd);
1103         if (r < 0)
1104                 return r;
1105         if (r == 0) {
1106                 if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY))
1107                         return -EISDIR;
1108
1109                 r = btrfs_subvol_make(new_path);
1110                 if (r < 0)
1111                         return r;
1112
1113                 r = copy_directory_fd(old_fd, new_path, true);
1114                 if (r < 0) {
1115                         btrfs_subvol_remove(new_path, false);
1116                         return r;
1117                 }
1118
1119                 if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1120                         r = btrfs_subvol_set_read_only(new_path, true);
1121                         if (r < 0) {
1122                                 btrfs_subvol_remove(new_path, false);
1123                                 return r;
1124                         }
1125                 }
1126
1127                 return 0;
1128         }
1129
1130         r = extract_subvolume_name(new_path, &subvolume);
1131         if (r < 0)
1132                 return r;
1133
1134         new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1135         if (new_fd < 0)
1136                 return new_fd;
1137
1138         return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags);
1139 }
1140
1141 int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) {
1142         _cleanup_close_ int old_fd = -1;
1143
1144         assert(old_path);
1145         assert(new_path);
1146
1147         old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1148         if (old_fd < 0)
1149                 return -errno;
1150
1151         return btrfs_subvol_snapshot_fd(old_fd, new_path, flags);
1152 }