summaryrefslogtreecommitdiff
path: root/lib/ext2fs/openfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ext2fs/openfs.c')
-rw-r--r--lib/ext2fs/openfs.c91
1 files changed, 75 insertions, 16 deletions
diff --git a/lib/ext2fs/openfs.c b/lib/ext2fs/openfs.c
index 220d954b..a1a35176 100644
--- a/lib/ext2fs/openfs.c
+++ b/lib/ext2fs/openfs.c
@@ -37,17 +37,24 @@ blk64_t ext2fs_descriptor_block_loc2(ext2_filsys fs, blk64_t group_block,
dgrp_t i)
{
int bg;
- int has_super = 0;
+ int has_super = 0, group_zero_adjust = 0;
blk64_t ret_blk;
+ /*
+ * On a bigalloc FS with 1K blocks, block 0 is reserved for non-ext4
+ * stuff, so adjust for that if we're being asked for group 0.
+ */
+ if (i == 0 && fs->blocksize == 1024 && EXT2FS_CLUSTER_RATIO(fs) > 1)
+ group_zero_adjust = 1;
+
if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) ||
(i < fs->super->s_first_meta_bg))
- return (group_block + i + 1);
+ return group_block + i + 1 + group_zero_adjust;
bg = EXT2_DESC_PER_BLOCK(fs->super) * i;
if (ext2fs_bg_has_super(fs, bg))
has_super = 1;
- ret_blk = ext2fs_group_first_block2(fs, bg) + has_super;
+ ret_blk = ext2fs_group_first_block2(fs, bg);
/*
* If group_block is not the normal value, we're trying to use
* the backup group descriptors and superblock --- so use the
@@ -57,10 +64,21 @@ blk64_t ext2fs_descriptor_block_loc2(ext2_filsys fs, blk64_t group_block,
* have the infrastructure in place to do that.
*/
if (group_block != fs->super->s_first_data_block &&
- ((ret_blk + fs->super->s_blocks_per_group) <
- ext2fs_blocks_count(fs->super)))
+ ((ret_blk + has_super + fs->super->s_blocks_per_group) <
+ ext2fs_blocks_count(fs->super))) {
ret_blk += fs->super->s_blocks_per_group;
- return ret_blk;
+
+ /*
+ * If we're going to jump forward a block group, make sure
+ * that we adjust has_super to account for the next group's
+ * backup superblock (or lack thereof).
+ */
+ if (ext2fs_bg_has_super(fs, bg + 1))
+ has_super = 1;
+ else
+ has_super = 0;
+ }
+ return ret_blk + has_super + group_zero_adjust;
}
blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, dgrp_t i)
@@ -99,10 +117,12 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
errcode_t retval;
unsigned long i, first_meta_bg;
__u32 features;
- unsigned int groups_per_block, blocks_per_group, io_flags;
+ unsigned int blocks_per_group, io_flags;
blk64_t group_block, blk;
char *dest, *cp;
+ int group_zero_adjust = 0;
#ifdef WORDS_BIGENDIAN
+ unsigned int groups_per_block;
struct ext2_group_desc *gdp;
int j;
#endif
@@ -144,7 +164,7 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
goto cleanup;
fs->image_io = fs->io;
fs->io->app_data = fs;
- retval = ext2fs_get_memalign(SUPERBLOCK_SIZE, 512, &fs->super);
+ retval = io_channel_alloc_buf(fs->io, -SUPERBLOCK_SIZE, &fs->super);
if (retval)
goto cleanup;
if (flags & EXT2_FLAG_IMAGE_FILE) {
@@ -252,6 +272,18 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
retval = EXT2_ET_CORRUPT_SUPERBLOCK;
goto cleanup;
}
+
+ /*
+ * bigalloc requires cluster-aware bitfield operations, which at the
+ * moment means we need EXT2_FLAG_64BITS.
+ */
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_BIGALLOC) &&
+ !(flags & EXT2_FLAG_64BITS)) {
+ retval = EXT2_ET_CANT_USE_LEGACY_BITMAPS;
+ goto cleanup;
+ }
+
if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
EXT4_FEATURE_RO_COMPAT_BIGALLOC) &&
(fs->super->s_log_block_size != fs->super->s_log_cluster_size)) {
@@ -329,16 +361,30 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
goto cleanup;
if (!group_block)
group_block = fs->super->s_first_data_block;
+ /*
+ * On a FS with a 1K blocksize, block 0 is reserved for bootloaders
+ * so we must increment block numbers to any group 0 items.
+ *
+ * However, we cannot touch group_block directly because in the meta_bg
+ * case, the ext2fs_descriptor_block_loc2() function will interpret
+ * group_block != s_first_data_block to mean that we want to access the
+ * backup group descriptors. This is not what we want if the caller
+ * set superblock == 0 (i.e. auto-detect the superblock), which is
+ * what's going on here.
+ */
if (group_block == 0 && fs->blocksize == 1024)
- group_block = 1; /* Deal with 1024 blocksize && bigalloc */
+ group_zero_adjust = 1;
dest = (char *) fs->group_desc;
+#ifdef WORDS_BIGENDIAN
groups_per_block = EXT2_DESC_PER_BLOCK(fs->super);
+#endif
if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
first_meta_bg = fs->super->s_first_meta_bg;
else
first_meta_bg = fs->desc_blocks;
if (first_meta_bg) {
- retval = io_channel_read_blk(fs->io, group_block+1,
+ retval = io_channel_read_blk(fs->io, group_block +
+ group_zero_adjust + 1,
first_meta_bg, dest);
if (retval)
goto cleanup;
@@ -388,9 +434,6 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
ext2fs_mark_super_dirty(fs);
}
- fs->flags &= ~EXT2_FLAG_NOFREE_ON_ERROR;
- *ret_fs = fs;
-
if ((fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_MMP) &&
!(flags & EXT2_FLAG_SKIP_MMP) &&
(flags & (EXT2_FLAG_RW | EXT2_FLAG_EXCLUSIVE))) {
@@ -402,12 +445,16 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
}
}
+ fs->flags &= ~EXT2_FLAG_NOFREE_ON_ERROR;
+ *ret_fs = fs;
+
return 0;
cleanup:
- if (flags & EXT2_FLAG_NOFREE_ON_ERROR)
- *ret_fs = fs;
- else
+ if (!(flags & EXT2_FLAG_NOFREE_ON_ERROR)) {
ext2fs_free(fs);
+ fs = NULL;
+ }
+ *ret_fs = fs;
return retval;
}
@@ -436,8 +483,20 @@ errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io)
errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io)
{
+ errcode_t err;
+
if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0)
return EXT2_ET_NOT_IMAGE_FILE;
+ err = io_channel_set_blksize(new_io, fs->blocksize);
+ if (err)
+ return err;
+ if ((new_io == fs->image_io) || (new_io == fs->io))
+ return 0;
+ if ((fs->image_io != fs->io) &&
+ fs->image_io)
+ io_channel_close(fs->image_io);
+ if (fs->io)
+ io_channel_close(fs->io);
fs->io = fs->image_io = new_io;
fs->flags |= EXT2_FLAG_DIRTY | EXT2_FLAG_RW |
EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY;