diff options
Diffstat (limited to 'ext4_block_group.patch')
-rw-r--r-- | ext4_block_group.patch | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/ext4_block_group.patch b/ext4_block_group.patch new file mode 100644 index 000000000000..37f161a08ffe --- /dev/null +++ b/ext4_block_group.patch @@ -0,0 +1,177 @@ +Description: Fix ext4 block group handling + Backported distantly from grub2, particularly: + https://git.savannah.gnu.org/cgit/grub.git/commit/?id=e20aa39ea4298011ba716087713cff26c6c52006 +Author: Colin Watson <cjwatson@debian.org> +Bug-Debian: https://bugs.debian.org/511121 +Last-Update: 2018-10-25 + +Index: b/stage2/fsys_ext2fs.c +=================================================================== +--- a/stage2/fsys_ext2fs.c ++++ b/stage2/fsys_ext2fs.c +@@ -41,6 +41,8 @@ + typedef unsigned short __u16; + typedef __signed__ int __s32; + typedef unsigned int __u32; ++typedef __signed__ long long __s64; ++typedef unsigned long long __u64; + + /* + * Constants relative to the data blocks, from ext2_fs.h +@@ -51,6 +53,13 @@ + #define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) + #define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) + ++/* Superblock filesystem feature flags (RO compatible) */ ++#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 ++ ++/* Superblock filesystem feature flags (back-incompatible) */ ++#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 ++#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 ++ + /* Inode flags */ + #define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ + +@@ -122,7 +131,7 @@ + __u32 s_hash_seed[4]; /* HTREE hash seed */ + __u8 s_def_hash_version; /* Default hash version to use */ + __u8 s_jnl_backup_type; /* Default type of journal backup */ +- __u16 s_reserved_word_pad; ++ __u16 s_desc_size; /* Group desc. size: INCOMPAT_64BIT */ + __u32 s_default_mount_opts; + __u32 s_first_meta_bg; /* First metablock group */ + __u32 s_mkfs_time; /* When the filesystem was created */ +@@ -284,13 +293,20 @@ + #define PATH_MAX 1024 /* include/linux/limits.h */ + #define MAX_LINK_COUNT 5 /* number of symbolic links to follow */ + ++/* Information about a "mounted" ext2 filesystem. */ ++struct ext2_data ++ { ++ int log_group_desc_size; ++ }; ++ + /* made up, these are pointers into FSYS_BUF */ + /* read once, always stays there: */ + #define SUPERBLOCK \ + ((struct ext2_super_block *)(FSYS_BUF)) ++#define FS_DATA \ ++ ((struct ext2_data *)((int)SUPERBLOCK + sizeof(struct ext2_super_block))) + #define GROUP_DESC \ +- ((struct ext2_group_desc *) \ +- ((int)SUPERBLOCK + sizeof(struct ext2_super_block))) ++ ((int)((int)FS_DATA + sizeof(struct ext2_data))) + #define INODE \ + ((struct ext2_inode *)((int)GROUP_DESC + EXT2_BLOCK_SIZE(SUPERBLOCK))) + #define DATABLOCK1 \ +@@ -357,6 +373,20 @@ + || SUPERBLOCK->s_magic != EXT2_SUPER_MAGIC) + retval = 0; + ++ if (SUPERBLOCK->s_rev_level != EXT2_GOOD_OLD_REV ++ && (SUPERBLOCK->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) ++ && SUPERBLOCK->s_desc_size != 0 ++ && (SUPERBLOCK->s_desc_size & (SUPERBLOCK->s_desc_size - 1)) == 0 ++ && (SUPERBLOCK->s_desc_size & 0x1fe0)) ++ { ++ __u16 b = SUPERBLOCK->s_desc_size; ++ for (FS_DATA->log_group_desc_size = 0; ++ b != (1 << FS_DATA->log_group_desc_size); ++ FS_DATA->log_group_desc_size++); ++ } ++ else ++ FS_DATA->log_group_desc_size = 5; ++ + return retval; + } + +@@ -623,6 +653,32 @@ + return INODE->i_blocks == ea_blocks; + } + ++static int ++is_power_of (__u64 a, __u64 b) ++{ ++ __u64 c; ++ /* Prevent overflow assuming b < 8. */ ++ if (a >= (1LL << 60)) ++ return 0; ++ for (c = 1; c <= a; c *= b); ++ return (c == a); ++} ++ ++static int ++ext2fs_group_has_super_block (__u64 group) ++{ ++ if (!(SUPERBLOCK->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) ++ return 1; ++ /* Algorithm looked up in Linux source. */ ++ if (group <= 1) ++ return 1; ++ /* Even number is never a power of odd number. */ ++ if (!(group & 1)) ++ return 0; ++ return (is_power_of (group, 7) || is_power_of (group, 5) || ++ is_power_of (group, 3)); ++} ++ + /* preconditions: ext2fs_mount already executed, therefore supblk in buffer + * known as SUPERBLOCK + * returns: 0 if error, nonzero iff we were able to find the file successfully +@@ -635,8 +691,9 @@ + { + int current_ino = EXT2_ROOT_INO; /* start at the root */ + int updir_ino = current_ino; /* the parent of the current directory */ +- int group_id; /* which group the inode is in */ +- int group_desc; /* fs pointer to that group */ ++ __u64 full_offset; ++ __u64 group_id; /* which group the inode is in */ ++ __u64 group_desc; /* fs pointer to that group */ + int desc; /* index within that group */ + int ino_blk; /* fs pointer of the inode's information */ + int str_chk = 0; /* used to hold the results of a string compare */ +@@ -675,23 +732,37 @@ + + /* look up an inode */ + group_id = (current_ino - 1) / (SUPERBLOCK->s_inodes_per_group); +- group_desc = group_id >> log2 (EXT2_DESC_PER_BLOCK (SUPERBLOCK)); +- desc = group_id & (EXT2_DESC_PER_BLOCK (SUPERBLOCK) - 1); ++ full_offset = group_id << FS_DATA->log_group_desc_size; ++ group_desc = full_offset >> EXT2_BLOCK_SIZE_BITS (SUPERBLOCK); ++ desc = full_offset & (EXT2_BLOCK_SIZE (SUPERBLOCK) - 1); ++ if ((SUPERBLOCK->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) ++ && group_desc >= SUPERBLOCK->s_first_meta_bg) ++ { ++ __u64 first_block_group; ++ /* Find the first block group for which a descriptor is stored in ++ the given block. */ ++ first_block_group = (group_desc << (EXT2_BLOCK_SIZE_BITS (SUPERBLOCK) ++ - FS_DATA->log_group_desc_size)); ++ group_desc = first_block_group * SUPERBLOCK->s_blocks_per_group; ++ if (ext2fs_group_has_super_block (first_block_group)) ++ group_desc++; ++ } ++ else ++ /* Superblock. */ ++ group_desc++; + #ifdef E2DEBUG + printf ("ipg=%d, dpb=%d\n", SUPERBLOCK->s_inodes_per_group, + EXT2_DESC_PER_BLOCK (SUPERBLOCK)); + printf ("group_id=%d group_desc=%d desc=%d\n", group_id, group_desc, desc); + #endif /* E2DEBUG */ +- if (!ext2_rdfsb ( +- (WHICH_SUPER + group_desc + SUPERBLOCK->s_first_data_block), +- (int) GROUP_DESC)) ++ if (!ext2_rdfsb (SUPERBLOCK->s_first_data_block + group_desc, GROUP_DESC)) + { + return 0; + } +- gdp = GROUP_DESC; ++ gdp = (struct ext2_group_desc *)(GROUP_DESC + desc); + inodes_per_block = EXT2_BLOCK_SIZE (SUPERBLOCK) / EXT2_INODE_SIZE(SUPERBLOCK); + inode_offset = ((current_ino - 1) % (SUPERBLOCK->s_inodes_per_group)); +- ino_blk = gdp[desc].bg_inode_table + (inode_offset / inodes_per_block); ++ ino_blk = gdp->bg_inode_table + (inode_offset / inodes_per_block); + #ifdef E2DEBUG + printf ("inode table fsblock=%d\n", ino_blk); + #endif /* E2DEBUG */ |