Btrfs-progs: check, ability to detect and fix outdated snapshot root items
[platform/upstream/btrfs-progs.git] / disk-io.c
index 5f3f484..77fc610 100644 (file)
--- a/disk-io.c
+++ b/disk-io.c
@@ -34,6 +34,7 @@
 #include "crc32c.h"
 #include "utils.h"
 #include "print-tree.h"
+#include "rbtree-utils.h"
 
 static int check_tree_block(struct btrfs_root *root, struct extent_buffer *buf)
 {
@@ -134,31 +135,26 @@ struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root,
                                   blocksize);
 }
 
-int readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize,
-                        u64 parent_transid)
+void readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize,
+                         u64 parent_transid)
 {
-       int ret;
        struct extent_buffer *eb;
        u64 length;
        struct btrfs_multi_bio *multi = NULL;
        struct btrfs_device *device;
 
        eb = btrfs_find_tree_block(root, bytenr, blocksize);
-       if (eb && btrfs_buffer_uptodate(eb, parent_transid)) {
-               free_extent_buffer(eb);
-               return 0;
+       if (!(eb && btrfs_buffer_uptodate(eb, parent_transid)) &&
+           !btrfs_map_block(&root->fs_info->mapping_tree, READ,
+                            bytenr, &length, &multi, 0, NULL)) {
+               device = multi->stripes[0].dev;
+               device->total_ios++;
+               blocksize = min(blocksize, (u32)(64 * 1024));
+               readahead(device->fd, multi->stripes[0].physical, blocksize);
        }
 
-       length = blocksize;
-       ret = btrfs_map_block(&root->fs_info->mapping_tree, READ,
-                             bytenr, &length, &multi, 0, NULL);
-       BUG_ON(ret);
-       device = multi->stripes[0].dev;
-       device->total_ios++;
-       blocksize = min(blocksize, (u32)(64 * 1024));
-       readahead(device->fd, multi->stripes[0].physical, blocksize);
+       free_extent_buffer(eb);
        kfree(multi);
-       return 0;
 }
 
 static int verify_parent_transid(struct extent_io_tree *io_tree,
@@ -180,6 +176,7 @@ static int verify_parent_transid(struct extent_io_tree *io_tree,
               (unsigned long long)parent_transid,
               (unsigned long long)btrfs_header_generation(eb));
        if (ignore) {
+               eb->flags |= EXTENT_BAD_TRANSID;
                printk("Ignoring transid failure\n");
                return 0;
        }
@@ -205,7 +202,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);
@@ -274,6 +272,12 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
                    csum_tree_block(root, eb, 1) == 0 &&
                    verify_parent_transid(eb->tree, eb, parent_transid, ignore)
                    == 0) {
+                       if (eb->flags & EXTENT_BAD_TRANSID &&
+                           list_empty(&eb->recow)) {
+                               list_add_tail(&eb->recow,
+                                             &root->fs_info->recow_ebs);
+                               eb->refs++;
+                       }
                        btrfs_set_buffer_uptodate(eb);
                        return eb;
                }
@@ -290,7 +294,7 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
                        ignore = 1;
                        continue;
                }
-               if (btrfs_header_generation(eb) > best_transid) {
+               if (btrfs_header_generation(eb) > best_transid && mirror_num) {
                        best_transid = btrfs_header_generation(eb);
                        good_mirror = mirror_num;
                }
@@ -305,9 +309,9 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
        return NULL;
 }
 
