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