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