-static int write_tree_block(struct btrfs_trans_handle *trans,
-                           struct btrfs_root *root,
-                           struct extent_buffer *eb)
+int write_and_map_eb(struct btrfs_trans_handle *trans,
+                    struct btrfs_root *root,
+                    struct extent_buffer *eb)
 {
        int ret;
        int dev_nr;
@@ -315,15 +319,6 @@ static int write_tree_block(struct btrfs_trans_handle *trans,
        u64 *raid_map = NULL;
        struct btrfs_multi_bio *multi = NULL;
 
-       if (check_tree_block(root, eb))
-               BUG();
-
-       if (!btrfs_buffer_uptodate(eb, trans->transid))
-               BUG();
-
-       btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN);
-       csum_tree_block(root, eb, 0);
-
        dev_nr = 0;
        length = eb->len;
        ret = btrfs_map_block(&root->fs_info->mapping_tree, WRITE,
@@ -346,6 +341,22 @@ static int write_tree_block(struct btrfs_trans_handle *trans,
        return 0;
 }
 
+static int write_tree_block(struct btrfs_trans_handle *trans,
+                    struct btrfs_root *root,
+                    struct extent_buffer *eb)
+{
+       if (check_tree_block(root, eb))
+               BUG();
+
+       if (!btrfs_buffer_uptodate(eb, trans->transid))
+               BUG();
+
+       btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN);
+       csum_tree_block(root, eb, 0);
+
+       return write_and_map_eb(trans, root, eb);
+}
+
 int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
                        u32 stripesize, struct btrfs_root *root,
                        struct btrfs_fs_info *fs_info, u64 objectid)
@@ -464,6 +475,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;
@@ -555,7 +568,6 @@ static int find_and_setup_log_root(struct btrfs_root *tree_root,
        return 0;
 }
 
