[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1375436989-18948-5-git-send-email-wenqing.lz@taobao.com>
Date: Fri, 2 Aug 2013 17:49:31 +0800
From: Zheng Liu <gnehzuil.liu@...il.com>
To: linux-ext4@...r.kernel.org
Cc: Theodore Ts'o <tytso@....edu>, Zheng Liu <wenqing.lz@...bao.com>
Subject: [PATCH v1 04/22] libext2fs: handle inline data in dir iterator function
From: Zheng Liu <wenqing.lz@...bao.com>
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@....edu>
Signed-off-by: Zheng Liu <wenqing.lz@...bao.com>
---
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@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists