[syslinux:elflink] xfs: Add xfs_iget() to filesystem ops

syslinux-bot for Paulo Alcantara pcacjr at zytor.com
Tue Nov 27 12:57:07 PST 2012


Commit-ID:  533d97658da0fb3ae0930c58520bf7a27177fa8f
Gitweb:     http://www.syslinux.org/commit/533d97658da0fb3ae0930c58520bf7a27177fa8f
Author:     Paulo Alcantara <pcacjr at zytor.com>
AuthorDate: Fri, 13 Jul 2012 16:45:56 -0300
Committer:  Paulo Alcantara <pcacjr at zytor.com>
CommitDate: Sat, 21 Jul 2012 01:21:46 -0300

xfs: Add xfs_iget() to filesystem ops

xfs_iget() function is responsible for finding an entry from a given
filename and parent inode of the entry being looked up.

XFS_INO_TO_FSB() returns a sparse enconding of the disk location, and not
the right filesystem block number we expected to have from the wrong byte
offset.

Now, there is a ino_to_bytes() macro who calculates correctly the block
offset from a given inode number, and then we get the *right* filesystem
block number we expected.

Also, use agnumber_to_bytes() in order to get the right filesystem block
where the given AG number resides.

Signed-off-by: Paulo Alcantara <pcacjr at zytor.com>

---
 core/fs/xfs/xfs.c | 273 ++++++++++++++++++++++++++++++++++++++++++++++--------
 core/fs/xfs/xfs.h |  88 ++++++++++++++++--
 2 files changed, 316 insertions(+), 45 deletions(-)

diff --git a/core/fs/xfs/xfs.c b/core/fs/xfs/xfs.c
index 1987fa7..eaad4ea 100644
--- a/core/fs/xfs/xfs.c
+++ b/core/fs/xfs/xfs.c
@@ -57,7 +57,7 @@ static xfs_agi_t *xfs_get_agi(struct fs_info *fs, xfs_ino_t ino)
 	goto out;
     }
 
