aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChen Baozi <baozich@gmail.com>2012-07-21 18:07:02 +0800
committerPaulo Alcantara <pcacjr@zytor.com>2012-07-22 19:31:36 -0300
commit0a66332f83676b829e034a8a95c329b047cee55a (patch)
tree33257a2dcd5447572075c282aa3d86d7ef769307
parentc6ebbeeae4df1ba6adde3dca6c25097a77aad67a (diff)
downloadsyslinux-0a66332f83676b829e034a8a95c329b047cee55a.tar.gz
syslinux-0a66332f83676b829e034a8a95c329b047cee55a.tar.xz
syslinux-0a66332f83676b829e034a8a95c329b047cee55a.zip
xfs: Implement xfs_dir2_leaf_find_entry() logic.
xfs_dir2_leaf_find_entry() will first do binary search with hashval of target name in the leaf block, and then calculate which directory block the entry locates. Besides, xfs_dir2_isleaf() has also been implemented to judge whether it is a leaf directory or not. Signed-off-by: Chen Baozi <baozich@gmail.com> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
-rw-r--r--core/fs/xfs/xfs.c195
-rw-r--r--core/fs/xfs/xfs.h85
2 files changed, 265 insertions, 15 deletions
diff --git a/core/fs/xfs/xfs.c b/core/fs/xfs/xfs.c
index c91e9c36..dc4faad7 100644
--- a/core/fs/xfs/xfs.c
+++ b/core/fs/xfs/xfs.c
@@ -34,6 +34,33 @@
#include "xfs.h"
#include "xfs_ag.h"
+uint32_t xfs_da_hashname(const uint8_t *name, int namelen)
+{
+ uint32_t hash;
+
+ /*
+ * Do four characters at a time as long as we can.
+ */
+ for (hash = 0; namelen >= 4; namelen -=4, name += 4)
+ hash = (name[0] << 21) ^ (name[1] << 14) ^ (name[2] << 7) ^
+ (name[3] << 0) ^ rol32(hash, 7 * 4);
+
+ /*
+ * Now do the rest of the characters.
+ */
+ switch (namelen) {
+ case 3:
+ return (name[0] << 14) ^ (name[1] << 7) ^ (name[2] << 0) ^
+ rol32(hash, 7 * 3);
+ case 2:
+ return (name[0] << 7) ^ (name[1] << 0) ^ rol32(hash, 7 * 2);
+ case 1:
+ return (name[0] << 0) ^ rol32(hash, 7 * 1);
+ default: /* case 0: */
+ return hash;
+ }
+}
+
static inline struct inode *xfs_new_inode(struct fs_info *fs)
{
struct inode *inode;
@@ -107,12 +134,16 @@ static char *get_entry_name(uint8_t *start, uint8_t *end)
}
/* See if the directory is a single-leaf form directory. */
-static bool xfs_dir2_isleaf(xfs_dinode_t *dip)
+static bool xfs_dir2_isleaf(struct fs_info *fs, xfs_dinode_t *dip)
{
- (void) dip;
- xfs_debug("in");
-
- return true;
+ uint64_t last = 0;
+ xfs_bmbt_irec_t irec;
+
+ bmbt_irec_get(&irec, ((xfs_bmbt_rec_t *)&dip->di_literal_area[0]) +
+ be32_to_cpu(dip->di_nextents) - 1);
+ last = irec.br_startoff + irec.br_blockcount;
+
+ return (last == XFS_INFO(fs)->dirleafblk + (1 << XFS_INFO(fs)->dirblklog));
}
static void *get_dirblk(struct fs_info *fs, block_t startblock)
@@ -403,7 +434,7 @@ static int xfs_fmt_extents_readdir(struct file *file, struct dirent *dirent,
if (be32_to_cpu(core->di_nextents) <= 1) {
/* Single-block Directories */
retval = xfs_dir2_block_readdir(file, dirent, core);
- } else if (xfs_dir2_isleaf(core)) {
+ } else if (xfs_dir2_isleaf(file->fs, core)) {
/* Leaf Directory */
retval = xfs_dir2_leaf_readdir(file, dirent, core);
} else {
@@ -607,11 +638,142 @@ static struct inode *xfs_dir2_leaf_find_entry(const char *dname,
struct inode *parent,
xfs_dinode_t *core)
{
- (void)dname;
- (void)parent;
- (void)core;
+ xfs_dir2_leaf_t *leaf;
+ xfs_bmbt_irec_t irec;
+ block_t leaf_blk, dir_blk;
+ xfs_dir2_leaf_entry_t *lep;
+ int low, high, mid;
+ uint32_t hash, hashwant;
+ uint32_t newdb, curdb = -1;
+ xfs_dir2_data_entry_t *dep;
+ struct inode *ip;
+ xfs_dir2_data_hdr_t *data_hdr;
+ uint8_t *start_name, *end_name;
+ char *name;
+ xfs_intino_t ino;
+ xfs_dinode_t *ncore;
+ uint8_t *buf = NULL;
+
+ bmbt_irec_get(&irec, ((xfs_bmbt_rec_t *)&core->di_literal_area[0]) +
+ 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 *)get_dirblk(parent->fs, leaf_blk);
+ if (be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR2_LEAF1_MAGIC) {
+ xfs_error("Single leaf block header's magic number does not match!");
+ goto out;
+ }
+
+ if (!leaf->hdr.count)
+ goto out;
+
+ hashwant = xfs_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; ) {
+ mid = (low + high) >> 1;
+ if ((hash = be32_to_cpu(lep[mid].hashval)) == hashwant)
+ break;
+ if (hash < hashwant)
+ low = mid + 1;
+ else
+ high = mid + 1;
+ }
+
+ if (hash == hashwant)
+ while (mid > 0 && be32_to_cpu(lep[mid - 1].hashval) == hashwant)
+ mid--;
+ else
+ goto out;
+
+ for (lep = &leaf->ents[mid];
+ mid < be16_to_cpu(leaf->hdr.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;
+
+ newdb = xfs_dir2_dataptr_to_db(parent->fs, be32_to_cpu(lep->address));
+ if (newdb != curdb) {
+ if (buf)
+ free(buf);
+
+ bmbt_irec_get(&irec,
+ ((xfs_bmbt_rec_t *)&core->di_literal_area[0]) + newdb);
+ dir_blk = fsblock_to_bytes(parent->fs, irec.br_startblock) >>
+ BLOCK_SHIFT(parent->fs);
+ buf = get_dirblk(parent->fs, dir_blk);
+ data_hdr = (xfs_dir2_data_hdr_t *)buf;
+ if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC) {
+ xfs_error("Leaf directory's data magic number does not much!");
+ goto out1;
+ }
+ curdb = newdb;
+ }
+ /* Point to the data entry */
+ 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];
+ end_name = start_name + dep->namelen;
+ name = get_entry_name(start_name, end_name);
+
+ if (!strncmp(name, dname, strlen(dname))) {
+ free(name);
+ goto found;
+ }
+ free(name);
+ }
+out1:
+ free(buf);
+out:
+ free(leaf);
return NULL;
+
+found:
+ ip = xfs_new_inode(parent->fs);
+
+ ino = be64_to_cpu(dep->inumber);
+
+ xfs_debug("entry inode's number %lu", ino);
+
+ ncore = xfs_get_ino_core(parent->fs, ino);
+ if (!ncore) {
+ xfs_error("Failed to get dinode!");
+ goto failed;
+ }
+
+ fill_xfs_inode_pvt(parent->fs, ip, ino);
+
+ ip->ino = ino;
+ XFS_PVT(ip)->i_ino_blk = ino_to_bytes(parent->fs, ino) >>
+ BLOCK_SHIFT(parent->fs);
+ ip->size = be64_to_cpu(ncore->di_size);
+
+ if (be16_to_cpu(ncore->di_mode) & S_IFDIR) {
+ ip->mode = DT_DIR;
+ xfs_debug("Found a directory inode!");
+ } else if (be16_to_cpu(ncore->di_mode) & S_IFREG) {
+ ip->mode = DT_REG;
+ xfs_debug("Found a file inode!");
+ xfs_debug("inode size %llu", ip->size);
+ }
+
+ xfs_debug("entry inode's number %lu", ino);
+
+ free(buf);
+ free(leaf);
+ return ip;
+
+failed:
+ free(ip);
+ free(buf);
+ free(leaf);
+ return ip;
}
static struct inode *xfs_dir2_node_find_entry(const char *dname,
@@ -631,15 +793,16 @@ static struct inode *xfs_fmt_extents_find_entry(const char *dname,
{
struct inode *inode;
+ xfs_debug("ino: %d", parent->ino);
if (be32_to_cpu(core->di_nextents) <= 1) {
- /* Single-block Directories */
- inode = xfs_dir2_block_find_entry(dname, parent, core);
- } else if (xfs_dir2_isleaf(core)) {
- /* Leaf Directory */
+ /* Single-block Directories */
+ inode = xfs_dir2_block_find_entry(dname, parent, core);
+ } else if (xfs_dir2_isleaf(parent->fs, core)) {
+ /* Leaf Directory */
inode = xfs_dir2_leaf_find_entry(dname, parent, core);
} else {
- /* Node Directory */
- inode = xfs_dir2_node_find_entry(dname, parent, core);
+ /* Node Directory */
+ inode = xfs_dir2_node_find_entry(dname, parent, core);
}
return inode;
@@ -797,6 +960,8 @@ static int xfs_fs_init(struct fs_info *fs)
cache_init(fs->fs_dev, BLOCK_SHIFT(fs));
+ XFS_INFO(fs)->dirleafblk = xfs_dir2_db_to_da(fs, XFS_DIR2_LEAF_FIRSTDB(fs));
+
return BLOCK_SHIFT(fs);
out:
diff --git a/core/fs/xfs/xfs.h b/core/fs/xfs/xfs.h
index 52380aec..a525a971 100644
--- a/core/fs/xfs/xfs.h
+++ b/core/fs/xfs/xfs.h
@@ -107,6 +107,8 @@ struct xfs_fs_info;
#define XFS_DIR2_DATA_MAGIC 0x58443244U /* XD2D: multiblock dirs */
#define XFS_DIR2_FREE_MAGIC 0x58443246U /* XD2F: free index blocks */
+#define XFS_DIR2_NULL_DATAPTR ((uint32_t)0)
+
/* File types and modes */
#define S_IFMT 00170000
#define S_IFSOCK 0140000
@@ -200,6 +202,7 @@ struct xfs_fs_info {
uint8_t dirblklog;
uint8_t inopb_shift;
uint8_t agblk_shift;
+ uint32_t dirleafblk;
/* AG number bits (MSB of the inode number) */
uint8_t ag_number_ino_shift;
@@ -418,6 +421,12 @@ static inline xfs_intino_t xfs_dir2_sf_get_inumber(xfs_dir2_sf_t *sfp,
#define XFS_DIR2_DATA_FREE_TAG 0xffff
#define XFS_DIR2_DATA_FD_COUNT 3
+/*
+ * Directory address space divided into sections.
+ * spaces separated by 32GB.
+ */
+#define XFS_DIR2_SPACE_SIZE (1ULL << (32 + XFS_DIR2_DATA_ALIGN_LOG))
+
typedef struct xfs_dir2_data_free {
uint16_t offset;
uint16_t length;
@@ -442,6 +451,16 @@ typedef struct xfs_dir2_data_unused {
/* uint16_t tag; */ /* starting offset of us */
} __attribute__((__packed__)) xfs_dir2_data_unused_t;
+/**
+ * rol32 - rotate a 32-bit value left
+ * @word: value to rotate
+ * @shift: bits to roll
+ */
+static inline uint32_t rol32(uint32_t word, signed int shift)
+{
+ return (word << shift) | (word >> (32 - shift));
+}
+
#define roundup(x, y) ( \
{ \
const typeof(y) __y = y; \
@@ -481,11 +500,77 @@ xfs_dir2_block_tail_p(struct xfs_fs_info *fs_info, struct xfs_dir2_data_hdr *hdr
((char *)hdr + fs_info->dirblksize)) - 1;
}
+static inline uint32_t
+xfs_dir2_db_to_da(struct fs_info *fs, uint32_t db)
+{
+ return db << XFS_INFO(fs)->dirblklog;
+}
+
+static inline int64_t
+xfs_dir2_dataptr_to_byte(uint32_t dp)
+{
+ return (int64_t)dp << XFS_DIR2_DATA_ALIGN_LOG;
+}
+
+static inline uint32_t
+xfs_dir2_byte_to_db(struct fs_info *fs, int64_t by)
+{
+ return (uint32_t)
+ (by >> (XFS_INFO(fs)->block_shift + XFS_INFO(fs)->dirblklog));
+}
+
+static inline uint32_t
+xfs_dir2_dataptr_to_db(struct fs_info *fs, uint32_t dp)
+{
+ return xfs_dir2_byte_to_db(fs, xfs_dir2_dataptr_to_byte(dp));
+}
+
+static inline unsigned int
+xfs_dir2_byte_to_off(struct fs_info *fs, int64_t by)
+{
+ return (unsigned int)(by &
+ (( 1 << (XFS_INFO(fs)->block_shift + XFS_INFO(fs)->dirblklog)) - 1));
+}
+
+static inline unsigned int
+xfs_dir2_dataptr_to_off(struct fs_info *fs, uint32_t dp)
+{
+ return xfs_dir2_byte_to_off(fs, xfs_dir2_dataptr_to_byte(dp));
+}
+
+#define XFS_DIR2_LEAF_SPACE 1
+#define XFS_DIR2_LEAF_OFFSET (XFS_DIR2_LEAF_SPACE * XFS_DIR2_SPACE_SIZE)
+#define XFS_DIR2_LEAF_FIRSTDB(fs) \
+ xfs_dir2_byte_to_db(fs, XFS_DIR2_LEAF_OFFSET)
+
+typedef struct xfs_da_blkinfo {
+ uint32_t forw;
+ uint32_t back;
+ uint16_t magic;
+ uint16_t pad;
+} __attribute__((__packed__)) xfs_da_blkinfo_t;
+
+typedef struct xfs_dir2_leaf_hdr {
+ xfs_da_blkinfo_t info;
+ uint16_t count;
+ uint16_t stale;
+} __attribute__((__packed__)) xfs_dir2_leaf_hdr_t;
+
typedef struct xfs_dir2_leaf_entry {
uint32_t hashval; /* hash value of name */
uint32_t address; /* address of data entry */
} __attribute__((__packed__)) xfs_dir2_leaf_entry_t;
+typedef struct xfs_dir2_leaf {
+ xfs_dir2_leaf_hdr_t hdr; /* leaf header */
+ xfs_dir2_leaf_entry_t ents[]; /* entries */
+} __attribute__((__packed__)) xfs_dir2_leaf_t;
+
+#define XFS_DA_NODE_MAGIC 0xfebe /* magic number: non-leaf blocks */
+#define XFS_ATTR_LEAF_MAGIC 0xfbee /* magic number: attribute leaf blks */
+#define XFS_DIR2_LEAF1_MAGIC 0xd2f1 /* magic number: v2 dirlf single blks */
+#define XFS_DIR2_LEAFN_MAGIC 0xd2ff /* magic number: V2 dirlf multi blks */
+
static inline bool xfs_is_valid_magicnum(const xfs_sb_t *sb)
{
return sb->sb_magicnum == *(uint32_t *)XFS_SB_MAGIC;