btrfs-progs: Fix wrong address accessing by subthread in btrfs-convert
[platform/upstream/btrfs-progs.git] / btrfs-image.c
index feb4a62..3684a05 100644 (file)
@@ -25,6 +25,8 @@
 #include <unistd.h>
 #include <dirent.h>
 #include <zlib.h>
+#include <getopt.h>
+
 #include "kerncompat.h"
 #include "crc32c.h"
 #include "ctree.h"
@@ -133,6 +135,7 @@ struct mdrestore_struct {
        size_t num_items;
        u32 leafsize;
        u64 devid;
+       u64 alloced_chunks;
        u64 last_physical_offset;
        u8 uuid[BTRFS_UUID_SIZE];
        u8 fsid[BTRFS_FSID_SIZE];
@@ -147,7 +150,6 @@ struct mdrestore_struct {
        struct btrfs_fs_info *info;
 };
 
-static void print_usage(void) __attribute__((noreturn));
 static int search_for_chunk_blocks(struct mdrestore_struct *mdres,
                                   u64 search, u64 cluster_bytenr);
 static struct extent_buffer *alloc_dummy_eb(u64 bytenr, u32 size);
@@ -1856,6 +1858,7 @@ static int mdrestore_init(struct mdrestore_struct *mdres,
        mdres->multi_devices = multi_devices;
        mdres->clear_space_cache = 0;
        mdres->last_physical_offset = 0;
+       mdres->alloced_chunks = 0;
 
        if (!num_threads)
                return 0;
@@ -2087,6 +2090,7 @@ static int read_chunk_block(struct mdrestore_struct *mdres, u8 *buffer,
                    mdres->last_physical_offset)
                        mdres->last_physical_offset = fs_chunk->physical +
                                fs_chunk->bytes;
+               mdres->alloced_chunks += fs_chunk->bytes;
                tree_insert(&mdres->chunk_tree, &fs_chunk->l, chunk_cmp);
        }
 out:
@@ -2372,9 +2376,107 @@ static void remap_overlapping_chunks(struct mdrestore_struct *mdres)
        }
 }
 
-static int __restore_metadump(const char *input, FILE *out, int old_restore,
-                             int num_threads, int fixup_offset,
-                             const char *target, int multi_devices)
+static int fixup_devices(struct btrfs_fs_info *fs_info,
+                        struct mdrestore_struct *mdres, off_t dev_size)
+{
+       struct btrfs_trans_handle *trans;
+       struct btrfs_dev_item *dev_item;
+       struct btrfs_path *path;
+       struct extent_buffer *leaf;
+       struct btrfs_root *root = fs_info->chunk_root;
+       struct btrfs_key key;
+       u64 devid, cur_devid;
+       int ret;
+
+       path = btrfs_alloc_path();
+       if (!path) {
+               fprintf(stderr, "Error alloc'ing path\n");
+               return -ENOMEM;
+       }
+
+       trans = btrfs_start_transaction(fs_info->tree_root, 1);
+       if (IS_ERR(trans)) {
+               fprintf(stderr, "Error starting transaction %ld\n",
+                       PTR_ERR(trans));
+               btrfs_free_path(path);
+               return PTR_ERR(trans);
+       }
+
+       dev_item = &fs_info->super_copy->dev_item;
+
+       devid = btrfs_stack_device_id(dev_item);
+
+       btrfs_set_stack_device_total_bytes(dev_item, dev_size);
+       btrfs_set_stack_device_bytes_used(dev_item, mdres->alloced_chunks);
+
+       key.objectid = BTRFS_DEV_ITEMS_OBJECTID;
+       key.type = BTRFS_DEV_ITEM_KEY;
+       key.offset = 0;
+
+again:
+       ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
+       if (ret < 0) {
+               fprintf(stderr, "search failed %d\n", ret);
+               exit(1);
+       }
+
+       while (1) {
+               leaf = path->nodes[0];
+               if (path->slots[0] >= btrfs_header_nritems(leaf)) {
+                       ret = btrfs_next_leaf(root, path);
+                       if (ret < 0) {
+                               fprintf(stderr, "Error going to next leaf "
+                                       "%d\n", ret);
+                               exit(1);
+                       }
+                       if (ret > 0) {
+                               ret = 0;
+                               break;
+                       }
+                       leaf = path->nodes[0];
+               }
+
+               btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
+               if (key.type > BTRFS_DEV_ITEM_KEY)
+                       break;
+               if (key.type != BTRFS_DEV_ITEM_KEY) {
+                       path->slots[0]++;
+                       continue;
+               }
+
+               dev_item = btrfs_item_ptr(leaf, path->slots[0],
+                                         struct btrfs_dev_item);
+               cur_devid = btrfs_device_id(leaf, dev_item);
+               if (devid != cur_devid) {
+                       ret = btrfs_del_item(trans, root, path);
+                       if (ret) {
+                               fprintf(stderr, "Error deleting item %d\n",
+                                       ret);
+                               exit(1);
+                       }
+                       btrfs_release_path(path);
+                       goto again;
+               }
+
+               btrfs_set_device_total_bytes(leaf, dev_item, dev_size);
+               btrfs_set_device_bytes_used(leaf, dev_item,
+                                           mdres->alloced_chunks);
+               btrfs_mark_buffer_dirty(leaf);
+               path->slots[0]++;
+       }
+
+       btrfs_free_path(path);
+       ret = btrfs_commit_transaction(trans, fs_info->tree_root);
+       if (ret) {
+               fprintf(stderr, "Commit failed %d\n", ret);
+               return ret;
+       }
+       return 0;
+}
+
+static int restore_metadump(const char *input, FILE *out, int old_restore,
+                           int num_threads, int fixup_offset,
+                           const char *target, int multi_devices)
 {
        struct meta_cluster *cluster = NULL;
        struct meta_cluster_header *header;
@@ -2454,6 +2556,33 @@ static int __restore_metadump(const char *input, FILE *out, int old_restore,
                }
        }
        ret = wait_for_worker(&mdrestore);