-    blk = XFS_AGNO_TO_FSB(fs, agno);
+    blk = agnumber_to_bytes(fs, agno) >> BLOCK_SHIFT(fs);
     agi = XFS_AGI_OFFS(fs, get_cache(fs->fs_dev, blk));
     if (!agi) {
 	xfs_error("Error in reading filesystem block 0x%llX (%llu)", blk, blk);
@@ -84,7 +84,12 @@ static xfs_dinode_t *xfs_get_ino_core(struct fs_info *fs, xfs_ino_t ino)
     block_t blk;
     xfs_dinode_t *core;
 
-    blk = ino << XFS_INFO(fs)->inode_shift >> BLOCK_SHIFT(fs);
+    xfs_debug("ino %lu", ino);
+
+    blk = ino_to_bytes(fs, ino) >> BLOCK_SHIFT(fs);
+
+    xfs_debug("blk %llu block offset 0x%llx", blk, blk << BLOCK_SHIFT(fs));
+
     core = (xfs_dinode_t *)get_cache(fs->fs_dev, blk);
     if (!core) {
 	xfs_error("Error in reading filesystem block 0x%llX (%llu)", blk, blk);
@@ -94,6 +99,8 @@ static xfs_dinode_t *xfs_get_ino_core(struct fs_info *fs, xfs_ino_t ino)
     if (be16_to_cpu(core->di_magic) !=
 	be16_to_cpu(*(uint16_t *)XFS_DINODE_MAGIC)) {
 	xfs_error("Inode core's magic number does not match!");
+	xfs_debug("magic number 0x%04x", (be16_to_cpu(core->di_magic)));
+
 	goto out;
     }
 
@@ -125,7 +132,7 @@ static const void *xfs_get_ino_chunk(struct fs_info *fs, xfs_ino_t ino)
     while (nblks--) {
 	p = (uint8_t *)get_cache(fs->fs_dev, start_blk++);
 
-	memcpy(buf, p, BLOCK_SIZE(fs));
+	memcpy(buf + offset, p, BLOCK_SIZE(fs));
 	offset += BLOCK_SIZE(fs);
     }
 
@@ -134,26 +141,220 @@ static const void *xfs_get_ino_chunk(struct fs_info *fs, xfs_ino_t ino)
 
 /* Find an inode from a chunk of 64 inodes by giving its inode # */
 static xfs_dinode_t *xfs_find_chunk_ino(struct fs_info *fs,
-					block_t start_ino, xfs_ino_t ino)
+					block_t start_ino, xfs_ino_t ino,
+					uint64_t *chunk_offset)
 {
     uint8_t *p;
-    uint64_t offset;
 
-    if (start_ino == ino)
+    if (start_ino == ino) {
+	*chunk_offset = 0;
 	return xfs_get_ino_core(fs, ino);
+    }
 
     p = (uint8_t *)xfs_get_ino_chunk(fs, start_ino);
-    offset = (ino - start_ino) << XFS_INFO(fs)->inode_shift;
+    *chunk_offset = (ino - start_ino) << XFS_INFO(fs)->inode_shift;
+
+    return (xfs_dinode_t *)p + *chunk_offset;
+}
+
+static char *get_entry_name(uint8_t *start, uint8_t *end)
+{
+    char *s;
+    char *p;
+
+    s = malloc(end - start + 1);
+    if (!s)
+	malloc_error("string");
 
-    return (xfs_dinode_t *)p + offset;
+    p = s;
+    while (start < end)
+	*p++ = *start++;
+
+    *p = '\0';
+
+    return s;
 }
 
-static struct inode *xfs_iget(const char *unused_0, struct inode *unused_1)
+struct inode *xfs_fmt_local_find_entry(const char *dname, struct inode *parent,
+				       xfs_dinode_t *core)
 {
-    (void)unused_0;
-    (void)unused_1;
+    xfs_dir2_sf_t *sf = (xfs_dir2_sf_t *)&core->di_literal_area[0];
+    xfs_dir2_sf_entry_t *sf_entry;
+    uint8_t count = sf->hdr.i8count ? sf->hdr.i8count : sf->hdr.count;
+    struct fs_info *fs = parent->fs;
+    struct inode *inode;
+    xfs_intino_t ino;
+    block_t parent_blk;
+    block_t blk;
+    xfs_agi_t *agi;
+    xfs_btree_sblock_t *ibt_hdr;
+    uint32_t i;
+    xfs_inobt_rec_t *rec;
+    xfs_dinode_t *ncore = NULL;
+
+    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->hdr.i8count ? 4 : 0));
+    while (count--) {
+	uint8_t *start_name = &sf_entry->name[0];
+	uint8_t *end_name = start_name + sf_entry->namelen;
+	char *name;
 
-    xfs_debug("in");
+	name = get_entry_name(start_name, end_name);
+
+	xfs_debug("entry name: %s", name);
+
+	if (!strncmp(name, dname, strlen(dname)))
+	    goto found;
+
+	free(name);
+
+	sf_entry = (xfs_dir2_sf_entry_t *)((uint8_t *)sf_entry +
+					   offsetof(struct xfs_dir2_sf_entry,
+						    name[0]) +
+					   sf_entry->namelen +
+					   (sf->hdr.i8count ? 8 : 4));
+    }
+
+    return NULL;
+
+found:
+    inode = xfs_new_inode(fs);
+
+    XFS_PVT(inode)->i_chunk_offset = 0;
+
+    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)));
+
+    xfs_debug("entry inode's number %lu", ino);
+
+    /* Check if the inode's filesystem block is the as the parent inode */
+    parent_blk = parent->ino << XFS_INFO(fs)->inode_shift >> BLOCK_SHIFT(fs);
+    blk = ino << XFS_INFO(fs)->inode_shift >> BLOCK_SHIFT(fs);
+
+    xfs_debug("parent_blk %llu blk %llu", parent_blk, blk);
+
+    if (parent_blk != blk)
+	goto no_agi_needed;
+
+    agi = xfs_get_agi(fs, ino);
+    if (!agi) {
+	xfs_error("Failed to get AGI from inode %lu", ino);
+	goto out;
+    }
+
+    blk = agnumber_to_bytes(fs, XFS_INO_TO_AGNO(fs, ino)) >> BLOCK_SHIFT(fs);
+    XFS_PVT(inode)->i_agblock = blk;
+
+    /* Get block number relative to the AG containing the root of the inode
+     * B+tree.
+     */
+    blk += be32_to_cpu(agi->agi_root);;
+
+    xfs_debug("inode B+tree's block %llu", blk);
+
+    ibt_hdr = (xfs_btree_sblock_t *)get_cache(fs->fs_dev, blk);
+    if (!ibt_hdr) {
+	xfs_error("Error in reading filesystem block 0x%llX (%llu)", blk, blk);
+	goto out;
+    }
+
+    if (be32_to_cpu(ibt_hdr->bb_magic) !=
+	be32_to_cpu(*(uint32_t *)XFS_IBT_MAGIC)) {
+	xfs_error("AGI inode B+tree header's magic number does not match!");
+	goto out;
+    }
+
+    xfs_debug("bb_level %lu", ibt_hdr->bb_level);
+    xfs_debug("bb_numrecs %lu", ibt_hdr->bb_numrecs);
+
+    rec = (xfs_inobt_rec_t *)((uint8_t *)ibt_hdr + sizeof *ibt_hdr);
+    for (i = ibt_hdr->bb_numrecs; i--; rec++) {
+	xfs_debug("freecount %lu free 0x%llx", be32_to_cpu(rec->ir_freecount),
+		  be64_to_cpu(rec->ir_free));
+
+	ncore = xfs_find_chunk_ino(fs, be32_to_cpu(rec->ir_startino),
+				   ino, &XFS_PVT(inode)->i_chunk_offset);
+	if (ncore)
+	    goto core_found;
+    }
+
+out:
+    free(inode);
+
+    if (ncore && XFS_PVT(inode)->i_chunk_offset)
+	free(ncore);
+
+    return NULL;
+
+no_agi_needed:
+    ncore = xfs_get_ino_core(fs, ino);
+
+core_found:
+    inode->ino = ino;
+
+    XFS_PVT(inode)->i_ino_blk	= ino_to_bytes(fs, ino) >> BLOCK_SHIFT(fs);
+    inode->ino			= ino;
+    inode->mode 		= DT_DIR;
+    inode->size 		= be64_to_cpu(ncore->di_size);
+
+    if (be16_to_cpu(ncore->di_mode) & S_IFDIR)
+	inode->mode = DT_DIR;
+    else if (be16_to_cpu(ncore->di_mode) & S_IFREG)
+	inode->mode = DT_REG;
+
+    xfs_debug("Found a %s inode", inode->mode == DT_DIR ? "directory" : "file");
+
+    if (ncore && XFS_PVT(inode)->i_chunk_offset)
+	free(ncore);
+
+    return inode;
+}
+
+static struct inode *xfs_iget(const char *dname, struct inode *parent)
+{
+    struct fs_info *fs = parent->fs;
+    xfs_dinode_t *core;
+    struct inode *inode = NULL;
+
+    xfs_debug("dname %s parent %p parent ino %lu", dname, parent, parent->ino);
+
+    /* Check if we need the region for the chunk of 64 inodes */
+    if (XFS_PVT(parent)->i_chunk_offset) {
+	core = (xfs_dinode_t *)((uint8_t *)xfs_get_ino_chunk(fs, parent->ino) +
+				XFS_PVT(parent)->i_chunk_offset);
+
+	xfs_debug("core's magic number 0x%04x", be16_to_cpu(core->di_magic));
+
+	if (be16_to_cpu(core->di_magic) !=
+	    be16_to_cpu(*(uint16_t *)XFS_DINODE_MAGIC)) {
+	    xfs_error("Inode core's magic number does not match!");
+	    goto out;
+	}
+    } else {
+	core = xfs_get_ino_core(fs, parent->ino);
+    }
+
+    if (parent->mode == DT_DIR) { /* Is this inode a directory ? */
+	/* TODO: Handle both shortform directories and directory blocks */
+	if (core->di_format == XFS_DINODE_FMT_LOCAL) {
+	    inode = xfs_fmt_local_find_entry(dname, parent, core);
+	} else {
+	    xfs_debug("format %hhu", core->di_format);
+	    xfs_debug("TODO: format \"local\" is the only supported ATM");
+	    goto out;
+	}
+    }
+
+    return inode;
+
+out:
+    if (XFS_PVT(parent)->i_chunk_offset)
+	free(core);
 
     return NULL;
 }