-
 int btrfs_free_fs_root(struct btrfs_root *root)
 {
        if (root->node)
@@ -620,7 +632,6 @@ struct btrfs_root *btrfs_read_fs_root_no_cache(struct btrfs_fs_info *fs_info,
        memcpy(&root->root_key, location, sizeof(*location));
        ret = 0;
 out:
-       btrfs_release_path(path);
        btrfs_free_path(path);
        if (ret) {
                free(root);
@@ -630,7 +641,10 @@ 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);
-       BUG_ON(!root->node);
+       if (!root->node) {
+               free(root);
+               return ERR_PTR(-EIO);
+       }
 insert:
        root->ref_cows = 1;
        return root;
@@ -666,6 +680,7 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
        struct btrfs_root *root;
        struct rb_node *node;
        int ret;
+       u64 objectid = location->objectid;
 
        if (location->objectid == BTRFS_ROOT_TREE_OBJECTID)
                return fs_info->tree_root;
@@ -677,11 +692,13 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
                return fs_info->dev_root;
        if (location->objectid == BTRFS_CSUM_TREE_OBJECTID)
                return fs_info->csum_root;
+       if (location->objectid == BTRFS_QUOTA_TREE_OBJECTID)
+               return fs_info->csum_root;
 
        BUG_ON(location->objectid == BTRFS_TREE_RELOC_OBJECTID ||
               location->offset != (u64)-1);
 
-       node = rb_search(&fs_info->fs_root_tree, (void *)&location->objectid,
+       node = rb_search(&fs_info->fs_root_tree, (void *)&objectid,
                         btrfs_fs_roots_compare_objectids, NULL);
        if (node)
                return container_of(node, struct btrfs_root, rb_node);
@@ -703,6 +720,7 @@ void btrfs_free_fs_info(struct btrfs_fs_info *fs_info)
        free(fs_info->chunk_root);
        free(fs_info->dev_root);
        free(fs_info->csum_root);
+       free(fs_info->quota_root);
        free(fs_info->super_copy);
        free(fs_info->log_root_tree);
        free(fs_info);
@@ -723,11 +741,13 @@ struct btrfs_fs_info *btrfs_new_fs_info(int writable, u64 sb_bytenr)
        fs_info->chunk_root = malloc(sizeof(struct btrfs_root));
        fs_info->dev_root = malloc(sizeof(struct btrfs_root));
        fs_info->csum_root = malloc(sizeof(struct btrfs_root));
+       fs_info->quota_root = malloc(sizeof(struct btrfs_root));
        fs_info->super_copy = malloc(BTRFS_SUPER_INFO_SIZE);
 
        if (!fs_info->tree_root || !fs_info->extent_root ||
            !fs_info->chunk_root || !fs_info->dev_root ||
-           !fs_info->csum_root || !fs_info->super_copy)
+           !fs_info->csum_root || !fs_info->quota_root ||
+           !fs_info->super_copy)
                goto free_all;
 
        memset(fs_info->super_copy, 0, BTRFS_SUPER_INFO_SIZE);
@@ -736,6 +756,7 @@ struct btrfs_fs_info *btrfs_new_fs_info(int writable, u64 sb_bytenr)
        memset(fs_info->chunk_root, 0, sizeof(struct btrfs_root));
        memset(fs_info->dev_root, 0, sizeof(struct btrfs_root));
        memset(fs_info->csum_root, 0, sizeof(struct btrfs_root));
+       memset(fs_info->quota_root, 0, sizeof(struct btrfs_root));
 
        extent_io_tree_init(&fs_info->extent_cache);
        extent_io_tree_init(&fs_info->free_space_cache);
@@ -749,6 +770,7 @@ struct btrfs_fs_info *btrfs_new_fs_info(int writable, u64 sb_bytenr)
        mutex_init(&fs_info->fs_mutex);
        INIT_LIST_HEAD(&fs_info->dirty_cowonly_roots);
        INIT_LIST_HEAD(&fs_info->space_info);
+       INIT_LIST_HEAD(&fs_info->recow_ebs);
 
        if (!writable)
                fs_info->readonly = 1;
@@ -793,8 +815,27 @@ int btrfs_check_fs_compatibility(struct btrfs_super_block *sb, int writable)
        return 0;
 }
 
-int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info,
-                         u64 root_tree_bytenr, int partial)
+static int find_best_backup_root(struct btrfs_super_block *super)
+{
+       struct btrfs_root_backup *backup;
+       u64 orig_gen = btrfs_super_generation(super);
+       u64 gen = 0;
+       int best_index = 0;
+       int i;
+
+       for (i = 0; i < BTRFS_NUM_BACKUP_ROOTS; i++) {
+               backup = super->super_roots + i;
+               if (btrfs_backup_tree_root_gen(backup) != orig_gen &&
+                   btrfs_backup_tree_root_gen(backup) > gen) {
+                       best_index = i;
+                       gen = btrfs_backup_tree_root_gen(backup);
+               }
+       }
+       return best_index;
+}
+
+int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
+                         enum btrfs_open_ctree_flags flags)
 {
        struct btrfs_super_block *sb = fs_info->super_copy;
        struct btrfs_root *root;
@@ -818,8 +859,20 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info,
        blocksize = btrfs_level_size(root, btrfs_super_root_level(sb));
        generation = btrfs_super_generation(sb);
 
-       if (!root_tree_bytenr)
+       if (!root_tree_bytenr && !(flags & OPEN_CTREE_BACKUP_ROOT)) {
                root_tree_bytenr = btrfs_super_root(sb);
+       } else if (flags & OPEN_CTREE_BACKUP_ROOT) {
+               struct btrfs_root_backup *backup;
+               int index = find_best_backup_root(sb);
+               if (index >= BTRFS_NUM_BACKUP_ROOTS) {
+                       fprintf(stderr, "Invalid backup root number\n");
+                       return -EIO;
+               }
+               backup = fs_info->super_copy->super_roots + index;
+               root_tree_bytenr = btrfs_backup_tree_root(backup);
+               generation = btrfs_backup_tree_root_gen(backup);
+       }
+
        root->node = read_tree_block(root, root_tree_bytenr, blocksize,
                                     generation);
        if (!extent_buffer_uptodate(root->node)) {
@@ -831,7 +884,17 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info,
                                  fs_info->extent_root);
        if (ret) {
                printk("Couldn't setup extent tree\n");
-               return -EIO;
+               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);
        }
        fs_info->extent_root->track_dirty = 1;
 
