btrfs-util.c revision 1c7dd82563ff2e71a067aea20d2acb2d0553644b
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering This file is part of systemd.
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering Copyright 2014 Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering systemd is free software; you can redistribute it and/or modify it
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering under the terms of the GNU Lesser General Public License as published by
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering (at your option) any later version.
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering systemd is distributed in the hope that it will be useful, but
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering Lesser General Public License for more details.
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering You should have received a copy of the GNU Lesser General Public License
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poetteringstatic int validate_subvolume_name(const char *name) {
04d39279245834494baccfdb9349db8bf80abd13Lennart Poettering if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poetteringstatic int open_parent(const char *path, int flags) {
e56056e93d33619a3acf13e483900b4f8938228fThomas Hindoe Paaboel Andersen _cleanup_free_ char *parent = NULL;
a1da85830bfaa77b9eb9c54693e5573559c97e50Tom Gundersenstatic int extract_subvolume_name(const char *path, const char **subvolume) {
a1da85830bfaa77b9eb9c54693e5573559c97e50Tom Gundersen const char *fn;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering /* On btrfs subvolumes always have the inode 256 */
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
5b30bef856e89a571df57b7b953e9a1409d9acedLennart Poetteringint btrfs_subvol_snapshot(const char *old_path, const char *new_path, bool read_only, bool fallback_copy) {
5b30bef856e89a571df57b7b953e9a1409d9acedLennart Poettering _cleanup_close_ int old_fd = -1, new_fd = -1;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
aa1936ea1a89c2bb968ba33e3274898a4eeae771Lennart Poettering r = copy_directory_fd(old_fd, new_path, true);
aa1936ea1a89c2bb968ba33e3274898a4eeae771Lennart Poettering r = btrfs_subvol_set_read_only(new_path, true);
a7893c6b28772edbc7e1fea3c209caa54d465648Lennart Poettering r = extract_subvolume_name(new_path, &subvolume);
9d12709626bccc0cae677a7035f62efe6aabb4abLennart Poettering new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
9d12709626bccc0cae677a7035f62efe6aabb4abLennart Poettering strncpy(args.name, subvolume, sizeof(args.name)-1);
aa1936ea1a89c2bb968ba33e3274898a4eeae771Lennart Poettering if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &args) < 0)
878cd7e95ca303f9851d227a22d2022bd49944b0Lennart Poettering r = extract_subvolume_name(path, &subvolume);
878cd7e95ca303f9851d227a22d2022bd49944b0Lennart Poettering fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
878cd7e95ca303f9851d227a22d2022bd49944b0Lennart Poettering strncpy(args.name, subvolume, sizeof(args.name)-1);
878cd7e95ca303f9851d227a22d2022bd49944b0Lennart Poettering if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0)
878cd7e95ca303f9851d227a22d2022bd49944b0Lennart Poetteringint btrfs_subvol_make_label(const char *path) {
878cd7e95ca303f9851d227a22d2022bd49944b0Lennart Poettering r = mac_selinux_create_file_prepare(path, S_IFDIR);
878cd7e95ca303f9851d227a22d2022bd49944b0Lennart Poettering return mac_smack_fix(path, false, false);
878cd7e95ca303f9851d227a22d2022bd49944b0Lennart Poetteringint btrfs_subvol_remove(const char *path) {
878cd7e95ca303f9851d227a22d2022bd49944b0Lennart Poettering r = extract_subvolume_name(path, &subvolume);
878cd7e95ca303f9851d227a22d2022bd49944b0Lennart Poettering fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
878cd7e95ca303f9851d227a22d2022bd49944b0Lennart Poettering strncpy(args.name, subvolume, sizeof(args.name)-1);
878cd7e95ca303f9851d227a22d2022bd49944b0Lennart Poettering if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args) < 0)
878cd7e95ca303f9851d227a22d2022bd49944b0Lennart Poetteringint btrfs_subvol_set_read_only(const char *path, bool b) {
717603e391b52983ca1fd218e7333a1b9dfc5c05Lennart Poettering fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
717603e391b52983ca1fd218e7333a1b9dfc5c05Lennart Poettering if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
717603e391b52983ca1fd218e7333a1b9dfc5c05Lennart Poettering if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0)
717603e391b52983ca1fd218e7333a1b9dfc5c05Lennart Poetteringint btrfs_subvol_get_read_only_fd(int fd) {
717603e391b52983ca1fd218e7333a1b9dfc5c05Lennart Poettering if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sieversint btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) {
f48e75cb9a8112d35855c44a156934f2ee0edb2eLennart Poettering r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args);
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poetteringint btrfs_get_block_device(const char *path, dev_t *dev) {
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering struct btrfs_ioctl_fs_info_args fsi = {};
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering /* We won't do this for btrfs RAID */
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
f48e75cb9a8112d35855c44a156934f2ee0edb2eLennart Poetteringint btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
f48e75cb9a8112d35855c44a156934f2ee0edb2eLennart Poettering struct btrfs_ioctl_ino_lookup_args args = {
f48e75cb9a8112d35855c44a156934f2ee0edb2eLennart Poettering if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
878cd7e95ca303f9851d227a22d2022bd49944b0Lennart Poetteringstatic bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) {
89f7c8465cd1ab37347dd0c15920bce31e8225dfLennart Poettering /* the objectid, type, offset together make up the btrfs key,
89f7c8465cd1ab37347dd0c15920bce31e8225dfLennart Poettering * which is considered a single 136byte integer when
89f7c8465cd1ab37347dd0c15920bce31e8225dfLennart Poettering * comparing. This call increases the counter by one, dealing
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering * with the overflow between the overflows */
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering if (args->key.min_offset < (uint64_t) -1) {
e7e9b6bb0b0bc5b1eb256a44f8afec6b634f26efZbigniew Jędrzejewski-Szmek if (args->key.min_objectid < (uint64_t) -1) {
f48e75cb9a8112d35855c44a156934f2ee0edb2eLennart Poetteringstatic void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) {
f48e75cb9a8112d35855c44a156934f2ee0edb2eLennart Poetteringstatic int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
f48e75cb9a8112d35855c44a156934f2ee0edb2eLennart Poettering /* Compare min and max */
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers if (args->key.min_objectid < args->key.max_objectid)
a6c616024db23fef34152c1432892824a07799ccLennart Poettering if (args->key.min_objectid > args->key.max_objectid)
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering if (args->key.min_type < args->key.max_type)
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering if (args->key.min_type > args->key.max_type)
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers if (args->key.min_offset < args->key.max_offset)
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers if (args->key.min_offset > args->key.max_offset)
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering#define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) \
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering for ((i) = 0, \
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len))
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header)))
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sieversint btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering /* Tree of tree roots */
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering /* Look precisely for the subvolume items */
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers /* No restrictions on the other components */
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering args.key.min_objectid = args.key.max_objectid = subvol_id;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering while (btrfs_ioctl_search_args_compare(&args) <= 0) {
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering /* Make sure we start the next search at least from this entry */
a1da85830bfaa77b9eb9c54693e5573559c97e50Tom Gundersen /* Older versions of the struct lacked the otime setting */
a1da85830bfaa77b9eb9c54693e5573559c97e50Tom Gundersen if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
a1da85830bfaa77b9eb9c54693e5573559c97e50Tom Gundersen ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
a1da85830bfaa77b9eb9c54693e5573559c97e50Tom Gundersen (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
5b30bef856e89a571df57b7b953e9a1409d9acedLennart Poettering ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering /* Increase search key by one, to read the next item, if we can. */
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poetteringint btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
a1da85830bfaa77b9eb9c54693e5573559c97e50Tom Gundersen /* Tree of quota items */
a1da85830bfaa77b9eb9c54693e5573559c97e50Tom Gundersen /* The object ID is always 0 */
a1da85830bfaa77b9eb9c54693e5573559c97e50Tom Gundersen /* Look precisely for the quota items */
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering /* No restrictions on the other components */
1dba654b27918c22e413ac5b3c19301f1ff86ad2Lennart Poettering bool found_info = false, found_limit = false;
1dba654b27918c22e413ac5b3c19301f1ff86ad2Lennart Poettering r = btrfs_subvol_get_id_fd(fd, &subvol_id);
1dba654b27918c22e413ac5b3c19301f1ff86ad2Lennart Poettering args.key.min_offset = args.key.max_offset = subvol_id;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering while (btrfs_ioctl_search_args_compare(&args) <= 0) {
1dba654b27918c22e413ac5b3c19301f1ff86ad2Lennart Poettering const struct btrfs_ioctl_search_header *sh;
923d8fd381bced1c2d90ca53d18629d61a0f454aLennart Poettering if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
923d8fd381bced1c2d90ca53d18629d61a0f454aLennart Poettering FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
923d8fd381bced1c2d90ca53d18629d61a0f454aLennart Poettering /* Make sure we start the next search at least from this entry */
923d8fd381bced1c2d90ca53d18629d61a0f454aLennart Poettering const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
3d94f76c99da13e5603831d0b278f8c8c21bcb02Lennart Poettering } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
a4475f577bd0daf762d6c3b4e58bc484e0cb74afLennart Poettering const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh);
04d39279245834494baccfdb9349db8bf80abd13Lennart Poettering ret->referred_max = le64toh(qli->max_rfer);
04d39279245834494baccfdb9349db8bf80abd13Lennart Poettering ret->exclusive_max = le64toh(qli->max_excl);
34a6778fb9d1065f3fbb8e2243b9f0f25d1d18f1Zbigniew Jędrzejewski-Szmek ret->referred_max = (uint64_t) -1;
34a6778fb9d1065f3fbb8e2243b9f0f25d1d18f1Zbigniew Jędrzejewski-Szmek ret->exclusive_max = (uint64_t) -1;
04d39279245834494baccfdb9349db8bf80abd13Lennart Poettering /* Increase search key by one, to read the next item, if we can. */
04d39279245834494baccfdb9349db8bf80abd13Lennart Poettering if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
04d39279245834494baccfdb9349db8bf80abd13Lennart Poetteringint btrfs_defrag(const char *p) {