@@ -165,7 +366,7 @@ static struct inode *xfs_iget_root(struct fs_info *fs)
     xfs_btree_sblock_t *ibt_hdr;
     uint32_t i;
     xfs_inobt_rec_t *rec;
-    xfs_dinode_t *core;
+    xfs_dinode_t *core = NULL;
     struct inode *inode = xfs_new_inode(fs);
 
     xfs_debug("Looking for the root inode...");
@@ -176,7 +377,8 @@ static struct inode *xfs_iget_root(struct fs_info *fs)
 	goto out;
     }
 
-    blk = XFS_AGNO_TO_FSB(fs, XFS_INO_TO_AGNO(fs, XFS_INFO(fs)->rootino));
+    blk = agnumber_to_bytes(fs, XFS_INO_TO_AGNO(fs, XFS_INFO(fs)->rootino)) >>
+								BLOCK_SHIFT(fs);
     XFS_PVT(inode)->i_agblock = blk;
 
     /* Get block number relative to the AG containing the root of the inode
@@ -207,15 +409,13 @@ static struct inode *xfs_iget_root(struct fs_info *fs)
 		  be64_to_cpu(rec->ir_free));
 
 	core = xfs_find_chunk_ino(fs, be32_to_cpu(rec->ir_startino),
-				  XFS_INFO(fs)->rootino);
+				  XFS_INFO(fs)->rootino,
+				  &XFS_PVT(inode)->i_chunk_offset);
 	if (core)
 	    goto found;
     }
 
     xfs_error("Root inode not found!");
-
-    free(core);
-
     goto not_found;
 
 found:
@@ -226,13 +426,14 @@ found:
 	goto out;
     }
 
-    XFS_PVT(inode)->i_ino_blk	= XFS_INFO(fs)->rootino <<
-				XFS_INFO(fs)->inode_shift >> BLOCK_SHIFT(fs);
+    XFS_PVT(inode)->i_ino_blk	= ino_to_bytes(fs, XFS_INFO(fs)->rootino) >>
+								BLOCK_SHIFT(fs);
     inode->ino			= XFS_INFO(fs)->rootino;
     inode->mode 		= DT_DIR;
     inode->size 		= be64_to_cpu(core->di_size);
 
-    free(core);
+    if (core && XFS_PVT(inode)->i_chunk_offset)
+	free(core);
 
     return inode;
 
@@ -241,6 +442,9 @@ not_found:
 out:
     free(inode);
 
+    if (core && XFS_PVT(inode)->i_chunk_offset)
+	free(core);
+
     return NULL;
 }
 
@@ -262,23 +466,16 @@ static struct xfs_fs_info *xfs_new_sb_info(xfs_sb_t *sb)
     if (!info)
 	malloc_error("xfs_fs_info structure");
 
-    info->blocksize = be32_to_cpu(sb->sb_blocksize);
-    info->block_shift = sb->sb_blocklog;
-
-    /* Calculate the bits used in the Relative inode number */
-    info->ag_relative_ino_shift = sb->sb_inopblog + sb->sb_agblklog;
-    /* The MSB is the AG number in Absolute inode numbers */
-    info->ag_number_ino_shift = 8 * sizeof(xfs_ino_t) -
-				info->ag_relative_ino_shift;
-
-    info->rootino = be64_to_cpu(sb->sb_rootino);
-
-    info->agblocks = be32_to_cpu(sb->sb_agblocks);
-    info->agblocks_shift = sb->sb_agblklog;
-    info->agcount = be32_to_cpu(sb->sb_agcount);
-
-    info->inodesize = be16_to_cpu(sb->sb_inodesize);
-    info->inode_shift = sb->sb_inodelog;
+    info->blocksize		= be32_to_cpu(sb->sb_blocksize);
+    info->block_shift		= sb->sb_blocklog;
+    info->inopb_shift 		= sb->sb_inopblog;
+    info->agblk_shift 		= sb->sb_agblklog;
+    info->rootino 		= be64_to_cpu(sb->sb_rootino);
+    info->agblocks 		= be32_to_cpu(sb->sb_agblocks);
+    info->agblocks_shift 	= sb->sb_agblklog;
+    info->agcount 		= be32_to_cpu(sb->sb_agcount);
+    info->inodesize 		= be16_to_cpu(sb->sb_inodesize);
+    info->inode_shift 		= sb->sb_inodelog;
 
     return info;
 }