@@ -847,11 +910,23 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info,
                                  fs_info->csum_root);
        if (ret) {
                printk("Couldn't setup csum tree\n");
-               if (!partial)
+               if (!(flags & OPEN_CTREE_PARTIAL))
                        return -EIO;
+               /* do the same thing as extent tree rebuilding */
+               fs_info->csum_root->node =
+                       btrfs_find_create_tree_block(fs_info->extent_root, 0,
+                                                    leafsize);
+               if (!fs_info->csum_root->node)
+                       return -ENOMEM;
+               clear_extent_buffer_uptodate(NULL, fs_info->csum_root->node);
        }
        fs_info->csum_root->track_dirty = 1;
 
+       ret = find_and_setup_root(root, fs_info, BTRFS_QUOTA_TREE_OBJECTID,
+                                 fs_info->quota_root);
+       if (ret == 0)
+               fs_info->quota_enabled = 1;
+
        ret = find_and_setup_log_root(root, fs_info, sb);
        if (ret) {
                printk("Couldn't setup log root tree\n");
@@ -860,20 +935,24 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info,
 
        fs_info->generation = generation;
        fs_info->last_trans_committed = generation;
-       btrfs_read_block_groups(fs_info->tree_root);
+       if (extent_buffer_uptodate(fs_info->extent_root->node) &&
+           !(flags & OPEN_CTREE_NO_BLOCK_GROUPS))
+               btrfs_read_block_groups(fs_info->tree_root);
 
        key.objectid = BTRFS_FS_TREE_OBJECTID;
        key.type = BTRFS_ROOT_ITEM_KEY;
        key.offset = (u64)-1;
        fs_info->fs_root = btrfs_read_fs_root(fs_info, &key);
 
-       if (!fs_info->fs_root)
+       if (IS_ERR(fs_info->fs_root))
                return -EIO;
        return 0;
 }
 
 void btrfs_release_all_roots(struct btrfs_fs_info *fs_info)
 {
+       if (fs_info->quota_root)
+               free_extent_buffer(fs_info->quota_root->node);
        if (fs_info->csum_root)
                free_extent_buffer(fs_info->csum_root->node);
        if (fs_info->dev_root)
@@ -900,6 +979,13 @@ FREE_EXTENT_CACHE_BASED_TREE(mapping_cache, free_map_lookup);
 
 void btrfs_cleanup_all_caches(struct btrfs_fs_info *fs_info)
 {
+       while (!list_empty(&fs_info->recow_ebs)) {
+               struct extent_buffer *eb;
+               eb = list_first_entry(&fs_info->recow_ebs,
+                                     struct extent_buffer, recow);
+               list_del_init(&eb->recow);
+               free_extent_buffer(eb);
+       }
        free_mapping_cache_tree(&fs_info->mapping_tree.cache_tree);
        extent_io_tree_cleanup(&fs_info->extent_cache);
        extent_io_tree_cleanup(&fs_info->free_space_cache);
@@ -911,7 +997,7 @@ 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)
+                         u64 sb_bytenr, int super_recover)
 {
        u64 total_devs;
        int ret;
@@ -919,14 +1005,14 @@ int btrfs_scan_fs_devices(int fd, const char *path,
                sb_bytenr = BTRFS_SUPER_INFO_OFFSET;
 
        ret = btrfs_scan_one_device(fd, path, fs_devices,
-                                   &total_devs, sb_bytenr);
+                                   &total_devs, sb_bytenr, super_recover);
        if (ret) {
                fprintf(stderr, "No valid Btrfs found on %s\n", path);
                return ret;
        }
 
        if (total_devs != 1) {
-               ret = btrfs_scan_for_fsid(run_ioctl);
+               ret = btrfs_scan_lblkid(!BTRFS_UPDATE_KERNEL);
                if (ret)
                        return ret;
        }
@@ -981,15 +1067,15 @@ int btrfs_setup_chunk_tree_and_device_map(struct btrfs_fs_info *fs_info)
 
 static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
                                             u64 sb_bytenr,
-                                            u64 root_tree_bytenr, int writes,
-                                            int partial, int restore,
-                                            int recover_super)
+                                            u64 root_tree_bytenr,
+                                            enum btrfs_open_ctree_flags flags)
 {
        struct btrfs_fs_info *fs_info;
        struct btrfs_super_block *disk_super;
        struct btrfs_fs_devices *fs_devices = NULL;
        struct extent_buffer *eb;
        int ret;
+       int oflags;
 
        if (sb_bytenr == 0)
                sb_bytenr = BTRFS_SUPER_INFO_OFFSET;
@@ -998,34 +1084,38 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
        if (posix_fadvise(fp, 0, 0, POSIX_FADV_DONTNEED))
                fprintf(stderr, "Warning, could not drop caches\n");
 
-       fs_info = btrfs_new_fs_info(writes, sb_bytenr);
+       fs_info = btrfs_new_fs_info(flags & OPEN_CTREE_WRITES, sb_bytenr);
        if (!fs_info) {
                fprintf(stderr, "Failed to allocate memory for fs_info\n");
                return NULL;
        }
-       if (restore)
+       if (flags & OPEN_CTREE_RESTORE)
                fs_info->on_restoring = 1;
 
        ret = btrfs_scan_fs_devices(fp, path, &fs_devices, sb_bytenr,
-                                   !recover_super);
+                                   (flags & OPEN_CTREE_RECOVER_SUPER));
        if (ret)
                goto out;
 
        fs_info->fs_devices = fs_devices;
