1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
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.
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.
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/>.
26 #ifdef HAVE_LINUX_BTRFS_H
27 #include <linux/btrfs.h>
32 #include "path-util.h"
35 #include "selinux-util.h"
36 #include "smack-util.h"
37 #include "btrfs-ctree.h"
38 #include "btrfs-util.h"
40 static int validate_subvolume_name(const char *name) {
42 if (!filename_is_valid(name))
45 if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
51 static int open_parent(const char *path, int flags) {
52 _cleanup_free_ char *parent = NULL;
57 r = path_get_parent(path, &parent);
61 fd = open(parent, flags);
68 static int extract_subvolume_name(const char *path, const char **subvolume) {
77 r = validate_subvolume_name(fn);
85 int btrfs_is_snapshot(int fd) {
89 /* On btrfs subvolumes always have the inode 256 */
91 if (fstat(fd, &st) < 0)
94 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
97 if (fstatfs(fd, &sfs) < 0)
100 return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
103 int btrfs_subvol_snapshot(const char *old_path, const char *new_path, bool read_only, bool fallback_copy) {
104 struct btrfs_ioctl_vol_args_v2 args = {
105 .flags = read_only ? BTRFS_SUBVOL_RDONLY : 0,
107 _cleanup_close_ int old_fd = -1, new_fd = -1;
108 const char *subvolume;
113 old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
117 r = btrfs_is_snapshot(old_fd);
123 r = btrfs_subvol_make(new_path);
127 r = copy_directory_fd(old_fd, new_path, true);
129 btrfs_subvol_remove(new_path);
134 r = btrfs_subvol_set_read_only(new_path, true);
136 btrfs_subvol_remove(new_path);
147 r = extract_subvolume_name(new_path, &subvolume);
151 new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
155 strncpy(args.name, subvolume, sizeof(args.name)-1);
158 if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &args) < 0)
164 int btrfs_subvol_make(const char *path) {
165 struct btrfs_ioctl_vol_args args = {};
166 _cleanup_close_ int fd = -1;
167 const char *subvolume;
172 r = extract_subvolume_name(path, &subvolume);
176 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
180 strncpy(args.name, subvolume, sizeof(args.name)-1);
182 if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
188 int btrfs_subvol_make_label(const char *path) {
193 r = mac_selinux_create_file_prepare(path, S_IFDIR);
197 r = btrfs_subvol_make(path);
198 mac_selinux_create_file_clear();
203 return mac_smack_fix(path, false, false);
206 int btrfs_subvol_remove(const char *path) {
207 struct btrfs_ioctl_vol_args args = {};
208 _cleanup_close_ int fd = -1;
209 const char *subvolume;
214 r = extract_subvolume_name(path, &subvolume);
218 fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
222 strncpy(args.name, subvolume, sizeof(args.name)-1);
224 if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args) < 0)
230 int btrfs_subvol_set_read_only_fd(int fd, bool b) {
231 uint64_t flags, nflags;
236 if (fstat(fd, &st) < 0)
239 if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
242 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
246 nflags = flags | BTRFS_SUBVOL_RDONLY;
248 nflags = flags & ~BTRFS_SUBVOL_RDONLY;
253 if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
259 int btrfs_subvol_set_read_only(const char *path, bool b) {
260 _cleanup_close_ int fd = -1;
262 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
266 return btrfs_subvol_set_read_only_fd(fd, b);
269 int btrfs_subvol_get_read_only_fd(int fd) {
272 if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
275 return !!(flags & BTRFS_SUBVOL_RDONLY);
278 int btrfs_reflink(int infd, int outfd) {
284 r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
291 int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) {
292 struct btrfs_ioctl_clone_range_args args = {
294 .src_offset = in_offset,
296 .dest_offset = out_offset,
304 r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args);
311 int btrfs_get_block_device(const char *path, dev_t *dev) {
312 struct btrfs_ioctl_fs_info_args fsi = {};
313 _cleanup_close_ int fd = -1;
319 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
323 if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
326 /* We won't do this for btrfs RAID */
327 if (fsi.num_devices != 1)
330 for (id = 1; id <= fsi.max_id; id++) {
331 struct btrfs_ioctl_dev_info_args di = {
336 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
343 if (stat((char*) di.path, &st) < 0)
346 if (!S_ISBLK(st.st_mode))
349 if (major(st.st_rdev) == 0)
359 int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
360 struct btrfs_ioctl_ino_lookup_args args = {
361 .objectid = BTRFS_FIRST_FREE_OBJECTID
367 if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
374 static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) {
377 /* the objectid, type, offset together make up the btrfs key,
378 * which is considered a single 136byte integer when
379 * comparing. This call increases the counter by one, dealing
380 * with the overflow between the overflows */
382 if (args->key.min_offset < (uint64_t) -1) {
383 args->key.min_offset++;
387 if (args->key.min_type < (uint8_t) -1) {
388 args->key.min_type++;
389 args->key.min_offset = 0;
393 if (args->key.min_objectid < (uint64_t) -1) {
394 args->key.min_objectid++;
395 args->key.min_offset = 0;
396 args->key.min_type = 0;
403 static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) {
407 args->key.min_objectid = h->objectid;
408 args->key.min_type = h->type;
409 args->key.min_offset = h->offset;
412 static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
415 /* Compare min and max */
417 if (args->key.min_objectid < args->key.max_objectid)
419 if (args->key.min_objectid > args->key.max_objectid)
422 if (args->key.min_type < args->key.max_type)
424 if (args->key.min_type > args->key.max_type)
427 if (args->key.min_offset < args->key.max_offset)
429 if (args->key.min_offset > args->key.max_offset)
435 #define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) \
437 (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \
438 (i) < (args).key.nr_items; \
440 (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len))
442 #define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh) \
443 ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header)))
445 int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
446 struct btrfs_ioctl_search_args args = {
447 /* Tree of tree roots */
448 .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
450 /* Look precisely for the subvolume items */
451 .key.min_type = BTRFS_ROOT_ITEM_KEY,
452 .key.max_type = BTRFS_ROOT_ITEM_KEY,
455 .key.max_offset = (uint64_t) -1,
457 /* No restrictions on the other components */
458 .key.min_transid = 0,
459 .key.max_transid = (uint64_t) -1,
469 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
473 args.key.min_objectid = args.key.max_objectid = subvol_id;
475 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
476 const struct btrfs_ioctl_search_header *sh;
479 args.key.nr_items = 256;
480 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
483 if (args.key.nr_items <= 0)
486 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
488 const struct btrfs_root_item *ri;
490 /* Make sure we start the next search at least from this entry */
491 btrfs_ioctl_search_args_set(&args, sh);
493 if (sh->objectid != subvol_id)
495 if (sh->type != BTRFS_ROOT_ITEM_KEY)
498 /* Older versions of the struct lacked the otime setting */
499 if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
502 ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
504 ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
505 (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
507 ret->subvol_id = subvol_id;
508 ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
510 assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
511 memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
512 memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
518 /* Increase search key by one, to read the next item, if we can. */
519 if (!btrfs_ioctl_search_args_inc(&args))
530 int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
532 struct btrfs_ioctl_search_args args = {
533 /* Tree of quota items */
534 .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
536 /* The object ID is always 0 */
537 .key.min_objectid = 0,
538 .key.max_objectid = 0,
540 /* Look precisely for the quota items */
541 .key.min_type = BTRFS_QGROUP_STATUS_KEY,
542 .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
544 /* No restrictions on the other components */
545 .key.min_transid = 0,
546 .key.max_transid = (uint64_t) -1,
550 bool found_info = false, found_limit = false;
556 r = btrfs_subvol_get_id_fd(fd, &subvol_id);
560 args.key.min_offset = args.key.max_offset = subvol_id;
562 while (btrfs_ioctl_search_args_compare(&args) <= 0) {
563 const struct btrfs_ioctl_search_header *sh;
566 args.key.nr_items = 256;
567 if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
570 if (args.key.nr_items <= 0)
573 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
575 /* Make sure we start the next search at least from this entry */
576 btrfs_ioctl_search_args_set(&args, sh);
578 if (sh->objectid != 0)
580 if (sh->offset != subvol_id)
583 if (sh->type == BTRFS_QGROUP_INFO_KEY) {
584 const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
586 ret->referred = le64toh(qii->rfer);
587 ret->exclusive = le64toh(qii->excl);
591 } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
592 const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
594 ret->referred_max = le64toh(qli->max_rfer);
595 ret->exclusive_max = le64toh(qli->max_excl);
597 if (ret->referred_max == 0)
598 ret->referred_max = (uint64_t) -1;
599 if (ret->exclusive_max == 0)
600 ret->exclusive_max = (uint64_t) -1;
605 if (found_info && found_limit)
609 /* Increase search key by one, to read the next item, if we can. */
610 if (!btrfs_ioctl_search_args_inc(&args))
615 if (!found_limit && !found_info)
619 ret->referred = (uint64_t) -1;
620 ret->exclusive = (uint64_t) -1;
624 ret->referred_max = (uint64_t) -1;
625 ret->exclusive_max = (uint64_t) -1;
631 int btrfs_defrag_fd(int fd) {
634 if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
640 int btrfs_defrag(const char *p) {
641 _cleanup_close_ int fd = -1;
643 fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
647 return btrfs_defrag_fd(fd);
650 int btrfs_quota_enable_fd(int fd, bool b) {
651 struct btrfs_ioctl_quota_ctl_args args = {
652 .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE,
657 if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0)
663 int btrfs_quota_enable(const char *path, bool b) {
664 _cleanup_close_ int fd = -1;
666 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
670 return btrfs_quota_enable_fd(fd, b);