diff --git a/core/fs/xfs/xfs.h b/core/fs/xfs/xfs.h
index 2aae8c1..17720f8 100644
--- a/core/fs/xfs/xfs.h
+++ b/core/fs/xfs/xfs.h
@@ -40,14 +40,50 @@ struct xfs_fs_info;
 #define XFS_INFO(fs) ((struct xfs_fs_info *)((fs)->fs_info))
 #define XFS_PVT(ino) ((struct xfs_inode *)((ino)->pvt))
 
+#define XFS_INO_MASK(k)                 (uint32_t)((1ULL << (k)) - 1)
+#define XFS_INO_AGINO_BITS(fs) \
+    (XFS_INFO((fs))->inopb_shift + XFS_INFO((fs))->agblk_shift)
+
+#define XFS_INO_TO_AGINO(fs, i) \
+    ((xfs_agino_t)(i) & XFS_INO_MASK(XFS_INO_AGINO_BITS(fs)))
+
 #define XFS_INO_TO_AGNO(fs, ino) \
-    (xfs_agnumber_t)((ino) >> XFS_INFO((fs))->ag_relative_ino_shift)
+    ((xfs_agnumber_t)((ino) >> (XFS_INFO((fs))->inopb_shift + \
+				XFS_INFO((fs))->agblk_shift)))
 
 #define XFS_AGNO_TO_FSB(fs, agno) \
