]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/btrfs-util.c
selinux: always use *_raw API from libselinux
[thirdparty/systemd.git] / src / basic / btrfs-util.c
CommitLineData
d7c7c334
LP
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"
d7c7c334 34#include "copy.h"
d7b8eec7
LP
35#include "selinux-util.h"
36#include "smack-util.h"
efe02862 37#include "fileio.h"
10f9c755 38#include "btrfs-ctree.h"
d7c7c334
LP
39#include "btrfs-util.h"
40
62572894
LP
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
d7c7c334
LP
49static 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
60static 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
77static 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
21222ea5 94int btrfs_is_filesystem(int fd) {
d7c7c334
LP
95 struct statfs sfs;
96
21222ea5
LP
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
105int btrfs_is_subvol(int fd) {
106 struct stat st;
107
108 assert(fd >= 0);
109
cd61c3bf
LP
110 /* On btrfs subvolumes always have the inode 256 */
111
112 if (fstat(fd, &st) < 0)
d7c7c334
LP
113 return -errno;
114
cd61c3bf 115 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
d7c7c334
LP
116 return 0;
117
21222ea5 118 return btrfs_is_filesystem(fd);
d7c7c334
LP
119}
120
d7c7c334
LP
121int 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
d7b8eec7
LP
145int 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
0d6e763b 163int btrfs_subvol_set_read_only_fd(int fd, bool b) {
d7c7c334 164 uint64_t flags, nflags;
0d6e763b 165 struct stat st;
d7c7c334 166
0d6e763b
LP
167 assert(fd >= 0);
168
169 if (fstat(fd, &st) < 0)
d7c7c334
LP
170 return -errno;
171
0d6e763b
LP
172 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
173 return -EINVAL;
174
d7c7c334
LP
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
0d6e763b
LP
192int 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
10f9c755 202int btrfs_subvol_get_read_only_fd(int fd) {
cd61c3bf 203 uint64_t flags;
62572894
LP
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;
cd61c3bf
LP
213
214 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
215 return -errno;
216
217 return !!(flags & BTRFS_SUBVOL_RDONLY);
218}
219
d7c7c334 220int btrfs_reflink(int infd, int outfd) {
62572894 221 struct stat st;
d7c7c334
LP
222 int r;
223
224 assert(infd >= 0);
225 assert(outfd >= 0);
226
62572894
LP
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
d7c7c334
LP
236 r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
237 if (r < 0)
238 return -errno;
239
240 return 0;
241}
242
1c7dd825
LP
243int 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 };
62572894 250 struct stat st;
1c7dd825
LP
251 int r;
252
253 assert(infd >= 0);
254 assert(outfd >= 0);
255 assert(sz > 0);
256
62572894
LP
257 if (fstat(outfd, &st) < 0)
258 return -errno;
259
260 if (!S_ISREG(st.st_mode))
261 return -EINVAL;
262
1c7dd825
LP
263 r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args);
264 if (r < 0)
265 return -errno;
266
267 return 0;
268}
269
efe02862 270int btrfs_get_block_device_fd(int fd, dev_t *dev) {
d7c7c334 271 struct btrfs_ioctl_fs_info_args fsi = {};
d7c7c334 272 uint64_t id;
62572894 273 int r;
d7c7c334 274
efe02862 275 assert(fd >= 0);
d7c7c334
LP
276 assert(dev);
277
62572894
LP
278 r = btrfs_is_filesystem(fd);
279 if (r < 0)
280 return r;
281 if (!r)
282 return -ENOTTY;
283
d7c7c334
LP
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}
10f9c755 319
efe02862
LP
320int 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
10f9c755
LP
333int 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 };
62572894 337 int r;
10f9c755
LP
338
339 assert(fd >= 0);
340 assert(ret);
341
62572894
LP
342 r = btrfs_is_filesystem(fd);
343 if (r < 0)
344 return r;
345 if (!r)
346 return -ENOTTY;
347
10f9c755
LP
348 if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
349 return -errno;
350
351 *ret = args.treeid;
352 return 0;
353}
354
90578cbd
LP
355int btrfs_subvol_get_id(int fd, const char *subvol, uint64_t *ret) {
356 _cleanup_close_ int subvol_fd = -1;
357
358 assert(fd >= 0);
359 assert(ret);
360
361 subvol_fd = openat(fd, subvol, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
362 if (subvol_fd < 0)
363 return -errno;
364
365 return btrfs_subvol_get_id_fd(subvol_fd, ret);
366}
367
5743a585
LP
368static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) {
369 assert(args);
370
371 /* the objectid, type, offset together make up the btrfs key,
372 * which is considered a single 136byte integer when
373 * comparing. This call increases the counter by one, dealing
374 * with the overflow between the overflows */
375
376 if (args->key.min_offset < (uint64_t) -1) {
377 args->key.min_offset++;
378 return true;
379 }
380
381 if (args->key.min_type < (uint8_t) -1) {
382 args->key.min_type++;
383 args->key.min_offset = 0;
384 return true;
385 }
386
387 if (args->key.min_objectid < (uint64_t) -1) {
388 args->key.min_objectid++;
389 args->key.min_offset = 0;
390 args->key.min_type = 0;
391 return true;
392 }
393
394 return 0;
395}
396
397static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) {
398 assert(args);
399 assert(h);
400
401 args->key.min_objectid = h->objectid;
402 args->key.min_type = h->type;
403 args->key.min_offset = h->offset;
404}
405
406static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
407 assert(args);
408
409 /* Compare min and max */
410
411 if (args->key.min_objectid < args->key.max_objectid)
412 return -1;
413 if (args->key.min_objectid > args->key.max_objectid)
414 return 1;
415
416 if (args->key.min_type < args->key.max_type)
417 return -1;
418 if (args->key.min_type > args->key.max_type)
419 return 1;
420
421 if (args->key.min_offset < args->key.max_offset)
422 return -1;
423 if (args->key.min_offset > args->key.max_offset)
424 return 1;
425
426 return 0;
427}
428
429#define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) \
430 for ((i) = 0, \
431 (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \
432 (i) < (args).key.nr_items; \
433 (i)++, \
434 (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len))
435
436#define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh) \
437 ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header)))
438
10f9c755
LP
439int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
440 struct btrfs_ioctl_search_args args = {
441 /* Tree of tree roots */
b6b18498 442 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
10f9c755
LP
443
444 /* Look precisely for the subvolume items */
445 .key.min_type = BTRFS_ROOT_ITEM_KEY,
446 .key.max_type = BTRFS_ROOT_ITEM_KEY,
447
10f9c755
LP
448 .key.min_offset = 0,
449 .key.max_offset = (uint64_t) -1,
5743a585
LP
450
451 /* No restrictions on the other components */
10f9c755
LP
452 .key.min_transid = 0,
453 .key.max_transid = (uint64_t) -1,
10f9c755
LP
454 };
455
10f9c755 456 uint64_t subvol_id;
b6b18498 457 bool found = false;
10f9c755
LP
458 int r;
459
460 assert(fd >= 0);
461 assert(ret);
462
463 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
464 if (r < 0)
465 return r;
466
467 args.key.min_objectid = args.key.max_objectid = subvol_id;
10f9c755 468
5743a585 469 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
b6b18498
LP
470 const struct btrfs_ioctl_search_header *sh;
471 unsigned i;
472
473 args.key.nr_items = 256;
474 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
475 return -errno;
476
477 if (args.key.nr_items <= 0)
478 break;
10f9c755 479
5743a585 480 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
10f9c755 481
b6b18498
LP
482 const struct btrfs_root_item *ri;
483
5743a585
LP
484 /* Make sure we start the next search at least from this entry */
485 btrfs_ioctl_search_args_set(&args, sh);
486
b6b18498
LP
487 if (sh->objectid != subvol_id)
488 continue;
489 if (sh->type != BTRFS_ROOT_ITEM_KEY)
490 continue;
5743a585
LP
491
492 /* Older versions of the struct lacked the otime setting */
b6b18498
LP
493 if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
494 continue;
10f9c755 495
5743a585 496 ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
10f9c755 497
b6b18498
LP
498 ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
499 (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
10f9c755 500
b6b18498
LP
501 ret->subvol_id = subvol_id;
502 ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
10f9c755 503
b6b18498
LP
504 assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
505 memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
506 memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
507
508 found = true;
509 goto finish;
510 }
511
5743a585
LP
512 /* Increase search key by one, to read the next item, if we can. */
513 if (!btrfs_ioctl_search_args_inc(&args))
b6b18498
LP
514 break;
515 }
516
517finish:
518 if (!found)
519 return -ENODATA;
520
521 return 0;
522}
523
524int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
525
526 struct btrfs_ioctl_search_args args = {
527 /* Tree of quota items */
528 .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
529
5743a585
LP
530 /* The object ID is always 0 */
531 .key.min_objectid = 0,
532 .key.max_objectid = 0,
533
b6b18498
LP
534 /* Look precisely for the quota items */
535 .key.min_type = BTRFS_QGROUP_STATUS_KEY,
536 .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
537
b6b18498
LP
538 /* No restrictions on the other components */
539 .key.min_transid = 0,
540 .key.max_transid = (uint64_t) -1,
541 };
542
543 uint64_t subvol_id;
544 bool found_info = false, found_limit = false;
545 int r;
546
547 assert(fd >= 0);
548 assert(ret);
549
550 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
551 if (r < 0)
552 return r;
553
554 args.key.min_offset = args.key.max_offset = subvol_id;
555
5743a585 556 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
b6b18498
LP
557 const struct btrfs_ioctl_search_header *sh;
558 unsigned i;
559
560 args.key.nr_items = 256;
561 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
562 return -errno;
563
564 if (args.key.nr_items <= 0)
565 break;
566
5743a585 567 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
b6b18498 568
5743a585
LP
569 /* Make sure we start the next search at least from this entry */
570 btrfs_ioctl_search_args_set(&args, sh);
b6b18498
LP
571
572 if (sh->objectid != 0)
573 continue;
574 if (sh->offset != subvol_id)
575 continue;
576
b6b18498 577 if (sh->type == BTRFS_QGROUP_INFO_KEY) {
5743a585 578 const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
b6b18498 579
cb81cd80 580 ret->referenced = le64toh(qii->rfer);
b6b18498
LP
581 ret->exclusive = le64toh(qii->excl);
582
583 found_info = true;
584
585 } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
5743a585 586 const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
b6b18498 587
cb81cd80 588 ret->referenced_max = le64toh(qli->max_rfer);
b6b18498
LP
589 ret->exclusive_max = le64toh(qli->max_excl);
590
cb81cd80
LP
591 if (ret->referenced_max == 0)
592 ret->referenced_max = (uint64_t) -1;
b6b18498
LP
593 if (ret->exclusive_max == 0)
594 ret->exclusive_max = (uint64_t) -1;
595
596 found_limit = true;
597 }
598
599 if (found_info && found_limit)
600 goto finish;
601 }
602
5743a585
LP
603 /* Increase search key by one, to read the next item, if we can. */
604 if (!btrfs_ioctl_search_args_inc(&args))
b6b18498
LP
605 break;
606 }
607
608finish:
609 if (!found_limit && !found_info)
610 return -ENODATA;
611
612 if (!found_info) {
cb81cd80 613 ret->referenced = (uint64_t) -1;
b6b18498
LP
614 ret->exclusive = (uint64_t) -1;
615 }
616
617 if (!found_limit) {
cb81cd80 618 ret->referenced_max = (uint64_t) -1;
b6b18498
LP
619 ret->exclusive_max = (uint64_t) -1;
620 }
10f9c755
LP
621
622 return 0;
623}
f27a3864
LP
624
625int btrfs_defrag_fd(int fd) {
62572894
LP
626 struct stat st;
627
f27a3864
LP
628 assert(fd >= 0);
629
62572894
LP
630 if (fstat(fd, &st) < 0)
631 return -errno;
632
633 if (!S_ISREG(st.st_mode))
634 return -EINVAL;
635
f27a3864
LP
636 if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
637 return -errno;
638
639 return 0;
640}
641
642int btrfs_defrag(const char *p) {
643 _cleanup_close_ int fd = -1;
644
645 fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
646 if (fd < 0)
647 return -errno;
648
649 return btrfs_defrag_fd(fd);
650}
754061ce
LP
651
652int btrfs_quota_enable_fd(int fd, bool b) {
653 struct btrfs_ioctl_quota_ctl_args args = {
654 .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE,
655 };
62572894 656 int r;
754061ce
LP
657
658 assert(fd >= 0);
659
62572894
LP
660 r = btrfs_is_filesystem(fd);
661 if (r < 0)
662 return r;
663 if (!r)
664 return -ENOTTY;
665
754061ce
LP
666 if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0)
667 return -errno;
668
669 return 0;
670}
671
672int btrfs_quota_enable(const char *path, bool b) {
673 _cleanup_close_ int fd = -1;
674
675 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
676 if (fd < 0)
677 return -errno;
678
679 return btrfs_quota_enable_fd(fd, b);
680}
d6ce17c7 681
cb81cd80 682int btrfs_quota_limit_fd(int fd, uint64_t referenced_max) {
d6ce17c7
LP
683 struct btrfs_ioctl_qgroup_limit_args args = {
684 .lim.max_rfer =
cb81cd80
LP
685 referenced_max == (uint64_t) -1 ? 0 :
686 referenced_max == 0 ? 1 : referenced_max,
d6ce17c7
LP
687 .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER,
688 };
62572894 689 int r;
d6ce17c7
LP
690
691 assert(fd >= 0);
692
62572894
LP
693 r = btrfs_is_filesystem(fd);
694 if (r < 0)
695 return r;
696 if (!r)
697 return -ENOTTY;
698
d6ce17c7
LP
699 if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0)
700 return -errno;
701
702 return 0;
703}
704
cb81cd80 705int btrfs_quota_limit(const char *path, uint64_t referenced_max) {
d6ce17c7
LP
706 _cleanup_close_ int fd = -1;
707
708 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
709 if (fd < 0)
710 return -errno;
711
cb81cd80 712 return btrfs_quota_limit_fd(fd, referenced_max);
d6ce17c7 713}
efe02862 714
26166c88 715int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
efe02862
LP
716 struct btrfs_ioctl_vol_args args = {};
717 _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL;
718 _cleanup_close_ int loop_fd = -1, backing_fd = -1;
719 struct stat st;
a7f7d1bd 720 dev_t dev = 0;
efe02862
LP
721 int r;
722
723 /* btrfs cannot handle file systems < 16M, hence use this as minimum */
724 if (new_size < 16*1024*1024)
725 new_size = 16*1024*1024;
726
727 r = btrfs_get_block_device_fd(fd, &dev);
728 if (r < 0)
729 return r;
730 if (r == 0)
731 return -ENODEV;
732
733 if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0)
734 return -ENOMEM;
735 r = read_one_line_file(p, &backing);
26166c88
LP
736 if (r == -ENOENT)
737 return -ENODEV;
efe02862
LP
738 if (r < 0)
739 return r;
740 if (isempty(backing) || !path_is_absolute(backing))
741 return -ENODEV;
742
743 backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY);
744 if (backing_fd < 0)
745 return -errno;
746
747 if (fstat(backing_fd, &st) < 0)
748 return -errno;
749 if (!S_ISREG(st.st_mode))
750 return -ENODEV;
751
752 if (new_size == (uint64_t) st.st_size)
753 return 0;
754
26166c88
LP
755 if (grow_only && new_size < (uint64_t) st.st_size)
756 return -EINVAL;
757
efe02862
LP
758 if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0)
759 return -ENOMEM;
760 loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY);
761 if (loop_fd < 0)
762 return -errno;
763
764 if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name))
765 return -EINVAL;
766
767 if (new_size < (uint64_t) st.st_size) {
768 /* Decrease size: first decrease btrfs size, then shorten loopback */
769 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
770 return -errno;
771 }
772
773 if (ftruncate(backing_fd, new_size) < 0)
774 return -errno;
775
776 if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0)
777 return -errno;
778
779 if (new_size > (uint64_t) st.st_size) {
780 /* Increase size: first enlarge loopback, then increase btrfs size */
781 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
782 return -errno;
783 }
784
26166c88
LP
785 /* Make sure the free disk space is correctly updated for both file systems */
786 (void) fsync(fd);
787 (void) fsync(backing_fd);
788
789 return 1;
efe02862
LP
790}
791
26166c88 792int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) {
efe02862
LP
793 _cleanup_close_ int fd = -1;
794
795 fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
796 if (fd < 0)
797 return -errno;
798
26166c88 799 return btrfs_resize_loopback_fd(fd, new_size, grow_only);
efe02862 800}
d9e2daaf
LP
801
802static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol_id, bool recursive) {
803 struct btrfs_ioctl_search_args args = {
804 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
805
806 .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
807 .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
808
809 .key.min_type = BTRFS_ROOT_BACKREF_KEY,
810 .key.max_type = BTRFS_ROOT_BACKREF_KEY,
811
812 .key.min_transid = 0,
813 .key.max_transid = (uint64_t) -1,
814 };
815
816 struct btrfs_ioctl_vol_args vol_args = {};
817 _cleanup_close_ int subvol_fd = -1;
62572894 818 struct stat st;
3986b258 819 bool made_writable = false;
d9e2daaf
LP
820 int r;
821
822 assert(fd >= 0);
823 assert(subvolume);
824
62572894
LP
825 if (fstat(fd, &st) < 0)
826 return -errno;
827
828 if (!S_ISDIR(st.st_mode))
829 return -EINVAL;
830
d9e2daaf
LP
831 /* First, try to remove the subvolume. If it happens to be
832 * already empty, this will just work. */
833 strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
834 if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) >= 0)
835 return 0;
836 if (!recursive || errno != ENOTEMPTY)
837 return -errno;
838
839 /* OK, the subvolume is not empty, let's look for child
840 * subvolumes, and remove them, first */
841 subvol_fd = openat(fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
842 if (subvol_fd < 0)
843 return -errno;
844
845 if (subvol_id == 0) {
846 r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id);
847 if (r < 0)
848 return r;
849 }
850
851 args.key.min_offset = args.key.max_offset = subvol_id;
852
853 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
854 const struct btrfs_ioctl_search_header *sh;
855 unsigned i;
856
857 args.key.nr_items = 256;
858 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
859 return -errno;
860
861 if (args.key.nr_items <= 0)
862 break;
863
864 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
865 _cleanup_free_ char *p = NULL;
866 const struct btrfs_root_ref *ref;
867 struct btrfs_ioctl_ino_lookup_args ino_args;
868
869 btrfs_ioctl_search_args_set(&args, sh);
870
871 if (sh->type != BTRFS_ROOT_BACKREF_KEY)
872 continue;
873 if (sh->offset != subvol_id)
874 continue;
875
876 ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
877
878 p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
879 if (!p)
880 return -ENOMEM;
881
882 zero(ino_args);
883 ino_args.treeid = subvol_id;
cbf21ecc 884 ino_args.objectid = htole64(ref->dirid);
d9e2daaf
LP
885
886 if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
887 return -errno;
888
3986b258
LP
889 if (!made_writable) {
890 r = btrfs_subvol_set_read_only_fd(subvol_fd, false);
891 if (r < 0)
892 return r;
893
894 made_writable = true;
895 }
896
d9e2daaf
LP
897 if (isempty(ino_args.name))
898 /* Subvolume is in the top-level
899 * directory of the subvolume. */
900 r = subvol_remove_children(subvol_fd, p, sh->objectid, recursive);
901 else {
902 _cleanup_close_ int child_fd = -1;
903
904 /* Subvolume is somewhere further down,
905 * hence we need to open the
906 * containing directory first */
907
908 child_fd = openat(subvol_fd, ino_args.name, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
909 if (child_fd < 0)
910 return -errno;
911
912 r = subvol_remove_children(child_fd, p, sh->objectid, recursive);
913 }
914 if (r < 0)
915 return r;
916 }
917
918 /* Increase search key by one, to read the next item, if we can. */
919 if (!btrfs_ioctl_search_args_inc(&args))
920 break;
921 }
922
923 /* OK, the child subvolumes should all be gone now, let's try
924 * again to remove the subvolume */
925 if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) < 0)
926 return -errno;
927
928 return 0;
929}
930
931int btrfs_subvol_remove(const char *path, bool recursive) {
932 _cleanup_close_ int fd = -1;
933 const char *subvolume;
934 int r;
935
936 assert(path);
937
938 r = extract_subvolume_name(path, &subvolume);
939 if (r < 0)
940 return r;
941
942 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
943 if (fd < 0)
944 return fd;
945
946 return subvol_remove_children(fd, subvolume, 0, recursive);
947}
948
949int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive) {
950 return subvol_remove_children(fd, subvolume, 0, recursive);
951}
f70a17f8 952
90578cbd 953static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolume, uint64_t old_subvol_id, BtrfsSnapshotFlags flags) {
f70a17f8
LP
954
955 struct btrfs_ioctl_search_args args = {
956 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
957
958 .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID,
959 .key.max_objectid = BTRFS_LAST_FREE_OBJECTID,
960
961 .key.min_type = BTRFS_ROOT_BACKREF_KEY,
962 .key.max_type = BTRFS_ROOT_BACKREF_KEY,
963
964 .key.min_transid = 0,
965 .key.max_transid = (uint64_t) -1,
966 };
967
968 struct btrfs_ioctl_vol_args_v2 vol_args = {
969 .flags = flags & BTRFS_SNAPSHOT_READ_ONLY ? BTRFS_SUBVOL_RDONLY : 0,
970 .fd = old_fd,
971 };
ffb296b2 972 _cleanup_close_ int subvolume_fd = -1;
90578cbd
LP
973 uint64_t new_subvol_id;
974 int r;
f70a17f8
LP
975
976 assert(old_fd >= 0);
977 assert(new_fd >= 0);
978 assert(subvolume);
979
980 strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1);
981 vol_args.fd = old_fd;
982
983 if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &vol_args) < 0)
984 return -errno;
985
986 if (!(flags & BTRFS_SNAPSHOT_RECURSIVE))
987 return 0;
988
90578cbd
LP
989 if (old_subvol_id == 0) {
990 r = btrfs_subvol_get_id_fd(old_fd, &old_subvol_id);
f70a17f8
LP
991 if (r < 0)
992 return r;
993 }
994
90578cbd
LP
995 r = btrfs_subvol_get_id(new_fd, vol_args.name, &new_subvol_id);
996 if (r < 0)
997 return r;
998
999 args.key.min_offset = args.key.max_offset = old_subvol_id;
f70a17f8
LP
1000
1001 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
1002 const struct btrfs_ioctl_search_header *sh;
1003 unsigned i;
1004
1005 args.key.nr_items = 256;
1006 if (ioctl(old_fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
1007 return -errno;
1008
1009 if (args.key.nr_items <= 0)
1010 break;
1011
1012 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
1013 _cleanup_free_ char *p = NULL, *c = NULL, *np = NULL;
1014 struct btrfs_ioctl_ino_lookup_args ino_args;
1015 const struct btrfs_root_ref *ref;
1016 _cleanup_close_ int old_child_fd = -1, new_child_fd = -1;
1017
1018 btrfs_ioctl_search_args_set(&args, sh);
1019
1020 if (sh->type != BTRFS_ROOT_BACKREF_KEY)
1021 continue;
90578cbd
LP
1022
1023 /* Avoid finding the source subvolume a second
1024 * time */
1025 if (sh->offset != old_subvol_id)
f70a17f8
LP
1026 continue;
1027
90578cbd
LP
1028 /* Avoid running into loops if the new
1029 * subvolume is below the old one. */
1030 if (sh->objectid == new_subvol_id)
1031 continue;
f70a17f8 1032
90578cbd 1033 ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
f70a17f8
LP
1034 p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len));
1035 if (!p)
1036 return -ENOMEM;
1037
1038 zero(ino_args);
90578cbd 1039 ino_args.treeid = old_subvol_id;
f70a17f8
LP
1040 ino_args.objectid = htole64(ref->dirid);
1041
1042 if (ioctl(old_fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
1043 return -errno;
1044
1045 /* The kernel returns an empty name if the
1046 * subvolume is in the top-level directory,
1047 * and otherwise appends a slash, so that we
1048 * can just concatenate easily here, without
1049 * adding a slash. */
1050 c = strappend(ino_args.name, p);
1051 if (!c)
1052 return -ENOMEM;
1053
1054 old_child_fd = openat(old_fd, c, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1055 if (old_child_fd < 0)
1056 return -errno;
1057
1058 np = strjoin(subvolume, "/", ino_args.name, NULL);
1059 if (!np)
1060 return -ENOMEM;
1061
1062 new_child_fd = openat(new_fd, np, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1063 if (new_child_fd < 0)
1064 return -errno;
1065
ffb296b2
LP
1066 if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1067 /* If the snapshot is read-only we
1068 * need to mark it writable
1069 * temporarily, to put the subsnapshot
1070 * into place. */
1071
1072 if (subvolume_fd < 0) {
1073 subvolume_fd = openat(new_fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1074 if (subvolume_fd < 0)
1075 return -errno;
1076 }
1077
1078 r = btrfs_subvol_set_read_only_fd(subvolume_fd, false);
1079 if (r < 0)
1080 return r;
1081 }
1082
f70a17f8 1083 /* When btrfs clones the subvolumes, child
90578cbd 1084 * subvolumes appear as empty directories. Remove
f70a17f8
LP
1085 * them, so that we can create a new snapshot
1086 * in their place */
ffb296b2
LP
1087 if (unlinkat(new_child_fd, p, AT_REMOVEDIR) < 0) {
1088 int k = -errno;
1089
1090 if (flags & BTRFS_SNAPSHOT_READ_ONLY)
1091 (void) btrfs_subvol_set_read_only_fd(subvolume_fd, true);
1092
1093 return k;
1094 }
f70a17f8
LP
1095
1096 r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid, flags & ~BTRFS_SNAPSHOT_FALLBACK_COPY);
ffb296b2
LP
1097
1098 /* Restore the readonly flag */
1099 if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1100 int k;
1101
1102 k = btrfs_subvol_set_read_only_fd(subvolume_fd, true);
1103 if (r >= 0 && k < 0)
1104 return k;
1105 }
1106
f70a17f8
LP
1107 if (r < 0)
1108 return r;
1109 }
1110
1111 /* Increase search key by one, to read the next item, if we can. */
1112 if (!btrfs_ioctl_search_args_inc(&args))
1113 break;
1114 }
1115
1116 return 0;
1117}
1118
1119int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
1120 _cleanup_close_ int new_fd = -1;
1121 const char *subvolume;
1122 int r;
1123
1124 assert(old_fd >= 0);
1125 assert(new_path);
1126
21222ea5 1127 r = btrfs_is_subvol(old_fd);
f70a17f8
LP
1128 if (r < 0)
1129 return r;
1130 if (r == 0) {
1131 if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY))
1132 return -EISDIR;
1133
1134 r = btrfs_subvol_make(new_path);
1135 if (r < 0)
1136 return r;
1137
1138 r = copy_directory_fd(old_fd, new_path, true);
1139 if (r < 0) {
1140 btrfs_subvol_remove(new_path, false);
1141 return r;
1142 }
1143
1144 if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
1145 r = btrfs_subvol_set_read_only(new_path, true);
1146 if (r < 0) {
1147 btrfs_subvol_remove(new_path, false);
1148 return r;
1149 }
1150 }
1151
1152 return 0;
1153 }
1154
1155 r = extract_subvolume_name(new_path, &subvolume);
1156 if (r < 0)
1157 return r;
1158
1159 new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1160 if (new_fd < 0)
1161 return new_fd;
1162
1163 return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags);
1164}
1165
1166int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) {
1167 _cleanup_close_ int old_fd = -1;
1168
1169 assert(old_path);
1170 assert(new_path);
1171
1172 old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1173 if (old_fd < 0)
1174 return -errno;
1175
1176 return btrfs_subvol_snapshot_fd(old_fd, new_path, flags);
1177}