-       if (writes)
-               ret = btrfs_open_devices(fs_devices, O_RDWR);
+       if (flags & OPEN_CTREE_WRITES)
+               oflags = O_RDWR;
        else
-               ret = btrfs_open_devices(fs_devices, O_RDONLY);
-       if (ret)
-               goto out_devices;
+               oflags = O_RDONLY;
 
+       if (flags & OPEN_CTREE_EXCLUSIVE)
+               oflags |= O_EXCL;
+
+       ret = btrfs_open_devices(fs_devices, oflags);
+       if (ret)
+               goto out;
 
        disk_super = fs_info->super_copy;
-       if (!recover_super)
+       if (!(flags & OPEN_CTREE_RECOVER_SUPER))
                ret = btrfs_read_dev_super(fs_devices->latest_bdev,
-                                          disk_super, sb_bytenr);
+                                          disk_super, sb_bytenr, 1);
        else
-               ret = btrfs_read_dev_super(fp, disk_super, sb_bytenr);
+               ret = btrfs_read_dev_super(fp, disk_super, sb_bytenr, 0);
        if (ret) {
                printk("No valid btrfs found\n");
                goto out_devices;
@@ -1033,7 +1123,8 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
 
        memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE);
 
-       ret = btrfs_check_fs_compatibility(fs_info->super_copy, writes);
+       ret = btrfs_check_fs_compatibility(fs_info->super_copy,
+                                          flags & OPEN_CTREE_WRITES);
        if (ret)
                goto out_devices;
 
@@ -1043,18 +1134,15 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
 
        eb = fs_info->chunk_root->node;
        read_extent_buffer(eb, fs_info->chunk_tree_uuid,
-                          (unsigned long)btrfs_header_chunk_tree_uuid(eb),
+                          btrfs_header_chunk_tree_uuid(eb),
                           BTRFS_UUID_SIZE);
 
-       ret = btrfs_setup_all_roots(fs_info, root_tree_bytenr, partial);
+       ret = btrfs_setup_all_roots(fs_info, root_tree_bytenr, flags);
        if (ret)
-               goto out_failed;
+               goto out_chunk;
 
        return fs_info;
 
-out_failed:
-       if (partial)
-               return fs_info;
 out_chunk:
        btrfs_release_all_roots(fs_info);
        btrfs_cleanup_all_caches(fs_info);
@@ -1065,101 +1153,58 @@ out:
        return NULL;
 }
 
