btrfs-progs: subvol show: print received uuid
[platform/upstream/btrfs-progs.git] / disk-io.c
index 26a532e..a6e6056 100644 (file)
--- a/disk-io.c
+++ b/disk-io.c
  * Boston, MA 021110-1307, USA.
  */
 
-#define _XOPEN_SOURCE 600
-#define __USE_XOPEN2K
-#define _GNU_SOURCE 1
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <uuid/uuid.h>
 #include "kerncompat.h"
 #include "radix-tree.h"
 #include "ctree.h"
 #include "crc32c.h"
 #include "utils.h"
 #include "print-tree.h"
+#include "rbtree-utils.h"
+
+/* specified errno for check_tree_block */
+#define BTRFS_BAD_BYTENR               (-1)
+#define BTRFS_BAD_FSID                 (-2)
+#define BTRFS_BAD_LEVEL                        (-3)
+#define BTRFS_BAD_NRITEMS              (-4)
+
+/* Calculate max possible nritems for a leaf/node */
+static u32 max_nritems(u8 level, u32 nodesize)
+{
+
+       if (level == 0)
+               return ((nodesize - sizeof(struct btrfs_header)) /
+                       sizeof(struct btrfs_item));
+       return ((nodesize - sizeof(struct btrfs_header)) /
+               sizeof(struct btrfs_key_ptr));
+}
 
 static int check_tree_block(struct btrfs_root *root, struct extent_buffer *buf)
 {
 
        struct btrfs_fs_devices *fs_devices;
-       int ret = 1;
+       int ret = BTRFS_BAD_FSID;
 
-       if (buf->start != btrfs_header_bytenr(buf)) {
-               printk("Check tree block failed, want=%Lu, have=%Lu\n",
-                      buf->start, btrfs_header_bytenr(buf));
-               return ret;
-       }
+       if (buf->start != btrfs_header_bytenr(buf))
+               return BTRFS_BAD_BYTENR;
+       if (btrfs_header_level(buf) >= BTRFS_MAX_LEVEL)
+               return BTRFS_BAD_LEVEL;
+       if (btrfs_header_nritems(buf) > max_nritems(btrfs_header_level(buf),
+                                                   root->nodesize))
+               return BTRFS_BAD_NRITEMS;
 
        fs_devices = root->fs_info->fs_devices;
        while (fs_devices) {
-               if (!memcmp_extent_buffer(buf, fs_devices->fsid,
+               if (root->fs_info->ignore_fsid_mismatch ||
+                   !memcmp_extent_buffer(buf, fs_devices->fsid,
                                          btrfs_header_fsid(),
                                          BTRFS_FSID_SIZE)) {
                        ret = 0;
@@ -60,6 +79,38 @@ static int check_tree_block(struct btrfs_root *root, struct extent_buffer *buf)
        return ret;
 }
 
+static void print_tree_block_error(struct btrfs_root *root,
+                               struct extent_buffer *eb,
+                               int err)
+{
+       char fs_uuid[BTRFS_UUID_UNPARSED_SIZE] = {'\0'};
+       char found_uuid[BTRFS_UUID_UNPARSED_SIZE] = {'\0'};
+       u8 buf[BTRFS_UUID_SIZE];
+
+       switch (err) {
+       case BTRFS_BAD_FSID:
+               read_extent_buffer(eb, buf, btrfs_header_fsid(),
+                                  BTRFS_UUID_SIZE);
+               uuid_unparse(buf, found_uuid);
+               uuid_unparse(root->fs_info->fsid, fs_uuid);
+               fprintf(stderr, "fsid mismatch, want=%s, have=%s\n",
+                       fs_uuid, found_uuid);
+               break;
+       case BTRFS_BAD_BYTENR:
+               fprintf(stderr, "bytenr mismatch, want=%llu, have=%llu\n",
+                       eb->start, btrfs_header_bytenr(eb));
+               break;
+       case BTRFS_BAD_LEVEL:
+               fprintf(stderr, "bad level, %u > %u\n",
+                       btrfs_header_level(eb), BTRFS_MAX_LEVEL);
+               break;
+       case BTRFS_BAD_NRITEMS:
+               fprintf(stderr, "invalid nr_items: %u\n",
+                       btrfs_header_nritems(eb));
+               break;
+       }
+}
+
 u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len)
 {
        return crc32c(seed, data, len);
@@ -117,6 +168,8 @@ int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf,
 {
        u16 csum_size =
                btrfs_super_csum_size(root->fs_info->super_copy);
+       if (verify && root->fs_info->suppress_check_block_errors)
+               return verify_tree_block_csum_silent(buf, csum_size);
        return csum_tree_block_size(buf, csum_size, verify);
 }
 
@@ -201,7 +254,8 @@ int read_whole_eb(struct btrfs_fs_info *info, struct extent_buffer *eb, int mirr
                read_len = bytes_left;
                device = NULL;
 
-               if (!info->on_restoring) {
+               if (!info->on_restoring &&
+                   eb->start != BTRFS_SUPER_INFO_OFFSET) {
                        ret = btrfs_map_block(&info->mapping_tree, READ,
                                              eb->start + offset, &read_len, &multi,
                                              mirror, NULL);
@@ -259,15 +313,15 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
 
        eb = btrfs_find_create_tree_block(root, bytenr, blocksize);
        if (!eb)
-               return NULL;
+               return ERR_PTR(-ENOMEM);
 
        if (btrfs_buffer_uptodate(eb, parent_transid))
                return eb;
 
        while (1) {
                ret = read_whole_eb(root->fs_info, eb, mirror_num);
-               if (ret == 0 && check_tree_block(root, eb) == 0 &&
-                   csum_tree_block(root, eb, 1) == 0 &&
+               if (ret == 0 && csum_tree_block(root, eb, 1) == 0 &&
+                   check_tree_block(root, eb) == 0 &&
                    verify_parent_transid(eb->tree, eb, parent_transid, ignore)
                    == 0) {
                        if (eb->flags & EXTENT_BAD_TRANSID &&
@@ -280,10 +334,15 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
                        return eb;
                }
                if (ignore) {
-                       if (check_tree_block(root, eb))
-                               printk("read block failed check_tree_block\n");
-                       else
-                               printk("Csum didn't match\n");
+                       if (check_tree_block(root, eb)) {
+                               if (!root->fs_info->suppress_check_block_errors)
+                                       print_tree_block_error(root, eb,
+                                               check_tree_block(root, eb));
+                       } else {
+                               if (!root->fs_info->suppress_check_block_errors)
+                                       fprintf(stderr, "Csum didn't match\n");
+                       }
+                       ret = -EIO;
                        break;
                }
                num_copies = btrfs_num_copies(&root->fs_info->mapping_tree,
@@ -304,7 +363,7 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
                }
        }
        free_extent_buffer(eb);
-       return NULL;
+       return ERR_PTR(ret);
 }
 
 int write_and_map_eb(struct btrfs_trans_handle *trans,
@@ -339,14 +398,16 @@ int write_and_map_eb(struct btrfs_trans_handle *trans,
        return 0;
 }
 
-static int write_tree_block(struct btrfs_trans_handle *trans,
+int write_tree_block(struct btrfs_trans_handle *trans,
                     struct btrfs_root *root,
                     struct extent_buffer *eb)
 {
-       if (check_tree_block(root, eb))
+       if (check_tree_block(root, eb)) {
+               print_tree_block_error(root, eb, check_tree_block(root, eb));
                BUG();
+       }
 
-       if (!btrfs_buffer_uptodate(eb, trans->transid))
+       if (trans && !btrfs_buffer_uptodate(eb, trans->transid))
                BUG();
 
        btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN);
@@ -375,6 +436,7 @@ int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
        root->last_inode_alloc = 0;
 
        INIT_LIST_HEAD(&root->dirty_list);
+       INIT_LIST_HEAD(&root->orphan_data_extents);
        memset(&root->root_key, 0, sizeof(root->root_key));
        memset(&root->root_item, 0, sizeof(root->root_item));
        root->root_key.objectid = objectid;
@@ -473,6 +535,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
 
        if (root->commit_root == root->node)
                goto commit_tree;
+       if (root == root->fs_info->tree_root)
+               goto commit_tree;
 
        free_extent_buffer(root->commit_root);
        root->commit_root = NULL;
@@ -637,7 +701,7 @@ out:
        blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item));
        root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item),
                                     blocksize, generation);
-       if (!root->node) {
+       if (!extent_buffer_uptodate(root->node)) {
                free(root);
                return ERR_PTR(-EIO);
        }
@@ -689,7 +753,7 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
        if (location->objectid == BTRFS_CSUM_TREE_OBJECTID)
                return fs_info->csum_root;
        if (location->objectid == BTRFS_QUOTA_TREE_OBJECTID)
-               return fs_info->csum_root;
+               return fs_info->quota_root;
 
        BUG_ON(location->objectid == BTRFS_TREE_RELOC_OBJECTID ||
               location->offset != (u64)-1);
@@ -760,6 +824,8 @@ struct btrfs_fs_info *btrfs_new_fs_info(int writable, u64 sb_bytenr)
        extent_io_tree_init(&fs_info->pinned_extents);
        extent_io_tree_init(&fs_info->pending_del);
        extent_io_tree_init(&fs_info->extent_ins);
+       fs_info->excluded_extents = NULL;
+
        fs_info->fs_root_tree = RB_ROOT;
        cache_tree_init(&fs_info->mapping_tree.cache_tree);
 
@@ -830,6 +896,35 @@ static int find_best_backup_root(struct btrfs_super_block *super)
        return best_index;
 }
 
+static int setup_root_or_create_block(struct btrfs_fs_info *fs_info,
+                                     enum btrfs_open_ctree_flags flags,
+                                     struct btrfs_root *info_root,
+                                     u64 objectid, char *str)
+{
+       struct btrfs_super_block *sb = fs_info->super_copy;
+       struct btrfs_root *root = fs_info->tree_root;
+       u32 leafsize = btrfs_super_leafsize(sb);
+       int ret;
+
+       ret = find_and_setup_root(root, fs_info, objectid, info_root);
+       if (ret) {
+               printk("Couldn't setup %s tree\n", str);
+               if (!(flags & OPEN_CTREE_PARTIAL))
+                       return -EIO;
+               /*
+                * Need a blank node here just so we don't screw up in the
+                * million of places that assume a root has a valid ->node
+                */
+               info_root->node =
+                       btrfs_find_create_tree_block(info_root, 0, leafsize);
+               if (!info_root->node)
+                       return -ENOMEM;
+               clear_extent_buffer_uptodate(NULL, info_root->node);
+       }
+
+       return 0;
+}
+
 int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
                          enum btrfs_open_ctree_flags flags)
 {
@@ -876,22 +971,10 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
                return -EIO;
        }
 
-       ret = find_and_setup_root(root, fs_info, BTRFS_EXTENT_TREE_OBJECTID,
-                                 fs_info->extent_root);
-       if (ret) {
-               printk("Couldn't setup extent tree\n");
-               if (!(flags & OPEN_CTREE_PARTIAL))
-                       return -EIO;
-               /* Need a blank node here just so we don't screw up in the
-                * million of places that assume a root has a valid ->node
-                */
-               fs_info->extent_root->node =
-                       btrfs_find_create_tree_block(fs_info->extent_root, 0,
-                                                    leafsize);
-               if (!fs_info->extent_root->node)
-                       return -ENOMEM;
-               clear_extent_buffer_uptodate(NULL, fs_info->extent_root->node);
-       }
+       ret = setup_root_or_create_block(fs_info, flags, fs_info->extent_root,
+                                        BTRFS_EXTENT_TREE_OBJECTID, "extent");
+       if (ret)
+               return ret;
        fs_info->extent_root->track_dirty = 1;
 
        ret = find_and_setup_root(root, fs_info, BTRFS_DEV_TREE_OBJECTID,
@@ -902,13 +985,10 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
        }
        fs_info->dev_root->track_dirty = 1;
 
-       ret = find_and_setup_root(root, fs_info, BTRFS_CSUM_TREE_OBJECTID,
-                                 fs_info->csum_root);
-       if (ret) {
-               printk("Couldn't setup csum tree\n");
-               if (!(flags & OPEN_CTREE_PARTIAL))
-                       return -EIO;
-       }
+       ret = setup_root_or_create_block(fs_info, flags, fs_info->csum_root,
+                                        BTRFS_CSUM_TREE_OBJECTID, "csum");
+       if (ret)
+               return ret;
        fs_info->csum_root->track_dirty = 1;
 
        ret = find_and_setup_root(root, fs_info, BTRFS_QUOTA_TREE_OBJECTID,
@@ -919,7 +999,8 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
        ret = find_and_setup_log_root(root, fs_info, sb);
        if (ret) {
                printk("Couldn't setup log root tree\n");
-               return -EIO;
+               if (!(flags & OPEN_CTREE_PARTIAL))
+                       return -EIO;
        }
 
        fs_info->generation = generation;
@@ -986,13 +1067,27 @@ void btrfs_cleanup_all_caches(struct btrfs_fs_info *fs_info)
 
 int btrfs_scan_fs_devices(int fd, const char *path,
                          struct btrfs_fs_devices **fs_devices,
-                         u64 sb_bytenr, int run_ioctl, int super_recover)
+                         u64 sb_bytenr, int super_recover,
+                         int skip_devices)
 {
        u64 total_devs;
+       u64 dev_size;
+       off_t seek_ret;
        int ret;
        if (!sb_bytenr)
                sb_bytenr = BTRFS_SUPER_INFO_OFFSET;
 
+       seek_ret = lseek(fd, 0, SEEK_END);
+       if (seek_ret < 0)
+               return -errno;
+
+       dev_size = seek_ret;
+       lseek(fd, 0, SEEK_SET);
+       if (sb_bytenr > dev_size) {
+               fprintf(stderr, "Superblock bytenr is larger than device size\n");
+               return -EINVAL;
+       }
+
        ret = btrfs_scan_one_device(fd, path, fs_devices,
                                    &total_devs, sb_bytenr, super_recover);
        if (ret) {
@@ -1000,8 +1095,8 @@ int btrfs_scan_fs_devices(int fd, const char *path,
                return ret;
        }
 
-       if (total_devs != 1) {
-               ret = btrfs_scan_for_fsid(run_ioctl);
+       if (!skip_devices && total_devs != 1) {
+               ret = btrfs_scan_lblkid();
                if (ret)
                        return ret;
        }
@@ -1038,8 +1133,7 @@ int btrfs_setup_chunk_tree_and_device_map(struct btrfs_fs_info *fs_info)
        fs_info->chunk_root->node = read_tree_block(fs_info->chunk_root,
                                                    btrfs_super_chunk_root(sb),
                                                    blocksize, generation);
-       if (!fs_info->chunk_root->node ||
-           !extent_buffer_uptodate(fs_info->chunk_root->node)) {
+       if (!extent_buffer_uptodate(fs_info->chunk_root->node)) {
                fprintf(stderr, "Couldn't read chunk root\n");
                return -EIO;
        }
@@ -1080,10 +1174,14 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
        }
        if (flags & OPEN_CTREE_RESTORE)
                fs_info->on_restoring = 1;
+       if (flags & OPEN_CTREE_SUPPRESS_CHECK_BLOCK_ERRORS)
+               fs_info->suppress_check_block_errors = 1;
+       if (flags & OPEN_CTREE_IGNORE_FSID_MISMATCH)
+               fs_info->ignore_fsid_mismatch = 1;
 
        ret = btrfs_scan_fs_devices(fp, path, &fs_devices, sb_bytenr,
-                                   !(flags & OPEN_CTREE_RECOVER_SUPER),
-                                   (flags & OPEN_CTREE_RECOVER_SUPER));
+                                   (flags & OPEN_CTREE_RECOVER_SUPER),
+                                   (flags & OPEN_CTREE_NO_DEVICES));
        if (ret)
                goto out;
 
@@ -1111,6 +1209,12 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
                goto out_devices;
        }
 
+       if (btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_CHANGING_FSID &&
+           !fs_info->ignore_fsid_mismatch) {
+               fprintf(stderr, "ERROR: Filesystem UUID change in progress\n");
+               goto out_devices;
+       }
+
        memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE);
 
        ret = btrfs_check_fs_compatibility(fs_info->super_copy,
@@ -1128,14 +1232,11 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
                           BTRFS_UUID_SIZE);
 
        ret = btrfs_setup_all_roots(fs_info, root_tree_bytenr, flags);
-       if (ret)
-               goto out_failed;
+       if (ret && !(flags & __OPEN_CTREE_RETURN_CHUNK_ROOT))
+               goto out_chunk;
 
        return fs_info;
 
-out_failed:
-       if (flags & OPEN_CTREE_PARTIAL)
-               return fs_info;
 out_chunk:
        btrfs_release_all_roots(fs_info);
        btrfs_cleanup_all_caches(fs_info);
@@ -1176,6 +1277,8 @@ struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr,
        info = open_ctree_fs_info(filename, sb_bytenr, 0, flags);
        if (!info)
                return NULL;
+       if (flags & __OPEN_CTREE_RETURN_CHUNK_ROOT)
+               return info->chunk_root;
        return info->fs_root;
 }
 
@@ -1186,6 +1289,8 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
        info = __open_ctree_fd(fp, path, sb_bytenr, 0, flags);
        if (!info)
                return NULL;
+       if (flags & __OPEN_CTREE_RETURN_CHUNK_ROOT)
+               return info->chunk_root;
        return info->fs_root;
 }