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