summarylogtreecommitdiffstats
path: root/ext4_block_group.patch
diff options
context:
space:
mode:
Diffstat (limited to 'ext4_block_group.patch')
-rw-r--r--ext4_block_group.patch177
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 */