diff options
author | Paulo Alcantara <pcacjr@zytor.com> | 2012-07-13 16:45:56 -0300 |
---|---|---|
committer | Paulo Alcantara <pcacjr@zytor.com> | 2012-07-21 01:21:46 -0300 |
commit | 533d97658da0fb3ae0930c58520bf7a27177fa8f (patch) | |
tree | 3474424a78c24f6c2b6334b6e42c0f765cb4e6e9 | |
parent | 15b96c3fa40825b2469e3e86fd820d58976631d9 (diff) | |
download | syslinux-533d97658da0fb3ae0930c58520bf7a27177fa8f.tar.gz syslinux-533d97658da0fb3ae0930c58520bf7a27177fa8f.tar.xz syslinux-533d97658da0fb3ae0930c58520bf7a27177fa8f.zip |
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@zytor.com>
-rw-r--r-- | core/fs/xfs/xfs.c | 273 | ||||
-rw-r--r-- | 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 1987fa74..eaad4ea4 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 2aae8c18..17720f87 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; |