diff options
author | Chen Baozi <baozich@gmail.com> | 2012-08-15 17:32:42 +0800 |
---|---|---|
committer | Paulo Alcantara <pcacjr@zytor.com> | 2012-09-02 19:18:36 -0300 |
commit | f9ad46af812f493ccd1c41a3540005b0f5f21ed6 (patch) | |
tree | 590a07b9522d81614af04b5fb9f44575089d66bb | |
parent | 2cfd136969466cb77f2d47f7a6e7354bda3b460e (diff) | |
download | syslinux-f9ad46af812f493ccd1c41a3540005b0f5f21ed6.tar.gz syslinux-f9ad46af812f493ccd1c41a3540005b0f5f21ed6.tar.xz syslinux-f9ad46af812f493ccd1c41a3540005b0f5f21ed6.zip |
xfs: Rework xfs_dir2_get_right_blk()
In B+tree directory, besides the node/leaf blocks, all the
extents which store xfs_bmbt_rec_t entries are also organized
in B+tree. We need to rework xfs_dir2_get_right_blk() to
make it workable in all cases.
Signed-off-by: Chen Baozi <baozich@gmail.com>
Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
-rw-r--r-- | core/fs/xfs/xfs.h | 128 | ||||
-rw-r--r-- | core/fs/xfs/xfs_dir2.c | 129 | ||||
-rw-r--r-- | core/fs/xfs/xfs_dir2.h | 3 | ||||
-rw-r--r-- | core/fs/xfs/xfs_readdir.c | 6 |
4 files changed, 237 insertions, 29 deletions
diff --git a/core/fs/xfs/xfs.h b/core/fs/xfs/xfs.h index 21abf58d..64d01fdd 100644 --- a/core/fs/xfs/xfs.h +++ b/core/fs/xfs/xfs.h @@ -254,6 +254,8 @@ typedef struct xfs_bmbt_rec { uint64_t l1; } __attribute__((__packed__)) xfs_bmbt_rec_t; +typedef xfs_bmbt_rec_t xfs_bmdr_rec_t; + /* * Possible extent states. */ @@ -293,6 +295,12 @@ typedef struct xfs_timestamp { int32_t t_nsec; } __attribute__((__packed__)) xfs_timestamp_t; +/* + * Fork identifiers. + */ +#define XFS_DATA_FORK 0 +#define xFS_ATTR_FORK 1 + typedef enum xfs_dinode_fmt { XFS_DINODE_FMT_DEV, XFS_DINODE_FMT_LOCAL, @@ -334,6 +342,34 @@ typedef struct xfs_dinode { uint8_t di_literal_area[1]; } __attribute__((packed)) xfs_dinode_t; +/* + * Inode size for given fs. + */ +#define XFS_LITINO(fs) \ + ((int)((XFS_INFO(fs)->inodesize) - sizeof(struct xfs_dinode) - 1)) + +#define XFS_BROOT_SIZE_ADJ \ + (XFS_BTREE_LBLOCK_LEN - sizeof(xfs_bmdr_block_t)) + +/* + * 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)) + struct xfs_inode { xfs_agblock_t i_agblock; block_t i_ino_blk; @@ -606,4 +642,96 @@ static inline void fill_xfs_inode_pvt(struct fs_info *fs, struct inode *inode, XFS_INFO(fs)->inode_shift; } +/* + * Generic btree header. + * + * This is a combination of the actual format used on disk for short and long + * format btrees. The first three fields are shared by both format, but + * the pointers are different and should be used with care. + * + * To get the size of the actual short or long form headers please use + * the size macros belows. Never use sizeof(xfs_btree_block); + */ +typedef struct xfs_btree_block { + uint32_t bb_magic; /* magic number for block type */ + uint16_t bb_level; /* 0 is a leaf */ + uint16_t bb_numrecs; /* current # of data records */ + union { + struct { + uint32_t bb_leftsib; + uint32_t bb_rightsib; + } s; /* short form pointers */ + struct { + uint64_t bb_leftsib; + uint64_t bb_rightsib; + } l; /* long form pointers */ + } bb_u; /* rest */ +} xfs_btree_block_t; + +#define XFS_BTREE_SBLOCK_LEN 16 /* size of a short form block */ +#define XFS_BTREE_LBLOCK_LEN 24 /* size of a long form block */ + +/* + * Bmap root header, on-disk form only. + */ +typedef struct xfs_bmdr_block { + uint16_t bb_level; /* 0 is a leaf */ + uint16_t bb_numrecs; /* current # of data records */ +} xfs_bmdr_block_t; + +/* + * Key structure for non-leaf levels of the tree. + */ +typedef struct xfs_bmbt_key { + uint64_t br_startoff; /* starting file offset */ +} xfs_bmbt_key_t, xfs_bmdr_key_t; + +/* btree pointer type */ +typedef uint64_t xfs_bmbt_ptr_t, xfs_bmdr_ptr_t; + +/* + * Btree block header size depends on a superblock flag. + * + * (not quite yet, but soon) + */ +#define XFS_BMBT_BLOCK_LEN(fs) XFS_BTREE_LBLOCK_LEN + +#define XFS_BMBT_REC_ADDR(fs, block, index) \ + ((xfs_bmbt_rec_t *) \ + ((char *)(block) + \ + XFS_BMBT_BLOCK_LEN(fs) + \ + ((index) - 1) * sizeof(xfs_bmbt_rec_t))) + +#define XFS_BMBT_KEY_ADDR(fs, block, index) \ + ((xfs_bmbt_key_t *) \ + ((char *)(block) + \ + XFS_BMBT_BLOCK_LEN(fs) + \ + ((index) - 1) * sizeof(xfs_bmbt_key_t))) + +#define XFS_BMBT_PTR_ADDR(fs, block, index, maxrecs) \ + ((xfs_bmbt_ptr_t *) \ + ((char *)(block) + \ + XFS_BMBT_BLOCK_LEN(fs) + \ + (maxrecs) * sizeof(xfs_bmbt_key_t) + \ + ((index) - 1) * sizeof(xfs_bmbt_ptr_t))) + +#define XFS_BMDR_REC_ADDR(block, index) \ + ((xfs_bmdr_rec_t *) \ + ((char *)(block) + \ + sizeof(struct xfs_bmdr_block) + \ + ((index) - 1) * sizeof(xfs_bmdr_rec_t))) + +#define XFS_BMDR_KEY_ADDR(block, index) \ + ((xfs_bmdr_key_t *) \ + ((char *)(block) + \ + sizeof(struct xfs_bmdr_block) + \ + ((index) - 1) * sizeof(xfs_bmdr_key_t))) + +#define XFS_BMDR_PTR_ADDR(block, index, maxrecs) \ + ((xfs_bmdr_ptr_t *) \ + ((char *)(block) + \ + sizeof(struct xfs_bmdr_block) + \ + (maxrecs) * sizeof(xfs_bmdr_key_t) + \ + ((index) - 1) * sizeof(xfs_bmdr_ptr_t))) + #endif /* XFS_H_ */ diff --git a/core/fs/xfs/xfs_dir2.c b/core/fs/xfs/xfs_dir2.c index db1a1b14..7d1e8ee9 100644 --- a/core/fs/xfs/xfs_dir2.c +++ b/core/fs/xfs/xfs_dir2.c @@ -432,20 +432,108 @@ failed: return ip; } +/* + * Calculate number of records in a bmap btree inode root. + */ +static inline int +xfs_bmdr_maxrecs(int blocklen, int leaf) +{ + blocklen -= sizeof(xfs_bmdr_block_t); + + if (leaf) + return blocklen / sizeof(xfs_bmdr_rec_t); + return blocklen / (sizeof(xfs_bmdr_key_t) + sizeof(xfs_bmdr_ptr_t)); +} + +static xfs_fsblock_t +select_child(xfs_dfiloff_t off, + xfs_bmbt_key_t *kp, + xfs_bmbt_ptr_t *pp, + int nrecs) +{ + int i; + + for (i = 0; i < nrecs; i++) { + if (be64_to_cpu(kp[i].br_startoff) == off) + return be64_to_cpu(pp[i]); + if (be64_to_cpu(kp[i].br_startoff) > off) { + if (i == 0) + return be64_to_cpu(pp[i]); + else + return be64_to_cpu(pp[i-1]); + } + } + + return be64_to_cpu(pp[nrecs - 1]); +} + block_t xfs_dir2_get_right_blk(struct fs_info *fs, xfs_dinode_t *core, - uint32_t from, uint32_t to, block_t fsblkno, - int *error) + block_t fsblkno, int *error) { uint32_t idx; xfs_bmbt_irec_t irec; + block_t bno; + block_t nextbno; + xfs_bmdr_block_t *rblock; + int fsize; + int nextents; + xfs_bmbt_ptr_t *pp; + xfs_bmbt_key_t *kp; + xfs_btree_block_t *blk; + xfs_bmbt_rec_t *xp; *error = 0; - for (idx = from; idx < to; idx++) { - bmbt_irec_get(&irec, - ((xfs_bmbt_rec_t *)&core->di_literal_area[0]) + idx); - if (fsblkno >= irec.br_startoff && - fsblkno < irec.br_startoff + irec.br_blockcount) - break; + if (core->di_format == XFS_DINODE_FMT_EXTENTS) { + 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); + if (fsblkno >= irec.br_startoff && + fsblkno < irec.br_startoff + irec.br_blockcount) + break; + } + } 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]; + 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); + bno = fsblock_to_bytes(fs, + select_child(fsblkno, kp, pp, + be16_to_cpu(rblock->bb_numrecs))) >> BLOCK_SHIFT(fs); + + /* Find the leaf */ + for (;;) { + blk = (xfs_btree_block_t *)get_cache(fs->fs_dev, bno); + if (be16_to_cpu(blk->bb_level) == 0) + break; + pp = XFS_BMBT_PTR_ADDR(fs, blk, 1, + xfs_bmdr_maxrecs(XFS_INFO(fs)->blocksize, 0)); + kp = XFS_BMBT_KEY_ADDR(fs, blk, 1); + bno = fsblock_to_bytes(fs, + select_child(fsblkno, kp, pp, + be16_to_cpu(blk->bb_numrecs))) >> BLOCK_SHIFT(fs); + } + + /* Find the records among leaves */ + for (;;) { + nextbno = be64_to_cpu(blk->bb_u.l.bb_rightsib); + nextents = be16_to_cpu(blk->bb_numrecs); + xp = (xfs_bmbt_rec_t *)XFS_BMBT_REC_ADDR(fs, blk, 1); + for (idx = 0; idx < nextents; idx++) { + bmbt_irec_get(&irec, xp + idx); + if (fsblkno >= irec.br_startoff && + fsblkno < irec.br_startoff + irec.br_blockcount) { + nextbno = NULLFSBLOCK; + break; + } + } + if (nextbno == NULLFSBLOCK) + break; + bno = fsblock_to_bytes(fs, nextbno) >> BLOCK_SHIFT(fs); + blk = (xfs_btree_block_t *)get_cache(fs->fs_dev, bno); + } } if (fsblkno < irec.br_startoff || @@ -460,8 +548,6 @@ block_t xfs_dir2_get_right_blk(struct fs_info *fs, xfs_dinode_t *core, struct inode *xfs_dir2_node_find_entry(const char *dname, struct inode *parent, xfs_dinode_t *core) { - xfs_bmbt_irec_t irec; - uint32_t node_off = 0; block_t fsblkno; xfs_da_intnode_t *node = NULL; uint32_t hashwant; @@ -487,16 +573,16 @@ struct inode *xfs_dir2_node_find_entry(const char *dname, struct inode *parent, xfs_dinode_t *ncore; uint8_t *buf = NULL; - 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(parent->fs, XFS_DIR2_LEAF_OFFSET)); - hashwant = xfs_dir2_da_hashname((uint8_t *)dname, strlen(dname)); - fsblkno = fsblock_to_bytes(parent->fs, irec.br_startblock) >> - BLOCK_SHIFT(parent->fs); + fsblkno = xfs_dir2_get_right_blk(parent->fs, core, + xfs_dir2_byte_to_db(parent->fs, XFS_DIR2_LEAF_OFFSET), + &error); + if (error) { + xfs_error("Cannot find right rec!"); + return NULL; + } + node = (xfs_da_intnode_t *)xfs_dir2_get_dirblks(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!"); @@ -542,9 +628,7 @@ struct inode *xfs_dir2_node_find_entry(const char *dname, struct inode *parent, else fsblkno = be32_to_cpu(node->btree[probe].before); - fsblkno = xfs_dir2_get_right_blk(parent->fs, core, node_off + 1, - be32_to_cpu(core->di_nextents) - 1, - fsblkno, &error); + fsblkno = xfs_dir2_get_right_blk(parent->fs, core, fsblkno, &error); if (error) { xfs_error("Cannot find right rec!"); goto out; @@ -597,8 +681,7 @@ struct inode *xfs_dir2_node_find_entry(const char *dname, struct inode *parent, if (buf) free(buf); - fsblkno = xfs_dir2_get_right_blk(parent->fs, core, 0, node_off, - newdb, &error); + fsblkno = xfs_dir2_get_right_blk(parent->fs, core, newdb, &error); if (error) { xfs_error("Cannot find data block!"); goto out; diff --git a/core/fs/xfs/xfs_dir2.h b/core/fs/xfs/xfs_dir2.h index c9398332..e1b96227 100644 --- a/core/fs/xfs/xfs_dir2.h +++ b/core/fs/xfs/xfs_dir2.h @@ -28,8 +28,7 @@ void *xfs_dir2_get_dirblks(struct fs_info *fs, block_t startblock, xfs_filblks_t c); uint32_t xfs_dir2_da_hashname(const uint8_t *name, int namelen); block_t xfs_dir2_get_right_blk(struct fs_info *fs, xfs_dinode_t *core, - uint32_t from, uint32_t to, block_t fsblkno, - int *error); + block_t fsblkno, int *error); struct inode *xfs_dir2_local_find_entry(const char *dname, struct inode *parent, xfs_dinode_t *core); diff --git a/core/fs/xfs/xfs_readdir.c b/core/fs/xfs/xfs_readdir.c index e68ce636..a3be247d 100644 --- a/core/fs/xfs/xfs_readdir.c +++ b/core/fs/xfs/xfs_readdir.c @@ -315,9 +315,7 @@ try_next_btree: goto out; fsblkno = be32_to_cpu(node->btree[XFS_PVT(inode)->i_btree_offset].before); - fsblkno = xfs_dir2_get_right_blk(fs, core, node_off + 1, - be32_to_cpu(core->di_nextents) - 1, - fsblkno, &error); + fsblkno = xfs_dir2_get_right_blk(fs, core, fsblkno, &error); if (error) { xfs_error("Cannot find leaf rec!"); goto out; @@ -355,7 +353,7 @@ try_next_btree: db = xfs_dir2_dataptr_to_db(fs, be32_to_cpu(lep->address)); - fsblkno = xfs_dir2_get_right_blk(fs, core, 0, node_off, db, &error); + fsblkno = xfs_dir2_get_right_blk(fs, core, db, &error); if (error) { xfs_error("Cannot find data block!"); goto out1; |