btrfs-progs: check: introduce traversal function for fsck
[platform/upstream/btrfs-progs.git] / disk-io.c
index 4302e80..3647ecc 100644 (file)
--- a/disk-io.c
+++ b/disk-io.c
@@ -56,7 +56,7 @@ static int check_tree_block(struct btrfs_fs_info *fs_info,
 {
 
        struct btrfs_fs_devices *fs_devices;
-       u32 leafsize = btrfs_super_leafsize(fs_info->super_copy);
+       u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
        int ret = BTRFS_BAD_FSID;
 
        if (buf->start != btrfs_header_bytenr(buf))
@@ -64,7 +64,12 @@ static int check_tree_block(struct btrfs_fs_info *fs_info,
        if (btrfs_header_level(buf) >= BTRFS_MAX_LEVEL)
                return BTRFS_BAD_LEVEL;
        if (btrfs_header_nritems(buf) > max_nritems(btrfs_header_level(buf),
-                                                   leafsize))
+                                                   nodesize))
+               return BTRFS_BAD_NRITEMS;
+
+       /* Only leaf can be empty */
+       if (btrfs_header_nritems(buf) == 0 &&
+           btrfs_header_level(buf) != 0)
                return BTRFS_BAD_NRITEMS;
 
        fs_devices = fs_info->fs_devices;
@@ -120,7 +125,7 @@ u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len)
 
 void btrfs_csum_final(u32 crc, char *result)
 {
-       *(__le32 *)result = ~cpu_to_le32(crc);
+       put_unaligned_le32(~crc, result);
 }
 
 static int __csum_tree_block_size(struct extent_buffer *buf, u16 csum_size,
@@ -619,7 +624,7 @@ static int find_and_setup_root(struct btrfs_root *tree_root,
        if (ret)
                return ret;
 
-       blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item));
+       blocksize = root->nodesize;
        generation = btrfs_root_generation(&root->root_item);
        root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item),
                                     blocksize, generation);
@@ -645,8 +650,7 @@ static int find_and_setup_log_root(struct btrfs_root *tree_root,
                return 0;
        }
 
-       blocksize = btrfs_level_size(tree_root,
-                            btrfs_super_log_root_level(disk_super));
+       blocksize = tree_root->nodesize;
 
        __setup_root(tree_root->nodesize, tree_root->leafsize,
                     tree_root->sectorsize, tree_root->stripesize,
@@ -737,7 +741,7 @@ out:
                return ERR_PTR(ret);
        }
        generation = btrfs_root_generation(&root->root_item);
