X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fshared%2Fbtrfs-util.c;h=254483c31a3ce5649fc0d722456c448a5461d450;hb=1c7dd82563ff2e71a067aea20d2acb2d0553644b;hp=84c81106fa2a1fdd0cc9438408ac1a637e1ddb2e;hpb=10f9c75519671e7c7ab8993b54fe22da7c2d0c38;p=elogind.git diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c index 84c81106f..254483c31 100644 --- a/src/shared/btrfs-util.c +++ b/src/shared/btrfs-util.c @@ -33,6 +33,8 @@ #include "macro.h" #include "strv.h" #include "copy.h" +#include "selinux-util.h" +#include "smack-util.h" #include "btrfs-ctree.h" #include "btrfs-util.h" @@ -184,6 +186,24 @@ int btrfs_subvol_make(const char *path) { return 0; } +int btrfs_subvol_make_label(const char *path) { + int r; + + assert(path); + + r = mac_selinux_create_file_prepare(path, S_IFDIR); + if (r < 0) + return r; + + r = btrfs_subvol_make(path); + mac_selinux_create_file_clear(); + + if (r < 0) + return r; + + return mac_smack_fix(path, false, false); +} + int btrfs_subvol_remove(const char *path) { struct btrfs_ioctl_vol_args args = {}; _cleanup_close_ int fd = -1; @@ -255,6 +275,26 @@ int btrfs_reflink(int infd, int outfd) { return 0; } +int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) { + struct btrfs_ioctl_clone_range_args args = { + .src_fd = infd, + .src_offset = in_offset, + .src_length = sz, + .dest_offset = out_offset, + }; + int r; + + assert(infd >= 0); + assert(outfd >= 0); + assert(sz > 0); + + r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args); + if (r < 0) + return -errno; + + return 0; +} + int btrfs_get_block_device(const char *path, dev_t *dev) { struct btrfs_ioctl_fs_info_args fsi = {}; _cleanup_close_ int fd = -1; @@ -318,28 +358,96 @@ int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) { return 0; } +static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) { + assert(args); + + /* the objectid, type, offset together make up the btrfs key, + * which is considered a single 136byte integer when + * comparing. This call increases the counter by one, dealing + * with the overflow between the overflows */ + + if (args->key.min_offset < (uint64_t) -1) { + args->key.min_offset++; + return true; + } + + if (args->key.min_type < (uint8_t) -1) { + args->key.min_type++; + args->key.min_offset = 0; + return true; + } + + if (args->key.min_objectid < (uint64_t) -1) { + args->key.min_objectid++; + args->key.min_offset = 0; + args->key.min_type = 0; + return true; + } + + return 0; +} + +static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) { + assert(args); + assert(h); + + args->key.min_objectid = h->objectid; + args->key.min_type = h->type; + args->key.min_offset = h->offset; +} + +static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) { + assert(args); + + /* Compare min and max */ + + if (args->key.min_objectid < args->key.max_objectid) + return -1; + if (args->key.min_objectid > args->key.max_objectid) + return 1; + + if (args->key.min_type < args->key.max_type) + return -1; + if (args->key.min_type > args->key.max_type) + return 1; + + if (args->key.min_offset < args->key.max_offset) + return -1; + if (args->key.min_offset > args->key.max_offset) + return 1; + + return 0; +} + +#define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) \ + for ((i) = 0, \ + (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \ + (i) < (args).key.nr_items; \ + (i)++, \ + (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len)) + +#define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh) \ + ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header))) + int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) { struct btrfs_ioctl_search_args args = { /* Tree of tree roots */ - .key.tree_id = 1, + .key.tree_id = BTRFS_ROOT_TREE_OBJECTID, /* Look precisely for the subvolume items */ .key.min_type = BTRFS_ROOT_ITEM_KEY, .key.max_type = BTRFS_ROOT_ITEM_KEY, - /* No restrictions on the other components */ .key.min_offset = 0, .key.max_offset = (uint64_t) -1, + + /* No restrictions on the other components */ .key.min_transid = 0, .key.max_transid = (uint64_t) -1, - - /* Some large value */ - .key.nr_items = 2, }; - struct btrfs_ioctl_search_header *sh; - struct btrfs_root_item *ri; uint64_t subvol_id; + bool found = false; int r; assert(fd >= 0); @@ -350,30 +458,178 @@ int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) { return r; args.key.min_objectid = args.key.max_objectid = subvol_id; - if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) - return -errno; - if (args.key.nr_items != 1) - return -EIO; + while (btrfs_ioctl_search_args_compare(&args) <= 0) { + const struct btrfs_ioctl_search_header *sh; + unsigned i; - sh = (struct btrfs_ioctl_search_header*) args.buf; - assert(sh->type == BTRFS_ROOT_ITEM_KEY); - assert(sh->objectid == subvol_id); + args.key.nr_items = 256; + if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) + return -errno; - if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec)) - return -ENOTSUP; + if (args.key.nr_items <= 0) + break; - ri = (struct btrfs_root_item *)(args.buf + sizeof(struct btrfs_ioctl_search_header)); + FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) { - ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC + - (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC; + const struct btrfs_root_item *ri; - ret->subvol_id = subvol_id; - ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY); + /* Make sure we start the next search at least from this entry */ + btrfs_ioctl_search_args_set(&args, sh); - assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid)); - memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid)); - memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid)); + if (sh->objectid != subvol_id) + continue; + if (sh->type != BTRFS_ROOT_ITEM_KEY) + continue; + + /* Older versions of the struct lacked the otime setting */ + if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec)) + continue; + + ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh); + + ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC + + (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC; + + ret->subvol_id = subvol_id; + ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY); + + assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid)); + memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid)); + memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid)); + + found = true; + goto finish; + } + + /* Increase search key by one, to read the next item, if we can. */ + if (!btrfs_ioctl_search_args_inc(&args)) + break; + } + +finish: + if (!found) + return -ENODATA; return 0; } + +int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) { + + struct btrfs_ioctl_search_args args = { + /* Tree of quota items */ + .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID, + + /* The object ID is always 0 */ + .key.min_objectid = 0, + .key.max_objectid = 0, + + /* Look precisely for the quota items */ + .key.min_type = BTRFS_QGROUP_STATUS_KEY, + .key.max_type = BTRFS_QGROUP_LIMIT_KEY, + + /* No restrictions on the other components */ + .key.min_transid = 0, + .key.max_transid = (uint64_t) -1, + }; + + uint64_t subvol_id; + bool found_info = false, found_limit = false; + int r; + + assert(fd >= 0); + assert(ret); + + r = btrfs_subvol_get_id_fd(fd, &subvol_id); + if (r < 0) + return r; + + args.key.min_offset = args.key.max_offset = subvol_id; + + while (btrfs_ioctl_search_args_compare(&args) <= 0) { + const struct btrfs_ioctl_search_header *sh; + unsigned i; + + args.key.nr_items = 256; + if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) + return -errno; + + if (args.key.nr_items <= 0) + break; + + FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) { + + /* Make sure we start the next search at least from this entry */ + btrfs_ioctl_search_args_set(&args, sh); + + if (sh->objectid != 0) + continue; + if (sh->offset != subvol_id) + continue; + + if (sh->type == BTRFS_QGROUP_INFO_KEY) { + const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh); + + ret->referred = le64toh(qii->rfer); + ret->exclusive = le64toh(qii->excl); + + found_info = true; + + } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) { + const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh); + + ret->referred_max = le64toh(qli->max_rfer); + ret->exclusive_max = le64toh(qli->max_excl); + + if (ret->referred_max == 0) + ret->referred_max = (uint64_t) -1; + if (ret->exclusive_max == 0) + ret->exclusive_max = (uint64_t) -1; + + found_limit = true; + } + + if (found_info && found_limit) + goto finish; + } + + /* Increase search key by one, to read the next item, if we can. */ + if (!btrfs_ioctl_search_args_inc(&args)) + break; + } + +finish: + if (!found_limit && !found_info) + return -ENODATA; + + if (!found_info) { + ret->referred = (uint64_t) -1; + ret->exclusive = (uint64_t) -1; + } + + if (!found_limit) { + ret->referred_max = (uint64_t) -1; + ret->exclusive_max = (uint64_t) -1; + } + + return 0; +} + +int btrfs_defrag_fd(int fd) { + assert(fd >= 0); + + if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0) + return -errno; + + return 0; +} + +int btrfs_defrag(const char *p) { + _cleanup_close_ int fd = -1; + + fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); + if (fd < 0) + return -errno; + + return btrfs_defrag_fd(fd); +}