lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20131011233342.GB20432@birch.djwong.org>
Date:	Fri, 11 Oct 2013 16:33:42 -0700
From:	"Darrick J. Wong" <darrick.wong@...cle.com>
To:	Zheng Liu <gnehzuil.liu@...il.com>
Cc:	linux-ext4@...r.kernel.org, "Theodore Ts'o" <tytso@....edu>,
	Zheng Liu <wenqing.lz@...bao.com>
Subject: Re: [PATCH v1 04/22] libext2fs: handle inline data in dir iterator
 function

On Fri, Aug 02, 2013 at 05:49:31PM +0800, Zheng Liu wrote:
> 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"

"extended 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"

"i_extra_isize value"

> +
>  	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;

Perhaps this function should be called ext2fs_inline_dirblock_iterate()?
The name alone makes me think this function could work for regular inline_data
files, if such things ever exist.

--D

> +
> +	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
--
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

Powered by Openwall GNU/*/Linux Powered by OpenVZ