return crc32c(seed, data, len);
}
-void btrfs_csum_final(u32 crc, char *result)
+void btrfs_csum_final(u32 crc, u8 *result)
{
put_unaligned_le32(~crc, result);
}
static int __csum_tree_block_size(struct extent_buffer *buf, u16 csum_size,
int verify, int silent)
{
- char result[BTRFS_CSUM_SIZE];
+ u8 result[BTRFS_CSUM_SIZE];
u32 len;
u32 crc = ~(u32)0;
ret = 1;
out:
- clear_extent_buffer_uptodate(io_tree, eb);
+ clear_extent_buffer_uptodate(eb);
return ret;
}
* Such unaligned tree block will free overlapping extent buffer,
* causing use-after-free bugs for fuzzed images.
*/
- if (!IS_ALIGNED(bytenr, sectorsize)) {
+ if (bytenr < sectorsize || !IS_ALIGNED(bytenr, sectorsize)) {
error("tree block bytenr %llu is not aligned to sectorsize %u",
bytenr, sectorsize);
return ERR_PTR(-EIO);
}
- if (!IS_ALIGNED(blocksize, nodesize)) {
+ if (blocksize < nodesize || !IS_ALIGNED(blocksize, nodesize)) {
error("tree block size %u is not aligned to nodesize %u",
blocksize, nodesize);
return ERR_PTR(-EIO);
}
+
eb = btrfs_find_create_tree_block(fs_info, bytenr, blocksize);
if (!eb)
return ERR_PTR(-ENOMEM);
return write_and_map_eb(trans, root, eb);
}
-int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
+void btrfs_setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
u32 stripesize, struct btrfs_root *root,
struct btrfs_fs_info *fs_info, u64 objectid)
{
memset(&root->root_key, 0, sizeof(root->root_key));
memset(&root->root_item, 0, sizeof(root->root_item));
root->root_key.objectid = objectid;
- return 0;
}
static int update_cowonly_root(struct btrfs_trans_handle *trans,
u32 blocksize;
u64 generation;
- __setup_root(tree_root->nodesize, tree_root->leafsize,
+ btrfs_setup_root(tree_root->nodesize, tree_root->leafsize,
tree_root->sectorsize, tree_root->stripesize,
root, fs_info, objectid);
ret = btrfs_find_last_root(tree_root, objectid,
blocksize = tree_root->nodesize;
- __setup_root(tree_root->nodesize, tree_root->leafsize,
+ btrfs_setup_root(tree_root->nodesize, tree_root->leafsize,
tree_root->sectorsize, tree_root->stripesize,
log_root, fs_info, BTRFS_TREE_LOG_OBJECTID);
goto insert;
}
- __setup_root(tree_root->nodesize, tree_root->leafsize,
+ btrfs_setup_root(tree_root->nodesize, tree_root->leafsize,
tree_root->sectorsize, tree_root->stripesize,
root, fs_info, location->objectid);
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path) {
+ free(root);
+ return ERR_PTR(-ENOMEM);
+ }
+
ret = btrfs_search_slot(NULL, tree_root, location, path, 0, 0);
if (ret != 0) {
if (ret > 0)
return NULL;
}
-int btrfs_check_fs_compatibility(struct btrfs_super_block *sb, int writable)
+int btrfs_check_fs_compatibility(struct btrfs_super_block *sb,
+ unsigned int flags)
{
u64 features;
btrfs_set_super_incompat_flags(sb, features);
}
- features = btrfs_super_compat_ro_flags(sb) &
- ~BTRFS_FEATURE_COMPAT_RO_SUPP;
- if (writable && features) {
- printk("couldn't open RDWR because of unsupported "
- "option features (%Lx).\n",
- (unsigned long long)features);
- return -ENOTSUP;
+ features = btrfs_super_compat_ro_flags(sb);
+ if (flags & OPEN_CTREE_WRITES) {
+ if (flags & OPEN_CTREE_INVALIDATE_FST) {
+ /* Clear the FREE_SPACE_TREE_VALID bit on disk... */
+ features &= ~BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID;
+ btrfs_set_super_compat_ro_flags(sb, features);
+ /* ... and ignore the free space tree bit. */
+ features &= ~BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE;
+ }
+ if (features & ~BTRFS_FEATURE_COMPAT_RO_SUPP) {
+ printk("couldn't open RDWR because of unsupported "
+ "option features (%Lx).\n",
+ (unsigned long long)features);
+ return -ENOTSUP;
+ }
+
}
return 0;
}
btrfs_find_create_tree_block(fs_info, 0, nodesize);
if (!info_root->node)
return -ENOMEM;
- clear_extent_buffer_uptodate(NULL, info_root->node);
+ clear_extent_buffer_uptodate(info_root->node);
}
return 0;
stripesize = btrfs_super_stripesize(sb);
root = fs_info->tree_root;
- __setup_root(nodesize, leafsize, sectorsize, stripesize,
+ btrfs_setup_root(nodesize, leafsize, sectorsize, stripesize,
root, fs_info, BTRFS_ROOT_TREE_OBJECTID);
blocksize = root->nodesize;
generation = btrfs_super_generation(sb);
dev_size = seek_ret;
lseek(fd, 0, SEEK_SET);
if (sb_bytenr > dev_size) {
- fprintf(stderr, "Superblock bytenr is larger than device size\n");
+ error("superblock bytenr %llu is larger than device size %llu",
+ (unsigned long long)sb_bytenr,
+ (unsigned long long)dev_size);
return -EINVAL;
}
}
if (!skip_devices && total_devs != 1) {
- ret = btrfs_scan_lblkid();
+ ret = btrfs_scan_devices();
if (ret)
return ret;
}
sectorsize = btrfs_super_sectorsize(sb);
stripesize = btrfs_super_stripesize(sb);
- __setup_root(nodesize, leafsize, sectorsize, stripesize,
+ btrfs_setup_root(nodesize, leafsize, sectorsize, stripesize,
fs_info->chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID);
ret = btrfs_read_sys_array(fs_info->chunk_root);
memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE);
- ret = btrfs_check_fs_compatibility(fs_info->super_copy,
- flags & OPEN_CTREE_WRITES);
+ ret = btrfs_check_fs_compatibility(fs_info->super_copy, flags);
if (ret)
goto out_devices;
struct btrfs_fs_info *info;
/* This flags may not return fs_info with any valid root */
- BUG_ON(flags & OPEN_CTREE_IGNORE_CHUNK_TREE_ERROR);
+ if (flags & OPEN_CTREE_IGNORE_CHUNK_TREE_ERROR) {
+ error("invalid open_ctree flags: 0x%llx",
+ (unsigned long long)flags);
+ return NULL;
+ }
info = __open_ctree_fd(fp, path, sb_bytenr, 0, 0, flags);
if (!info)
return NULL;
*/
static int check_super(struct btrfs_super_block *sb, unsigned sbflags)
{
- char result[BTRFS_CSUM_SIZE];
+ u8 result[BTRFS_CSUM_SIZE];
u32 crc;
u16 csum_type;
int csum_size;
csum_type = btrfs_super_csum_type(sb);
if (csum_type >= ARRAY_SIZE(btrfs_csum_sizes)) {
- error("unsupported checksum algorithm %u\n", csum_type);
+ error("unsupported checksum algorithm %u", csum_type);
return -EIO;
}
csum_size = btrfs_csum_sizes[csum_type];
}
if (btrfs_super_sys_array_size(sb) < sizeof(struct btrfs_disk_key)
+ sizeof(struct btrfs_chunk)) {
- error("system chunk array too small %u < %lu",
+ error("system chunk array too small %u < %zu",
btrfs_super_sys_array_size(sb),
sizeof(struct btrfs_disk_key) +
sizeof(struct btrfs_chunk));
if (sb_bytenr != BTRFS_SUPER_INFO_OFFSET) {
ret = pread64(fd, buf, BTRFS_SUPER_INFO_SIZE, sb_bytenr);
+ /* real error */
+ if (ret < 0)
+ return -errno;
+
+ /* Not large enough sb, return -ENOENT instead of normal -EIO */
if (ret < BTRFS_SUPER_INFO_SIZE)
- return -1;
+ return -ENOENT;
if (btrfs_super_bytenr(buf) != sb_bytenr)
- return -1;
+ return -EIO;
- if (check_super(buf, sbflags))
- return -1;
+ ret = check_super(buf, sbflags);
+ if (ret < 0)
+ return ret;
memcpy(sb, buf, BTRFS_SUPER_INFO_SIZE);
return 0;
}
crc = ~(u32)0;
crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc,
BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
- btrfs_csum_final(crc, (char *)&sb->csum[0]);
+ btrfs_csum_final(crc, &sb->csum[0]);
/*
* super_copy is BTRFS_SUPER_INFO_SIZE bytes and is
crc = ~(u32)0;
crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc,
BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
- btrfs_csum_final(crc, (char *)&sb->csum[0]);
+ btrfs_csum_final(crc, &sb->csum[0]);
/*
* super_copy is BTRFS_SUPER_INFO_SIZE bytes and is