[syslinux] [PATCH] xfs: Add support for v3 directories

Paulo Alcantara pcacjr at gmail.com
Mon Dec 14 16:54:00 PST 2015


Besides supporting newer version of xfs file system, this patch also
does some code refactoring and fix completely broken listing and
searching on v2-3 node directories.

Cc: Gene Cumm <gene.cumm at gmail.com>
Cc: H. Peter Anvin <hpa at zytor.com>
Cc: Raphael S. Carvalho <raphael.scarv at gmail.com>
Cc: Ady <ady-sf at hotmail.com>
Signed-off-by: Paulo Alcantara <pcacjr at zytor.com>
---
 core/fs/xfs/xfs.c         |  19 +--
 core/fs/xfs/xfs.h         | 155 +++++++++++++++++++-----
 core/fs/xfs/xfs_dinode.c  |   2 +-
 core/fs/xfs/xfs_dir2.c    | 296 +++++++++++++++++++++++++---------------------
 core/fs/xfs/xfs_dir2.h    |   6 +-
 core/fs/xfs/xfs_readdir.c | 151 ++++++++++++++---------
 6 files changed, 399 insertions(+), 230 deletions(-)

diff --git a/core/fs/xfs/xfs.c b/core/fs/xfs/xfs.c
index 8dbc803..6f45973 100644
--- a/core/fs/xfs/xfs.c
+++ b/core/fs/xfs/xfs.c
@@ -74,10 +74,13 @@ static int xfs_readdir(struct file *file, struct dirent *dirent)
 	return -1;
     }
 
-    if (core->di_format == XFS_DINODE_FMT_LOCAL)
+    switch (core->di_format) {
+    case XFS_DINODE_FMT_LOCAL:
 	return xfs_fmt_local_readdir(file, dirent, core);
-    else if (core->di_format == XFS_DINODE_FMT_EXTENTS)
+    case XFS_DINODE_FMT_EXTENTS:
+    case XFS_DINODE_FMT_BTREE:
 	return xfs_fmt_extents_readdir(file, dirent, core);
+    }
 
     return -1;
 }
@@ -117,8 +120,10 @@ static int xfs_next_extent(struct inode *inode, uint32_t lstart)
         goto out;
 
     if (core->di_format == XFS_DINODE_FMT_EXTENTS) {
-	bmbt_irec_get(&rec, (xfs_bmbt_rec_t *)&core->di_literal_area[0] +
-						XFS_PVT(inode)->i_cur_extent++);
+	bmbt_irec_get(&rec, (xfs_bmbt_rec_t *)XFS_DFORK_PTR(core,
+							    XFS_DATA_FORK) +
+						XFS_PVT(inode)->i_cur_extent);
+	XFS_PVT(inode)->i_cur_extent++;
 
 	bno = fsblock_to_bytes(fs, rec.br_startblock) >> BLOCK_SHIFT(fs);
 
@@ -130,7 +135,7 @@ static int xfs_next_extent(struct inode *inode, uint32_t lstart)
     } else if (core->di_format == XFS_DINODE_FMT_BTREE) {
         xfs_debug("XFS_DINODE_FMT_BTREE");
         index = XFS_PVT(inode)->i_cur_extent++;
-        rblock = (xfs_bmdr_block_t *)&core->di_literal_area[0];
+        rblock = XFS_DFORK_PTR(core, XFS_DATA_FORK);
         fsize = XFS_DFORK_SIZE(core, fs, XFS_DATA_FORK);
         pp = XFS_BMDR_PTR_ADDR(rblock, 1, xfs_bmdr_maxrecs(fsize, 0));
         bno = fsblock_to_bytes(fs, be64_to_cpu(pp[0])) >> BLOCK_SHIFT(fs);
@@ -278,9 +283,9 @@ static int xfs_readlink(struct inode *inode, char *buf)
     }
 
     if (core->di_format == XFS_DINODE_FMT_LOCAL) {
-	memcpy(buf, (char *)&core->di_literal_area[0], pathlen);
+	memcpy(buf, XFS_DFORK_PTR(core, XFS_DATA_FORK), pathlen);
     } else if (core->di_format == XFS_DINODE_FMT_EXTENTS) {
-	bmbt_irec_get(&rec, (xfs_bmbt_rec_t *)&core->di_literal_area[0]);
+	bmbt_irec_get(&rec, XFS_DFORK_PTR(core, XFS_DATA_FORK));
 	db = fsblock_to_bytes(fs, rec.br_startblock) >> BLOCK_SHIFT(fs);
 	dir_buf = xfs_dir2_dirblks_get_cached(fs, db, rec.br_blockcount);
 
diff --git a/core/fs/xfs/xfs.h b/core/fs/xfs/xfs.h
index a8bfa93..65acd17 100644
--- a/core/fs/xfs/xfs.h
+++ b/core/fs/xfs/xfs.h
@@ -116,6 +116,9 @@ struct xfs_fs_info;
 
 #define XFS_DIR2_NULL_DATAPTR   ((uint32_t)0)
 
+#define	XFS_DIR3_BLOCK_MAGIC	0x58444233	/* XDB3: single block dirs */
+#define	XFS_DIR3_DATA_MAGIC	0x58444433	/* XDD3: multiblock dirs */
+
 /* File types and modes */
 #define S_IFMT  	00170000
 #define S_IFSOCK 	0140000
@@ -346,8 +349,19 @@ typedef struct xfs_dinode {
 
     /* di_next_unlinked is the only non-core field in the old dinode */
     uint32_t		di_next_unlinked;/* agi unlinked list ptr */
-    uint8_t		di_literal_area[1];
-} __attribute__((packed)) xfs_dinode_t;
+
+    /* start of the extended dinode, writable fields */
+    uint32_t		di_crc;		 /* CRC of the inode */
+    uint64_t		di_changecount;	 /* number of attribute changes */
+    uint64_t		di_lsn;		 /* flush sequence */
+    uint64_t		di_flags2;	 /* more random flags */
+    uint8_t		di_pad2[16];
+
+    /* fields only written to during inode creation */
+    xfs_timestamp_t	di_crtime;	 /* time created */
+    uint64_t		di_ino;		 /* inode number */
+    uuid_t		di_uuid;	 /* UUID of the filesystem */
+} __attribute__((__packed__)) xfs_dinode_t;
 
 /*
  * Inode size for given fs.
@@ -359,23 +373,41 @@ typedef struct xfs_dinode {
         (XFS_BTREE_LBLOCK_LEN - sizeof(xfs_bmdr_block_t))
 
 /*
+ * Size of the core inode on disk.  Version 1 and 2 inodes have
+ * the same size, but version 3 has grown a few additional fields.
+ */
+static inline unsigned int xfs_dinode_size(int version)
+{
+    if (version == 3)
+	return sizeof(struct xfs_dinode);
+    return offsetof(struct xfs_dinode, di_crc);
+}
+
+/*
  * Inode data & attribute fork sizes, per inode.
  */
 #define XFS_DFORK_Q(dip)	((dip)->di_forkoff != 0)
 #define XFS_DFORK_BOFF(dip)	((int)((dip)->di_forkoff << 3))
 