-struct btrfs_fs_info *open_ctree_fs_info_restore(const char *filename,
-                                        u64 sb_bytenr, u64 root_tree_bytenr,
-                                        int writes, int partial)
-{
-       int fp;
-       struct btrfs_fs_info *info;
-       int flags = O_CREAT | O_RDWR;
-       int restore = 1;
-
-       if (!writes)
-               flags = O_RDONLY;
-
-       fp = open(filename, flags, 0600);
-       if (fp < 0) {
-               fprintf (stderr, "Could not open %s\n", filename);
-               return NULL;
-       }
-       info = __open_ctree_fd(fp, filename, sb_bytenr, root_tree_bytenr,
-                              writes, partial, restore, 0);
-       close(fp);
-       return info;
-}
-
 struct btrfs_fs_info *open_ctree_fs_info(const char *filename,
                                         u64 sb_bytenr, u64 root_tree_bytenr,
-                                        int writes, int partial)
+                                        enum btrfs_open_ctree_flags flags)
 {
        int fp;
        struct btrfs_fs_info *info;
-       int flags = O_CREAT | O_RDWR;
+       int oflags = O_CREAT | O_RDWR;
 
-       if (!writes)
-               flags = O_RDONLY;
+       if (!(flags & OPEN_CTREE_WRITES))
+               oflags = O_RDONLY;
 
-       fp = open(filename, flags, 0600);
+       fp = open(filename, oflags, 0600);
        if (fp < 0) {
                fprintf (stderr, "Could not open %s\n", filename);
                return NULL;
        }
        info = __open_ctree_fd(fp, filename, sb_bytenr, root_tree_bytenr,
-                              writes, partial, 0, 0);
+                              flags);
        close(fp);
        return info;
 }
 
-struct btrfs_root *open_ctree_with_broken_super(const char *filename,
-                                       u64 sb_bytenr, int writes)
-{
-       int fp;
-       struct btrfs_fs_info *info;
-       int flags = O_CREAT | O_RDWR;
-
-       if (!writes)
-               flags = O_RDONLY;
-
-       fp = open(filename, flags, 0600);
-       if (fp < 0) {
-               fprintf(stderr, "Could not open %s\n", filename);
-               return NULL;
-       }
-       info = __open_ctree_fd(fp, filename, sb_bytenr, 0,
-                              writes, 0, 0, 1);
-       close(fp);
-       if (info)
-               return info->fs_root;
-       return NULL;
-}
-
-struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr, int writes)
+struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr,
+                             enum btrfs_open_ctree_flags flags)
 {
        struct btrfs_fs_info *info;
 
-       info = open_ctree_fs_info(filename, sb_bytenr, 0, writes, 0);
+       info = open_ctree_fs_info(filename, sb_bytenr, 0, flags);
        if (!info)
                return NULL;
        return info->fs_root;
 }
 
 struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
-                                int writes)
+                                enum btrfs_open_ctree_flags flags)
 {
        struct btrfs_fs_info *info;
-       info = __open_ctree_fd(fp, path, sb_bytenr, 0, writes, 0, 0, 0);
+       info = __open_ctree_fd(fp, path, sb_bytenr, 0, flags);
        if (!info)
                return NULL;
        return info->fs_root;
 }
 
-int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr)
+int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr,
+                        int super_recover)
 {
        u8 fsid[BTRFS_FSID_SIZE];
        int fsid_is_initialized = 0;
        struct btrfs_super_block buf;
        int i;
        int ret;
+       int max_super = super_recover ? BTRFS_SUPER_MIRROR_MAX : 1;
        u64 transid = 0;
        u64 bytenr;
 
@@ -1176,7 +1221,14 @@ int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr)
                return 0;
        }
 
-       for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
+       /*
+       * we would like to check all the supers, but that would make
+       * a btrfs mount succeed after a mkfs from a different FS.
+       * So, we need to add a special mount option to scan for
+       * later supers, using BTRFS_SUPER_MIRROR_MAX instead
+       */
+
+       for (i = 0; i < max_super; i++) {
                bytenr = btrfs_sb_offset(i);
                ret = pread64(fd, &buf, sizeof(buf), bytenr);
                if (ret < sizeof(buf))