-       blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item));
+       blocksize = root->nodesize;
        root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item),
                                     blocksize, generation);
        if (!extent_buffer_uptodate(root->node)) {
@@ -934,7 +938,7 @@ static int setup_root_or_create_block(struct btrfs_fs_info *fs_info,
 {
        struct btrfs_super_block *sb = fs_info->super_copy;
        struct btrfs_root *root = fs_info->tree_root;
-       u32 leafsize = btrfs_super_leafsize(sb);
+       u32 nodesize = btrfs_super_nodesize(sb);
        int ret;
 
        ret = find_and_setup_root(root, fs_info, objectid, info_root);
@@ -947,7 +951,7 @@ static int setup_root_or_create_block(struct btrfs_fs_info *fs_info,
                 * million of places that assume a root has a valid ->node
                 */
                info_root->node =
-                       btrfs_find_create_tree_block(fs_info, 0, leafsize);
+                       btrfs_find_create_tree_block(fs_info, 0, nodesize);
                if (!info_root->node)
                        return -ENOMEM;
                clear_extent_buffer_uptodate(NULL, info_root->node);
@@ -978,7 +982,7 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
        root = fs_info->tree_root;
        __setup_root(nodesize, leafsize, sectorsize, stripesize,
                     root, fs_info, BTRFS_ROOT_TREE_OBJECTID);
-       blocksize = btrfs_level_size(root, btrfs_super_root_level(sb));
+       blocksize = root->nodesize;
        generation = btrfs_super_generation(sb);
 
        if (!root_tree_bytenr && !(flags & OPEN_CTREE_BACKUP_ROOT)) {
@@ -1170,8 +1174,7 @@ int btrfs_setup_chunk_tree_and_device_map(struct btrfs_fs_info *fs_info,
        if (ret)
                return ret;
 
-       blocksize = btrfs_level_size(fs_info->chunk_root,
-                                    btrfs_super_chunk_root_level(sb));
+       blocksize = fs_info->chunk_root->nodesize;
        generation = btrfs_super_chunk_root_generation(sb);
 
        if (chunk_root_bytenr && !IS_ALIGNED(chunk_root_bytenr,
@@ -1264,7 +1267,7 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
                goto out;
 
        disk_super = fs_info->super_copy;
-       if (!(flags & OPEN_CTREE_RECOVER_SUPER))
+       if (flags & OPEN_CTREE_RECOVER_SUPER)
                ret = btrfs_read_dev_super(fs_devices->latest_bdev,
                                           disk_super, sb_bytenr, 1);
        else
@@ -1325,7 +1328,7 @@ struct btrfs_fs_info *open_ctree_fs_info(const char *filename,
        int fp;
        int ret;
        struct btrfs_fs_info *info;
-       int oflags = O_CREAT | O_RDWR;
+       int oflags = O_RDWR;
        struct stat st;
 
        ret = stat(filename, &st);
@@ -1341,7 +1344,7 @@ struct btrfs_fs_info *open_ctree_fs_info(const char *filename,
        if (!(flags & OPEN_CTREE_WRITES))
                oflags = O_RDONLY;
 
-       fp = open(filename, oflags, 0600);
+       fp = open(filename, oflags);
        if (fp < 0) {
                error("cannot open '%s': %s", filename, strerror(errno));
                return NULL;
@@ -1397,14 +1400,13 @@ static int check_super(struct btrfs_super_block *sb)
        int csum_size;
 
        if (btrfs_super_magic(sb) != BTRFS_MAGIC) {
-               fprintf(stderr, "ERROR: superblock magic doesn't match\n");
+               error("superblock magic doesn't match");
                return -EIO;
        }
 
        csum_type = btrfs_super_csum_type(sb);
        if (csum_type >= ARRAY_SIZE(btrfs_csum_sizes)) {
-               fprintf(stderr, "ERROR: unsupported checksum algorithm %u\n",
-                       csum_type);
+               error("unsupported checksum algorithm %u\n", csum_type);
                return -EIO;
        }
        csum_size = btrfs_csum_sizes[csum_type];
@@ -1415,59 +1417,69 @@ static int check_super(struct btrfs_super_block *sb)
        btrfs_csum_final(crc, result);
 
        if (memcmp(result, sb->csum, csum_size)) {
-               fprintf(stderr, "ERROR: superblock checksum mismatch\n");
+               error("superblock checksum mismatch");
                return -EIO;
        }
        if (btrfs_super_root_level(sb) >= BTRFS_MAX_LEVEL) {
-               fprintf(stderr, "ERROR: tree_root level too big: %d >= %d\n",
+               error("tree_root level too big: %d >= %d",
                        btrfs_super_root_level(sb), BTRFS_MAX_LEVEL);
-               return -EIO;
+               goto error_out;
        }
        if (btrfs_super_chunk_root_level(sb) >= BTRFS_MAX_LEVEL) {
-               fprintf(stderr, "ERROR: chunk_root level too big: %d >= %d\n",
+               error("chunk_root level too big: %d >= %d",
                        btrfs_super_chunk_root_level(sb), BTRFS_MAX_LEVEL);
-               return -EIO;
+               goto error_out;
        }
        if (btrfs_super_log_root_level(sb) >= BTRFS_MAX_LEVEL) {
-               fprintf(stderr, "ERROR: log_root level too big: %d >= %d\n",
+               error("log_root level too big: %d >= %d",
                        btrfs_super_log_root_level(sb), BTRFS_MAX_LEVEL);
-               return -EIO;
+               goto error_out;
        }
 
        if (!IS_ALIGNED(btrfs_super_root(sb), 4096)) {
-               fprintf(stderr, "ERROR: tree_root block unaligned: %llu\n",
-                       btrfs_super_root(sb));
-               return -EIO;
+               error("tree_root block unaligned: %llu", btrfs_super_root(sb));
+               goto error_out;
        }
        if (!IS_ALIGNED(btrfs_super_chunk_root(sb), 4096)) {
-               fprintf(stderr, "ERROR: chunk_root block unaligned: %llu\n",
+               error("chunk_root block unaligned: %llu",
                        btrfs_super_chunk_root(sb));
-               return -EIO;
+               goto error_out;
        }
        if (!IS_ALIGNED(btrfs_super_log_root(sb), 4096)) {
-               fprintf(stderr, "ERROR: log_root block unaligned: %llu\n",
+               error("log_root block unaligned: %llu",
                        btrfs_super_log_root(sb));
-               return -EIO;
+               goto error_out;
        }
        if (btrfs_super_nodesize(sb) < 4096) {
-               fprintf(stderr, "ERROR: nodesize too small: %u < 4096\n",
+               error("nodesize too small: %u < 4096",
                        btrfs_super_nodesize(sb));
-               return -EIO;
+               goto error_out;
        }
        if (!IS_ALIGNED(btrfs_super_nodesize(sb), 4096)) {
-               fprintf(stderr, "ERROR: nodesize unaligned: %u\n",
-                       btrfs_super_nodesize(sb));
-               return -EIO;
+               error("nodesize unaligned: %u", btrfs_super_nodesize(sb));
+               goto error_out;
        }
        if (btrfs_super_sectorsize(sb) < 4096) {
-               fprintf(stderr, "ERROR: sectorsize too small: %u < 4096\n",
+               error("sectorsize too small: %u < 4096",
                        btrfs_super_sectorsize(sb));
-               return -EIO;
+               goto error_out;
        }
        if (!IS_ALIGNED(btrfs_super_sectorsize(sb), 4096)) {
-               fprintf(stderr, "ERROR: sectorsize unaligned: %u\n",
-                       btrfs_super_sectorsize(sb));
-               return -EIO;
+               error("sectorsize unaligned: %u", btrfs_super_sectorsize(sb));
+               goto error_out;
+       }
+       if (btrfs_super_total_bytes(sb) == 0) {
+               error("invalid total_bytes 0");
+               goto error_out;
+       }
+       if (btrfs_super_bytes_used(sb) < 6 * btrfs_super_nodesize(sb)) {
+               error("invalid bytes_used %llu", btrfs_super_bytes_used(sb));
+               goto error_out;
+       }
+       if ((btrfs_super_stripesize(sb) != 4096)
+               && (btrfs_super_stripesize(sb) != btrfs_super_sectorsize(sb))) {
+               error("invalid stripesize %u", btrfs_super_stripesize(sb));
+               goto error_out;
        }
 
        if (memcmp(sb->fsid, sb->dev_item.fsid, BTRFS_UUID_SIZE) != 0) {
@@ -1476,23 +1488,22 @@ static int check_super(struct btrfs_super_block *sb)
 
                uuid_unparse(sb->fsid, fsid);
                uuid_unparse(sb->dev_item.fsid, dev_fsid);
-               printk(KERN_ERR
-                       "ERROR: dev_item UUID does not match fsid: %s != %s\n",
+               error("dev_item UUID does not match fsid: %s != %s",
                        dev_fsid, fsid);
-               return -EIO;
+               goto error_out;
        }
 
        /*
         * Hint to catch really bogus numbers, bitflips or so
         */
        if (btrfs_super_num_devices(sb) > (1UL << 31)) {
-               fprintf(stderr, "WARNING: suspicious number of devices: %llu\n",
+               warning("suspicious number of devices: %llu",
                        btrfs_super_num_devices(sb));
        }
 
        if (btrfs_super_num_devices(sb) == 0) {
-               fprintf(stderr, "ERROR: number of devices is 0\n");
-               return -EIO;
+               error("number of devices is 0");
+               goto error_out;
        }
 
        /*
@@ -1500,21 +1511,25 @@ static int check_super(struct btrfs_super_block *sb)
         * and one chunk
         */
        if (btrfs_super_sys_array_size(sb) > BTRFS_SYSTEM_CHUNK_ARRAY_SIZE) {
-               fprintf(stderr, "BTRFS: system chunk array too big %u > %u\n",
-                       btrfs_super_sys_array_size(sb),
-                       BTRFS_SYSTEM_CHUNK_ARRAY_SIZE);
-               return -EIO;
+               error("system chunk array too big %u > %u",
+                     btrfs_super_sys_array_size(sb),
+                     BTRFS_SYSTEM_CHUNK_ARRAY_SIZE);
+               goto error_out;
        }
        if (btrfs_super_sys_array_size(sb) < sizeof(struct btrfs_disk_key)
                        + sizeof(struct btrfs_chunk)) {
-               fprintf(stderr, "BTRFS: system chunk array too small %u < %lu\n",
-                       btrfs_super_sys_array_size(sb),
-                       sizeof(struct btrfs_disk_key) +
-                       sizeof(struct btrfs_chunk));
-               return -EIO;
+               error("system chunk array too small %u < %lu",
+                     btrfs_super_sys_array_size(sb),
+                     sizeof(struct btrfs_disk_key) +
+                     sizeof(struct btrfs_chunk));
+               goto error_out;
        }
 
        return 0;
+
+error_out:
+       error("superblock checksum matches but it has invalid members");
+       return -EIO;
 }
 
 int btrfs_read_dev_super(int fd, struct btrfs_super_block *sb, u64 sb_bytenr,