-#define XFS_DFORK_DSIZE(dip, fs) \
-        (XFS_DFORK_Q(dip) ? \
-                XFS_DFORK_BOFF(dip) : \
-                XFS_LITINO(fs))
-#define XFS_DFORK_ASIZE(dip, fs) \
-        (XFS_DFORK_Q(dip) ? \
-                XFS_LITINO(fs) - XFS_DFORK_BOFF(dip) : \
-                0)
-#define XFS_DFORK_SIZE(dip, fs, w) \
-        ((w) == XFS_DATA_FORK ? \
-                XFS_DFORK_DSIZE(dip, fs) : \
-                XFS_DFORK_ASIZE(dip, fs))
+#define XFS_DFORK_DSIZE(dip, fs)		\
+    (XFS_DFORK_Q(dip) ?				\
+     XFS_DFORK_BOFF(dip) :			\
+     XFS_LITINO(fs))
+#define XFS_DFORK_ASIZE(dip, fs)		\
+    (XFS_DFORK_Q(dip) ?				\
+     XFS_LITINO(fs) - XFS_DFORK_BOFF(dip) :	\
+     0)
+#define XFS_DFORK_SIZE(dip, fs, w)		\
+    ((w) == XFS_DATA_FORK ?			\
+     XFS_DFORK_DSIZE(dip, fs) :			\
+     XFS_DFORK_ASIZE(dip, fs))
+
+#define XFS_DFORK_DPTR(dip) \
+    ((void *)((uint8_t *)dip + xfs_dinode_size(dip->di_version)))
+#define XFS_DFORK_APTR(dip) \
+    ((void *)((uint8_t *)XFS_DFORK_DPTR(dip) + XFS_DFORK_BOFF(dip)))
+#define XFS_DFORK_PTR(dip,w) \
+    ((w) == XFS_DATA_FORK ? XFS_DFORK_DPTR(dip) : XFS_DFORK_APTR(dip))
 
 struct xfs_inode {
     xfs_agblock_t 	i_agblock;
@@ -406,13 +438,12 @@ typedef struct xfs_dir2_sf_hdr {
 typedef struct xfs_dir2_sf_entry {
     uint8_t             namelen;        /* actual name length */
     xfs_dir2_sf_off_t   offset;         /* saved offset */
-    uint8_t             name[1];        /* name, variable size */
-    xfs_dir2_inou_t	inumber;	/* inode number, var. offset */
+    uint8_t             name[];         /* name, variable size */
 } __attribute__((__packed__)) xfs_dir2_sf_entry_t;
 
 typedef struct xfs_dir2_sf {
-    xfs_dir2_sf_hdr_t       hdr;            /* shortform header */
-    xfs_dir2_sf_entry_t     list[1];        /* shortform entries */
+    xfs_dir2_sf_hdr_t       hdr;        /* shortform header */
+    xfs_dir2_sf_entry_t     list[];     /* shortform entries */
 } __attribute__((__packed__)) xfs_dir2_sf_t;
 
 typedef xfs_ino_t	xfs_intino_t;
@@ -483,6 +514,21 @@ typedef struct xfs_dir2_data_unused {
  /* uint16_t tag; */  /* starting offset of us */
 } __attribute__((__packed__)) xfs_dir2_data_unused_t;
 
+struct xfs_dir3_blk_hdr {
+    uint32_t			magic;	/* magic number */
+    uint32_t			crc;	/* CRC of block */
+    uint64_t			blkno;	/* first block of the buffer */
+    uint64_t			lsn;	/* sequence number of last write */
+    uuid_t			uuid;	/* filesystem we belong to */
+    uint64_t			owner;	/* inode that owns the block */
+} __attribute__((__packed__));
+
+struct xfs_dir3_data_hdr {
+    struct xfs_dir3_blk_hdr	hdr;
+    xfs_dir2_data_free_t	best_free[XFS_DIR2_DATA_FD_COUNT];
+    uint32_t			pad;	/* 64 bit alignment */
+} __attribute__((__packed__));
+
 /**
  * rol32 - rotate a 32-bit value left
  * @word: value to rotate
@@ -502,10 +548,17 @@ static inline uint32_t rol32(uint32_t word, signed int shift)
 
 static inline int xfs_dir2_data_entsize(int n)
 {
-    return (int)roundup(offsetof(struct xfs_dir2_data_entry, name[0]) + n + 
+    return (int)roundup(offsetof(struct xfs_dir2_data_entry, name[0]) + n +
 			(unsigned int)sizeof(uint16_t), XFS_DIR2_DATA_ALIGN);
 }
 
+static inline int xfs_dir3_data_entsize(int n)
+{
+    return (int)roundup(offsetof(struct xfs_dir2_data_entry, name[0]) + n +
+			(unsigned int)sizeof(uint16_t) + sizeof(uint8_t),
+			XFS_DIR2_DATA_ALIGN);
+}
+
 static inline uint16_t *
 xfs_dir2_data_entry_tag_p(struct xfs_dir2_data_entry *dep)
 {
@@ -598,25 +651,67 @@ typedef struct xfs_dir2_leaf {
     xfs_dir2_leaf_entry_t	ents[];	/* entries */
 } __attribute__((__packed__)) xfs_dir2_leaf_t;
 
+typedef struct xfs_da3_blkinfo {
+    /*
+     * the node link manipulation code relies on the fact that the first
+     * element of this structure is the struct xfs_da_blkinfo so it can
+     * ignore the differences in the rest of the structures.
+     */
+    xfs_da_blkinfo_t	hdr;
+    uint32_t		crc;	/* CRC of block */
+    uint64_t		blkno;	/* first block of the buffer */
+    uint64_t		lsn;	/* sequence number of last write */
+    uuid_t		uuid;	/* filesystem we belong to */
+    uint64_t		owner;	/* inode that owns the block */
+} __attribute__((__packed__)) xfs_da3_blkinfo_t;
+
+typedef struct xfs_dir3_leaf_hdr {
+    xfs_da3_blkinfo_t           info;		/* header for da routines */
+    uint16_t			count;		/* count of entries */
+    uint16_t			stale;		/* count of stale entries */
+    uint32_t			pad;		/* 64 bit alignment */
+} __attribute__((__packed__)) xfs_dir3_leaf_hdr_t;
+
+typedef struct xfs_dir3_leaf {
+    xfs_dir3_leaf_hdr_t 	hdr;	/* leaf header */
+    xfs_dir2_leaf_entry_t	ents[];	/* entries */
+} __attribute__((__packed__)) xfs_dir3_leaf_t;
+
 #define XFS_DA_NODE_MAGIC	0xfebeU	/* magic number: non-leaf blocks */
 #define XFS_ATTR_LEAF_MAGIC	0xfbeeU	/* magic number: attribute leaf blks */
 #define XFS_DIR2_LEAF1_MAGIC	0xd2f1U /* magic number: v2 dirlf single blks */
 #define XFS_DIR2_LEAFN_MAGIC	0xd2ffU	/* magic number: V2 dirlf multi blks */
 
+#define XFS_DA3_NODE_MAGIC	0x3ebe	/* magic number: non-leaf blocks */
+#define XFS_ATTR3_LEAF_MAGIC	0x3bee	/* magic number: attribute leaf blks */
+#define	XFS_DIR3_LEAF1_MAGIC	0x3df1	/* magic number: v2 dirlf single blks */
+#define	XFS_DIR3_LEAFN_MAGIC	0x3dff	/* magic number: v2 dirlf multi blks */
+
+#define	XFS_DIR3_LEAF1_MAGIC	0x3df1	/* magic number: v2 dirlf single blks */
+#define	XFS_DIR3_LEAFN_MAGIC	0x3dff	/* magic number: v2 dirlf multi blks */
+
+typedef struct xfs_da_node_hdr {
+    xfs_da_blkinfo_t info;	/* block type, links, etc. */
+    uint16_t count;		/* count of active entries */
+    uint16_t level;		/* level above leaves (leaf == 0) */
+} __attribute__((__packed__)) xfs_da_node_hdr_t;
+
+typedef struct xfs_da_node_entry {
+    uint32_t hashval;	/* hash value for this descendant */
+    uint32_t before;	/* Btree block before this key */
+} __attribute__((__packed__)) xfs_da_node_entry_t;
+
 typedef struct xfs_da_intnode {
-    struct xfs_da_node_hdr {	/* constant-structure header block */
-	xfs_da_blkinfo_t info;	/* block type, links, etc. */
-	uint16_t count;		/* count of active entries */
-	uint16_t level;		/* level above leaves (leaf == 0) */
-    } hdr;
-    struct xfs_da_node_entry {
-	uint32_t hashval;	/* hash value for this descendant */
-	uint32_t before;	/* Btree block before this key */
-    } btree[1];
+    xfs_da_node_hdr_t hdr;
+    xfs_da_node_entry_t btree[];
 } __attribute__((__packed__)) xfs_da_intnode_t;
 
-typedef struct xfs_da_node_hdr xfs_da_node_hdr_t;
-typedef struct xfs_da_node_entry xfs_da_node_entry_t;
+typedef struct xfs_da3_node_hdr {
+    xfs_da3_blkinfo_t		info;  /* block type, links, etc. */
+    uint16_t			count; /* count of active entries */
+    uint16_t			level; /* level above leaves (leaf == 0) */
+    uint32_t			pad32;
+} __attribute__((__packed__)) xfs_da3_node_hdr_t;
 
 static inline bool xfs_is_valid_sb(const xfs_sb_t *sb)
 {
diff --git a/core/fs/xfs/xfs_dinode.c b/core/fs/xfs/xfs_dinode.c
index 55be6e2..dff7382 100644
--- a/core/fs/xfs/xfs_dinode.c
+++ b/core/fs/xfs/xfs_dinode.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013 Paulo Alcantara <pcacjr at zytor.com>
+ * Copyright (c) 2012-2015 Paulo Alcantara <pcacjr at zytor.com>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
diff --git a/core/fs/xfs/xfs_dir2.c b/core/fs/xfs/xfs_dir2.c
index de37ef7..f738a58 100644
--- a/core/fs/xfs/xfs_dir2.c
+++ b/core/fs/xfs/xfs_dir2.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013 Paulo Alcantara <pcacjr at zytor.com>
+ * Copyright (c) 2012-2015 Paulo Alcantara <pcacjr at zytor.com>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -67,30 +67,29 @@ uint32_t xfs_dir2_da_hashname(const uint8_t *name, int namelen)
 }
 
 static void *get_dirblks(struct fs_info *fs, block_t startblock,
-			 xfs_filblks_t c)
+                         xfs_filblks_t c)
 {
-    int count = c << XFS_INFO(fs)->dirblklog;
-    uint8_t *p;
-    uint8_t *buf;
-    off_t offset = 0;
+    const size_t len = c * XFS_INFO(fs)->dirblksize;
+    uint64_t offs = startblock << BLOCK_SHIFT(fs);
+    void *buf;
+    size_t ret;
 
-    buf = malloc(c * XFS_INFO(fs)->dirblksize);
+    buf = malloc(len);
     if (!buf)
         malloc_error("buffer memory");
+    memset(buf, 0, len);
 
-    memset(buf, 0, XFS_INFO(fs)->dirblksize);
-
-    while (count--) {
-        p = (uint8_t *)get_cache(fs->fs_dev, startblock++);
-        memcpy(buf + offset, p,  BLOCK_SIZE(fs));
-        offset += BLOCK_SIZE(fs);
+    ret = cache_read(fs, buf, offs, len);
+    if (ret != len) {
+        xfs_error("failed to read contiguous directory blocks\n");
+        free(buf);
+        return NULL;
     }
-
     return buf;
 }
 
-const void *xfs_dir2_dirblks_get_cached(struct fs_info *fs, block_t startblock,
-					xfs_filblks_t c)
+void *xfs_dir2_dirblks_get_cached(struct fs_info *fs, block_t startblock,
+				  xfs_filblks_t c)
 {
     unsigned char i;
     void *buf;
@@ -100,6 +99,8 @@ const void *xfs_dir2_dirblks_get_cached(struct fs_info *fs, block_t startblock,
 
     if (!dirblks_cached_count) {
 	buf = get_dirblks(fs, startblock, c);
+	if (!buf)
+	    return NULL;
 
 	dirblks_cache[dirblks_cached_count].dc_startblock = startblock;
 	dirblks_cache[dirblks_cached_count].dc_blkscount = c;
@@ -116,6 +117,8 @@ const void *xfs_dir2_dirblks_get_cached(struct fs_info *fs, block_t startblock,
 	}
 
 	buf = get_dirblks(fs, startblock, c);
+	if (!buf)
+	    return NULL;
 
 	dirblks_cache[XFS_DIR2_DIRBLKS_CACHE_SIZE / 2].dc_startblock =
 	    startblock;
@@ -144,6 +147,8 @@ const void *xfs_dir2_dirblks_get_cached(struct fs_info *fs, block_t startblock,
 	    }
 
 	    buf = get_dirblks(fs, startblock, c);
+	    if (!buf)
+		return NULL;
 
 	    dirblks_cache[dirblks_cached_count].dc_startblock = startblock;
 	    dirblks_cache[dirblks_cached_count].dc_blkscount = c;
@@ -152,7 +157,6 @@ const void *xfs_dir2_dirblks_get_cached(struct fs_info *fs, block_t startblock,
 	    return dirblks_cache[dirblks_cached_count++].dc_area;
 	}
     }
-
     return NULL;
 }
 
@@ -171,23 +175,27 @@ void xfs_dir2_dirblks_flush_cache(void)
 struct inode *xfs_dir2_local_find_entry(const char *dname, struct inode *parent,
 					xfs_dinode_t *core)
 {
-    xfs_dir2_sf_t *sf = (xfs_dir2_sf_t *)&core->di_literal_area[0];
+    xfs_dir2_sf_t *sf = XFS_DFORK_PTR(core, XFS_DATA_FORK);
     xfs_dir2_sf_entry_t *sf_entry;
+    uint8_t ftypelen = core->di_version == 3 ? 1 : 0;
     uint8_t count = sf->hdr.i8count ? sf->hdr.i8count : sf->hdr.count;
     struct fs_info *fs = parent->fs;
     struct inode *inode;
+    xfs_dir2_inou_t *inou;
     xfs_intino_t ino;
     xfs_dinode_t *ncore = NULL;
 
     xfs_debug("dname %s parent %p core %p", dname, parent, core);
     xfs_debug("count %hhu i8count %hhu", sf->hdr.count, sf->hdr.i8count);
 
-    sf_entry = (xfs_dir2_sf_entry_t *)((uint8_t *)&sf->list[0] -
+    sf_entry = (xfs_dir2_sf_entry_t *)((uint8_t *)sf->list -
 				       (!sf->hdr.i8count ? 4 : 0));
     while (count--) {
-	uint8_t *start_name = &sf_entry->name[0];
+	uint8_t *start_name = sf_entry->name;
 	uint8_t *end_name = start_name + sf_entry->namelen;
 
+	xfs_debug("namelen %u", sf_entry->namelen);
+
 	if (!xfs_dir2_entry_name_cmp(start_name, end_name, dname)) {
 	    xfs_debug("Found entry %s", dname);
 	    goto found;
@@ -195,8 +203,9 @@ struct inode *xfs_dir2_local_find_entry(const char *dname, struct inode *parent,
 
 	sf_entry = (xfs_dir2_sf_entry_t *)((uint8_t *)sf_entry +
 					   offsetof(struct xfs_dir2_sf_entry,
-						    name[0]) +
+						    name) +
 					   sf_entry->namelen +
+					   ftypelen +
 					   (sf->hdr.i8count ? 8 : 4));
     }
 
@@ -205,11 +214,12 @@ struct inode *xfs_dir2_local_find_entry(const char *dname, struct inode *parent,
 found:
     inode = xfs_new_inode(fs);
 
-    ino = xfs_dir2_sf_get_inumber(sf, (xfs_dir2_inou_t *)(
-				      (uint8_t *)sf_entry +
-				      offsetof(struct xfs_dir2_sf_entry,
-					       name[0]) +
-				      sf_entry->namelen));
+    inou = (xfs_dir2_inou_t *)((uint8_t *)sf_entry +
+			       offsetof(struct xfs_dir2_sf_entry,
+					name) +
+			       sf_entry->namelen +
+			       ftypelen);
+    ino = xfs_dir2_sf_get_inumber(sf, inou);
 
     xfs_debug("entry inode's number %lu", ino);
 
@@ -251,6 +261,7 @@ struct inode *xfs_dir2_block_find_entry(const char *dname, struct inode *parent,
     block_t dir_blk;
     struct fs_info *fs = parent->fs;
     const uint8_t *dirblk_buf;
+    bool isdir3;
     uint8_t *p, *endp;
     xfs_dir2_data_hdr_t *hdr;
     struct inode *inode = NULL;
@@ -262,18 +273,26 @@ struct inode *xfs_dir2_block_find_entry(const char *dname, struct inode *parent,
 
     xfs_debug("dname %s parent %p core %p", dname, parent, core);
 
-    bmbt_irec_get(&r, (xfs_bmbt_rec_t *)&core->di_literal_area[0]);
+    bmbt_irec_get(&r, XFS_DFORK_PTR(core, XFS_DATA_FORK));
     dir_blk = fsblock_to_bytes(fs, r.br_startblock) >> BLOCK_SHIFT(fs);
 
     dirblk_buf = xfs_dir2_dirblks_get_cached(fs, dir_blk, r.br_blockcount);
+    if (!dirblk_buf)
+        return NULL;
+
     hdr = (xfs_dir2_data_hdr_t *)dirblk_buf;
-    if (be32_to_cpu(hdr->magic) != XFS_DIR2_BLOCK_MAGIC) {
+    if (be32_to_cpu(hdr->magic) == XFS_DIR2_BLOCK_MAGIC) {
+	isdir3 = false;
+    } else if (be32_to_cpu(hdr->magic) == XFS_DIR3_BLOCK_MAGIC) {
+	isdir3 = true;
+    } else {
         xfs_error("Block directory header's magic number does not match!");
         xfs_debug("hdr->magic: 0x%lx", be32_to_cpu(hdr->magic));
         goto out;
     }
 
-    p = (uint8_t *)(hdr + 1);
+    p = (uint8_t *)dirblk_buf + (isdir3 ? sizeof(struct xfs_dir3_data_hdr) :
+				 sizeof(struct xfs_dir2_data_hdr));
 
     btp = xfs_dir2_block_tail_p(XFS_INFO(fs), hdr);
     endp = (uint8_t *)((xfs_dir2_leaf_entry_t *)btp - be32_to_cpu(btp->count));
@@ -290,7 +309,7 @@ struct inode *xfs_dir2_block_find_entry(const char *dname, struct inode *parent,
 
         dep = (xfs_dir2_data_entry_t *)p;
 
-        start_name = &dep->name[0];
+        start_name = dep->name;
         end_name = start_name + dep->namelen;
 
 	if (!xfs_dir2_entry_name_cmp(start_name, end_name, dname)) {
@@ -298,7 +317,8 @@ struct inode *xfs_dir2_block_find_entry(const char *dname, struct inode *parent,
             goto found;
         }
 
-	p += xfs_dir2_data_entsize(dep->namelen);
+	p += (isdir3 ? xfs_dir3_data_entsize(dep->namelen) :
+	      xfs_dir2_data_entsize(dep->namelen));
     }
 
 out:
@@ -348,7 +368,9 @@ failed:
 struct inode *xfs_dir2_leaf_find_entry(const char *dname, struct inode *parent,
 				       xfs_dinode_t *core)
 {
-    xfs_dir2_leaf_t *leaf;
+    xfs_dir2_leaf_hdr_t *hdr;
+    xfs_dir2_leaf_entry_t *ents;
+    uint16_t count;
     xfs_bmbt_irec_t irec;
     block_t leaf_blk, dir_blk;
     xfs_dir2_leaf_entry_t *lep;
@@ -358,37 +380,47 @@ struct inode *xfs_dir2_leaf_find_entry(const char *dname, struct inode *parent,
     uint32_t hash = 0;
     uint32_t hashwant;
     uint32_t newdb, curdb = -1;
-    xfs_dir2_data_entry_t *dep;
+    xfs_dir2_data_entry_t *dep = NULL;
     struct inode *ip;
     xfs_dir2_data_hdr_t *data_hdr;
     uint8_t *start_name;
     uint8_t *end_name;
     xfs_intino_t ino;
     xfs_dinode_t *ncore;
-    const uint8_t *buf = NULL;
+    uint8_t *buf = NULL;
 
     xfs_debug("dname %s parent %p core %p", dname, parent, core);
 
-    bmbt_irec_get(&irec, ((xfs_bmbt_rec_t *)&core->di_literal_area[0]) +
+    bmbt_irec_get(&irec, (xfs_bmbt_rec_t *)XFS_DFORK_PTR(core, XFS_DATA_FORK) +
 					be32_to_cpu(core->di_nextents) - 1);
     leaf_blk = fsblock_to_bytes(parent->fs, irec.br_startblock) >>
 	    BLOCK_SHIFT(parent->fs);
 
-    leaf = (xfs_dir2_leaf_t *)xfs_dir2_dirblks_get_cached(parent->fs, leaf_blk,
-							  irec.br_blockcount);
-    if (be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR2_LEAF1_MAGIC) {
+    hdr = xfs_dir2_dirblks_get_cached(parent->fs, leaf_blk,
+				      irec.br_blockcount);
+    if (!hdr)
+        return NULL;
+
+    if (be16_to_cpu(hdr->info.magic) == XFS_DIR2_LEAF1_MAGIC) {
+	count = be16_to_cpu(hdr->count);
+	ents = (xfs_dir2_leaf_entry_t *)((uint8_t *)hdr +
+					 sizeof(struct xfs_dir2_leaf_hdr));
+    } else if (be16_to_cpu(hdr->info.magic) == XFS_DIR3_LEAF1_MAGIC) {
+	count = be16_to_cpu(((xfs_dir3_leaf_hdr_t *)hdr)->count);
+	ents = (xfs_dir2_leaf_entry_t *)((uint8_t *)hdr +
+					 sizeof(struct xfs_dir3_leaf_hdr));
+    } else {
         xfs_error("Single leaf block header's magic number does not match!");
         goto out;
     }
 
-    if (!leaf->hdr.count)
+    if (!count)
 	goto out;
 
     hashwant = xfs_dir2_da_hashname((uint8_t *)dname, strlen(dname));
 
     /* Binary search */
-    for (lep = leaf->ents, low = 0, high = be16_to_cpu(leaf->hdr.count) - 1;
-	 low <= high; ) {
+    for (lep = ents, low = 0, high = count - 1; low <= high; ) {
         mid = (low + high) >> 1;
         if ((hash = be32_to_cpu(lep[mid].hashval)) == hashwant)
             break;
@@ -407,10 +439,8 @@ struct inode *xfs_dir2_leaf_find_entry(const char *dname, struct inode *parent,
     while (mid > 0 && be32_to_cpu(lep[mid - 1].hashval) == hashwant)
 	mid--;
 
-    for (lep = &leaf->ents[mid];
-	 mid < be16_to_cpu(leaf->hdr.count) &&
-	 be32_to_cpu(lep->hashval) == hashwant;
-	 lep++, mid++) {
+    for (lep = &ents[mid];
+	 mid < count && be32_to_cpu(lep->hashval) == hashwant; lep++, mid++) {
         /* Skip over stale leaf entries. */
         if (be32_to_cpu(lep->address) == XFS_DIR2_NULL_DATAPTR)
             continue;
@@ -418,13 +448,17 @@ struct inode *xfs_dir2_leaf_find_entry(const char *dname, struct inode *parent,
         newdb = xfs_dir2_dataptr_to_db(parent->fs, be32_to_cpu(lep->address));
         if (newdb != curdb) {
             bmbt_irec_get(&irec,
-		  ((xfs_bmbt_rec_t *)&core->di_literal_area[0]) + newdb);
+			  (xfs_bmbt_rec_t *)XFS_DFORK_PTR(core,
+							  XFS_DATA_FORK) +
+			  newdb);
             dir_blk = fsblock_to_bytes(parent->fs, irec.br_startblock) >>
 
 		      BLOCK_SHIFT(parent->fs);
-            buf = xfs_dir2_dirblks_get_cached(parent->fs, dir_blk, irec.br_blockcount);
+            buf = xfs_dir2_dirblks_get_cached(parent->fs, dir_blk,
+					      irec.br_blockcount);
             data_hdr = (xfs_dir2_data_hdr_t *)buf;
-            if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC) {
+            if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC &&
+		be32_to_cpu(data_hdr->magic) != XFS_DIR3_DATA_MAGIC) {
                 xfs_error("Leaf directory's data magic No. does not match!");
                 goto out;
             }
@@ -432,10 +466,10 @@ struct inode *xfs_dir2_leaf_find_entry(const char *dname, struct inode *parent,
             curdb = newdb;
         }
 
-        dep = (xfs_dir2_data_entry_t *)((char *)buf +
-               xfs_dir2_dataptr_to_off(parent->fs, be32_to_cpu(lep->address)));
-
-        start_name = &dep->name[0];
+        dep = (xfs_dir2_data_entry_t *)(
+            buf + xfs_dir2_dataptr_to_off(parent->fs,
+                                          be32_to_cpu(lep->address)));
+        start_name = dep->name;
         end_name = start_name + dep->namelen;
 
 	if (!xfs_dir2_entry_name_cmp(start_name, end_name, dname)) {
@@ -523,7 +557,7 @@ block_t xfs_dir2_get_right_blk(struct fs_info *fs, xfs_dinode_t *core,
     int nextents;
     xfs_bmbt_ptr_t *pp;
     xfs_bmbt_key_t *kp;
-    xfs_btree_block_t *blk;
+    const xfs_btree_block_t *blk;
     xfs_bmbt_rec_t *xp;
 
     *error = 0;
@@ -531,7 +565,8 @@ block_t xfs_dir2_get_right_blk(struct fs_info *fs, xfs_dinode_t *core,
         xfs_debug("XFS_DINODE_FMT_EXTENTS");
         for (idx = 0; idx < be32_to_cpu(core->di_nextents); idx++) {
             bmbt_irec_get(&irec,
-                          ((xfs_bmbt_rec_t *)&core->di_literal_area[0]) + idx);
+                          (xfs_bmbt_rec_t *)
+			  XFS_DFORK_PTR(core, XFS_DATA_FORK) + idx);
             if (fsblkno >= irec.br_startoff &&
                 fsblkno < irec.br_startoff + irec.br_blockcount)
                 break;
@@ -539,7 +574,7 @@ block_t xfs_dir2_get_right_blk(struct fs_info *fs, xfs_dinode_t *core,
     } else if (core->di_format == XFS_DINODE_FMT_BTREE) {
         xfs_debug("XFS_DINODE_FMT_BTREE");
         bno = NULLFSBLOCK;
-        rblock = (xfs_bmdr_block_t *)&core->di_literal_area[0];
+        rblock = XFS_DFORK_PTR(core, XFS_DATA_FORK);
         fsize = XFS_DFORK_SIZE(core, fs, XFS_DATA_FORK);
         pp = XFS_BMDR_PTR_ADDR(rblock, 1, xfs_bmdr_maxrecs(fsize, 0));
         kp = XFS_BMDR_KEY_ADDR(rblock, 1);
@@ -549,7 +584,7 @@ block_t xfs_dir2_get_right_blk(struct fs_info *fs, xfs_dinode_t *core,
 
         /* Find the leaf */
         for (;;) {
-            blk = (xfs_btree_block_t *)get_cache(fs->fs_dev, bno);
+            blk = get_cache(fs->fs_dev, bno);
             if (be16_to_cpu(blk->bb_level) == 0)
                 break;
             pp = XFS_BMBT_PTR_ADDR(fs, blk, 1,
@@ -576,7 +611,7 @@ block_t xfs_dir2_get_right_blk(struct fs_info *fs, xfs_dinode_t *core,
             if (nextbno == NULLFSBLOCK)
                 break;
             bno = fsblock_to_bytes(fs, nextbno) >> BLOCK_SHIFT(fs);
-            blk = (xfs_btree_block_t *)get_cache(fs->fs_dev, bno);
+            blk = get_cache(fs->fs_dev, bno);
         }
     }
 
@@ -593,110 +628,99 @@ struct inode *xfs_dir2_node_find_entry(const char *dname, struct inode *parent,
 				       xfs_dinode_t *core)
 {
     block_t fsblkno;
-    xfs_da_intnode_t *node = NULL;
+    xfs_da_node_hdr_t *nhdr;
     uint32_t hashwant;
-    uint32_t hash = 0;
+    uint32_t hash;
+    uint16_t i;
+    uint16_t count;
     xfs_da_node_entry_t *btree;
-    uint16_t max;
-    uint16_t span;
-    uint16_t probe;
     int error;
+    xfs_dir2_leaf_hdr_t *lhdr;
     xfs_dir2_data_hdr_t *data_hdr;
-    xfs_dir2_leaf_t *leaf;
     xfs_dir2_leaf_entry_t *lep;
     xfs_dir2_data_entry_t *dep;
+    xfs_dir2_leaf_entry_t *ents;
     struct inode *ip;
     uint8_t *start_name;
     uint8_t *end_name;
     int low;
     int high;
-    int mid = 0;
-    uint32_t newdb, curdb = -1;
+    int mid;
+    uint32_t newdb;
+    uint32_t curdb;
     xfs_intino_t ino;
     xfs_dinode_t *ncore;
-    const uint8_t *buf = NULL;
+    uint8_t *buf = NULL;
 
     xfs_debug("dname %s parent %p core %p", dname, parent, core);
 
-    hashwant = xfs_dir2_da_hashname((uint8_t *)dname, strlen(dname));
-
-    fsblkno = xfs_dir2_get_right_blk(parent->fs, core,
-                  xfs_dir2_byte_to_db(parent->fs, XFS_DIR2_LEAF_OFFSET),
-                  &error);
+    curdb = xfs_dir2_byte_to_db(parent->fs, XFS_DIR2_LEAF_OFFSET);
+    fsblkno = xfs_dir2_get_right_blk(parent->fs, core, curdb, &error);
     if (error) {
         xfs_error("Cannot find right rec!");
         return NULL;
     }
 
-    node = (xfs_da_intnode_t *)xfs_dir2_dirblks_get_cached(parent->fs, fsblkno,
-							   1);
-    if (be16_to_cpu(node->hdr.info.magic) != XFS_DA_NODE_MAGIC) {
-        xfs_error("Node's magic number does not match!");
+    nhdr = xfs_dir2_dirblks_get_cached(parent->fs, fsblkno, 1);
+    if (be16_to_cpu(nhdr->info.magic) == XFS_DA_NODE_MAGIC) {
+	count = be16_to_cpu(nhdr->count);
+	btree = (xfs_da_node_entry_t *)((uint8_t *)nhdr +
+					sizeof(struct xfs_da_node_hdr));
+    } else if (be16_to_cpu(nhdr->info.magic) == XFS_DA3_NODE_MAGIC) {
+	count = be16_to_cpu(((xfs_da3_node_hdr_t *)nhdr)->count);
+	btree = (xfs_da_node_entry_t *)((uint8_t *)nhdr +
+					sizeof(struct xfs_da3_node_hdr));
+    } else {
+        xfs_error("Node's magic number (0x%04x) does not match!");
         goto out;
     }
 
-    do {
-        if (!node->hdr.count)
-            goto out;
-
-        /* Given a hash to lookup, you read the node's btree array and first
-         * "hashval" in the array that exceeds the given hash and it can then
-         * be found in the block pointed by the "before" value.
-         */
-        max = be16_to_cpu(node->hdr.count);
-
-        probe = span = max/2;
-        for (btree = &node->btree[probe];
-             span > 4; btree = &node->btree[probe]) {
-            span /= 2;
-            hash = be32_to_cpu(btree->hashval);
-
-            if (hash < hashwant)
-                probe += span;
-            else if (hash > hashwant)
-                probe -= span;
-            else
-                break;
-        }
-
-        while ((probe > 0) && (be32_to_cpu(btree->hashval) >= hashwant)) {
-            btree--;
-            probe--;
-        }
-
-        while ((probe < max) && (be32_to_cpu(btree->hashval) < hashwant)) {
-            btree++;
-            probe++;
-        }
+    hashwant = xfs_dir2_da_hashname((uint8_t *)dname, strlen(dname));
 
-        if (probe == max)
-            fsblkno = be32_to_cpu(node->btree[max-1].before);
-        else
-            fsblkno = be32_to_cpu(node->btree[probe].before);
+    /* Given a hash to lookup, you read the node's btree array and first
+     * "hashval" in the array that exceeds the given hash and it can then
+     * be found in the block pointed by the "before" value.
+     */
+    fsblkno = 0;
+    for (i = 0; i < count; i++) {
+	if (hashwant < be32_to_cpu(btree[i].hashval)) {
+	    fsblkno = be32_to_cpu(btree[i].before);
+	    break;
+	}
+    }
 
-        fsblkno = xfs_dir2_get_right_blk(parent->fs, core, fsblkno, &error);
-        if (error) {
-            xfs_error("Cannot find right rec!");
-            goto out;
-        }
+    if (!fsblkno)
+	goto out;
 
-        node = (xfs_da_intnode_t *)xfs_dir2_dirblks_get_cached(parent->fs,
-							       fsblkno, 1);
-    } while(be16_to_cpu(node->hdr.info.magic) == XFS_DA_NODE_MAGIC);
+    fsblkno = xfs_dir2_get_right_blk(parent->fs, core, fsblkno, &error);
+    if (error) {
+	xfs_error("Cannot find leaf record");
+	goto out;
+    }
 
-    leaf = (xfs_dir2_leaf_t*)node;
-    if (be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR2_LEAFN_MAGIC) {
-        xfs_error("Leaf's magic number does not match!");
+    lhdr = xfs_dir2_dirblks_get_cached(parent->fs, fsblkno, 1);
+    if (be16_to_cpu(lhdr->info.magic) == XFS_DIR2_LEAFN_MAGIC) {
+	count = be16_to_cpu(lhdr->count);
+	ents = (xfs_dir2_leaf_entry_t *)((uint8_t *)lhdr +
+					 sizeof(struct xfs_dir2_leaf_hdr));
+    } else if (be16_to_cpu(lhdr->info.magic) == XFS_DIR3_LEAFN_MAGIC) {
+	count = be16_to_cpu(((xfs_dir3_leaf_hdr_t *)lhdr)->count);
+	ents = (xfs_dir2_leaf_entry_t *)((uint8_t *)lhdr +
+					 sizeof(struct xfs_dir3_leaf_hdr));
+    } else {
+        xfs_error("Leaf's magic number does not match (0x%04x)!",
+		  be16_to_cpu(lhdr->info.magic));
         goto out;
     }
 
-    if (!leaf->hdr.count)
-        goto out;
+    if (!count)
+	goto out;
 
-    for (lep = leaf->ents, low = 0, high = be16_to_cpu(leaf->hdr.count) - 1;
-         low <= high; ) {
+    lep = ents;
+    low = 0;
+    high = count - 1;
+    while (low <= high) {
         mid = (low + high) >> 1;
-
         if ((hash = be32_to_cpu(lep[mid].hashval)) == hashwant)
             break;
         if (hash < hashwant)
@@ -714,9 +738,8 @@ struct inode *xfs_dir2_node_find_entry(const char *dname, struct inode *parent,
     while (mid > 0 && be32_to_cpu(lep[mid - 1].hashval) == hashwant)
         mid--;
 
-    for (lep = &leaf->ents[mid];
-         mid < be16_to_cpu(leaf->hdr.count) &&
-         be32_to_cpu(lep->hashval) == hashwant;
+    curdb = -1;
+    for (lep = &ents[mid]; mid < count && be32_to_cpu(lep->hashval) == hashwant;
          lep++, mid++) {
         /* Skip over stale leaf entries. */
         if (be32_to_cpu(lep->address) == XFS_DIR2_NULL_DATAPTR)
@@ -732,7 +755,8 @@ struct inode *xfs_dir2_node_find_entry(const char *dname, struct inode *parent,
 
             buf = xfs_dir2_dirblks_get_cached(parent->fs, fsblkno, 1);
             data_hdr = (xfs_dir2_data_hdr_t *)buf;
-            if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC) {
+            if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC &&
+		be32_to_cpu(data_hdr->magic) != XFS_DIR3_DATA_MAGIC) {
                 xfs_error("Leaf directory's data magic No. does not match!");
                 goto out;
             }
@@ -740,10 +764,10 @@ struct inode *xfs_dir2_node_find_entry(const char *dname, struct inode *parent,
             curdb = newdb;
         }
 
-        dep = (xfs_dir2_data_entry_t *)((char *)buf +
-               xfs_dir2_dataptr_to_off(parent->fs, be32_to_cpu(lep->address)));
-
-        start_name = &dep->name[0];
+        dep = (xfs_dir2_data_entry_t *)(
+            buf + xfs_dir2_dataptr_to_off(parent->fs,
+                                          be32_to_cpu(lep->address)));
+        start_name = dep->name;
         end_name = start_name + dep->namelen;
 
 	if (!xfs_dir2_entry_name_cmp(start_name, end_name, dname)) {
diff --git a/core/fs/xfs/xfs_dir2.h b/core/fs/xfs/xfs_dir2.h
index 158cf44..1e1fb57 100644
--- a/core/fs/xfs/xfs_dir2.h
+++ b/core/fs/xfs/xfs_dir2.h
@@ -23,8 +23,8 @@
 
 #include "xfs.h"
 
-const void *xfs_dir2_dirblks_get_cached(struct fs_info *fs, block_t startblock,
-					xfs_filblks_t c);
+void *xfs_dir2_dirblks_get_cached(struct fs_info *fs, block_t startblock,
+				  xfs_filblks_t c);
 void xfs_dir2_dirblks_flush_cache(void);
 
 uint32_t xfs_dir2_da_hashname(const uint8_t *name, int namelen);
@@ -46,7 +46,7 @@ static inline bool xfs_dir2_isleaf(struct fs_info *fs, xfs_dinode_t *dip)
     uint64_t last = 0;
     xfs_bmbt_irec_t irec;
 
-    bmbt_irec_get(&irec, ((xfs_bmbt_rec_t *)&dip->di_literal_area[0]) +
+    bmbt_irec_get(&irec, (xfs_bmbt_rec_t *)XFS_DFORK_PTR(dip, XFS_DATA_FORK) +
 		         be32_to_cpu(dip->di_nextents) - 1);
     last = irec.br_startoff + irec.br_blockcount;
 
diff --git a/core/fs/xfs/xfs_readdir.c b/core/fs/xfs/xfs_readdir.c
index 86c8a77..51518ef 100644
--- a/core/fs/xfs/xfs_readdir.c
+++ b/core/fs/xfs/xfs_readdir.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013 Paulo Alcantara <pcacjr at zytor.com>
+ * Copyright (c) 2012-2015 Paulo Alcantara <pcacjr at zytor.com>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -64,10 +64,12 @@ static int fill_dirent(struct fs_info *fs, struct dirent *dirent,
 int xfs_readdir_dir2_local(struct file *file, struct dirent *dirent,
 			   xfs_dinode_t *core)
 {
-    xfs_dir2_sf_t *sf = (xfs_dir2_sf_t *)&core->di_literal_area[0];
+    xfs_dir2_sf_t *sf = XFS_DFORK_PTR(core, XFS_DATA_FORK);
     xfs_dir2_sf_entry_t *sf_entry;
+    uint8_t ftypelen = core->di_version == 3 ? 1 : 0;
     uint8_t count = sf->hdr.i8count ? sf->hdr.i8count : sf->hdr.count;
     uint32_t offset = file->offset;
+    xfs_dir2_inou_t *inou;
     uint8_t *start_name;
     uint8_t *end_name;
     xfs_ino_t ino;
@@ -82,7 +84,7 @@ int xfs_readdir_dir2_local(struct file *file, struct dirent *dirent,
 
     file->offset++;
 
-    sf_entry = (xfs_dir2_sf_entry_t *)((uint8_t *)&sf->list[0] -
+    sf_entry = (xfs_dir2_sf_entry_t *)((uint8_t *)sf->list -
 				       (!sf->hdr.i8count ? 4 : 0));
 
     if (file->offset - 1) {
@@ -91,20 +93,22 @@ int xfs_readdir_dir2_local(struct file *file, struct dirent *dirent,
 	    sf_entry = (xfs_dir2_sf_entry_t *)(
 				(uint8_t *)sf_entry +
 				offsetof(struct xfs_dir2_sf_entry,
-					 name[0]) +
+					 name) +
 				sf_entry->namelen +
+				ftypelen +
 				(sf->hdr.i8count ? 8 : 4));
 	}
     }
 
-    start_name = &sf_entry->name[0];
+    start_name = sf_entry->name;
     end_name = start_name + sf_entry->namelen;
 
-    ino = xfs_dir2_sf_get_inumber(sf, (xfs_dir2_inou_t *)(
-				      (uint8_t *)sf_entry +
-				      offsetof(struct xfs_dir2_sf_entry,
-					       name[0]) +
-				      sf_entry->namelen));
+    inou = (xfs_dir2_inou_t *)((uint8_t *)sf_entry +
+			       offsetof(struct xfs_dir2_sf_entry,
+					name) +
+			       sf_entry->namelen +
+			       ftypelen);
+    ino = xfs_dir2_sf_get_inumber(sf, inou);
 
     retval = fill_dirent(fs, dirent, file->offset, ino, (char *)start_name,
 			 end_name - start_name);
@@ -126,6 +130,7 @@ int xfs_readdir_dir2_block(struct file *file, struct dirent *dirent,
     block_t dir_blk;
     struct fs_info *fs = file->fs;
     const uint8_t *dirblk_buf;
+    bool isdir3;
     uint8_t *p;
     uint32_t offset;
     xfs_dir2_data_hdr_t *hdr;
@@ -139,12 +144,16 @@ int xfs_readdir_dir2_block(struct file *file, struct dirent *dirent,
 
     xfs_debug("file %p dirent %p core %p", file, dirent, core);
 
-    bmbt_irec_get(&r, (xfs_bmbt_rec_t *)&core->di_literal_area[0]);
+    bmbt_irec_get(&r, XFS_DFORK_PTR(core, XFS_DATA_FORK));
     dir_blk = fsblock_to_bytes(fs, r.br_startblock) >> BLOCK_SHIFT(fs);
 
     dirblk_buf = xfs_dir2_dirblks_get_cached(fs, dir_blk, r.br_blockcount);
     hdr = (xfs_dir2_data_hdr_t *)dirblk_buf;
-    if (be32_to_cpu(hdr->magic) != XFS_DIR2_BLOCK_MAGIC) {
+    if (be32_to_cpu(hdr->magic) == XFS_DIR2_BLOCK_MAGIC) {
+	isdir3 = false;
+    } else if (be32_to_cpu(hdr->magic) == XFS_DIR3_BLOCK_MAGIC) {
+	isdir3 = true;
+    } else {
         xfs_error("Block directory header's magic number does not match!");
         xfs_debug("hdr->magic: 0x%lx", be32_to_cpu(hdr->magic));
 	goto out;
@@ -157,7 +166,8 @@ int xfs_readdir_dir2_block(struct file *file, struct dirent *dirent,
 
     file->offset++;
 
-    p = (uint8_t *)(hdr + 1);
+    p = (uint8_t *)dirblk_buf + (isdir3 ? sizeof(struct xfs_dir3_data_hdr) :
+				 sizeof(struct xfs_dir2_data_hdr));
 
     if (file->offset - 1) {
 	offset = file->offset;
@@ -170,7 +180,8 @@ int xfs_readdir_dir2_block(struct file *file, struct dirent *dirent,
 		continue;
 	    }
 
-	    p += xfs_dir2_data_entsize(dep->namelen);
+	    p += (isdir3 ? xfs_dir3_data_entsize(dep->namelen) :
+		  xfs_dir2_data_entsize(dep->namelen));
 	}
     }
 
@@ -197,9 +208,11 @@ out:
 int xfs_readdir_dir2_leaf(struct file *file, struct dirent *dirent,
 			  xfs_dinode_t *core)
 {
+    xfs_dir2_leaf_hdr_t *hdr;
+    xfs_dir2_leaf_entry_t *ents;
+    uint16_t count;
     xfs_bmbt_irec_t irec;
     struct fs_info *fs = file->fs;
-    xfs_dir2_leaf_t *leaf;
     block_t leaf_blk, dir_blk;
     xfs_dir2_leaf_entry_t *lep;
     uint32_t db;
@@ -214,39 +227,49 @@ int xfs_readdir_dir2_leaf(struct file *file, struct dirent *dirent,
 
     xfs_debug("file %p dirent %p core %p", file, dirent, core);
 
-    bmbt_irec_get(&irec, ((xfs_bmbt_rec_t *)&core->di_literal_area[0]) +
+    bmbt_irec_get(&irec, (xfs_bmbt_rec_t *)XFS_DFORK_PTR(core, XFS_DATA_FORK) +
 					be32_to_cpu(core->di_nextents) - 1);
     leaf_blk = fsblock_to_bytes(fs, irec.br_startblock) >>
 					BLOCK_SHIFT(file->fs);
 
-    leaf = (xfs_dir2_leaf_t *)xfs_dir2_dirblks_get_cached(fs, leaf_blk,
-							  irec.br_blockcount);
-    if (be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR2_LEAF1_MAGIC) {
+    hdr = xfs_dir2_dirblks_get_cached(fs, leaf_blk, irec.br_blockcount);
+    if (!hdr)
+        return -1;
+
+    if (be16_to_cpu(hdr->info.magic) == XFS_DIR2_LEAF1_MAGIC) {
+	count = be16_to_cpu(hdr->count);
+	ents = (xfs_dir2_leaf_entry_t *)((uint8_t *)hdr +
+					 sizeof(struct xfs_dir2_leaf_hdr));
+    } else if (be16_to_cpu(hdr->info.magic) == XFS_DIR3_LEAF1_MAGIC) {
+	count = be16_to_cpu(((xfs_dir3_leaf_hdr_t *)hdr)->count);
+	ents = (xfs_dir2_leaf_entry_t *)((uint8_t *)hdr +
+					 sizeof(struct xfs_dir3_leaf_hdr));
+    } else {
         xfs_error("Single leaf block header's magic number does not match!");
         goto out;
     }
 
-    if (!leaf->hdr.count)
-        goto out;
-
-    if (file->offset + 1 > be16_to_cpu(leaf->hdr.count))
+    if (!count || file->offset + 1 > count)
 	goto out;
 
-    lep = &leaf->ents[file->offset++];
+    lep = &ents[file->offset++];
 
     /* Skip over stale leaf entries */
     for ( ; be32_to_cpu(lep->address) == XFS_DIR2_NULL_DATAPTR;
-	  lep++, file->offset++);
+	  lep++, file->offset++)
+	;
 
     db = xfs_dir2_dataptr_to_db(fs, be32_to_cpu(lep->address));
 
-    bmbt_irec_get(&irec, (xfs_bmbt_rec_t *)&core->di_literal_area[0] + db);
+    bmbt_irec_get(&irec, (xfs_bmbt_rec_t *)XFS_DFORK_PTR(core,
+							 XFS_DATA_FORK) + db);
 
     dir_blk = fsblock_to_bytes(fs, irec.br_startblock) >> BLOCK_SHIFT(fs);
 
     buf = xfs_dir2_dirblks_get_cached(fs, dir_blk, irec.br_blockcount);
     data_hdr = (xfs_dir2_data_hdr_t *)buf;
-    if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC) {
+    if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC &&
+	be32_to_cpu(data_hdr->magic) != XFS_DIR3_DATA_MAGIC) {
 	xfs_error("Leaf directory's data magic number does not match!");
 	goto out;
     }
@@ -277,14 +300,16 @@ int xfs_readdir_dir2_node(struct file *file, struct dirent *dirent,
 			  xfs_dinode_t *core)
 {
     struct fs_info *fs = file->fs;
-    xfs_bmbt_irec_t irec;
-    uint32_t node_off = 0;
     block_t fsblkno;
-    xfs_da_intnode_t *node = NULL;
+    xfs_da_node_hdr_t *nhdr;
+    xfs_da_node_entry_t *btree;
+    uint16_t btcount;
+    uint16_t lfcount;
+    xfs_dir2_leaf_hdr_t *lhdr;
+    xfs_dir2_leaf_entry_t *ents;
     struct inode *inode = file->inode;
     int error;
     xfs_dir2_data_hdr_t *data_hdr;
-    xfs_dir2_leaf_t *leaf;
     xfs_dir2_leaf_entry_t *lep;
     unsigned int offset;
     xfs_dir2_data_entry_t *dep;
@@ -296,52 +321,71 @@ int xfs_readdir_dir2_node(struct file *file, struct dirent *dirent,
 
     xfs_debug("file %p dirent %p core %p", file, dirent, core);
 
-    do {
-        bmbt_irec_get(&irec, (xfs_bmbt_rec_t *)&core->di_literal_area[0] +
-								++node_off);
-    } while (irec.br_startoff < xfs_dir2_byte_to_db(fs, XFS_DIR2_LEAF_OFFSET));
-
-    fsblkno = fsblock_to_bytes(fs, irec.br_startblock) >> BLOCK_SHIFT(fs);
+    db = xfs_dir2_byte_to_db(fs, XFS_DIR2_LEAF_OFFSET);
+    fsblkno = xfs_dir2_get_right_blk(fs, core, db, &error);
+    if (error) {
+	xfs_error("Cannot find fs block");
+	return -1;
+    }
 
-    node = (xfs_da_intnode_t *)xfs_dir2_dirblks_get_cached(fs, fsblkno, 1);
-    if (be16_to_cpu(node->hdr.info.magic) != XFS_DA_NODE_MAGIC) {
-        xfs_error("Node's magic number does not match!");
+    nhdr = xfs_dir2_dirblks_get_cached(fs, fsblkno, 1);
+    if (be16_to_cpu(nhdr->info.magic) == XFS_DA_NODE_MAGIC) {
+	btcount = be16_to_cpu(nhdr->count);
+	btree = (xfs_da_node_entry_t *)((uint8_t *)nhdr +
+					sizeof(struct xfs_da_node_hdr));
+    } else if (be16_to_cpu(nhdr->info.magic) == XFS_DA3_NODE_MAGIC) {
+	btcount = be16_to_cpu(((xfs_da3_node_hdr_t *)nhdr)->count);
+	btree = (xfs_da_node_entry_t *)((uint8_t *)nhdr +
+					sizeof(struct xfs_da3_node_hdr));
+    } else {
+        xfs_error("Node's magic number (0x%04x) does not match!",
+		  be16_to_cpu(nhdr->info.magic));
         goto out;
     }
 
 try_next_btree:
-    if (!node->hdr.count ||
-	XFS_PVT(inode)->i_btree_offset >= be16_to_cpu(node->hdr.count))
+    if (!btcount ||
+	XFS_PVT(inode)->i_btree_offset >= btcount)
 	goto out;
 
-    fsblkno = be32_to_cpu(node->btree[XFS_PVT(inode)->i_btree_offset].before);
+    fsblkno = be32_to_cpu(btree[XFS_PVT(inode)->i_btree_offset].before);
     fsblkno = xfs_dir2_get_right_blk(fs, core, fsblkno, &error);
     if (error) {
         xfs_error("Cannot find leaf rec!");
         goto out;
     }
 
-    leaf = (xfs_dir2_leaf_t*)xfs_dir2_dirblks_get_cached(fs, fsblkno, 1);
-    if (be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR2_LEAFN_MAGIC) {
-        xfs_error("Leaf's magic number does not match!");
+    lhdr = xfs_dir2_dirblks_get_cached(fs, fsblkno, 1);
+    if (be16_to_cpu(lhdr->info.magic) == XFS_DIR2_LEAFN_MAGIC) {
+	lfcount = be16_to_cpu(lhdr->count);
+	ents = (xfs_dir2_leaf_entry_t *)((uint8_t *)lhdr +
+					 sizeof(struct xfs_dir2_leaf_hdr));
+    } else if (be16_to_cpu(lhdr->info.magic) == XFS_DIR3_LEAFN_MAGIC) {
+	lfcount = be16_to_cpu(((xfs_dir3_leaf_hdr_t *)lhdr)->count);
+	ents = (xfs_dir2_leaf_entry_t *)((uint8_t *)lhdr +
+					 sizeof(struct xfs_dir3_leaf_hdr));
+    } else {
+        xfs_error("Leaf's magic number does not match (0x%04x)!",
+		  be16_to_cpu(lhdr->info.magic));
         goto out;
     }
 
-    if (!leaf->hdr.count ||
-	XFS_PVT(inode)->i_leaf_ent_offset >= be16_to_cpu(leaf->hdr.count)) {
+    if (!lfcount ||
+	XFS_PVT(inode)->i_leaf_ent_offset >= lfcount) {
 	XFS_PVT(inode)->i_btree_offset++;
 	XFS_PVT(inode)->i_leaf_ent_offset = 0;
 	goto try_next_btree;
     }
 
-    lep = &leaf->ents[XFS_PVT(inode)->i_leaf_ent_offset];
+    lep = &ents[XFS_PVT(inode)->i_leaf_ent_offset];
 
     /* Skip over stale leaf entries */
-    for ( ; XFS_PVT(inode)->i_leaf_ent_offset < be16_to_cpu(leaf->hdr.count) &&
+    for ( ; XFS_PVT(inode)->i_leaf_ent_offset < lfcount &&
 			be32_to_cpu(lep->address) == XFS_DIR2_NULL_DATAPTR;
-	  lep++, XFS_PVT(inode)->i_leaf_ent_offset++);
+	  lep++, XFS_PVT(inode)->i_leaf_ent_offset++)
+	;
 
-    if (XFS_PVT(inode)->i_leaf_ent_offset == be16_to_cpu(leaf->hdr.count)) {
+    if (XFS_PVT(inode)->i_leaf_ent_offset == lfcount) {
 	XFS_PVT(inode)->i_btree_offset++;
 	XFS_PVT(inode)->i_leaf_ent_offset = 0;
 	goto try_next_btree;
@@ -359,7 +403,8 @@ try_next_btree:
 
     buf = xfs_dir2_dirblks_get_cached(fs, fsblkno, 1);
     data_hdr = (xfs_dir2_data_hdr_t *)buf;
-    if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC) {
+    if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC &&
+	be32_to_cpu(data_hdr->magic) != XFS_DIR3_DATA_MAGIC) {
 	xfs_error("Leaf directory's data magic No. does not match!");
 	goto out;
     }
-- 
2.4.3



More information about the Syslinux mailing list