diff options
author | Chen Baozi <baozich@gmail.com> | 2012-07-19 14:47:19 +0800 |
---|---|---|
committer | Paulo Alcantara <pcacjr@zytor.com> | 2012-07-21 01:21:47 -0300 |
commit | fa2b511f8b46c32c8dd1708d429331afa52d2003 (patch) | |
tree | 82e3a5f80a30757bbb42b8abaa5dcd238f5254f6 | |
parent | 71275b153c60891c9f892c89837553bca80f23dc (diff) | |
download | syslinux-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.c | 201 | ||||
-rw-r--r-- | core/fs/xfs/xfs.h | 157 |
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; |