aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChen Baozi <baozich@gmail.com>2012-07-19 14:47:19 +0800
committerPaulo Alcantara <pcacjr@zytor.com>2012-07-21 01:21:47 -0300
commitfa2b511f8b46c32c8dd1708d429331afa52d2003 (patch)
tree82e3a5f80a30757bbb42b8abaa5dcd238f5254f6
parent71275b153c60891c9f892c89837553bca80f23dc (diff)
downloadsyslinux-fa2b511f8b46c32c8dd1708d429331afa52d2003.tar.gz
syslinux-fa2b511f8b46c32c8dd1708d429331afa52d2003.tar.xz
syslinux-fa2b511f8b46c32c8dd1708d429331afa52d2003.zip
xfs: Implement dir2_block_find_entry() function.
dir2_block_find_entry() is used to handle the Block Directory, which is the sub-case of a XFS_DINODE_FMT_EXTENTS. Besides, we have established the frame work to handle all cases of XFS_DINODE_FMT_EXENTS. What to be worked next step is dir2_leaf_find_entry() and dir2_node_find_entry. To test this patch, I just touched 24 extra empty file named from 0 to 23 in the /boot directory, which number is more than what short format holds and less than the Leaf Directory holds. Signed-off-by: Chen Baozi <baozich@gmail.com> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
-rw-r--r--core/fs/xfs/xfs.c201
-rw-r--r--core/fs/xfs/xfs.h157
2 files changed, 336 insertions, 22 deletions
diff --git a/core/fs/xfs/xfs.c b/core/fs/xfs/xfs.c
index b5dacb4e..354e465a 100644
--- a/core/fs/xfs/xfs.c
+++ b/core/fs/xfs/xfs.c
@@ -30,9 +30,10 @@
#include "codepage.h"
#include "xfs_types.h"
#include "xfs_sb.h"
+#include "misc.h"
#include "xfs.h"
#include "xfs_ag.h"
-#include "misc.h"
+
static inline struct inode *xfs_new_inode(struct fs_info *fs)
{
@@ -51,9 +52,8 @@ static inline void fill_xfs_inode_pvt(struct fs_info *fs, struct inode *inode,
XFS_PVT(inode)->i_agblock =
agnumber_to_bytes(fs, XFS_INO_TO_AGNO(fs, ino)) >> BLOCK_SHIFT(fs);
XFS_PVT(inode)->i_ino_blk = ino_to_bytes(fs, ino) >> BLOCK_SHIFT(fs);
- XFS_PVT(inode)->i_block_offset =
- XFS_INO_TO_OFFSET((struct xfs_fs_info *)(fs->fs_info), ino) <<
- ((struct xfs_fs_info *)(fs->fs_info))->inode_shift;
+ XFS_PVT(inode)->i_block_offset = XFS_INO_TO_OFFSET(XFS_INFO(fs), ino) <<
+ XFS_INFO(fs)->inode_shift;
}
static xfs_dinode_t *xfs_get_ino_core(struct fs_info *fs, xfs_ino_t ino)
@@ -65,8 +65,7 @@ static xfs_dinode_t *xfs_get_ino_core(struct fs_info *fs, xfs_ino_t ino)
xfs_debug("ino %lu", ino);
blk = ino_to_bytes(fs, ino) >> BLOCK_SHIFT(fs);
- offset = XFS_INO_TO_OFFSET((struct xfs_fs_info *)(fs->fs_info), ino) <<
- ((struct xfs_fs_info *)(fs->fs_info))->inode_shift;
+ offset = XFS_INO_TO_OFFSET(XFS_INFO(fs), ino) << XFS_INFO(fs)->inode_shift;
if (offset > BLOCK_SIZE(fs)) {
xfs_error("Invalid inode offset in block!");
xfs_debug("offset: 0x%llx", offset);
@@ -108,6 +107,38 @@ static char *get_entry_name(uint8_t *start, uint8_t *end)
return s;
}
+/* See if the directory is a single-leaf form directory. */
+static bool xfs_dir2_isleaf(xfs_dinode_t *dip)
+{
+ (void) dip;
+ xfs_debug("in");
+
+ return true;
+}
+
+static void *get_dirblk(struct fs_info *fs, block_t startblock)
+{
+ int count = 1 << XFS_INFO(fs)->dirblklog;
+ uint8_t *p;
+ uint8_t *buf;
+ off_t offset = 0;
+
+ buf = malloc(XFS_INFO(fs)->dirblksize);
+ if (!buf)
+ malloc_error("buffer memory");
+
+ 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);
+ }
+
+ return buf;
+}
+
+
struct inode *xfs_fmt_local_find_entry(const char *dname, struct inode *parent,
xfs_dinode_t *core)
{
@@ -253,6 +284,151 @@ out:
return -1;
}
+static struct inode *dir2_block_find_entry(const char *dname,
+ struct inode *parent,
+ xfs_dinode_t *core)
+{
+ xfs_bmbt_irec_t r;
+ block_t dir_blk;
+ struct fs_info *fs = parent->fs;
+ uint8_t *dirblk_buf;
+ uint8_t *p, *endp;
+ xfs_dir2_data_hdr_t *hdr;
+ struct inode *inode = NULL;
+ xfs_dir2_block_tail_t *btp;
+ xfs_dir2_data_unused_t *dup;
+ xfs_dir2_data_entry_t *dep;
+ xfs_intino_t ino;
+ xfs_dinode_t *ncore;
+
+ bmbt_irec_get(&r, (xfs_bmbt_rec_t *)&core->di_literal_area[0]);
+ dir_blk = fsblock_to_bytes(fs, r.br_startblock) >> BLOCK_SHIFT(fs);
+
+ dirblk_buf = get_dirblk(fs, dir_blk);
+ hdr = (xfs_dir2_data_hdr_t *)dirblk_buf;
+ if (be32_to_cpu(hdr->magic) !=
+ XFS_DIR2_BLOCK_MAGIC) {
+ 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);
+
+ btp = xfs_dir2_block_tail_p(XFS_INFO(fs), hdr);
+ endp = (uint8_t *)((xfs_dir2_leaf_entry_t *)btp - be32_to_cpu(btp->count));
+
+ while (p < endp) {
+ uint8_t *start_name;
+ uint8_t *end_name;
+ char *name;
+
+ dup = (xfs_dir2_data_unused_t *)p;
+ if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
+ p += be16_to_cpu(dup->length);
+ continue;
+ }
+
+ dep = (xfs_dir2_data_entry_t *)p;
+
+ 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);
+ p += xfs_dir2_data_entsize(dep->namelen);
+ }
+
+out:
+ free(dirblk_buf);
+
+ return NULL;
+
+found:
+ inode = xfs_new_inode(fs);
+
+ ino = be64_to_cpu(dep->inumber);
+
+ xfs_debug("entry inode's number %lu", ino);
+
+ ncore = xfs_get_ino_core(fs, ino);
+ fill_xfs_inode_pvt(inode, fs, ino);
+ if (!ncore) {
+ xfs_error("Failed to get dinode!");
+ goto failed;
+ }
+
+ inode->ino = ino;
+ XFS_PVT(inode)->i_ino_blk = ino_to_bytes(fs, ino) >> BLOCK_SHIFT(fs);
+ inode->size = be64_to_cpu(ncore->di_size);
+
+ if (be16_to_cpu(ncore->di_mode) & S_IFDIR) {
+ inode->mode = DT_DIR;
+ xfs_debug("Found a directory inode!");
+ } else if (be16_to_cpu(ncore->di_mode) & S_IFREG) {
+ inode->mode = DT_REG;
+ xfs_debug("Found a file inode!");
+ xfs_debug("inode size %llu", inode->size);
+ }
+
+ xfs_debug("entry inode's number %lu", ino);
+
+ free(dirblk_buf);
+ return inode;
+
+failed:
+
+ free(inode);
+ free(dirblk_buf);
+
+ return NULL;
+}
+
+static struct inode *dir2_leaf_find_entry(const char *dname,
+ struct inode *parent,
+ xfs_dinode_t *core)
+{
+ (void) dname;
+ (void) parent;
+ (void) core;
+ return NULL;
+}
+
+static struct inode *dir2_node_find_entry(const char *dname,
+ struct inode *parent,
+ xfs_dinode_t *core)
+{
+ (void) dname;
+ (void) parent;
+ (void) core;
+ return NULL;
+}
+
+static struct inode *xfs_fmt_extents_find_entry(const char *dname,
+ struct inode *parent,
+ xfs_dinode_t *core)
+{
+ struct inode *inode;
+
+ if (be32_to_cpu(core->di_nextents) <= 1) {
+ /* Single-block Directories */
+ inode = dir2_block_find_entry(dname, parent, core);
+ } else if (xfs_dir2_isleaf(core)) {
+ /* Leaf Directory */
+ inode = dir2_leaf_find_entry(dname, parent, core);
+ } else {
+ /* Node Directory */
+ inode = dir2_node_find_entry(dname, parent, core);
+ }
+
+ return inode;
+}
+
static struct inode *xfs_iget(const char *dname, struct inode *parent)
{
struct fs_info *fs = parent->fs;
@@ -270,16 +446,19 @@ static struct inode *xfs_iget(const char *dname, struct inode *parent)
/* TODO: Handle both shortform and block directories */
if (core->di_format == XFS_DINODE_FMT_LOCAL) {
inode = xfs_fmt_local_find_entry(dname, parent, core);
- if (!inode) {
- xfs_error("Entry not found!");
- goto out;
- }
+ } else if (core->di_format == XFS_DINODE_FMT_EXTENTS) {
+ inode = xfs_fmt_extents_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;
}
+ if (!inode) {
+ xfs_error("Entry not found!");
+ goto out;
+ }
+
if (inode->mode == DT_REG) {
XFS_PVT(inode)->i_offset = 0;
XFS_PVT(inode)->i_cur_extent = 0;
@@ -347,6 +526,8 @@ static struct xfs_fs_info *xfs_new_sb_info(xfs_sb_t *sb)
info->blocksize = be32_to_cpu(sb->sb_blocksize);
info->block_shift = sb->sb_blocklog;
+ info->dirblksize = 1 << (sb->sb_blocklog + sb->sb_dirblklog);
+ info->dirblklog = sb->sb_dirblklog;
info->inopb_shift = sb->sb_inopblog;
info->agblk_shift = sb->sb_agblklog;
info->rootino = be64_to_cpu(sb->sb_rootino);
diff --git a/core/fs/xfs/xfs.h b/core/fs/xfs/xfs.h
index bfab2ae9..e0684689 100644
--- a/core/fs/xfs/xfs.h
+++ b/core/fs/xfs/xfs.h
@@ -103,6 +103,10 @@ struct xfs_fs_info;
#define XFS_IBT_MAGIC "IABT"
#define XFS_DINODE_MAGIC "IN"
+#define XFS_DIR2_BLOCK_MAGIC 0x58443242U /* XD2B: single block dirs */
+#define XFS_DIR2_DATA_MAGIC 0x58443244U /* XD2D: multiblock dirs */
+#define XFS_DIR2_FREE_MAGIC 0x58443246U /* XD2F: free index blocks */
+
/* File types and modes */
#define S_IFMT 00170000
#define S_IFSOCK 0140000
@@ -192,6 +196,8 @@ typedef struct xfs_sb {
struct xfs_fs_info {
uint32_t blocksize; /* Filesystem block size */
uint8_t block_shift; /* Filesystem block size in bits */
+ uint32_t dirblksize;
+ uint8_t dirblklog;
uint8_t inopb_shift;
uint8_t agblk_shift;
@@ -246,6 +252,48 @@ typedef struct xfs_inobt_rec {
uint64_t ir_free;
} __attribute__((__packed__)) xfs_inobt_rec_t;
+/*
+ * Bmap btree record and extent descriptor.
+ * l0:63 is an extent flag (value 1 indicates non-normal).
+ * l0:9-62 are startoff.
+ * l0:0-8 and l1:21-63 are startblock.
+ * l1:0-20 are blockcount.
+ */
+typedef struct xfs_bmbt_rec {
+ uint64_t l0, l1;
+} xfs_bmbt_rec_t;
+
+/*
+ * Possible extent states.
+ */
+typedef enum {
+ XFS_EXT_NORM, XFS_EXT_UNWRITTEN,
+ XFS_EXT_DMAPI_OFFLINE, XFS_EXT_INVALID
+} xfs_exntst_t;
+
+typedef struct xfs_bmbt_irec
+{
+ xfs_fileoff_t br_startoff; /* starting file offset */
+ xfs_fsblock_t br_startblock; /* starting block number */
+ xfs_filblks_t br_blockcount; /* number of blocks */
+ xfs_exntst_t br_state; /* extent state */
+} xfs_bmbt_irec_t;
+
+static inline void bmbt_irec_get(xfs_bmbt_irec_t *dest,
+ const xfs_bmbt_rec_t *src)
+{
+ uint64_t l0, l1;
+
+ l0 = be64_to_cpu(src->l0);
+ l1 = be64_to_cpu(src->l1);
+
+ dest->br_startoff = ((xfs_fileoff_t)l0 & 0x7ffffffffffffe00ULL) >> 9;
+ dest->br_startblock = (((xfs_fsblock_t)l0 & 0x00000000000001ffULL) << 43) |
+ (((xfs_fsblock_t)l1) >> 21);
+ dest->br_blockcount = (xfs_filblks_t)(l1 & 0x00000000001fffffULL);
+ dest->br_state = (l0 & 0x8000000000000000ULL) ? XFS_EXT_UNWRITTEN : XFS_EXT_NORM;
+}
+
typedef struct xfs_timestamp {
int32_t t_sec;
int32_t t_nsec;
@@ -328,18 +376,6 @@ typedef struct xfs_dir2_sf {
xfs_dir2_sf_entry_t list[1]; /* shortform entries */
} __attribute__((__packed__)) xfs_dir2_sf_t;
-typedef enum {
- XFS_EXT_NORM,
- XFS_EXT_UNWRITTEN,
- XFS_EXT_DMAPI_OFFLINE,
- XFS_EXT_INVALID,
-} xfs_exntst_t;
-
-typedef struct xfs_bmbt_rec {
- uint64_t l0;
- uint64_t l1;
-} __attribute__((__packed__)) xfs_bmbt_rec_t;
-
typedef xfs_ino_t xfs_intino_t;
static inline xfs_intino_t xfs_dir2_sf_get_inumber(xfs_dir2_sf_t *sfp,
@@ -350,6 +386,103 @@ static inline xfs_intino_t xfs_dir2_sf_get_inumber(xfs_dir2_sf_t *sfp,
(xfs_intino_t)XFS_GET_DIR_INO8((from)->i8));
}
+/*
+ * DIR2 Data block structures.
+ *
+ * A pure data block looks like the following drawing on disk:
+ *
+ * +-------------------------------------------------+
+ * | xfs_dir2_data_hdr_t |
+ * +-------------------------------------------------+
+ * | xfs_dir2_data_entry_t OR xfs_dir2_data_unused_t |
+ * | xfs_dir2_data_entry_t OR xfs_dir2_data_unused_t |
+ * | xfs_dir2_data_entry_t OR xfs_dir2_data_unused_t |
+ * | ... |
+ * +-------------------------------------------------+
+ * | unused space |
+ * +-------------------------------------------------+
+ *
+ * As all the entries are variable size structure the accessors below should
+ * be used to iterate over them.
+ *
+ * In addition to the pure data blocks for the data and node formats.
+ * most structures are also used for the combined data/freespace "block"
+ * format below.
+ */
+
+#define XFS_DIR2_DATA_ALIGN_LOG 3
+#define XFS_DIR2_DATA_ALIGN (1 << XFS_DIR2_DATA_ALIGN_LOG)
+#define XFS_DIR2_DATA_FREE_TAG 0xffff
+#define XFS_DIR2_DATA_FD_COUNT 3
+
+typedef struct xfs_dir2_data_free {
+ uint16_t offset;
+ uint16_t length;
+} xfs_dir2_data_free_t;
+
+typedef struct xfs_dir2_data_hdr {
+ uint32_t magic;
+ xfs_dir2_data_free_t bestfree[XFS_DIR2_DATA_FD_COUNT];
+} xfs_dir2_data_hdr_t;
+
+typedef struct xfs_dir2_data_entry {
+ uint64_t inumber; /* inode number */
+ uint8_t namelen; /* name length */
+ uint8_t name[]; /* name types, no null */
+ /* uint16_t tag; */ /* starting offset of us */
+} xfs_dir2_data_entry_t;
+
+typedef struct xfs_dir2_data_unused {
+ uint16_t freetag; /* XFS_DIR2_DATA_FREE_TAG */
+ uint16_t length; /* total free length */
+ /* variable offset */
+ /* uint16_t tag; */ /* starting offset of us */
+} xfs_dir2_data_unused_t;
+
+#define roundup(x, y) ( \
+{ \
+ const typeof(y) __y = y; \
+ (((x) + (__y - 1)) / __y) * __y; \
+} \
+)
+
+static inline int xfs_dir2_data_entsize(int n)
+{
+ return (int)roundup(offsetof(struct xfs_dir2_data_entry, name[0]) + n +
+ (unsigned int)sizeof(uint16_t), XFS_DIR2_DATA_ALIGN);
+}
+
+static inline uint16_t *
+xfs_dir2_data_entry_tag_p(struct xfs_dir2_data_entry *dep)
+{
+ return (uint16_t *)((char *)dep +
+ xfs_dir2_data_entsize(dep->namelen) - sizeof(uint16_t));
+}
+
+static inline uint16_t *
+xfs_dir2_data_unused_tag_p(struct xfs_dir2_data_unused *dup)
+{
+ return (uint16_t *)((char *)dup +
+ be16_to_cpu(dup->length) - sizeof(uint16_t));
+}
+
+typedef struct xfs_dir2_block_tail {
+ uint32_t count; /* count of leaf entries */
+ uint32_t stale; /* count of stale lf entries */
+} xfs_dir2_block_tail_t;
+
+static inline struct xfs_dir2_block_tail *
+xfs_dir2_block_tail_p(struct xfs_fs_info *fs_info, struct xfs_dir2_data_hdr *hdr)
+{
+ return ((struct xfs_dir2_block_tail *)
+ ((char *)hdr + fs_info->dirblksize)) - 1;
+}
+
+typedef struct xfs_dir2_leaf_entry {
+ uint32_t hashval; /* hash value of name */
+ uint32_t address; /* address of data entry */
+} xfs_dir2_leaf_entry_t;
+
static inline bool xfs_is_valid_magicnum(const xfs_sb_t *sb)
{
return sb->sb_magicnum == *(uint32_t *)XFS_SB_MAGIC;