btrfs-progs: check the free space tree in btrfsck
[platform/upstream/btrfs-progs.git] / utils.c
diff --git a/utils.c b/utils.c
index 39ca927..03648db 100644 (file)
--- a/utils.c
+++ b/utils.c
@@ -154,7 +154,7 @@ int test_uuid_unique(char *fs_uuid)
        blkid_dev dev = NULL;
        blkid_cache cache = NULL;
 
-       if (blkid_get_cache(&cache, 0) < 0) {
+       if (blkid_get_cache(&cache, NULL) < 0) {
                printf("ERROR: lblkid cache get failed\n");
                return 1;
        }
@@ -182,7 +182,7 @@ int test_uuid_unique(char *fs_uuid)
 int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
 {
        struct btrfs_super_block super;
-       struct extent_buffer *buf = NULL;
+       struct extent_buffer *buf;
        struct btrfs_root_item root_item;
        struct btrfs_disk_key disk_key;
        struct btrfs_extent_item *extent_item;
@@ -204,6 +204,10 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
                                 BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA);
        u64 num_bytes;
 
+       buf = malloc(sizeof(*buf) + max(cfg->sectorsize, cfg->nodesize));
+       if (!buf)
+               return -ENOMEM;
+
        first_free = BTRFS_SUPER_INFO_OFFSET + cfg->sectorsize * 2 - 1;
        first_free &= ~((u64)cfg->sectorsize - 1);
 
@@ -249,8 +253,6 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
        if (cfg->label)
                strncpy(super.label, cfg->label, BTRFS_LABEL_SIZE - 1);
 
-       buf = malloc(sizeof(*buf) + max(cfg->sectorsize, cfg->nodesize));
-
        /* create the tree of root objects */
        memset(buf->data, 0, cfg->nodesize);
        buf->len = cfg->nodesize;
@@ -550,12 +552,12 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
 
        /* and write out the super block */
        BUG_ON(sizeof(super) > cfg->sectorsize);
-       memset(buf->data, 0, cfg->sectorsize);
+       memset(buf->data, 0, BTRFS_SUPER_INFO_SIZE);
        memcpy(buf->data, &super, sizeof(super));
-       buf->len = cfg->sectorsize;
+       buf->len = BTRFS_SUPER_INFO_SIZE;
        csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
-       ret = pwrite(fd, buf->data, cfg->sectorsize, cfg->blocks[0]);
-       if (ret != cfg->sectorsize) {
+       ret = pwrite(fd, buf->data, BTRFS_SUPER_INFO_SIZE, cfg->blocks[0]);
+       if (ret != BTRFS_SUPER_INFO_SIZE) {
                ret = (ret < 0 ? -errno : -EIO);
                goto out;
        }
@@ -724,7 +726,7 @@ static int zero_dev_clamped(int fd, off_t start, ssize_t len, u64 dev_size)
 
 int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
                      struct btrfs_root *root, int fd, char *path,
-                     u64 block_count, u32 io_width, u32 io_align,
+                     u64 device_total_bytes, u32 io_width, u32 io_align,
                      u32 sectorsize)
 {
        struct btrfs_super_block *disk_super;
@@ -732,10 +734,12 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
        struct btrfs_device *device;
        struct btrfs_dev_item *dev_item;
        char *buf = NULL;
-       u64 total_bytes;
+       u64 fs_total_bytes;
        u64 num_devs;
        int ret;
 
+       device_total_bytes = (device_total_bytes / sectorsize) * sectorsize;
+
        device = kzalloc(sizeof(*device), GFP_NOFS);
        if (!device)
                goto err_nomem;
@@ -755,7 +759,7 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
        device->sector_size = sectorsize;
        device->fd = fd;
        device->writeable = 1;
-       device->total_bytes = block_count;
+       device->total_bytes = device_total_bytes;
        device->bytes_used = 0;
        device->total_ios = 0;
        device->dev_root = root->fs_info->dev_root;
@@ -763,11 +767,12 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
        if (!device->name)
                goto err_nomem;
 
+       INIT_LIST_HEAD(&device->dev_list);
        ret = btrfs_add_device(trans, root, device);
        BUG_ON(ret);
 
-       total_bytes = btrfs_super_total_bytes(super) + block_count;
-       btrfs_set_super_total_bytes(super, total_bytes);
+       fs_total_bytes = btrfs_super_total_bytes(super) + device_total_bytes;
+       btrfs_set_super_total_bytes(super, fs_total_bytes);
 
        num_devs = btrfs_super_num_devices(super) + 1;
        btrfs_set_super_num_devices(super, num_devs);
@@ -847,7 +852,7 @@ out:
 }
 
 int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret,
-                          u64 max_block_count, int *mixed, int discard)
+                          u64 max_block_count, int discard)
 {
        u64 block_count;
        struct stat st;
@@ -867,9 +872,6 @@ int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret,
        if (max_block_count)
                block_count = min(block_count, max_block_count);
 
-       if (block_count < BTRFS_MKFS_SMALL_VOLUME_SIZE && !(*mixed))
-               *mixed = 1;
-
        if (discard) {
                /*
                 * We intentionally ignore errors from the discard ioctl.  It
@@ -1170,6 +1172,34 @@ static int is_loop_device (const char* device) {
                MAJOR(statbuf.st_rdev) == LOOP_MAJOR);
 }
 
+/*
+ * Takes a loop device path (e.g. /dev/loop0) and returns
+ * the associated file (e.g. /images/my_btrfs.img) using
+ * loopdev API
+ */
+static int resolve_loop_device_with_loopdev(const char* loop_dev, char* loop_file)
+{
+       int fd;
+       int ret;
+       struct loop_info64 lo64;
+
+       fd = open(loop_dev, O_RDONLY | O_NONBLOCK);
+       if (fd < 0)
+               return -errno;
+       ret = ioctl(fd, LOOP_GET_STATUS64, &lo64);
+       if (ret < 0) {
+               ret = -errno;
+               goto out;
+       }
+
+       memcpy(loop_file, lo64.lo_file_name, sizeof(lo64.lo_file_name));
+       loop_file[sizeof(lo64.lo_file_name)] = 0;
+
+out:
+       close(fd);
+
+       return ret;
+}
 
 /* Takes a loop device path (e.g. /dev/loop0) and returns
  * the associated file (e.g. /images/my_btrfs.img) */
@@ -1185,8 +1215,15 @@ static int resolve_loop_device(const char* loop_dev, char* loop_file,
        if (!realpath(loop_dev, real_loop_dev))
                return -errno;
        snprintf(p, PATH_MAX, "/sys/block/%s/loop/backing_file", strrchr(real_loop_dev, '/'));
-       if (!(f = fopen(p, "r")))
+       if (!(f = fopen(p, "r"))) {
+               if (errno == ENOENT)
+                       /*
+                        * It's possibly a partitioned loop device, which is
+                        * resolvable with loopdev API.
+                        */
+                       return resolve_loop_device_with_loopdev(loop_dev, loop_file);
                return -errno;
+       }
 
        snprintf(fmt, 20, "%%%i[^\n]", max_len-1);
        ret = fscanf(f, fmt, loop_file);
@@ -1516,7 +1553,7 @@ int btrfs_register_all_devices(void)
 
        list_for_each_entry(fs_devices, all_uuids, list) {
                list_for_each_entry(device, &fs_devices->devices, dev_list) {
-                       if (strlen(device->name) != 0) {
+                       if (*device->name) {
                                err = btrfs_register_one_device(device->name);
                                if (err < 0)
                                        return err;
@@ -2426,7 +2463,7 @@ static int group_profile_devs_min(u64 flag)
 }
 
 int test_num_disk_vs_raid(u64 metadata_profile, u64 data_profile,
-       u64 dev_cnt, int mixed)
+       u64 dev_cnt, int mixed, int ssd)
 {
        u64 allowed = 0;
 
@@ -2467,11 +2504,9 @@ int test_num_disk_vs_raid(u64 metadata_profile, u64 data_profile,
                return 1;
        }
 
-       if (!mixed && (data_profile & BTRFS_BLOCK_GROUP_DUP)) {
-               fprintf(stderr,
-                       "ERROR: DUP for data is allowed only in mixed mode\n");
-               return 1;
-       }
+       warning_on(!mixed && (data_profile & BTRFS_BLOCK_GROUP_DUP) && ssd,
+                  "DUP may not actually lead to 2 copies on the device, see manual page");
+
        return 0;
 }
 
@@ -2566,7 +2601,7 @@ int btrfs_scan_lblkid(void)
        if (btrfs_scan_done)
                return 0;
 
-       if (blkid_get_cache(&cache, 0) < 0) {
+       if (blkid_get_cache(&cache, NULL) < 0) {
                printf("ERROR: lblkid cache get failed\n");
                return 1;
        }