|
|
28f7f8 |
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
|
ecb9bb |
From: Jan Kara <jack@suse.cz>
|
|
|
ecb9bb |
Date: Mon, 1 Jun 2015 14:28:46 +0200
|
|
|
28f7f8 |
Subject: [PATCH] xfs: V5 filesystem format support
|
|
|
ecb9bb |
|
|
|
ecb9bb |
Add support for new XFS on disk format. We have to handle optional
|
|
|
ecb9bb |
filetype fields in directory entries, additional CRC, LSN, UUID entries
|
|
|
ecb9bb |
in some structures, etc.
|
|
|
ecb9bb |
|
|
|
ecb9bb |
Signed-off-by: Jan Kara <jack@suse.cz>
|
|
|
ecb9bb |
---
|
|
|
ecb9bb |
grub-core/fs/xfs.c | 332 ++++++++++++++++++++++++++++++++++++++++-------------
|
|
|
ecb9bb |
1 file changed, 252 insertions(+), 80 deletions(-)
|
|
|
ecb9bb |
|
|
|
ecb9bb |
diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c
|
|
|
28f7f8 |
index 26d8147a92e..f00e43e7dc3 100644
|
|
|
ecb9bb |
--- a/grub-core/fs/xfs.c
|
|
|
ecb9bb |
+++ b/grub-core/fs/xfs.c
|
|
|
ecb9bb |
@@ -34,6 +34,50 @@ GRUB_MOD_LICENSE ("GPLv3+");
|
|
|
ecb9bb |
#define XFS_INODE_FORMAT_EXT 2
|
|
|
ecb9bb |
#define XFS_INODE_FORMAT_BTREE 3
|
|
|
ecb9bb |
|
|
|
ecb9bb |
+/* Superblock version field flags */
|
|
|
ecb9bb |
+#define XFS_SB_VERSION_NUMBITS 0x000f
|
|
|
ecb9bb |
+#define XFS_SB_VERSION_ATTRBIT 0x0010
|
|
|
ecb9bb |
+#define XFS_SB_VERSION_NLINKBIT 0x0020
|
|
|
ecb9bb |
+#define XFS_SB_VERSION_QUOTABIT 0x0040
|
|
|
ecb9bb |
+#define XFS_SB_VERSION_ALIGNBIT 0x0080
|
|
|
ecb9bb |
+#define XFS_SB_VERSION_DALIGNBIT 0x0100
|
|
|
ecb9bb |
+#define XFS_SB_VERSION_LOGV2BIT 0x0400
|
|
|
ecb9bb |
+#define XFS_SB_VERSION_SECTORBIT 0x0800
|
|
|
ecb9bb |
+#define XFS_SB_VERSION_EXTFLGBIT 0x1000
|
|
|
ecb9bb |
+#define XFS_SB_VERSION_DIRV2BIT 0x2000
|
|
|
ecb9bb |
+#define XFS_SB_VERSION_MOREBITSBIT 0x8000
|
|
|
ecb9bb |
+#define XFS_SB_VERSION_BITS_SUPPORTED \
|
|
|
ecb9bb |
+ (XFS_SB_VERSION_NUMBITS | \
|
|
|
ecb9bb |
+ XFS_SB_VERSION_ATTRBIT | \
|
|
|
ecb9bb |
+ XFS_SB_VERSION_NLINKBIT | \
|
|
|
ecb9bb |
+ XFS_SB_VERSION_QUOTABIT | \
|
|
|
ecb9bb |
+ XFS_SB_VERSION_ALIGNBIT | \
|
|
|
ecb9bb |
+ XFS_SB_VERSION_DALIGNBIT | \
|
|
|
ecb9bb |
+ XFS_SB_VERSION_LOGV2BIT | \
|
|
|
ecb9bb |
+ XFS_SB_VERSION_SECTORBIT | \
|
|
|
ecb9bb |
+ XFS_SB_VERSION_EXTFLGBIT | \
|
|
|
ecb9bb |
+ XFS_SB_VERSION_DIRV2BIT | \
|
|
|
ecb9bb |
+ XFS_SB_VERSION_MOREBITSBIT)
|
|
|
ecb9bb |
+
|
|
|
ecb9bb |
+/* Recognized xfs format versions */
|
|
|
ecb9bb |
+#define XFS_SB_VERSION_4 4 /* Good old XFS filesystem */
|
|
|
ecb9bb |
+#define XFS_SB_VERSION_5 5 /* CRC enabled filesystem */
|
|
|
ecb9bb |
+
|
|
|
ecb9bb |
+/* features2 field flags */
|
|
|
ecb9bb |
+#define XFS_SB_VERSION2_LAZYSBCOUNTBIT 0x00000002 /* Superblk counters */
|
|
|
ecb9bb |
+#define XFS_SB_VERSION2_ATTR2BIT 0x00000008 /* Inline attr rework */
|
|
|
ecb9bb |
+#define XFS_SB_VERSION2_PROJID32BIT 0x00000080 /* 32-bit project ids */
|
|
|
ecb9bb |
+#define XFS_SB_VERSION2_FTYPE 0x00000200 /* inode type in dir */
|
|
|
ecb9bb |
+#define XFS_SB_VERSION2_BITS_SUPPORTED \
|
|
|
ecb9bb |
+ (XFS_SB_VERSION2_LAZYSBCOUNTBIT | \
|
|
|
ecb9bb |
+ XFS_SB_VERSION2_ATTR2BIT | \
|
|
|
ecb9bb |
+ XFS_SB_VERSION2_PROJID32BIT | \
|
|
|
ecb9bb |
+ XFS_SB_VERSION2_FTYPE)
|
|
|
ecb9bb |
+
|
|
|
ecb9bb |
+/* incompat feature flags */
|
|
|
ecb9bb |
+#define XFS_SB_FEAT_INCOMPAT_FTYPE (1 << 0) /* filetype in dirent */
|
|
|
ecb9bb |
+#define XFS_SB_FEAT_INCOMPAT_SUPPORTED \
|
|
|
ecb9bb |
+ (XFS_SB_FEAT_INCOMPAT_FTYPE)
|
|
|
ecb9bb |
|
|
|
ecb9bb |
struct grub_xfs_sblock
|
|
|
ecb9bb |
{
|
|
|
ecb9bb |
@@ -45,7 +89,9 @@ struct grub_xfs_sblock
|
|
|
ecb9bb |
grub_uint64_t rootino;
|
|
|
ecb9bb |
grub_uint8_t unused3[20];
|
|
|
ecb9bb |
grub_uint32_t agsize;
|
|
|
ecb9bb |
- grub_uint8_t unused4[20];
|
|
|
ecb9bb |
+ grub_uint8_t unused4[12];
|
|
|
ecb9bb |
+ grub_uint16_t version;
|
|
|
ecb9bb |
+ grub_uint8_t unused5[6];
|
|
|
ecb9bb |
grub_uint8_t label[12];
|
|
|
ecb9bb |
grub_uint8_t log2_bsize;
|
|
|
ecb9bb |
grub_uint8_t log2_sect;
|
|
|
ecb9bb |
@@ -54,12 +100,19 @@ struct grub_xfs_sblock
|
|
|
ecb9bb |
grub_uint8_t log2_agblk;
|
|
|
ecb9bb |
grub_uint8_t unused6[67];
|
|
|
ecb9bb |
grub_uint8_t log2_dirblk;
|
|
|
ecb9bb |
+ grub_uint8_t unused7[7];
|
|
|
ecb9bb |
+ grub_uint32_t features2;
|
|
|
ecb9bb |
+ grub_uint8_t unused8[4];
|
|
|
ecb9bb |
+ grub_uint32_t sb_features_compat;
|
|
|
ecb9bb |
+ grub_uint32_t sb_features_ro_compat;
|
|
|
ecb9bb |
+ grub_uint32_t sb_features_incompat;
|
|
|
ecb9bb |
+ grub_uint32_t sb_features_log_incompat;
|
|
|
ecb9bb |
} GRUB_PACKED;
|
|
|
ecb9bb |
|
|
|
ecb9bb |
struct grub_xfs_dir_header
|
|
|
ecb9bb |
{
|
|
|
ecb9bb |
grub_uint8_t count;
|
|
|
ecb9bb |
- grub_uint8_t smallino;
|
|
|
ecb9bb |
+ grub_uint8_t largeino;
|
|
|
ecb9bb |
union
|
|
|
ecb9bb |
{
|
|
|
ecb9bb |
grub_uint32_t i4;
|
|
|
ecb9bb |
@@ -67,14 +120,16 @@ struct grub_xfs_dir_header
|
|
|
ecb9bb |
} GRUB_PACKED parent;
|
|
|
ecb9bb |
} GRUB_PACKED;
|
|
|
ecb9bb |
|
|
|
ecb9bb |
+/* Structure for directory entry inlined in the inode */
|
|
|
ecb9bb |
struct grub_xfs_dir_entry
|
|
|
ecb9bb |
{
|
|
|
ecb9bb |
grub_uint8_t len;
|
|
|
ecb9bb |
grub_uint16_t offset;
|
|
|
ecb9bb |
char name[1];
|
|
|
ecb9bb |
- /* Inode number follows, 32 bits. */
|
|
|
ecb9bb |
+ /* Inode number follows, 32 / 64 bits. */
|
|
|
ecb9bb |
} GRUB_PACKED;
|
|
|
ecb9bb |
|
|
|
ecb9bb |
+/* Structure for directory entry in a block */
|
|
|
ecb9bb |
struct grub_xfs_dir2_entry
|
|
|
ecb9bb |
{
|
|
|
ecb9bb |
grub_uint64_t inode;
|
|
|
ecb9bb |
@@ -90,7 +145,8 @@ struct grub_xfs_btree_node
|
|
|
ecb9bb |
grub_uint16_t numrecs;
|
|
|
ecb9bb |
grub_uint64_t left;
|
|
|
ecb9bb |
grub_uint64_t right;
|
|
|
ecb9bb |
- grub_uint64_t keys[1];
|
|
|
ecb9bb |
+ /* In V5 here follow crc, uuid, etc. */
|
|
|
ecb9bb |
+ /* Then follow keys and block pointers */
|
|
|
ecb9bb |
} GRUB_PACKED;
|
|
|
ecb9bb |
|
|
|
ecb9bb |
struct grub_xfs_btree_root
|
|
|
ecb9bb |
@@ -123,19 +179,11 @@ struct grub_xfs_inode
|
|
|
ecb9bb |
grub_uint16_t unused3;
|
|
|
ecb9bb |
grub_uint8_t fork_offset;
|
|
|
ecb9bb |
grub_uint8_t unused4[17];
|
|
|
ecb9bb |
- union
|
|
|
ecb9bb |
- {
|
|
|
ecb9bb |
- char raw[156];
|
|
|
ecb9bb |
- struct dir
|
|
|
ecb9bb |
- {
|
|
|
ecb9bb |
- struct grub_xfs_dir_header dirhead;
|
|
|
ecb9bb |
- struct grub_xfs_dir_entry direntry[1];
|
|
|
ecb9bb |
- } dir;
|
|
|
ecb9bb |
- grub_xfs_extent extents[XFS_INODE_EXTENTS];
|
|
|
ecb9bb |
- struct grub_xfs_btree_root btree;
|
|
|
ecb9bb |
- } GRUB_PACKED data;
|
|
|
ecb9bb |
} GRUB_PACKED;
|
|
|
ecb9bb |
|
|
|
ecb9bb |
+#define XFS_V2_INODE_SIZE sizeof(struct grub_xfs_inode)
|
|
|
ecb9bb |
+#define XFS_V3_INODE_SIZE (XFS_V2_INODE_SIZE + 76)
|
|
|
ecb9bb |
+
|
|
|
ecb9bb |
struct grub_xfs_dirblock_tail
|
|
|
ecb9bb |
{
|
|
|
ecb9bb |
grub_uint32_t leaf_count;
|
|
|
ecb9bb |
@@ -157,6 +205,8 @@ struct grub_xfs_data
|
|
|
ecb9bb |
int pos;
|
|
|
ecb9bb |
int bsize;
|
|
|
ecb9bb |
grub_uint32_t agsize;
|
|
|
ecb9bb |
+ unsigned int hasftype:1;
|
|
|
ecb9bb |
+ unsigned int hascrc:1;
|
|
|
ecb9bb |
struct grub_fshelp_node diropen;
|
|
|
ecb9bb |
};
|
|
|
ecb9bb |
|
|
|
ecb9bb |
@@ -164,6 +214,71 @@ static grub_dl_t my_mod;
|
|
|
ecb9bb |
|
|
|
ecb9bb |
|
|
|
ecb9bb |
|
|
|
ecb9bb |
+static int grub_xfs_sb_hascrc(struct grub_xfs_data *data)
|
|
|
ecb9bb |
+{
|
|
|
ecb9bb |
+ return (data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
|
|
|
ecb9bb |
+ grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5);
|
|
|
ecb9bb |
+}
|
|
|
ecb9bb |
+
|
|
|
ecb9bb |
+static int grub_xfs_sb_hasftype(struct grub_xfs_data *data)
|
|
|
ecb9bb |
+{
|
|
|
ecb9bb |
+ if ((data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
|
|
|
ecb9bb |
+ grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5) &&
|
|
|
ecb9bb |
+ data->sblock.sb_features_incompat & grub_cpu_to_be32_compile_time(XFS_SB_FEAT_INCOMPAT_FTYPE))
|
|
|
ecb9bb |
+ return 1;
|
|
|
ecb9bb |
+ if (data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_MOREBITSBIT) &&
|
|
|
ecb9bb |
+ data->sblock.features2 & grub_cpu_to_be32_compile_time(XFS_SB_VERSION2_FTYPE))
|
|
|
ecb9bb |
+ return 1;
|
|
|
ecb9bb |
+ return 0;
|
|
|
ecb9bb |
+}
|
|
|
ecb9bb |
+
|
|
|
ecb9bb |
+static int grub_xfs_sb_valid(struct grub_xfs_data *data)
|
|
|
ecb9bb |
+{
|
|
|
ecb9bb |
+ grub_dprintf("xfs", "Validating superblock\n");
|
|
|
ecb9bb |
+ if (grub_strncmp ((char *) (data->sblock.magic), "XFSB", 4)
|
|
|
ecb9bb |
+ || data->sblock.log2_bsize < GRUB_DISK_SECTOR_BITS
|
|
|
ecb9bb |
+ || ((int) data->sblock.log2_bsize
|
|
|
ecb9bb |
+ + (int) data->sblock.log2_dirblk) >= 27)
|
|
|
ecb9bb |
+ {
|
|
|
ecb9bb |
+ grub_error (GRUB_ERR_BAD_FS, "not a XFS filesystem");
|
|
|
ecb9bb |
+ return 0;
|
|
|
ecb9bb |
+ }
|
|
|
ecb9bb |
+ if ((data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
|
|
|
ecb9bb |
+ grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5))
|
|
|
ecb9bb |
+ {
|
|
|
ecb9bb |
+ grub_dprintf("xfs", "XFS v5 superblock detected\n");
|
|
|
ecb9bb |
+ if (data->sblock.sb_features_incompat &
|
|
|
ecb9bb |
+ grub_cpu_to_be32_compile_time(~XFS_SB_FEAT_INCOMPAT_SUPPORTED))
|
|
|
ecb9bb |
+ {
|
|
|
ecb9bb |
+ grub_error (GRUB_ERR_BAD_FS, "XFS filesystem has unsupported "
|
|
|
ecb9bb |
+ "incompatible features");
|
|
|
ecb9bb |
+ return 0;
|
|
|
ecb9bb |
+ }
|
|
|
ecb9bb |
+ return 1;
|
|
|
ecb9bb |
+ }
|
|
|
ecb9bb |
+ else if ((data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
|
|
|
ecb9bb |
+ grub_cpu_to_be16_compile_time(XFS_SB_VERSION_4))
|
|
|
ecb9bb |
+ {
|
|
|
ecb9bb |
+ grub_dprintf("xfs", "XFS v4 superblock detected\n");
|
|
|
ecb9bb |
+ if (!(data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_DIRV2BIT)))
|
|
|
ecb9bb |
+ {
|
|
|
ecb9bb |
+ grub_error (GRUB_ERR_BAD_FS, "XFS filesystem without V2 directories "
|
|
|
ecb9bb |
+ "is unsupported");
|
|
|
ecb9bb |
+ return 0;
|
|
|
ecb9bb |
+ }
|
|
|
ecb9bb |
+ if (data->sblock.version & grub_cpu_to_be16_compile_time(~XFS_SB_VERSION_BITS_SUPPORTED) ||
|
|
|
ecb9bb |
+ (data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_MOREBITSBIT) &&
|
|
|
ecb9bb |
+ data->sblock.features2 & grub_cpu_to_be16_compile_time(~XFS_SB_VERSION2_BITS_SUPPORTED)))
|
|
|
ecb9bb |
+ {
|
|
|
ecb9bb |
+ grub_error (GRUB_ERR_BAD_FS, "XFS filesystem has unsupported version "
|
|
|
ecb9bb |
+ "bits");
|
|
|
ecb9bb |
+ return 0;
|
|
|
ecb9bb |
+ }
|
|
|
ecb9bb |
+ return 1;
|
|
|
ecb9bb |
+ }
|
|
|
ecb9bb |
+ return 0;
|
|
|
ecb9bb |
+}
|
|
|
ecb9bb |
+
|
|
|
ecb9bb |
/* Filetype information as used in inodes. */
|
|
|
ecb9bb |
#define FILETYPE_INO_MASK 0170000
|
|
|
ecb9bb |
#define FILETYPE_INO_REG 0100000
|
|
|
ecb9bb |
@@ -219,18 +334,6 @@ GRUB_XFS_EXTENT_SIZE (grub_xfs_extent *exts, int ex)
|
|
|
ecb9bb |
return (grub_be_to_cpu32 (exts[ex][3]) & ((1 << 21) - 1));
|
|
|
ecb9bb |
}
|
|
|
ecb9bb |
|
|
|
ecb9bb |
-static inline int
|
|
|
ecb9bb |
-GRUB_XFS_ROUND_TO_DIRENT (int pos)
|
|
|
ecb9bb |
-{
|
|
|
ecb9bb |
- return ((((pos) + 8 - 1) / 8) * 8);
|
|
|
ecb9bb |
-}
|
|
|
ecb9bb |
-
|
|
|
ecb9bb |
-static inline int
|
|
|
ecb9bb |
-GRUB_XFS_NEXT_DIRENT (int pos, int len)
|
|
|
ecb9bb |
-{
|
|
|
ecb9bb |
- return (pos) + GRUB_XFS_ROUND_TO_DIRENT (8 + 1 + len + 2);
|
|
|
ecb9bb |
-}
|
|
|
ecb9bb |
-
|
|
|
ecb9bb |
|
|
|
ecb9bb |
static inline grub_uint64_t
|
|
|
ecb9bb |
grub_xfs_inode_block (struct grub_xfs_data *data,
|
|
|
ecb9bb |
@@ -274,6 +377,85 @@ grub_xfs_fshelp_size(struct grub_xfs_data *data)
|
|
|
ecb9bb |
+ grub_xfs_inode_size(data);
|
|
|
ecb9bb |
}
|
|
|
ecb9bb |
|
|
|
ecb9bb |
+static void *
|
|
|
ecb9bb |
+grub_xfs_inode_data(struct grub_xfs_inode *inode)
|
|
|
ecb9bb |
+{
|
|
|
ecb9bb |
+ if (inode->version <= 2)
|
|
|
ecb9bb |
+ return ((char *)inode) + XFS_V2_INODE_SIZE;
|
|
|
ecb9bb |
+ return ((char *)inode) + XFS_V3_INODE_SIZE;
|
|
|
ecb9bb |
+}
|
|
|
ecb9bb |
+
|
|
|
ecb9bb |
+static struct grub_xfs_dir_entry *
|
|
|
ecb9bb |
+grub_xfs_inline_de(struct grub_xfs_dir_header *head)
|
|
|
ecb9bb |
+{
|
|
|
ecb9bb |
+ /*
|
|
|
ecb9bb |
+ * With small inode numbers the header is 4 bytes smaller because of
|
|
|
ecb9bb |
+ * smaller parent pointer
|
|
|
ecb9bb |
+ */
|
|
|
ecb9bb |
+ return (void *)(((char *)head) + sizeof(struct grub_xfs_dir_header) -
|
|
|
ecb9bb |
+ (head->largeino ? 0 : sizeof(grub_uint32_t)));
|
|
|
ecb9bb |
+}
|
|
|
ecb9bb |
+
|
|
|
ecb9bb |
+static grub_uint8_t *
|
|
|
ecb9bb |
+grub_xfs_inline_de_inopos(struct grub_xfs_data *data,
|
|
|
ecb9bb |
+ struct grub_xfs_dir_entry *de)
|
|
|
ecb9bb |
+{
|
|
|
ecb9bb |
+ return ((grub_uint8_t *)(de + 1)) + de->len - 1 +
|
|
|
ecb9bb |
+ (data->hasftype ? 1 : 0);
|
|
|
ecb9bb |
+}
|
|
|
ecb9bb |
+
|
|
|
ecb9bb |
+static struct grub_xfs_dir_entry *
|
|
|
ecb9bb |
+grub_xfs_inline_next_de(struct grub_xfs_data *data,
|
|
|
ecb9bb |
+ struct grub_xfs_dir_header *head,
|
|
|
ecb9bb |
+ struct grub_xfs_dir_entry *de)
|
|
|
ecb9bb |
+{
|
|
|
ecb9bb |
+ char *p = (char *)de + sizeof(struct grub_xfs_dir_entry) - 1 + de->len;
|
|
|
ecb9bb |
+
|
|
|
ecb9bb |
+ p += head->largeino ? sizeof(grub_uint64_t) : sizeof(grub_uint32_t);
|
|
|
ecb9bb |
+ if (data->hasftype)
|
|
|
ecb9bb |
+ p++;
|
|
|
ecb9bb |
+
|
|
|
ecb9bb |
+ return (struct grub_xfs_dir_entry *)p;
|
|
|
ecb9bb |
+}
|
|
|
ecb9bb |
+
|
|
|
ecb9bb |
+static struct grub_xfs_dirblock_tail *
|
|
|
ecb9bb |
+grub_xfs_dir_tail(struct grub_xfs_data *data, void *dirblock)
|
|
|
ecb9bb |
+{
|
|
|
ecb9bb |
+ int dirblksize = 1 << (data->sblock.log2_bsize + data->sblock.log2_dirblk);
|
|
|
ecb9bb |
+
|
|
|
ecb9bb |
+ return (struct grub_xfs_dirblock_tail *)
|
|
|
ecb9bb |
+ ((char *)dirblock + dirblksize - sizeof (struct grub_xfs_dirblock_tail));
|
|
|
ecb9bb |
+}
|
|
|
ecb9bb |
+
|
|
|
ecb9bb |
+static struct grub_xfs_dir2_entry *
|
|
|
ecb9bb |
+grub_xfs_first_de(struct grub_xfs_data *data, void *dirblock)
|
|
|
ecb9bb |
+{
|
|
|
ecb9bb |
+ if (data->hascrc)
|
|
|
ecb9bb |
+ return (struct grub_xfs_dir2_entry *)((char *)dirblock + 64);
|
|
|
ecb9bb |
+ return (struct grub_xfs_dir2_entry *)((char *)dirblock + 16);
|
|
|
ecb9bb |
+}
|
|
|
ecb9bb |
+
|
|
|
ecb9bb |
+static struct grub_xfs_dir2_entry *
|
|
|
ecb9bb |
+grub_xfs_next_de(struct grub_xfs_data *data, struct grub_xfs_dir2_entry *de)
|
|
|
ecb9bb |
+{
|
|
|
ecb9bb |
+ int size = sizeof (struct grub_xfs_dir2_entry) + de->len + 2 /* Tag */;
|
|
|
ecb9bb |
+
|
|
|
ecb9bb |
+ if (data->hasftype)
|
|
|
ecb9bb |
+ size++; /* File type */
|
|
|
ecb9bb |
+ return (struct grub_xfs_dir2_entry *)(((char *)de) + ALIGN_UP(size, 8));
|
|
|
ecb9bb |
+}
|
|
|
ecb9bb |
+
|
|
|
ecb9bb |
+static grub_uint64_t *
|
|
|
ecb9bb |
+grub_xfs_btree_keys(struct grub_xfs_data *data,
|
|
|
ecb9bb |
+ struct grub_xfs_btree_node *leaf)
|
|
|
ecb9bb |
+{
|
|
|
ecb9bb |
+ grub_uint64_t *keys = (grub_uint64_t *)(leaf + 1);
|
|
|
ecb9bb |
+
|
|
|
ecb9bb |
+ if (data->hascrc)
|
|
|
ecb9bb |
+ keys += 6; /* skip crc, uuid, ... */
|
|
|
ecb9bb |
+ return keys;
|
|
|
ecb9bb |
+}
|
|
|
ecb9bb |
+
|
|
|
ecb9bb |
static grub_err_t
|
|
|
ecb9bb |
grub_xfs_read_inode (struct grub_xfs_data *data, grub_uint64_t ino,
|
|
|
ecb9bb |
struct grub_xfs_inode *inode)
|
|
|
ecb9bb |
@@ -281,6 +463,8 @@ grub_xfs_read_inode (struct grub_xfs_data *data, grub_uint64_t ino,
|
|
|
ecb9bb |
grub_uint64_t block = grub_xfs_inode_block (data, ino);
|
|
|
ecb9bb |
int offset = grub_xfs_inode_offset (data, ino);
|
|
|
ecb9bb |
|
|
|
ecb9bb |
+ grub_dprintf("xfs", "Reading inode (%"PRIuGRUB_UINT64_T") - %"PRIuGRUB_UINT64_T", %d\n",
|
|
|
ecb9bb |
+ ino, block, offset);
|
|
|
ecb9bb |
/* Read the inode. */
|
|
|
ecb9bb |
if (grub_disk_read (data->disk, block, offset, grub_xfs_inode_size(data),
|
|
|
ecb9bb |
inode))
|
|
|
ecb9bb |
@@ -303,6 +487,7 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
|
|
|
ecb9bb |
|
|
|
ecb9bb |
if (node->inode.format == XFS_INODE_FORMAT_BTREE)
|
|
|
ecb9bb |
{
|
|
|
ecb9bb |
+ struct grub_xfs_btree_root *root;
|
|
|
ecb9bb |
const grub_uint64_t *keys;
|
|
|
ecb9bb |
int recoffset;
|
|
|
ecb9bb |
|
|
|
ecb9bb |
@@ -310,15 +495,15 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
|
|
|
ecb9bb |
if (leaf == 0)
|
|
|
ecb9bb |
return 0;
|
|
|
ecb9bb |
|
|
|
ecb9bb |
- nrec = grub_be_to_cpu16 (node->inode.data.btree.numrecs);
|
|
|
ecb9bb |
- keys = &node->inode.data.btree.keys[0];
|
|
|
ecb9bb |
+ root = grub_xfs_inode_data(&node->inode);
|
|
|
ecb9bb |
+ nrec = grub_be_to_cpu16 (root->numrecs);
|
|
|
ecb9bb |
+ keys = &root->keys[0];
|
|
|
ecb9bb |
if (node->inode.fork_offset)
|
|
|
ecb9bb |
recoffset = (node->inode.fork_offset - 1) / 2;
|
|
|
ecb9bb |
else
|
|
|
ecb9bb |
recoffset = (grub_xfs_inode_size(node->data)
|
|
|
ecb9bb |
- - ((char *) &node->inode.data.btree.keys
|
|
|
ecb9bb |
- - (char *) &node->inode))
|
|
|
ecb9bb |
- / (2 * sizeof (grub_uint64_t));
|
|
|
ecb9bb |
+ - ((char *) keys - (char *) &node->inode))
|
|
|
ecb9bb |
+ / (2 * sizeof (grub_uint64_t));
|
|
|
ecb9bb |
do
|
|
|
ecb9bb |
{
|
|
|
ecb9bb |
int i;
|
|
|
ecb9bb |
@@ -340,7 +525,10 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
|
|
|
ecb9bb |
0, node->data->bsize, leaf))
|
|
|
ecb9bb |
return 0;
|
|
|
ecb9bb |
|
|
|
ecb9bb |
- if (grub_strncmp ((char *) leaf->magic, "BMAP", 4))
|
|
|
ecb9bb |
+ if ((!node->data->hascrc &&
|
|
|
ecb9bb |
+ grub_strncmp ((char *) leaf->magic, "BMAP", 4)) ||
|
|
|
ecb9bb |
+ (node->data->hascrc &&
|
|
|
ecb9bb |
+ grub_strncmp ((char *) leaf->magic, "BMA3", 4)))
|
|
|
ecb9bb |
{
|
|
|
ecb9bb |
grub_free (leaf);
|
|
|
ecb9bb |
grub_error (GRUB_ERR_BAD_FS, "not a correct XFS BMAP node");
|
|
|
ecb9bb |
@@ -348,8 +536,8 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
|
|
|
ecb9bb |
}
|
|
|
ecb9bb |
|
|
|
ecb9bb |
nrec = grub_be_to_cpu16 (leaf->numrecs);
|
|
|
ecb9bb |
- keys = &leaf->keys[0];
|
|
|
ecb9bb |
- recoffset = ((node->data->bsize - ((char *) &leaf->keys
|
|
|
ecb9bb |
+ keys = grub_xfs_btree_keys(node->data, leaf);
|
|
|
ecb9bb |
+ recoffset = ((node->data->bsize - ((char *) keys
|
|
|
ecb9bb |
- (char *) leaf))
|
|
|
ecb9bb |
/ (2 * sizeof (grub_uint64_t)));
|
|
|
ecb9bb |
}
|
|
|
ecb9bb |
@@ -359,7 +547,7 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
|
|
|
ecb9bb |
else if (node->inode.format == XFS_INODE_FORMAT_EXT)
|
|
|
ecb9bb |
{
|
|
|
ecb9bb |
nrec = grub_be_to_cpu32 (node->inode.nextents);
|
|
|
ecb9bb |
- exts = &node->inode.data.extents[0];
|
|
|
ecb9bb |
+ exts = grub_xfs_inode_data(&node->inode);
|
|
|
ecb9bb |
}
|
|
|
ecb9bb |
else
|
|
|
ecb9bb |
{
|
|
|
ecb9bb |
@@ -417,7 +605,7 @@ grub_xfs_read_symlink (grub_fshelp_node_t node)
|
|
|
ecb9bb |
switch (node->inode.format)
|
|
|
ecb9bb |
{
|
|
|
ecb9bb |
case XFS_INODE_FORMAT_INO:
|
|
|
ecb9bb |
- return grub_strndup (node->inode.data.raw, size);
|
|
|
ecb9bb |
+ return grub_strndup (grub_xfs_inode_data(&node->inode), size);
|
|
|
ecb9bb |
|
|
|
ecb9bb |
case XFS_INODE_FORMAT_EXT:
|
|
|
ecb9bb |
{
|
|
|
ecb9bb |
@@ -512,23 +700,18 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir,
|
|
|
ecb9bb |
{
|
|
|
ecb9bb |
case XFS_INODE_FORMAT_INO:
|
|
|
ecb9bb |
{
|
|
|
ecb9bb |
- struct grub_xfs_dir_entry *de = &diro->inode.data.dir.direntry[0];
|
|
|
ecb9bb |
- int smallino = !diro->inode.data.dir.dirhead.smallino;
|
|
|
ecb9bb |
+ struct grub_xfs_dir_header *head = grub_xfs_inode_data(&diro->inode);
|
|
|
ecb9bb |
+ struct grub_xfs_dir_entry *de = grub_xfs_inline_de(head);
|
|
|
ecb9bb |
+ int smallino = !head->largeino;
|
|
|
ecb9bb |
int i;
|
|
|
ecb9bb |
grub_uint64_t parent;
|
|
|
ecb9bb |
|
|
|
ecb9bb |
/* If small inode numbers are used to pack the direntry, the
|
|
|
ecb9bb |
parent inode number is small too. */
|
|
|
ecb9bb |
if (smallino)
|
|
|
ecb9bb |
- {
|
|
|
ecb9bb |
- parent = grub_be_to_cpu32 (diro->inode.data.dir.dirhead.parent.i4);
|
|
|
ecb9bb |
- /* The header is a bit smaller than usual. */
|
|
|
ecb9bb |
- de = (struct grub_xfs_dir_entry *) ((char *) de - 4);
|
|
|
ecb9bb |
- }
|
|
|
ecb9bb |
+ parent = grub_be_to_cpu32 (head->parent.i4);
|
|
|
ecb9bb |
else
|
|
|
ecb9bb |
- {
|
|
|
ecb9bb |
- parent = grub_be_to_cpu64(diro->inode.data.dir.dirhead.parent.i8);
|
|
|
ecb9bb |
- }
|
|
|
ecb9bb |
+ parent = grub_be_to_cpu64 (head->parent.i8);
|
|
|
ecb9bb |
|
|
|
ecb9bb |
/* Synthesize the direntries for `.' and `..'. */
|
|
|
ecb9bb |
if (iterate_dir_call_hook (diro->ino, ".", &ctx))
|
|
|
ecb9bb |
@@ -537,12 +720,10 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir,
|
|
|
ecb9bb |
if (iterate_dir_call_hook (parent, "..", &ctx))
|
|
|
ecb9bb |
return 1;
|
|
|
ecb9bb |
|
|
|
ecb9bb |
- for (i = 0; i < diro->inode.data.dir.dirhead.count; i++)
|
|
|
ecb9bb |
+ for (i = 0; i < head->count; i++)
|
|
|
ecb9bb |
{
|
|
|
ecb9bb |
grub_uint64_t ino;
|
|
|
ecb9bb |
- grub_uint8_t *inopos = (((grub_uint8_t *) de)
|
|
|
ecb9bb |
- + sizeof (struct grub_xfs_dir_entry)
|
|
|
ecb9bb |
- + de->len - 1);
|
|
|
ecb9bb |
+ grub_uint8_t *inopos = grub_xfs_inline_de_inopos(dir->data, de);
|
|
|
ecb9bb |
grub_uint8_t c;
|
|
|
ecb9bb |
|
|
|
ecb9bb |
/* inopos might be unaligned. */
|
|
|
ecb9bb |
@@ -567,10 +748,7 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir,
|
|
|
ecb9bb |
return 1;
|
|
|
ecb9bb |
de->name[de->len] = c;
|
|
|
ecb9bb |
|
|
|
ecb9bb |
- de = ((struct grub_xfs_dir_entry *)
|
|
|
ecb9bb |
- (((char *) de)+ sizeof (struct grub_xfs_dir_entry) + de->len
|
|
|
ecb9bb |
- + ((smallino ? sizeof (grub_uint32_t)
|
|
|
ecb9bb |
- : sizeof (grub_uint64_t))) - 1));
|
|
|
ecb9bb |
+ de = grub_xfs_inline_next_de(dir->data, head, de);
|
|
|
ecb9bb |
}
|
|
|
ecb9bb |
break;
|
|
|
ecb9bb |
}
|
|
|
ecb9bb |
@@ -597,15 +775,11 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir,
|
|
|
ecb9bb |
>> dirblk_log2);
|
|
|
ecb9bb |
blk++)
|
|
|
ecb9bb |
{
|
|
|
ecb9bb |
- /* The header is skipped, the first direntry is stored
|
|
|
ecb9bb |
- from byte 16. */
|
|
|
ecb9bb |
- int pos = 16;
|
|
|
ecb9bb |
+ struct grub_xfs_dir2_entry *direntry =
|
|
|
ecb9bb |
+ grub_xfs_first_de(dir->data, dirblock);
|
|
|
ecb9bb |
int entries;
|
|
|
ecb9bb |
- int tail_start = (dirblk_size
|
|
|
ecb9bb |
- - sizeof (struct grub_xfs_dirblock_tail));
|
|
|
ecb9bb |
-
|
|
|
ecb9bb |
- struct grub_xfs_dirblock_tail *tail;
|
|
|
ecb9bb |
- tail = (struct grub_xfs_dirblock_tail *) &dirblock[tail_start];
|
|
|
ecb9bb |
+ struct grub_xfs_dirblock_tail *tail =
|
|
|
ecb9bb |
+ grub_xfs_dir_tail(dir->data, dirblock);
|
|
|
ecb9bb |
|
|
|
ecb9bb |
numread = grub_xfs_read_file (dir, 0, 0,
|
|
|
ecb9bb |
blk << dirblk_log2,
|
|
|
ecb9bb |
@@ -617,13 +791,11 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir,
|
|
|
ecb9bb |
- grub_be_to_cpu32 (tail->leaf_stale));
|
|
|
ecb9bb |
|
|
|
ecb9bb |
/* Iterate over all entries within this block. */
|
|
|
ecb9bb |
- while (pos < tail_start)
|
|
|
ecb9bb |
+ while ((char *)direntry < (char *)tail)
|
|
|
ecb9bb |
{
|
|
|
ecb9bb |
- struct grub_xfs_dir2_entry *direntry;
|
|
|
ecb9bb |
grub_uint8_t *freetag;
|
|
|
ecb9bb |
char *filename;
|
|
|
ecb9bb |
|
|
|
ecb9bb |
- direntry = (struct grub_xfs_dir2_entry *) &dirblock[pos];
|
|
|
ecb9bb |
freetag = (grub_uint8_t *) direntry;
|
|
|
ecb9bb |
|
|
|
ecb9bb |
if (grub_get_unaligned16 (freetag) == 0XFFFF)
|
|
|
ecb9bb |
@@ -631,14 +803,16 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir,
|
|
|
ecb9bb |
grub_uint8_t *skip = (freetag + sizeof (grub_uint16_t));
|
|
|
ecb9bb |
|
|
|
ecb9bb |
/* This entry is not used, go to the next one. */
|
|
|
ecb9bb |
- pos += grub_be_to_cpu16 (grub_get_unaligned16 (skip));
|
|
|
ecb9bb |
+ direntry = (struct grub_xfs_dir2_entry *)
|
|
|
ecb9bb |
+ (((char *)direntry) +
|
|
|
ecb9bb |
+ grub_be_to_cpu16 (grub_get_unaligned16 (skip)));
|
|
|
ecb9bb |
|
|
|
ecb9bb |
continue;
|
|
|
ecb9bb |
}
|
|
|
ecb9bb |
|
|
|
ecb9bb |
- filename = &dirblock[pos + sizeof (*direntry)];
|
|
|
ecb9bb |
- /* The byte after the filename is for the tag, which
|
|
|
ecb9bb |
- is not used by GRUB. So it can be overwritten. */
|
|
|
ecb9bb |
+ filename = (char *)(direntry + 1);
|
|
|
ecb9bb |
+ /* The byte after the filename is for the filetype, padding, or
|
|
|
ecb9bb |
+ tag, which is not used by GRUB. So it can be overwritten. */
|
|
|
ecb9bb |
filename[direntry->len] = '\0';
|
|
|
ecb9bb |
|
|
|
ecb9bb |
if (iterate_dir_call_hook (grub_be_to_cpu64(direntry->inode),
|
|
|
ecb9bb |
@@ -655,8 +829,7 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir,
|
|
|
ecb9bb |
break;
|
|
|
ecb9bb |
|
|
|
ecb9bb |
/* Select the next directory entry. */
|
|
|
ecb9bb |
- pos = GRUB_XFS_NEXT_DIRENT (pos, direntry->len);
|
|
|
ecb9bb |
- pos = GRUB_XFS_ROUND_TO_DIRENT (pos);
|
|
|
ecb9bb |
+ direntry = grub_xfs_next_de(dir->data, direntry);
|
|
|
ecb9bb |
}
|
|
|
ecb9bb |
}
|
|
|
ecb9bb |
grub_free (dirblock);
|
|
|
ecb9bb |
@@ -681,19 +854,14 @@ grub_xfs_mount (grub_disk_t disk)
|
|
|
ecb9bb |
if (!data)
|
|
|
ecb9bb |
return 0;
|
|
|
ecb9bb |
|
|
|
ecb9bb |
+ grub_dprintf("xfs", "Reading sb\n");
|
|
|
ecb9bb |
/* Read the superblock. */
|
|
|
ecb9bb |
if (grub_disk_read (disk, 0, 0,
|
|
|
ecb9bb |
sizeof (struct grub_xfs_sblock), &data->sblock))
|
|
|
ecb9bb |
goto fail;
|
|
|
ecb9bb |
|
|
|
ecb9bb |
- if (grub_strncmp ((char *) (data->sblock.magic), "XFSB", 4)
|
|
|
ecb9bb |
- || data->sblock.log2_bsize < GRUB_DISK_SECTOR_BITS
|
|
|
ecb9bb |
- || ((int) data->sblock.log2_bsize
|
|
|
ecb9bb |
- + (int) data->sblock.log2_dirblk) >= 27)
|
|
|
ecb9bb |
- {
|
|
|
ecb9bb |
- grub_error (GRUB_ERR_BAD_FS, "not a XFS filesystem");
|
|
|
ecb9bb |
- goto fail;
|
|
|
ecb9bb |
- }
|
|
|
ecb9bb |
+ if (!grub_xfs_sb_valid(data))
|
|
|
ecb9bb |
+ goto fail;
|
|
|
ecb9bb |
|
|
|
ecb9bb |
data = grub_realloc (data,
|
|
|
ecb9bb |
sizeof (struct grub_xfs_data)
|
|
|
ecb9bb |
@@ -708,9 +876,13 @@ grub_xfs_mount (grub_disk_t disk)
|
|
|
ecb9bb |
data->diropen.inode_read = 1;
|
|
|
ecb9bb |
data->bsize = grub_be_to_cpu32 (data->sblock.bsize);
|
|
|
ecb9bb |
data->agsize = grub_be_to_cpu32 (data->sblock.agsize);
|
|
|
ecb9bb |
+ data->hasftype = grub_xfs_sb_hasftype(data);
|
|
|
ecb9bb |
+ data->hascrc = grub_xfs_sb_hascrc(data);
|
|
|
ecb9bb |
|
|
|
ecb9bb |
data->disk = disk;
|
|
|
ecb9bb |
data->pos = 0;
|
|
|
ecb9bb |
+ grub_dprintf("xfs", "Reading root ino %"PRIuGRUB_UINT64_T"\n",
|
|
|
ecb9bb |
+ grub_cpu_to_be64(data->sblock.rootino));
|
|
|
ecb9bb |
|
|
|
ecb9bb |
grub_xfs_read_inode (data, data->diropen.ino, &data->diropen.inode);
|
|
|
ecb9bb |
|