+
+       if (!ret && !multi_devices && !old_restore) {
+               struct btrfs_root *root;
+               struct stat st;
+
+               root = open_ctree_fd(fileno(out), target, 0,
+                                         OPEN_CTREE_PARTIAL |
+                                         OPEN_CTREE_WRITES |
+                                         OPEN_CTREE_NO_DEVICES);
+               if (!root) {
+                       fprintf(stderr, "unable to open %s\n", target);
+                       ret = -EIO;
+                       goto out;
+               }
+               info = root->fs_info;
+
+               if (stat(target, &st)) {
+                       fprintf(stderr, "statting %s failed\n", target);
+                       close_ctree(info->chunk_root);
+                       return 1;
+               }
+
+               ret = fixup_devices(info, &mdrestore, st.st_size);
+               close_ctree(info->chunk_root);
+               if (ret)
+                       goto out;
+       }
 out:
        mdrestore_destroy(&mdrestore, num_threads);
 failed_cluster:
@@ -2467,19 +2596,6 @@ failed_open:
        return ret;
 }
 
-static int restore_metadump(const char *input, FILE *out, int old_restore,
-                           int num_threads, int multi_devices)
-{
-       return __restore_metadump(input, out, old_restore, num_threads, 0, NULL,
-                                 multi_devices);
-}
-
-static int fixup_metadump(const char *input, FILE *out, int num_threads,
-                         const char *target)
-{
-       return __restore_metadump(input, out, 0, num_threads, 1, target, 1);
-}
-
 static int update_disk_super_on_device(struct btrfs_fs_info *info,
                                       const char *other_dev, u64 cur_devid)
 {
@@ -2574,7 +2690,7 @@ out:
        return 0;
 }
 
-static void print_usage(void)
+static void print_usage(int ret)
 {
        fprintf(stderr, "usage: btrfs-image [options] source target\n");
        fprintf(stderr, "\t-r      \trestore metadump image\n");
@@ -2587,7 +2703,7 @@ static void print_usage(void)
        fprintf(stderr, "\n");
        fprintf(stderr, "\tIn the dump mode, source is the btrfs device and target is the output file (use '-' for stdout).\n");
        fprintf(stderr, "\tIn the restore mode, source is the dumped image and target is the btrfs device/file.\n");
-       exit(1);
+       exit(ret);
 }
 
 int main(int argc, char *argv[])
@@ -2607,7 +2723,11 @@ int main(int argc, char *argv[])
        FILE *out;
 
        while (1) {
-               int c = getopt(argc, argv, "rc:t:oswm");
+               static const struct option long_options[] = {
+                       { "help", no_argument, NULL, GETOPT_VAL_HELP},
+                       { NULL, 0, NULL, 0 }
+               };
+               int c = getopt_long(argc, argv, "rc:t:oswm", long_options, NULL);
                if (c < 0)
                        break;
                switch (c) {
@@ -2617,12 +2737,12 @@ int main(int argc, char *argv[])
                case 't':
                        num_threads = arg_strtou64(optarg);
                        if (num_threads > 32)
-                               print_usage();
+                               print_usage(1);
                        break;
                case 'c':
                        compress_level = arg_strtou64(optarg);
                        if (compress_level > 9)
-                               print_usage();
+                               print_usage(1);
                        break;
                case 'o':
                        old_restore = 1;
@@ -2637,15 +2757,16 @@ int main(int argc, char *argv[])
                        create = 0;
                        multi_devices = 1;
                        break;
+                       case GETOPT_VAL_HELP:
                default:
-                       print_usage();
+                       print_usage(c != GETOPT_VAL_HELP);
                }
        }
 
        argc = argc - optind;
        set_argv0(argv);
        if (check_argc_min(argc, 2))
-               print_usage();
+               print_usage(1);
 
        dev_cnt = argc - 1;
 
@@ -2670,7 +2791,7 @@ int main(int argc, char *argv[])
        }
 
        if (usage_error)
-               print_usage();
+               print_usage(1);
 
        source = argv[optind];
        target = argv[optind + 1];
@@ -2705,7 +2826,7 @@ int main(int argc, char *argv[])
                                      compress_level, sanitize, walk_trees);
        } else {
                ret = restore_metadump(source, out, old_restore, num_threads,
-                                      multi_devices);
+                                      0, target, multi_devices);
        }
        if (ret) {
                printk("%s failed (%s)\n", (create) ? "create" : "restore",
@@ -2752,14 +2873,14 @@ int main(int argc, char *argv[])
                close_ctree(info->chunk_root);
 
                /* fix metadata block to map correct chunk */
-               ret = fixup_metadump(source, out, 1, target);
+               ret = restore_metadump(source, out, 0, num_threads, 1,
+                                      target, 1);
                if (ret) {
                        fprintf(stderr, "fix metadump failed (error=%d)\n",
                                ret);
                        exit(1);
                }
        }
-
 out:
        if (out == stdout) {
                fflush(out);