summaryrefslogtreecommitdiff
path: root/disk-io.c
diff options
context:
space:
mode:
Diffstat (limited to 'disk-io.c')
-rw-r--r--disk-io.c107
1 files changed, 62 insertions, 45 deletions
diff --git a/disk-io.c b/disk-io.c
index f1d46974..fbce506a 100644
--- a/disk-io.c
+++ b/disk-io.c
@@ -67,6 +67,11 @@ static int check_tree_block(struct btrfs_fs_info *fs_info,
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;
while (fs_devices) {
if (fs_info->ignore_fsid_mismatch ||
@@ -1262,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
@@ -1323,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);
@@ -1339,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;
@@ -1395,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];
@@ -1413,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) {
@@ -1474,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;
}
/*
@@ -1498,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,