-    (block_t)((agno) << XFS_INFO((fs))->agblocks_shift)
+    ((block_t)((agno) << XFS_INFO((fs))->agblocks_shift))
 
 #define XFS_AGI_OFFS(fs, mp) \
-    (xfs_agi_t *)((uint8_t *)(mp) + 2 * SECTOR_SIZE((fs)))
+    ((xfs_agi_t *)((uint8_t *)(mp) + 2 * SECTOR_SIZE((fs))))
+
+#define XFS_GET_DIR_INO4(di) \
+    (((uint32_t)(di).i[0] << 24) | ((di).i[1] << 16) | ((di).i[2] << 8) | \
+		((di).i[3]))
+
+#define XFS_DI_HI(di) \
+    (((uint32_t)(di).i[1] << 16) | ((di).i[2] << 8) | ((di).i[3]))
+
+#define XFS_DI_LO(di) \
+    (((uint32_t)(di).i[4] << 24) | ((di).i[5] << 16) | ((di).i[6] << 8) | \
+		((di).i[7]))
+
+#define XFS_GET_DIR_INO8(di) \
+    (((xfs_ino_t)XFS_DI_LO(di) & 0xffffffffULL) | \
+     ((xfs_ino_t)XFS_DI_HI(di) << 32))
+
+#define agblock_to_bytes(fs, x) \
+    ((uint64_t)(x) << BLOCK_SHIFT((fs)))
+#define agino_to_bytes(fs, x) \
+    ((uint64_t)(x) << XFS_INFO((fs))->inode_shift)
+#define agnumber_to_bytes(fs, x) \
+    agblock_to_bytes(fs, (uint64_t)(x) * XFS_INFO((fs))->agblocks)
+#define fsblock_to_bytes(x)     \
+    (agnumber_to_bytes(fs, XFS_FSB_TO_AGNO(mp, (x))) +	\
+     agblock_to_bytes(fs, XFS_FSB_TO_AGBNO(mp, (x))))
+#define ino_to_bytes(fs, x)			   \
+    (agnumber_to_bytes(fs, XFS_INO_TO_AGNO(fs, (x))) +	\
+     agino_to_bytes(fs, XFS_INO_TO_AGINO(fs, (x))))
 
 /* Superblock's LBA */
 #define XFS_SB_DADDR ((xfs_daddr_t)0) /* daddr in filesystem/ag */
