#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010
#define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040 /* Extents used */
#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080
+ #define EXT4_FEATURE_INCOMPAT_MMP 0x0100
#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200
+#define EXT4_FEATURE_INCOMPAT_EA_INODE 0x0400 /* EA in inode */
+#define EXT4_FEATURE_INCOMPAT_DIRDATA 0x1000 /* data in dirent */
+#define EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM 0x2000 /* use crc32c for bg */
+#define EXT4_FEATURE_INCOMPAT_LARGEDIR 0x4000 /* >2GB or 3-lvl htree */
+#define EXT4_FEATURE_INCOMPAT_INLINE_DATA 0x8000 /* data in inode */
/* The set of back-incompatible features this driver DOES support. Add (OR)
* flags here as the related features are implemented into the driver. */
#define EXT2_DRIVER_SUPPORTED_INCOMPAT ( EXT2_FEATURE_INCOMPAT_FILETYPE \
| EXT4_FEATURE_INCOMPAT_EXTENTS \
| EXT4_FEATURE_INCOMPAT_FLEX_BG \
- | EXT4_FEATURE_INCOMPAT_64BIT)
+ | EXT2_FEATURE_INCOMPAT_META_BG \
+ | EXT4_FEATURE_INCOMPAT_64BIT \
+ | EXT4_FEATURE_INCOMPAT_INLINE_DATA)
/* List of rationales for the ignored "incompatible" features:
* needs_recovery: Not really back-incompatible - was added as such to forbid
* ext2 drivers from mounting an ext3 volume with a dirty
\f
+ /* Check is a = b^x for some x. */
+ static inline int
+ is_power_of (grub_uint64_t a, grub_uint32_t b)
+ {
+ grub_uint64_t c;
+ /* Prevent overflow assuming b < 8. */
+ if (a >= (1LL << 60))
+ return 0;
+ for (c = 1; c <= a; c *= b);
+ return (c == a);
+ }
+
+
+ static inline int
+ group_has_super_block (struct grub_ext2_data *data, grub_uint64_t group)
+ {
+ if (!(data->sblock.feature_ro_compat
+ & grub_cpu_to_le32_compile_time(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));
+ }
+
+/* Inline data is stored using inline extended attributes. Attributes consist
+ of entry and value. Entries start after inode proper, following 4 bytes
+ magic header. Each entry is 4 bytes aligned, end of list is marked with
+ 4 bytes zero. Values are stored after entries.
+
+ Inline data is stored as system attribute with name "data". First part of
+ data is kept in space reserved for block pointers, so it is valid for value
+ size to be zero. Offset is apparently non-zero even in this case.
+ */
+inline static void
+grub_ext2_find_inline_data (struct grub_fshelp_node *node, grub_size_t isize,
+ struct grub_ext2_inode *inode)
+{
+ grub_size_t extra;
+ grub_uint8_t *ihdr;
+ struct grub_ext4_xattr_entry *entry;
+ grub_uint8_t *iend = (grub_uint8_t *) inode + isize - sizeof (grub_uint32_t);
+
+ node->inline_offs = 0;
+
+ if (isize < EXT2_GOOD_OLD_INODE_SIZE + sizeof (grub_uint16_t))
+ return;
+ extra = grub_le_to_cpu16 (*((grub_uint16_t *) inode
+ + (EXT2_GOOD_OLD_INODE_SIZE / 2)));
+ if (EXT2_GOOD_OLD_INODE_SIZE + extra + 4 > isize)
+ return;
+ ihdr = ((grub_uint8_t *) inode
+ + EXT2_GOOD_OLD_INODE_SIZE + extra);
+ if (grub_get_unaligned32 (ihdr)
+ != grub_cpu_to_le32_compile_time (EXT4_XATTR_MAGIC))
+ return;
+ entry = (struct grub_ext4_xattr_entry *) (ihdr + 4);
+ for (; (grub_uint8_t *) entry < iend && !IS_LAST_ENTRY(entry);
+ entry = EXT4_XATTR_NEXT(entry))
+ {
+ grub_size_t value_size;
+ grub_off_t value_offs;
+
+ if (entry->value_block)
+ continue;
+ if (entry->name_index != EXT4_XATTR_INDEX_SYSTEM)
+ continue;
+ if (entry->name_len != sizeof (EXT4_XATTR_SYSTEM_DATA) - 1)
+ continue;
+ if (grub_memcmp (entry->name, EXT4_XATTR_SYSTEM_DATA, sizeof (EXT4_XATTR_SYSTEM_DATA) - 1))
+ continue;
+ value_size = grub_le_to_cpu32 (entry->value_size);
+ value_offs = grub_le_to_cpu16 (entry->value_offs);
+ /* extra is additional size of base inode. Extended attributes start
+ after base inode, offset is calculated from the end of extended
+ attributes header.
+ */
+ if (EXT2_GOOD_OLD_INODE_SIZE + extra + 4 + value_offs + value_size > isize)
+ continue;
+ node->inline_offs = EXT2_GOOD_OLD_INODE_SIZE + extra + 4 + value_offs;
+ node->inline_size = value_size;
+ return;
+ }
+}
+
/* Read into BLKGRP the blockgroup descriptor of blockgroup GROUP of
the mounted filesystem DATA. */
inline static grub_err_t