diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in index 88808a3d..9de89259 100644 --- a/lib/ext2fs/Makefile.in +++ b/lib/ext2fs/Makefile.in @@ -59,6 +59,7 @@ OBJS= $(DEBUGFS_LIB_OBJS) $(RESIZE_LIB_OBJS) $(E2IMAGE_LIB_OBJS) \ ind_block.o \ initialize.o \ inline.o \ + inline_data.o \ inode.o \ io_manager.o \ ismounted.o \ @@ -133,6 +134,7 @@ SRCS= ext2_err.c \ $(srcdir)/ind_block.c \ $(srcdir)/initialize.c \ $(srcdir)/inline.c \ + $(srcdir)/inline_data.c \ $(srcdir)/inode.c \ $(srcdir)/inode_io.c \ $(srcdir)/imager.c \ @@ -767,6 +769,12 @@ inline.o: $(srcdir)/inline.c $(top_builddir)/lib/config.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h +inline_data.o: $(srcdir)/inline_data.c $(top_builddir)/lib/config.h \ + $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \ + $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ + $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h inode.o: $(srcdir)/inode.c $(top_builddir)/lib/config.h \ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \ diff --git a/lib/ext2fs/Makefile.pq b/lib/ext2fs/Makefile.pq index 2f7b654b..89082a7e 100644 --- a/lib/ext2fs/Makefile.pq +++ b/lib/ext2fs/Makefile.pq @@ -27,6 +27,7 @@ OBJS= alloc.obj \ icount.obj \ initialize.obj \ inline.obj \ + inline_data.obj \ inode.obj \ ismounted.obj \ link.obj \ diff --git a/lib/ext2fs/dir_iterate.c b/lib/ext2fs/dir_iterate.c index 306ffb03..8cb6740a 100644 --- a/lib/ext2fs/dir_iterate.c +++ b/lib/ext2fs/dir_iterate.c @@ -127,6 +127,11 @@ errcode_t ext2fs_dir_iterate2(ext2_filsys fs, ext2fs_process_dir_block, &ctx); if (!block_buf) ext2fs_free_mem(&ctx.buf); + if (retval == EXT2_ET_INLINE_DATA_CANT_ITERATE) { + ctx.flags |= DIRENT_FLAG_INCLUDE_INLINE_DATA; + (void) ext2fs_inline_data_dir_iterate(fs, dir, &ctx); + retval = 0; + } if (retval) return retval; return ctx.errcode; @@ -189,30 +194,40 @@ int ext2fs_process_dir_block(ext2_filsys fs, int ret = 0; int changed = 0; int do_abort = 0; - unsigned int rec_len, size; + unsigned int rec_len, size, buflen; int entry; struct ext2_dir_entry *dirent; int csum_size = 0; + int inline_data; + errcode_t retval = 0; if (blockcnt < 0) return 0; entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE; - ctx->errcode = ext2fs_read_dir_block4(fs, *blocknr, ctx->buf, 0, - ctx->dir); - if (ctx->errcode) - return BLOCK_ABORT; + /* If a dir has inline data, we don't need to read block */ + inline_data = !!(ctx->flags & DIRENT_FLAG_INCLUDE_INLINE_DATA); + if (!inline_data) { + ctx->errcode = ext2fs_read_dir_block4(fs, *blocknr, ctx->buf, 0, + ctx->dir); + if (ctx->errcode) + return BLOCK_ABORT; + /* If we handle a normal dir, we traverse the entire block */ + buflen = fs->blocksize; + } else { + buflen = ctx->buflen; + } if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) csum_size = sizeof(struct ext2_dir_entry_tail); - while (offset < fs->blocksize) { + while (offset < buflen) { dirent = (struct ext2_dir_entry *) (ctx->buf + offset); if (ext2fs_get_rec_len(fs, dirent, &rec_len)) return BLOCK_ABORT; - if (((offset + rec_len) > fs->blocksize) || + if (((offset + rec_len) > buflen) || (rec_len < 8) || ((rec_len % 4) != 0) || ((ext2fs_dirent_name_len(dirent)+8) > rec_len)) { @@ -220,7 +235,13 @@ int ext2fs_process_dir_block(ext2_filsys fs, return BLOCK_ABORT; } if (!dirent->inode) { - if ((offset == fs->blocksize - csum_size) && + /* + * We just need to check metadata_csum when this + * dir hasn't inline data. That means that 'buflen' + * should be blocksize. + */ + if (!inline_data && + (offset == buflen - csum_size) && (dirent->rec_len == csum_size) && (dirent->name_len == EXT2_DIR_NAME_LEN_CSUM)) { if (!(ctx->flags & DIRENT_FLAG_INCLUDE_CSUM)) @@ -234,7 +255,7 @@ int ext2fs_process_dir_block(ext2_filsys fs, (next_real_entry > offset) ? DIRENT_DELETED_FILE : entry, dirent, offset, - fs->blocksize, ctx->buf, + buflen, ctx->buf, ctx->priv_data); if (entry < DIRENT_OTHER_FILE) entry++; @@ -272,13 +293,21 @@ next: } if (changed) { - ctx->errcode = ext2fs_write_dir_block4(fs, *blocknr, ctx->buf, - 0, ctx->dir); - if (ctx->errcode) - return BLOCK_ABORT; + if (!inline_data) { + ctx->errcode = ext2fs_write_dir_block4(fs, *blocknr, + ctx->buf, + 0, ctx->dir); + if (ctx->errcode) + return BLOCK_ABORT; + } else { + /* + * return BLOCK_INLINE_DATA_CHANGED to notify caller + * that inline data has been changed. + */ + retval = BLOCK_INLINE_DATA_CHANGED; + } } if (do_abort) - return BLOCK_ABORT; - return 0; + return retval | BLOCK_ABORT; + return retval; } - diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in index 0a69aa31..a7c8902f 100644 --- a/lib/ext2fs/ext2_err.et.in +++ b/lib/ext2fs/ext2_err.et.in @@ -503,4 +503,7 @@ ec EXT2_ET_EA_NO_SPACE, ec EXT2_ET_MISSING_EA_FEATURE, "Filesystem is missing ext_attr or inline_data feature" +ec EXT2_ET_NO_INLINE_DATA, + "Inode doesn't have inline data" + end diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h index ac46ba87..21a8187b 100644 --- a/lib/ext2fs/ext2_fs.h +++ b/lib/ext2fs/ext2_fs.h @@ -916,4 +916,14 @@ struct mmp_struct { */ #define EXT4_MMP_MIN_CHECK_INTERVAL 5 +/* + * Minimum size of inline data. + */ +#define EXT4_MIN_INLINE_DATA_SIZE ((sizeof(__u32) * EXT2_N_BLOCKS)) + +/* + * Size of a parent inode in inline data directory. + */ +#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 b526f5c3..ad342912 100644 --- a/lib/ext2fs/ext2fs.h +++ b/lib/ext2fs/ext2fs.h @@ -298,9 +298,10 @@ struct struct_ext2_filsys { /* * Return flags for the block iterator functions */ -#define BLOCK_CHANGED 1 -#define BLOCK_ABORT 2 -#define BLOCK_ERROR 4 +#define BLOCK_CHANGED 1 +#define BLOCK_ABORT 2 +#define BLOCK_ERROR 4 +#define BLOCK_INLINE_DATA_CHANGED 8 /* * Block interate flags @@ -438,6 +439,7 @@ struct ext2_extent_info { #define DIRENT_FLAG_INCLUDE_EMPTY 1 #define DIRENT_FLAG_INCLUDE_REMOVED 2 #define DIRENT_FLAG_INCLUDE_CSUM 4 +#define DIRENT_FLAG_INCLUDE_INLINE_DATA 8 #define DIRENT_DOT_FILE 1 #define DIRENT_DOT_DOT_FILE 2 diff --git a/lib/ext2fs/ext2fsP.h b/lib/ext2fs/ext2fsP.h index 319e9b07..b2afd153 100644 --- a/lib/ext2fs/ext2fsP.h +++ b/lib/ext2fs/ext2fsP.h @@ -50,6 +50,7 @@ struct dir_context { ext2_ino_t dir; int flags; char *buf; + unsigned int buflen; int (*func)(ext2_ino_t dir, int entry, struct ext2_dir_entry *dirent, @@ -87,6 +88,10 @@ extern int ext2fs_process_dir_block(ext2_filsys fs, int ref_offset, void *priv_data); +extern int ext2fs_inline_data_dir_iterate(ext2_filsys fs, + ext2_ino_t ino, + void *priv_data); + /* Generic numeric progress meter */ struct ext2fs_numeric_progress_struct { diff --git a/lib/ext2fs/inline_data.c b/lib/ext2fs/inline_data.c new file mode 100644 index 00000000..9bfc8eff --- /dev/null +++ b/lib/ext2fs/inline_data.c @@ -0,0 +1,210 @@ +/* + * inline_data.c --- data in inode + * + * Copyright (C) 2012 Zheng Liu + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU library + * General Public License, version 2. + * %End-Header% + */ + +#include "config.h" +#include +#include + +#include "ext2_fs.h" +#include "ext2_ext_attr.h" + +#include "ext2fs.h" +#include "ext2fsP.h" + +struct ext2_inline_data { + ext2_filsys fs; + ext2_ino_t ino; + size_t ea_size; /* the size of inline data in ea area */ + void *ea_data; +}; + +static errcode_t ext2fs_inline_data_ea_set(struct ext2_inline_data *data) +{ + struct ext2_xattr_handle *handle; + errcode_t retval; + + retval = ext2fs_xattrs_open(data->fs, data->ino, &handle); + if (retval) + return retval; + + retval = ext2fs_xattrs_read(handle); + if (retval) + goto err; + + retval = ext2fs_xattr_set(handle, "system.data", + data->ea_data, data->ea_size); + if (retval) + goto err; + + retval = ext2fs_xattrs_write(handle); + +err: + (void) ext2fs_xattrs_close(&handle); + return retval; +} + +static errcode_t ext2fs_inline_data_ea_get(struct ext2_inline_data *data) +{ + struct ext2_xattr_handle *handle; + errcode_t retval; + + data->ea_size = 0; + data->ea_data = 0; + + retval = ext2fs_xattrs_open(data->fs, data->ino, &handle); + if (retval) + return retval; + + retval = ext2fs_xattrs_read(handle); + if (retval) + goto err; + + retval = ext2fs_xattr_get(handle, "system.data", + (void **)&data->ea_data, &data->ea_size); + if (retval) + goto err; + +err: + (void) ext2fs_xattrs_close(&handle); + return retval; +} + + +int ext2fs_inline_data_dir_iterate(ext2_filsys fs, ext2_ino_t ino, + void *priv_data) +{ + struct dir_context *ctx; + struct ext2_inode inode; + struct ext2_dir_entry dirent; + struct ext2_inline_data data; + int ret = BLOCK_ABORT; + e2_blkcnt_t blockcnt = 0; + + ctx = (struct dir_context *)priv_data; + + ctx->errcode = ext2fs_read_inode(fs, ino, &inode); + if (ctx->errcode) + goto out; + + if (!(inode.i_flags & EXT4_INLINE_DATA_FL)) { + ctx->errcode = EXT2_ET_NO_INLINE_DATA; + goto out; + } + + if (!LINUX_S_ISDIR(inode.i_mode)) { + ctx->errcode = EXT2_ET_NO_DIRECTORY; + goto out; + } + ret = 0; + + /* 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'; + ctx->buf = (char *)&dirent; + ext2fs_get_rec_len(fs, &dirent, &ctx->buflen); + ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data); + if (ret & BLOCK_ABORT) + goto out; + + dirent.inode = ext2fs_le32_to_cpu(inode.i_block[0]); + 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'; + ctx->buf = (char *)&dirent; + ext2fs_get_rec_len(fs, &dirent, &ctx->buflen); + ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data); + if (ret & BLOCK_INLINE_DATA_CHANGED) { + errcode_t err; + + inode.i_block[0] = ext2fs_cpu_to_le32(dirent.inode); + err = ext2fs_write_inode(fs, ino, &inode); + if (err) + goto out; + ret &= ~BLOCK_INLINE_DATA_CHANGED; + } + if (ret & BLOCK_ABORT) + goto out; + + ctx->buf = (char *)inode.i_block + EXT4_INLINE_DATA_DOTDOT_SIZE; + ctx->buflen = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DATA_DOTDOT_SIZE; +#ifdef WORDS_BIGENDIAN + ctx->errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0); + if (ctx->errcode) { + ret |= BLOCK_ABORT; + goto out; + } +#endif + ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data); + if (ret & BLOCK_INLINE_DATA_CHANGED) { +#ifdef WORDS_BIGENDIAN + ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf, + ctx->buflen, 0); + if (ctx->errcode) { + ret |= BLOCK_ABORT; + goto out; + } +#endif + ctx->errcode = ext2fs_write_inode(fs, ino, &inode); + if (ctx->errcode) + ret |= BLOCK_ABORT; + ret &= ~BLOCK_INLINE_DATA_CHANGED; + } + if (ret & BLOCK_ABORT) + goto out; + + data.fs = fs; + data.ino = ino; + ctx->errcode = ext2fs_inline_data_ea_get(&data); + if (ctx->errcode) { + ret |= BLOCK_ABORT; + goto out; + } + if (data.ea_size <= 0) + goto out; + + ctx->buf = data.ea_data; + ctx->buflen = data.ea_size; +#ifdef WORDS_BIGENDIAN + ctx.errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0); + if (ctx.errcode) { + ret |= BLOCK_ABORT; + goto out; + } +#endif + + ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data); + if (ret & BLOCK_INLINE_DATA_CHANGED) { +#ifdef WORDS_BIGENDIAN + ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf, + ctx->buflen, 0); + if (ctx->errcode) { + ret |= BLOCK_ABORT; + goto out1; + } +#endif + ctx->errcode = ext2fs_inline_data_ea_set(&data); + if (ctx->errcode) + ret |= BLOCK_ABORT; + } + +out1: + ext2fs_free_mem(&data.ea_data); + ctx->buf = 0; + +out: + ret &= ~(BLOCK_ABORT | BLOCK_INLINE_DATA_CHANGED); + return ret; +} diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c index 760f25f0..e3628b35 100644 --- a/lib/ext2fs/swapfs.c +++ b/lib/ext2fs/swapfs.c @@ -209,6 +209,7 @@ void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t, { unsigned i, has_data_blocks, extra_isize, attr_magic; int has_extents = 0; + int has_inline_data = 0; int islnk = 0; __u32 *eaf, *eat; @@ -235,12 +236,18 @@ void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t, (struct ext2_inode *) t); if (hostorder && (f->i_flags & EXT4_EXTENTS_FL)) has_extents = 1; + if (hostorder && (f->i_flags & EXT4_INLINE_DATA_FL)) + has_inline_data = 1; t->i_flags = ext2fs_swab32(f->i_flags); if (!hostorder && (t->i_flags & EXT4_EXTENTS_FL)) has_extents = 1; + if (!hostorder && (f->i_flags & EXT4_INLINE_DATA_FL)) + has_inline_data = 1; t->i_dir_acl = ext2fs_swab32(f->i_dir_acl); - /* extent data are swapped on access, not here */ - if (!has_extents && (!islnk || has_data_blocks)) { + /* + * Extent data and inline data are swapped on access, not here + */ + if (!has_extents && !has_inline_data && (!islnk || has_data_blocks)) { for (i = 0; i < EXT2_N_BLOCKS; i++) t->i_block[i] = ext2fs_swab32(f->i_block[i]); } else if (t != f) {