@@ -146,18 +182,16 @@ typedef struct xfs_sb {
 struct xfs_fs_info {
     uint32_t 		blocksize; /* Filesystem block size */
     uint8_t		block_shift; /* Filesystem block size in bits */
+    uint8_t		inopb_shift;
+    uint8_t		agblk_shift;
 
-    /* AG relative inode number bits (found within AG's inode structures) */
-    uint8_t		ag_relative_ino_shift;
     /* AG number bits (MSB of the inode number) */
     uint8_t		ag_number_ino_shift;
 
     xfs_ino_t 		rootino; /* Root inode number for the filesystem */
-
     xfs_agblock_t	agblocks; /* Size of each AG in blocks */
     uint8_t		agblocks_shift; /* agblocks in bits */
     xfs_agnumber_t	agcount; /* Number of AGs in the filesytem */
-
     uint16_t		inodesize; /* Size of the inode in bytes */
     uint8_t		inode_shift; /* Inode size in bits */
 } __attribute__((__packed__));
@@ -245,13 +279,53 @@ 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;
 
 struct xfs_inode {
     xfs_agblock_t 	i_agblock;
     block_t		i_ino_blk;
+    uint64_t		i_chunk_offset;
 };
 
+typedef struct { uint8_t i[8]; } __attribute__((__packed__)) xfs_dir2_ino8_t;
+typedef struct { uint8_t i[4]; } __attribute__((__packed__)) xfs_dir2_ino4_t;
+
+typedef union {
+    xfs_dir2_ino8_t i8;
+    xfs_dir2_ino4_t i4;
+} __attribute__((__packed__)) xfs_dir2_inou_t;
+
+typedef struct { uint8_t i[2]; } __attribute__((__packed__)) xfs_dir2_sf_off_t;
+
+typedef struct xfs_dir2_sf_hdr {
+    uint8_t		count;		/* count of entries */
+    uint8_t           	i8count;        /* count of 8-byte inode #s */
+    xfs_dir2_inou_t     parent;         /* parent dir inode number */
+} __attribute__((__packed__)) xfs_dir2_sf_hdr_t;
+
+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 */
+} __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 */
+} __attribute__((__packed__)) xfs_dir2_sf_t;
+
+typedef xfs_ino_t	xfs_intino_t;
+
+static inline xfs_intino_t xfs_dir2_sf_get_inumber(xfs_dir2_sf_t *sfp,
+						   xfs_dir2_inou_t *from)
+{
+    return ((sfp)->hdr.i8count == 0 ? \
+	    (xfs_intino_t)XFS_GET_DIR_INO4((from)->i4) : \
+	    (xfs_intino_t)XFS_GET_DIR_INO8((from)->i8));
+}
+
 static inline bool xfs_is_valid_magicnum(const xfs_sb_t *sb)
 {
     return sb->sb_magicnum == *(uint32_t *)XFS_SB_MAGIC;


More information about the Syslinux-commits mailing list