From: Zheng Liu <wenqing.lz@xxxxxxxxxx> Inline_data is handled in dir iterator because a lot of commands use this function to traverse directory entries in debugfs. We need to handle inline_data individually because inline_data is saved in two places. One is in i_block, and another is in ibody extended attribute. After applied this commit, the following commands in debugfs can support the inline_data feature: - ncheck - chroot - cd - ls - pwd - link* - unlink * If inline_data doesn't expand to ibody extended attribute, link command will allocate a new block for this inode instead of using extend attribute when we exhaust all space of i_block. Signed-off-by: Theodore Ts'o <tytso@xxxxxxx> Signed-off-by: Zheng Liu <wenqing.lz@xxxxxxxxxx> --- lib/ext2fs/dir_iterate.c | 102 ++++++++++++++++++++++++++++++++++- lib/ext2fs/ext2_err.et.in | 6 +++ lib/ext2fs/ext2_fs.h | 8 +++ lib/ext2fs/ext2fs.h | 11 ++++ lib/ext2fs/ext2fsP.h | 11 ++++ lib/ext2fs/inline_data.c | 132 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 268 insertions(+), 2 deletions(-) diff --git a/lib/ext2fs/dir_iterate.c b/lib/ext2fs/dir_iterate.c index 1a4bf5c..7d5b1da 100644 --- a/lib/ext2fs/dir_iterate.c +++ b/lib/ext2fs/dir_iterate.c @@ -123,8 +123,14 @@ errcode_t ext2fs_dir_iterate2(ext2_filsys fs, ctx.func = func; ctx.priv_data = priv_data; ctx.errcode = 0; - retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_READ_ONLY, 0, - ext2fs_process_dir_block, &ctx); + if (ext2fs_inode_has_inline_data(fs, dir)) + retval = ext2fs_inline_data_iterate(fs, dir, + BLOCK_FLAG_READ_ONLY, 0, + ext2fs_process_dir_inline_data, + &ctx); + else + retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_READ_ONLY, 0, + ext2fs_process_dir_block, &ctx); if (!block_buf) ext2fs_free_mem(&ctx.buf); if (retval) @@ -282,3 +288,95 @@ next: return 0; } +int ext2fs_process_dir_inline_data(ext2_filsys fs, + char *buf, + unsigned int buf_len, + e2_blkcnt_t blockcnt, + struct ext2_inode_large *inode, + void *priv_data) +{ + struct dir_context *ctx = (struct dir_context *) priv_data; + unsigned int offset = 0; + unsigned int next_real_entry = 0; + int ret = 0; + int changed = 0; + int do_abort = 0; + unsigned int rec_len, size; + int entry; + struct ext2_dir_entry *dirent; + + if (blockcnt < 0) + return 0; + + entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE; + + while (offset < buf_len) { + dirent = (struct ext2_dir_entry *) (buf + offset); + if (ext2fs_get_rec_len(fs, dirent, &rec_len)) + return BLOCK_ABORT; + if (((offset + rec_len) > buf_len) || + (rec_len < 8) || + ((rec_len % 4) != 0) || + ((((unsigned) dirent->name_len & 0xFF)+8) > rec_len)) { + ctx->errcode = EXT2_ET_DIR_CORRUPTED; + return BLOCK_ABORT; + } + if (!dirent->inode && + !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY)) + goto next; + + ret = (ctx->func)(ctx->dir, + (next_real_entry > offset) ? + DIRENT_DELETED_FILE : entry, + dirent, offset, + buf_len, buf, + ctx->priv_data); + if (entry < DIRENT_OTHER_FILE) + entry++; + + if (ret & DIRENT_CHANGED) { + if (ext2fs_get_rec_len(fs, dirent, &rec_len)) + return BLOCK_ABORT; + changed++; + } + if (ret & DIRENT_ABORT) { + do_abort++; + break; + } +next: + if (next_real_entry == offset) + next_real_entry += rec_len; + + if (ctx->flags & DIRENT_FLAG_INCLUDE_REMOVED) { + size = ((dirent->name_len & 0xFF) + 11) & ~3; + + if (rec_len != size) { + unsigned int final_offset; + + final_offset = offset + rec_len; + offset += size; + while (offset < final_offset && + !ext2fs_validate_entry(fs, buf, + offset, + final_offset)) + offset += 4; + continue; + } + } + offset += rec_len; + } + + if (changed) { + /* change parent ino */ + if (buf_len == EXT2_DIR_REC_LEN(2)) + ((struct ext2_dir_entry *)inode->i_block)->inode = + dirent->inode; + ctx->errcode = ext2fs_write_inode_full(fs, ctx->dir, (void *)inode, + EXT2_INODE_SIZE(fs->super)); + if (ctx->errcode) + return BLOCK_ABORT; + } + if (do_abort) + return BLOCK_ABORT; + return 0; +} diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in index 7e6d6e5..8a19cab 100644 --- a/lib/ext2fs/ext2_err.et.in +++ b/lib/ext2fs/ext2_err.et.in @@ -68,6 +68,9 @@ ec EXT2_ET_MAGIC_EXTENT_HANDLE, ec EXT2_ET_BAD_MAGIC, "Bad magic number in super-block" +ec EXT2_ET_BAD_EXT_ATTR_MAGIC, + "Bad magic number in extend attribute" + ec EXT2_ET_REV_TOO_HIGH, "Filesystem revision too high" @@ -479,4 +482,7 @@ ec EXT2_ET_FILE_EXISTS, ec EXT2_ET_EXT_ATTR_CURRUPTED, "Extended attribute currupted" +ec EXT2_ET_BAD_EXTRA_SIZE, + "Bad inode extra isizevalue" + end diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h index 0c0bbcb..d49b95c 100644 --- a/lib/ext2fs/ext2_fs.h +++ b/lib/ext2fs/ext2_fs.h @@ -906,4 +906,12 @@ struct mmp_struct { */ #define EXT4_MMP_MIN_CHECK_INTERVAL 5 +struct inline_data { + __u16 inline_off; + __u16 inline_size; +}; + +#define EXT4_MIN_INLINE_DATA_SIZE ((sizeof(__u32) * EXT2_N_BLOCKS)) +#define EXT4_INLINE_DATA_DOTDOT_SIZE (4) + #endif /* _LINUX_EXT2_FS_H */ diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h index 8c30197..7b05b48 100644 --- a/lib/ext2fs/ext2fs.h +++ b/lib/ext2fs/ext2fs.h @@ -1344,6 +1344,17 @@ extern errcode_t ext2fs_get_memalign(unsigned long size, /* inline_data.c */ extern int ext2fs_inode_has_inline_data(ext2_filsys fs, ext2_ino_t ino); +extern errcode_t ext2fs_inline_data_iterate(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + char *buf, + unsigned int buf_len, + e2_blkcnt_t blockcnt, + struct ext2_inode_large *inode, + void *priv_data), + void *priv_data); /* inode.c */ extern void ext2fs_free_inode_cache(struct ext2_inode_cache *icache); diff --git a/lib/ext2fs/ext2fsP.h b/lib/ext2fs/ext2fsP.h index 3de9278..e86d452 100644 --- a/lib/ext2fs/ext2fsP.h +++ b/lib/ext2fs/ext2fsP.h @@ -87,6 +87,17 @@ extern int ext2fs_process_dir_block(ext2_filsys fs, int ref_offset, void *priv_data); +extern int ext2fs_process_dir_inline_data(ext2_filsys fs, + char *buf, + unsigned int buf_len, + e2_blkcnt_t blockcnt, + struct ext2_inode_large *inode, + void *priv_data); + +extern errcode_t ext2fs_inline_data_find(ext2_filsys fs, + struct ext2_inode_large *inode, + struct inline_data *data); + /* Generic numeric progress meter */ struct ext2fs_numeric_progress_struct { diff --git a/lib/ext2fs/inline_data.c b/lib/ext2fs/inline_data.c index fcf82b5..922c959 100644 --- a/lib/ext2fs/inline_data.c +++ b/lib/ext2fs/inline_data.c @@ -14,10 +14,60 @@ #include <time.h> #include "ext2_fs.h" +#include "ext2_ext_attr.h" #include "ext2fs.h" #include "ext2fsP.h" +static void *ext2fs_get_inline_xattr_pos(struct ext2_inode_large *inode, + struct inline_data *data); + +errcode_t ext2fs_inline_data_find(ext2_filsys fs, + struct ext2_inode_large *inode, + struct inline_data *data) +{ + errcode_t retval; + + struct ext2_ext_attr_search s = { + .not_found = ENODATA, + }; + struct ext2_ext_attr_info i = { + .name_index = EXT4_EXT_ATTR_INDEX_SYSTEM, + .name = EXT4_EXT_ATTR_SYSTEM_DATA, + }; + + data->inline_off = 0; + if (inode->i_extra_isize > (EXT2_INODE_SIZE(fs->super) - + EXT2_GOOD_OLD_INODE_SIZE)) + return EXT2_ET_BAD_EXTRA_SIZE; + + retval = ext2fs_ext_attr_ibody_find(fs, inode, &i, &s); + if (retval) + return retval; + + if (!s.not_found) { + data->inline_off = (__u16)((char *)s.here - (char *)inode); + data->inline_size = EXT4_MIN_INLINE_DATA_SIZE + + s.here->e_value_size; + return 0; + } + + return EXT2_ET_BAD_EXT_ATTR_MAGIC; +} + +static void *ext2fs_get_inline_xattr_pos(struct ext2_inode_large *inode, + struct inline_data *data) +{ + struct ext2_ext_attr_entry *entry; + struct ext2_ext_attr_ibody_header *header; + + header = IHDR(inode); + entry = (struct ext2_ext_attr_entry *) + ((char *)inode + data->inline_off); + + return (void *) (IFIRST(header) + entry->e_value_offs); +} + int ext2fs_inode_has_inline_data(ext2_filsys fs, ext2_ino_t ino) { struct ext2_inode inode; @@ -29,3 +79,85 @@ int ext2fs_inode_has_inline_data(ext2_filsys fs, ext2_ino_t ino) return (inode.i_flags & EXT4_INLINE_DATA_FL); } + +errcode_t ext2fs_inline_data_iterate(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + char *buf, + unsigned int buf_len, + e2_blkcnt_t blockcnt, + struct ext2_inode_large *inode, + void *priv_data), + void *priv_data) +{ + struct dir_context *ctx; + struct ext2_inode_large *inode; + struct ext2_dir_entry dirent; + struct inline_data data; + errcode_t retval = 0; + e2_blkcnt_t blockcnt = 0; + void *inline_start; + int inline_size; + + ctx = (struct dir_context *)priv_data; + + retval = ext2fs_get_mem(EXT2_INODE_SIZE(fs->super), &inode); + if (retval) + return retval; + + retval = ext2fs_read_inode_full(fs, ino, (void *)inode, + EXT2_INODE_SIZE(fs->super)); + if (retval) + goto out; + + if (inode->i_size == 0) + goto out; + + /* we first check '.' and '..' dir */ + dirent.inode = ino; + dirent.name_len = 1; + ext2fs_set_rec_len(fs, EXT2_DIR_REC_LEN(2), &dirent); + dirent.name[0] = '.'; + dirent.name[1] = '\0'; + retval |= (*func)(fs, (void *)&dirent, dirent.rec_len, blockcnt++, + inode, priv_data); + if (retval & BLOCK_ABORT) + goto out; + + dirent.inode = (__u32)*inode->i_block; + dirent.name_len = 2; + ext2fs_set_rec_len(fs, EXT2_DIR_REC_LEN(3), &dirent); + dirent.name[0] = '.'; + dirent.name[1] = '.'; + dirent.name[2] = '\0'; + retval |= (*func)(fs, (void *)&dirent, dirent.rec_len, blockcnt++, + inode, priv_data); + if (retval & BLOCK_ABORT) + goto out; + + inline_start = (char *)inode->i_block + EXT4_INLINE_DATA_DOTDOT_SIZE; + inline_size = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DATA_DOTDOT_SIZE; + retval |= (*func)(fs, inline_start, inline_size, blockcnt++, + inode, priv_data); + if (retval & BLOCK_ABORT) + goto out; + + retval = ext2fs_inline_data_find(fs, inode, &data); + if (retval) + goto out; + if (data.inline_size > EXT4_MIN_INLINE_DATA_SIZE) { + inline_start = ext2fs_get_inline_xattr_pos(inode, &data); + inline_size = data.inline_size - EXT4_MIN_INLINE_DATA_SIZE; + retval |= (*func)(fs, inline_start, inline_size, blockcnt++, + inode, priv_data); + if (retval & BLOCK_ABORT) + goto out; + } + +out: + retval |= BLOCK_ERROR; + ext2fs_free_mem(&inode); + return retval & BLOCK_ERROR ? ctx->errcode : 0; +} -- 1.7.9.7 -- To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html