+static int update_super(u8 *buffer)
+{
+ struct btrfs_super_block *super = (struct btrfs_super_block *)buffer;
+ struct btrfs_chunk *chunk;
+ struct btrfs_disk_key *disk_key;
+ struct btrfs_key key;
+ u32 new_array_size = 0;
+ u32 array_size;
+ u32 cur = 0;
+ u32 new_cur = 0;
+ u8 *ptr, *write_ptr;
+ int old_num_stripes;
+
+ write_ptr = ptr = super->sys_chunk_array;
+ array_size = btrfs_super_sys_array_size(super);
+
+ while (cur < array_size) {
+ disk_key = (struct btrfs_disk_key *)ptr;
+ btrfs_disk_key_to_cpu(&key, disk_key);
+
+ new_array_size += sizeof(*disk_key);
+ memmove(write_ptr, ptr, sizeof(*disk_key));
+
+ write_ptr += sizeof(*disk_key);
+ ptr += sizeof(*disk_key);
+ cur += sizeof(*disk_key);
+ new_cur += sizeof(*disk_key);
+
+ if (key.type == BTRFS_CHUNK_ITEM_KEY) {
+ chunk = (struct btrfs_chunk *)ptr;
+ old_num_stripes = btrfs_stack_chunk_num_stripes(chunk);
+ chunk = (struct btrfs_chunk *)write_ptr;
+
+ memmove(write_ptr, ptr, sizeof(*chunk));
+ btrfs_set_stack_chunk_num_stripes(chunk, 1);
+ btrfs_set_stack_chunk_sub_stripes(chunk, 0);
+ btrfs_set_stack_chunk_type(chunk,
+ BTRFS_BLOCK_GROUP_SYSTEM);
+ chunk->stripe.devid = super->dev_item.devid;
+ chunk->stripe.offset = cpu_to_le64(key.offset);
+ memcpy(chunk->stripe.dev_uuid, super->dev_item.uuid,
+ BTRFS_UUID_SIZE);
+ new_array_size += sizeof(*chunk);
+ new_cur += sizeof(*chunk);
+ } else {
+ fprintf(stderr, "Bogus key in the sys chunk array "
+ "%d\n", key.type);
+ return -EIO;
+ }
+ write_ptr += sizeof(*chunk);
+ ptr += btrfs_chunk_item_size(old_num_stripes);
+ cur += btrfs_chunk_item_size(old_num_stripes);
+ }
+
+ btrfs_set_super_sys_array_size(super, new_array_size);
+ csum_block(buffer, 4096);
+
+ return 0;
+}
+
+static struct extent_buffer *alloc_dummy_eb(u64 bytenr, u32 size)
+{
+ struct extent_buffer *eb;
+
+ eb = malloc(sizeof(struct extent_buffer) + size);
+ if (!eb)
+ return NULL;
+ memset(eb, 0, sizeof(struct extent_buffer) + size);
+
+ eb->start = bytenr;
+ eb->len = size;
+ return eb;
+}
+
+static void truncate_item(struct extent_buffer *eb, int slot, u32 new_size)
+{
+ struct btrfs_item *item;
+ u32 nritems;
+ u32 old_size;
+ u32 old_data_start;
+ u32 size_diff;
+ u32 data_end;
+ int i;
+
+ old_size = btrfs_item_size_nr(eb, slot);
+ if (old_size == new_size)
+ return;
+
+ nritems = btrfs_header_nritems(eb);
+ data_end = btrfs_item_offset_nr(eb, nritems - 1);
+
+ old_data_start = btrfs_item_offset_nr(eb, slot);
+ size_diff = old_size - new_size;
+
+ for (i = slot; i < nritems; i++) {
+ u32 ioff;
+ item = btrfs_item_nr(eb, i);
+ ioff = btrfs_item_offset(eb, item);
+ btrfs_set_item_offset(eb, item, ioff + size_diff);
+ }
+
+ memmove_extent_buffer(eb, btrfs_leaf_data(eb) + data_end + size_diff,
+ btrfs_leaf_data(eb) + data_end,
+ old_data_start + new_size - data_end);
+ item = btrfs_item_nr(eb, slot);
+ btrfs_set_item_size(eb, item, new_size);
+}
+
+static int fixup_chunk_tree_block(struct mdrestore_struct *mdres,
+ struct async_work *async, u8 *buffer,
+ size_t size)
+{
+ struct extent_buffer *eb;
+ size_t size_left = size;
+ u64 bytenr = async->start;
+ int i;
+
+ if (size_left % mdres->leafsize)
+ return 0;
+
+ eb = alloc_dummy_eb(bytenr, mdres->leafsize);
+ if (!eb)
+ return -ENOMEM;
+
+ while (size_left) {
+ eb->start = bytenr;
+ memcpy(eb->data, buffer, mdres->leafsize);
+
+ if (btrfs_header_bytenr(eb) != bytenr)
+ break;
+ if (memcmp(mdres->fsid,
+ eb->data + offsetof(struct btrfs_header, fsid),
+ BTRFS_FSID_SIZE))
+ break;
+
+ if (btrfs_header_owner(eb) != BTRFS_CHUNK_TREE_OBJECTID)
+ goto next;
+
+ if (btrfs_header_level(eb) != 0)
+ goto next;
+
+ for (i = 0; i < btrfs_header_nritems(eb); i++) {
+ struct btrfs_chunk chunk;
+ struct btrfs_key key;
+ u64 type;
+
+ btrfs_item_key_to_cpu(eb, &key, i);
+ if (key.type != BTRFS_CHUNK_ITEM_KEY)
+ continue;
+ truncate_item(eb, i, sizeof(chunk));
+ read_extent_buffer(eb, &chunk,
+ btrfs_item_ptr_offset(eb, i),
+ sizeof(chunk));
+
+ /* Zero out the RAID profile */
+ type = btrfs_stack_chunk_type(&chunk);
+ type &= (BTRFS_BLOCK_GROUP_DATA |
+ BTRFS_BLOCK_GROUP_SYSTEM |
+ BTRFS_BLOCK_GROUP_METADATA);
+ btrfs_set_stack_chunk_type(&chunk, type);
+
+ btrfs_set_stack_chunk_num_stripes(&chunk, 1);
+ btrfs_set_stack_chunk_sub_stripes(&chunk, 0);
+ btrfs_set_stack_stripe_devid(&chunk.stripe, mdres->devid);
+ btrfs_set_stack_stripe_offset(&chunk.stripe, key.offset);
+ memcpy(chunk.stripe.dev_uuid, mdres->uuid,
+ BTRFS_UUID_SIZE);
+ write_extent_buffer(eb, &chunk,
+ btrfs_item_ptr_offset(eb, i),
+ sizeof(chunk));
+ }
+ memcpy(buffer, eb->data, eb->len);
+ csum_block(buffer, eb->len);
+next:
+ size_left -= mdres->leafsize;
+ buffer += mdres->leafsize;
+ bytenr += mdres->leafsize;
+ }
+
+ return 0;
+}
+
+static void write_backup_supers(int fd, u8 *buf)
+{
+ struct stat st;
+ u64 size;
+ u64 bytenr;
+ int i;
+ int ret;
+
+ if (fstat(fd, &st)) {
+ fprintf(stderr, "Couldn't stat restore point, won't be able "
+ "to write backup supers: %d\n", errno);
+ return;
+ }
+
+ size = btrfs_device_size(fd, &st);
+
+ for (i = 1; i < BTRFS_SUPER_MIRROR_MAX; i++) {
+ bytenr = btrfs_sb_offset(i);
+ if (bytenr + 4096 > size)
+ break;
+ ret = pwrite64(fd, buf, 4096, bytenr);
+ if (ret < 4096) {
+ if (ret < 0)
+ fprintf(stderr, "Problem writing out backup "
+ "super block %d, err %d\n", i, errno);
+ else
+ fprintf(stderr, "Short write writing out "
+ "backup super block\n");
+ break;
+ }
+ }
+}
+