2013-12-03 12:09:08

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 00/28] e2fsprogs: inline data refinement patch set

Hi all,

Here is my second round of inline data patch set refinement for
e2fsprogs. In this version, the major change is to be based against
darrick's extended attribute API. Now the patches have been rebased
against e2fsprogs/next branch. Feel free to test it. As always, any
comment or feedback are welcome.

Ted and Darrick give me a lot of comments. Thank you!

Now all interfaces for inline data are defined as below:

/* initialize extented attribute to set 'system.data' */
errcode_t ext2fs_inline_data_init(ext2_filsys fs, ext2_ino_t ino);

/* get the size of inline data */
errcode_t ext2fs_inline_data_size(ext2_filsys fs, ext2_ino_t ino,
size_t *size);

/* remove inline data in extented attribute */
errcode_t ext2fs_inline_data_ea_remove(ext2_filsys fs, ext2_ino_t ino);

/* expand a directory/file */
errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino);

/* iterate on a directory with inline data */
errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
ext2_ino_t ino,
int (*func)(ext2_filsys fs,
blk64_t *blocknr,
e2_blkcnt_t blockcnt,
blk64_t ref_blk,
int ref_offset,
void *priv_data),
void *priv_data);

/* get inline data */
errcode_t ext2fs_inline_data_get(ext2_filsys fs, ext2_ino_t ino,
struct ext2_inode *inode,
void *buf, size_t *size);

/* set inline data */
errcode_t ext2fs_inline_data_set(ext2_filsys fs, ext2_ino_t ino,
struct ext2_inode *inode,
void *buf, size_t size);

changelog:
v2:
* Rebase against e2fsprogs/next branch
* Based against darrick's extended attribute API
* Remove 'libext2fs: add INLINE_DATA into EXT2_LIB_SOFTSUPP_INCOMPAT'
patch that has been applied
* Refine the interface of inline data. Now no any interface is
exported to outside caller. All interfaces are only exported
for developers
* Refactor ext2fs_inline_data_dir_iterate() so that it can call
ext2fs_process_dir_block directly
* Fix big-endian problem in handle parent inode in i_block
* Remove ext2fs_inode_has_inline_data() interface
* Export inode cache creation interface for unit test
* Coding style fixes
* Bug fixes

v1:
* Revise the interfaces of liext2fs.

* Implement ext2fs_punch_inline_data in lib/ext2fs/punch.c. Now if you
want to truncate an inode with inline data, you need to call
ext2fs_punch(), and it will handle inline data.

* Remove ext2fs_inline_data_dirsearch() function. Now we don't support
dirsearch command for an inode with inline data in debugfs.

* Ext2fs_mkdir() refinement. Now if we want to create a directory with
inline data, we just need to call ext2fs_mkdir(). This function will
try to create a new directory with inline data if inline_data feature
is enabled.

* Fix big-endian bug in some functions. When ext2fs_read_inode() tries
to read an inode into memory, it will handle big-endian by itself.
Thus, we don't need to handle it manually.

* Fix a bug in e2fsck/pass3. When we try to expand a 'lost+found' dir,
we don't need to handle inline data because this dir shouldn't have
this flag.

Regards,
- Zheng

Darrick J. Wong (8):
libext2fs: support modifying arbitrary extended attributes
libext2fs: various tweaks to the xattr editor APIs
libext2fs: extend xattr api to query number of attrs
libext2fs: fix memory leaks in extended attribute code
libext2fs: fix block leak when releasing xattr block
libext2fs: remove redundant code
libext2fs: free key/value pairs before reading
debugfs: dump all extended attributes

Zheng Liu (20):
libext2fs: handle inline data in dir iterator function
libext2fs: handle inline_data in block iterator function
debugfs: make stat command support inline data
debugfs: make expand command support inline data
debugfs: make mkdir command support inline data
debugfs: make lsdel command support inline data
debugfs: handle inline_data feature in bmap command
debugfs: handle inline data feature in punch command
libext2fs: handle inline data in read/write function
libext2fs: add inline_data feature into EXT2_LIB_FEATURE_INCOMPAT_SUPP
mke2fs: add inline_data support in mke2fs
tune2fs: add inline_data feature in tune2fs
e2fsck: add problem descriptions and check inline data feature
e2fsck: check inline_data in pass1
e2fsck: check inline_data in pass2
e2fsck: check inline_data in pass3
tests: change result in f_bad_disconnected_inode
mke2fs: enable inline_data feature on ext4dev filesystem
libext2fs: export inode cahce creation function
libext2fs: add a unit test for inline data

debugfs/debugfs.c | 93 ++--
debugfs/filefrag.c | 12 +-
debugfs/lsdel.c | 20 +-
e2fsck/pass1.c | 84 ++-
e2fsck/pass2.c | 128 ++++-
e2fsck/pass3.c | 12 +
e2fsck/problem.c | 14 +
e2fsck/problem.h | 10 +
e2fsck/rehash.c | 2 +
lib/ext2fs/Makefile.in | 16 +-
lib/ext2fs/Makefile.pq | 1 +
lib/ext2fs/bmap.c | 7 +
lib/ext2fs/dblist_dir.c | 16 +-
lib/ext2fs/dir_iterate.c | 62 ++-
lib/ext2fs/expanddir.c | 2 +
lib/ext2fs/ext2_err.et.in | 24 +
lib/ext2fs/ext2_fs.h | 10 +
lib/ext2fs/ext2fs.h | 34 +-
lib/ext2fs/ext2fsP.h | 22 +
lib/ext2fs/ext_attr.c | 844 +++++++++++++++++++++++++++++++
lib/ext2fs/fileio.c | 108 ++++
lib/ext2fs/inline_data.c | 729 ++++++++++++++++++++++++++
lib/ext2fs/inode.c | 8 +-
lib/ext2fs/mkdir.c | 77 ++-
lib/ext2fs/newdir.c | 25 +
lib/ext2fs/punch.c | 28 +-
lib/ext2fs/swapfs.c | 12 +-
lib/ext2fs/valid_blk.c | 7 +
misc/mke2fs.8.in | 3 +
misc/mke2fs.c | 18 +-
misc/mke2fs.conf.in | 2 +-
misc/tune2fs.8.in | 5 +
misc/tune2fs.c | 19 +-
tests/f_bad_disconnected_inode/expect.1 | 9 +
tests/r_inline_xattr/expect | 6 +-
35 files changed, 2333 insertions(+), 136 deletions(-)
create mode 100644 lib/ext2fs/inline_data.c

--
1.7.9.7



2013-12-03 12:09:11

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 01/28] libext2fs: support modifying arbitrary extended attributes

From: "Darrick J. Wong" <[email protected]>

Add functions to allow clients to get, set, and remove extended
attributes from any file. It also supports modifying EAs living in
i_file_acl.

v2: Put the header declarations in the correct part of ext2fs.h,
provide a function to release an EA block from an inode, and check
i_extra_isize to make sure we actually have space for in-inode EAs.

[Modified by Zheng]
Ext_attr feature check in ext2fs_xattrs_read/write() is removed because
inline_data feature can be enabled without ext_attr.

Signed-off-by: Darrick J. Wong <[email protected]>
Signed-off-by: Zheng Liu <[email protected]>
---
lib/ext2fs/ext2_err.et.in | 18 ++
lib/ext2fs/ext2fs.h | 28 ++
lib/ext2fs/ext_attr.c | 754 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 800 insertions(+)

diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index 9cc1bd1..b819a90 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -482,4 +482,22 @@ ec EXT2_ET_BLOCK_BITMAP_CSUM_INVALID,
ec EXT2_ET_INLINE_DATA_CANT_ITERATE,
"Cannot block iterate on an inode containing inline data"

+ec EXT2_ET_EA_BAD_NAME_LEN,
+ "Extended attribute has an invalid name length"
+
+ec EXT2_ET_EA_BAD_VALUE_SIZE,
+ "Extended attribute has an invalid value length"
+
+ec EXT2_ET_BAD_EA_HASH,
+ "Extended attribute has an incorrect hash"
+
+ec EXT2_ET_BAD_EA_HEADER,
+ "Extended attribute block has a bad header"
+
+ec EXT2_ET_EA_KEY_NOT_FOUND,
+ "Extended attribute key not found"
+
+ec EXT2_ET_EA_NO_SPACE,
+ "Insufficient space to store extended attribute data"
+
end
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 67876ad..30bd4cf 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -637,6 +637,13 @@ typedef struct stat ext2fs_struct_stat;
#define EXT2_FLAG_FLUSH_NO_SYNC 1

/*
+ * Modify and iterate extended attributes
+ */
+struct ext2_xattr_handle;
+#define XATTR_ABORT 1
+#define XATTR_CHANGED 2
+
+/*
* function prototypes
*/
static inline int ext2fs_has_group_desc_csum(ext2_filsys fs)
@@ -1142,6 +1149,27 @@ extern errcode_t ext2fs_adjust_ea_refcount3(ext2_filsys fs, blk64_t blk,
char *block_buf,
int adjust, __u32 *newcount,
ext2_ino_t inum);
+errcode_t ext2fs_xattrs_expand(struct ext2_xattr_handle *h,
+ unsigned int expandby);
+errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle);
+errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle);
+errcode_t ext2fs_xattrs_iterate(struct ext2_xattr_handle *h,
+ int (*func)(char *name, char *value,
+ void *data),
+ void *data);
+errcode_t ext2fs_xattr_get(struct ext2_xattr_handle *h, const char *key,
+ void **value, unsigned int *value_len);
+errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *handle,
+ const char *key,
+ const void *value,
+ unsigned int value_len);
+errcode_t ext2fs_xattr_remove(struct ext2_xattr_handle *handle,
+ const char *key);
+errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_xattr_handle **handle);
+errcode_t ext2fs_xattrs_close(struct ext2_xattr_handle **handle);
+errcode_t ext2fs_free_ext_attr(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode_large *inode);

/* extent.c */
extern errcode_t ext2fs_extent_header_verify(void *ptr, int size);
diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
index 9649a14..4d40149 100644
--- a/lib/ext2fs/ext_attr.c
+++ b/lib/ext2fs/ext_attr.c
@@ -186,3 +186,757 @@ errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
return ext2fs_adjust_ea_refcount2(fs, blk, block_buf, adjust,
newcount);
}
+
+/* Manipulate the contents of extended attribute regions */
+struct ext2_xattr {
+ char *name;
+ void *value;
+ unsigned int value_len;
+};
+
+struct ext2_xattr_handle {
+ ext2_filsys fs;
+ struct ext2_xattr *attrs;
+ unsigned int length;
+ ext2_ino_t ino;
+ int dirty;
+};
+
+errcode_t ext2fs_xattrs_expand(struct ext2_xattr_handle *h,
+ unsigned int expandby)
+{
+ struct ext2_xattr *new_attrs;
+ errcode_t err;
+
+ err = ext2fs_get_arrayzero(h->length + expandby,
+ sizeof(struct ext2_xattr), &new_attrs);
+ if (err)
+ return err;
+
+ memcpy(new_attrs, h->attrs, h->length * sizeof(struct ext2_xattr));
+ ext2fs_free_mem(&h->attrs);
+ h->length += expandby;
+ h->attrs = new_attrs;
+
+ return 0;
+}
+
+struct ea_name_index {
+ int index;
+ const char *name;
+};
+
+static struct ea_name_index ea_names[] = {
+ {1, "user."},
+ {2, "system.posix_acl_access"},
+ {3, "system.posix_acl_default"},
+ {4, "trusted."},
+ {6, "security."},
+ {7, "system."},
+ {0, NULL},
+};
+
+static const char *find_ea_prefix(int index)
+{
+ struct ea_name_index *e;
+
+ for (e = ea_names; e->name; e++)
+ if (e->index == index)
+ return e->name;
+
+ return NULL;
+}
+
+static int find_ea_index(const char *fullname, char **name, int *index)
+{
+ struct ea_name_index *e;
+
+ for (e = ea_names; e->name; e++) {
+ if (memcmp(fullname, e->name, strlen(e->name)) == 0) {
+ *name = (char *)fullname + strlen(e->name);
+ *index = e->index;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+errcode_t ext2fs_free_ext_attr(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode_large *inode)
+{
+ struct ext2_ext_attr_header *header;
+ void *block_buf = NULL;
+ dgrp_t grp;
+ blk64_t blk, goal;
+ errcode_t err;
+ struct ext2_inode_large i;
+
+ /* Read inode? */
+ if (inode == NULL) {
+ err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&i,
+ sizeof(struct ext2_inode_large));
+ if (err)
+ return err;
+ inode = &i;
+ }
+
+ /* Do we already have an EA block? */
+ blk = ext2fs_file_acl_block(fs, (struct ext2_inode *)inode);
+ if (blk == 0)
+ return 0;
+
+ /* Find block, zero it, write back */
+ if ((blk < fs->super->s_first_data_block) ||
+ (blk >= ext2fs_blocks_count(fs->super))) {
+ err = EXT2_ET_BAD_EA_BLOCK_NUM;
+ goto out;
+ }
+
+ err = ext2fs_get_mem(fs->blocksize, &block_buf);
+ if (err)
+ goto out;
+
+ err = ext2fs_read_ext_attr3(fs, blk, block_buf, ino);
+ if (err)
+ goto out2;
+
+ header = (struct ext2_ext_attr_header *) block_buf;
+ if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
+ err = EXT2_ET_BAD_EA_HEADER;
+ goto out2;
+ }
+
+ header->h_refcount--;
+ err = ext2fs_write_ext_attr3(fs, blk, block_buf, ino);
+ if (err)
+ goto out2;
+
+ /* Erase link to block */
+ ext2fs_file_acl_block_set(fs, (struct ext2_inode *)inode, 0);
+ if (header->h_refcount == 0)
+ ext2fs_block_alloc_stats2(fs, blk, -1);
+
+ /* Write inode? */
+ if (inode == &i) {
+ err = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&i,
+ sizeof(struct ext2_inode_large));
+ if (err)
+ goto out2;
+ }
+
+out2:
+ ext2fs_free_mem(&block_buf);
+out:
+ return err;
+}
+
+static errcode_t prep_ea_block_for_write(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode_large *inode)
+{
+ struct ext2_ext_attr_header *header;
+ void *block_buf = NULL;
+ dgrp_t grp;
+ blk64_t blk, goal;
+ errcode_t err;
+
+ /* Do we already have an EA block? */
+ blk = ext2fs_file_acl_block(fs, (struct ext2_inode *)inode);
+ if (blk != 0) {
+ if ((blk < fs->super->s_first_data_block) ||
+ (blk >= ext2fs_blocks_count(fs->super))) {
+ err = EXT2_ET_BAD_EA_BLOCK_NUM;
+ goto out;
+ }
+
+ err = ext2fs_get_mem(fs->blocksize, &block_buf);
+ if (err)
+ goto out;
+
+ err = ext2fs_read_ext_attr3(fs, blk, block_buf, ino);
+ if (err)
+ goto out2;
+
+ header = (struct ext2_ext_attr_header *) block_buf;
+ if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
+ err = EXT2_ET_BAD_EA_HEADER;
+ goto out2;
+ }
+
+ /* Single-user block. We're done here. */
+ if (header->h_refcount == 1)
+ return 0;
+
+ /* We need to CoW the block. */
+ header->h_refcount--;
+ err = ext2fs_write_ext_attr3(fs, blk, block_buf, ino);
+ if (err)
+ goto out2;
+ } else {
+ /* No block, we must increment i_blocks */
+ err = ext2fs_iblk_add_blocks(fs, (struct ext2_inode *)inode,
+ 1);
+ if (err)
+ goto out;
+ }
+
+ /* Allocate a block */
+ grp = ext2fs_group_of_ino(fs, ino);
+ goal = ext2fs_inode_table_loc(fs, grp);
+ err = ext2fs_alloc_block2(fs, goal, NULL, &blk);
+ if (err)
+ return err;
+ ext2fs_file_acl_block_set(fs, (struct ext2_inode *)inode, blk);
+out2:
+ ext2fs_free_mem(&block_buf);
+out:
+ return err;
+}
+
+
+static errcode_t write_xattrs_to_buffer(struct ext2_xattr_handle *handle,
+ struct ext2_xattr **pos,
+ void *entries_start,
+ unsigned int storage_size,
+ unsigned int value_offset_correction)
+{
+ struct ext2_xattr *x = *pos;
+ struct ext2_ext_attr_entry *e = entries_start;
+ void *end = entries_start + storage_size;
+ char *shortname;
+ unsigned int entry_size, value_size;
+ int idx, ret;
+
+ /* For all remaining x... */
+ for (; x < handle->attrs + handle->length; x++) {
+ if (!x->name)
+ continue;
+
+ /* Calculate index and shortname position */
+ shortname = x->name;
+ ret = find_ea_index(x->name, &shortname, &idx);
+
+ /* Calculate entry and value size */
+ entry_size = (sizeof(*e) + strlen(shortname) +
+ EXT2_EXT_ATTR_PAD - 1) &
+ ~(EXT2_EXT_ATTR_PAD - 1);
+ value_size = ((x->value_len + EXT2_EXT_ATTR_PAD - 1) /
+ EXT2_EXT_ATTR_PAD) * EXT2_EXT_ATTR_PAD;
+
+ /*
+ * Would entry collide with value?
+ * Note that we must leave sufficient room for a (u32)0 to
+ * mark the end of the entries.
+ */
+ if ((void *)e + entry_size + sizeof(__u32) > end - value_size)
+ break;
+
+ /* Fill out e appropriately */
+ e->e_name_len = strlen(shortname);
+ e->e_name_index = (ret ? idx : 0);
+ e->e_value_offs = end - value_size - (void *)entries_start +
+ value_offset_correction;
+ e->e_value_block = 0;
+ e->e_value_size = x->value_len;
+
+ /* Store name and value */
+ end -= value_size;
+ memcpy((void *)e + sizeof(*e), shortname, e->e_name_len);
+ memcpy(end, x->value, e->e_value_size);
+
+ e->e_hash = ext2fs_ext_attr_hash_entry(e, end);
+
+ e = EXT2_EXT_ATTR_NEXT(e);
+ *(__u32 *)e = 0;
+ }
+ *pos = x;
+
+ return 0;
+}
+
+errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle)
+{
+ struct ext2_xattr *x;
+ struct ext2_inode_large *inode;
+ void *start, *block_buf = NULL;
+ struct ext2_ext_attr_header *header;
+ __u32 ea_inode_magic;
+ blk64_t blk;
+ unsigned int storage_size;
+ unsigned int i, written;
+ errcode_t err;
+
+ i = EXT2_INODE_SIZE(handle->fs->super);
+ if (i < sizeof(*inode))
+ i = sizeof(*inode);
+ err = ext2fs_get_memzero(i, &inode);
+ if (err)
+ return err;
+
+ err = ext2fs_read_inode_full(handle->fs, handle->ino,
+ (struct ext2_inode *)inode,
+ EXT2_INODE_SIZE(handle->fs->super));
+ if (err)
+ goto out;
+
+ x = handle->attrs;
+ /* Does the inode have size for EA? */
+ if (EXT2_INODE_SIZE(handle->fs->super) <= EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize +
+ sizeof(__u32))
+ goto write_ea_block;
+
+ /* Write the inode EA */
+ ea_inode_magic = EXT2_EXT_ATTR_MAGIC;
+ memcpy(((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize, &ea_inode_magic, sizeof(__u32));
+ storage_size = EXT2_INODE_SIZE(handle->fs->super) -
+ EXT2_GOOD_OLD_INODE_SIZE - inode->i_extra_isize -
+ sizeof(__u32);
+ start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize + sizeof(__u32);
+
+ err = write_xattrs_to_buffer(handle, &x, start, storage_size, 0);
+ if (err)
+ goto out;
+
+ /* Are we done? */
+ if (x == handle->attrs + handle->length)
+ goto skip_ea_block;
+
+write_ea_block:
+ /* Write the EA block */
+ err = ext2fs_get_mem(handle->fs->blocksize, &block_buf);
+ if (err)
+ goto out;
+
+ storage_size = handle->fs->blocksize -
+ sizeof(struct ext2_ext_attr_header);
+ start = block_buf + sizeof(struct ext2_ext_attr_header);
+
+ err = write_xattrs_to_buffer(handle, &x, start, storage_size,
+ (void *)start - block_buf);
+ if (err)
+ goto out2;
+
+ if (x < handle->attrs + handle->length) {
+ err = EXT2_ET_EA_NO_SPACE;
+ goto out2;
+ }
+
+ if (block_buf) {
+ /* Write a header on the EA block */
+ header = block_buf;
+ header->h_magic = EXT2_EXT_ATTR_MAGIC;
+ header->h_refcount = 1;
+ header->h_blocks = 1;
+
+ /* Get a new block for writing */
+ err = prep_ea_block_for_write(handle->fs, handle->ino, inode);
+ if (err)
+ goto out2;
+
+ /* Finally, write the new EA block */
+ blk = ext2fs_file_acl_block(handle->fs,
+ (struct ext2_inode *)inode);
+ err = ext2fs_write_ext_attr3(handle->fs, blk, block_buf,
+ handle->ino);
+ if (err)
+ goto out2;
+ }
+
+skip_ea_block:
+ blk = ext2fs_file_acl_block(handle->fs, (struct ext2_inode *)inode);
+ if (!block_buf && blk) {
+ /* xattrs shrunk, free the block */
+ ext2fs_file_acl_block_set(handle->fs,
+ (struct ext2_inode *)inode, 0);
+ err = ext2fs_iblk_sub_blocks(handle->fs,
+ (struct ext2_inode *)inode, 1);
+ if (err)
+ goto out;
+ ext2fs_block_alloc_stats2(handle->fs, blk, -1);
+ }
+
+ /* Write the inode */
+ err = ext2fs_write_inode_full(handle->fs, handle->ino,
+ (struct ext2_inode *)inode,
+ EXT2_INODE_SIZE(handle->fs->super));
+ if (err)
+ goto out2;
+
+out2:
+ ext2fs_free_mem(&block_buf);
+out:
+ ext2fs_free_mem(&inode);
+ handle->dirty = 0;
+ return err;
+}
+
+static errcode_t read_xattrs_from_buffer(struct ext2_xattr_handle *handle,
+ struct ext2_ext_attr_entry *entries,
+ unsigned int storage_size,
+ void *value_start)
+{
+ struct ext2_xattr *x;
+ struct ext2_ext_attr_entry *entry;
+ const char *prefix;
+ void *ptr;
+ unsigned int remain, prefix_len;
+ errcode_t err;
+
+ x = handle->attrs;
+ while (x->name)
+ x++;
+
+ entry = entries;
+ while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
+ __u32 hash;
+
+ /* header eats this space */
+ remain -= sizeof(struct ext2_ext_attr_entry);
+
+ /* is attribute name valid? */
+ if (EXT2_EXT_ATTR_SIZE(entry->e_name_len) > remain)
+ return EXT2_ET_EA_BAD_NAME_LEN;
+
+ /* attribute len eats this space */
+ remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
+
+ /* check value size */
+ if (entry->e_value_size > remain)
+ return EXT2_ET_EA_BAD_VALUE_SIZE;
+
+ /* e_value_block must be 0 in inode's ea */
+ if (entry->e_value_block != 0)
+ return EXT2_ET_BAD_EA_BLOCK_NUM;
+
+ hash = ext2fs_ext_attr_hash_entry(entry, value_start +
+ entry->e_value_offs);
+
+ /* e_hash may be 0 in older inode's ea */
+ if (entry->e_hash != 0 && entry->e_hash != hash)
+ return EXT2_ET_BAD_EA_HASH;
+
+ remain -= entry->e_value_size;
+
+ /* Allocate space for more attrs? */
+ if (x == handle->attrs + handle->length) {
+ err = ext2fs_xattrs_expand(handle, 4);
+ if (err)
+ return err;
+ x = handle->attrs + handle->length - 4;
+ }
+
+ /* Extract name/value */
+ prefix = find_ea_prefix(entry->e_name_index);
+ prefix_len = (prefix ? strlen(prefix) : 0);
+ err = ext2fs_get_memzero(entry->e_name_len + prefix_len + 1,
+ &x->name);
+ if (err)
+ return err;
+ if (prefix)
+ memcpy(x->name, prefix, prefix_len);
+ if (entry->e_name_len)
+ memcpy(x->name + prefix_len,
+ (void *)entry + sizeof(*entry),
+ entry->e_name_len);
+
+ err = ext2fs_get_mem(entry->e_value_size, &x->value);
+ if (err)
+ return err;
+ x->value_len = entry->e_value_size;
+ memcpy(x->value, value_start + entry->e_value_offs,
+ entry->e_value_size);
+ x++;
+ entry = EXT2_EXT_ATTR_NEXT(entry);
+ }
+
+ return 0;
+}
+
+errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle)
+{
+ struct ext2_xattr *attrs = NULL, *x;
+ unsigned int attrs_len;
+ struct ext2_inode_large *inode;
+ struct ext2_ext_attr_header *header;
+ __u32 ea_inode_magic;
+ unsigned int storage_size;
+ void *start, *block_buf = NULL;
+ blk64_t blk;
+ int i;
+ errcode_t err;
+
+ i = EXT2_INODE_SIZE(handle->fs->super);
+ if (i < sizeof(*inode))
+ i = sizeof(*inode);
+ err = ext2fs_get_memzero(i, &inode);
+ if (err)
+ return err;
+
+ err = ext2fs_read_inode_full(handle->fs, handle->ino,
+ (struct ext2_inode *)inode,
+ EXT2_INODE_SIZE(handle->fs->super));
+ if (err)
+ goto out;
+
+ /* Does the inode have size for EA? */
+ if (EXT2_INODE_SIZE(handle->fs->super) <= EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize +
+ sizeof(__u32))
+ goto read_ea_block;
+
+ /* Look for EA in the inode */
+ memcpy(&ea_inode_magic, ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize, sizeof(__u32));
+ if (ea_inode_magic == EXT2_EXT_ATTR_MAGIC) {
+ storage_size = EXT2_INODE_SIZE(handle->fs->super) -
+ EXT2_GOOD_OLD_INODE_SIZE - inode->i_extra_isize -
+ sizeof(__u32);
+ start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize + sizeof(__u32);
+
+ err = read_xattrs_from_buffer(handle, start, storage_size,
+ start);
+ if (err)
+ goto out;
+ }
+
+read_ea_block:
+ /* Look for EA in a separate EA block */
+ blk = ext2fs_file_acl_block(handle->fs, (struct ext2_inode *)inode);
+ if (blk != 0) {
+ if ((blk < handle->fs->super->s_first_data_block) ||
+ (blk >= ext2fs_blocks_count(handle->fs->super))) {
+ err = EXT2_ET_BAD_EA_BLOCK_NUM;
+ goto out;
+ }
+
+ err = ext2fs_get_mem(handle->fs->blocksize, &block_buf);
+ if (err)
+ goto out;
+
+ err = ext2fs_read_ext_attr3(handle->fs, blk, block_buf,
+ handle->ino);
+ if (err)
+ goto out3;
+
+ header = (struct ext2_ext_attr_header *) block_buf;
+ if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
+ err = EXT2_ET_BAD_EA_HEADER;
+ goto out3;
+ }
+
+ if (header->h_blocks != 1) {
+ err = EXT2_ET_BAD_EA_HEADER;
+ goto out3;
+ }
+
+ /* Read EAs */
+ storage_size = handle->fs->blocksize -
+ sizeof(struct ext2_ext_attr_header);
+ start = block_buf + sizeof(struct ext2_ext_attr_header);
+ err = read_xattrs_from_buffer(handle, start, storage_size,
+ block_buf);
+ if (err)
+ goto out3;
+
+ ext2fs_free_mem(&block_buf);
+ }
+
+ ext2fs_free_mem(&block_buf);
+ ext2fs_free_mem(&inode);
+ return 0;
+
+out3:
+ ext2fs_free_mem(&block_buf);
+out:
+ ext2fs_free_mem(&inode);
+ return err;
+}
+
+#define XATTR_ABORT 1
+#define XATTR_CHANGED 2
+errcode_t ext2fs_xattrs_iterate(struct ext2_xattr_handle *h,
+ int (*func)(char *name, char *value,
+ void *data),
+ void *data)
+{
+ struct ext2_xattr *x;
+ errcode_t err;
+ int ret;
+
+ for (x = h->attrs; x < h->attrs + h->length; x++) {
+ if (!x->name)
+ continue;
+
+ ret = func(x->name, x->value, data);
+ if (ret & XATTR_CHANGED)
+ h->dirty = 1;
+ if (ret & XATTR_ABORT)
+ return 0;
+ }
+
+ return 0;
+}
+
+errcode_t ext2fs_xattr_get(struct ext2_xattr_handle *h, const char *key,
+ void **value, unsigned int *value_len)
+{
+ struct ext2_xattr *x;
+ void *val;
+ errcode_t err;
+
+ for (x = h->attrs; x < h->attrs + h->length; x++) {
+ if (!x->name)
+ continue;
+
+ if (strcmp(x->name, key) == 0) {
+ err = ext2fs_get_mem(x->value_len, &val);
+ if (err)
+ return err;
+ memcpy(val, x->value, x->value_len);
+ *value = val;
+ *value_len = x->value_len;
+ return 0;
+ }
+ }
+
+ return EXT2_ET_EA_KEY_NOT_FOUND;
+}
+
+errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *handle,
+ const char *key,
+ const void *value,
+ unsigned int value_len)
+{
+ struct ext2_xattr *x, *last_empty;
+ char *new_value;
+ errcode_t err;
+
+ last_empty = NULL;
+ for (x = handle->attrs; x < handle->attrs + handle->length; x++) {
+ if (!x->name) {
+ last_empty = x;
+ continue;
+ }
+
+ /* Replace xattr */
+ if (strcmp(x->name, key) == 0) {
+ err = ext2fs_get_mem(value_len, &new_value);
+ if (err)
+ return err;
+ memcpy(new_value, value, value_len);
+ ext2fs_free_mem(&x->value);
+ x->value = new_value;
+ x->value_len = value_len;
+ handle->dirty = 1;
+ return 0;
+ }
+ }
+
+ /* Add attr to empty slot */
+ if (last_empty) {
+ err = ext2fs_get_mem(strlen(key) + 1, &last_empty->name);
+ if (err)
+ return err;
+ strcpy(last_empty->name, key);
+
+ err = ext2fs_get_mem(value_len, &last_empty->value);
+ if (err)
+ return err;
+ memcpy(last_empty->value, value, value_len);
+ last_empty->value_len = value_len;
+ handle->dirty = 1;
+ return 0;
+ }
+
+ /* Expand array, append slot */
+ err = ext2fs_xattrs_expand(handle, 4);
+ if (err)
+ return err;
+
+ x = handle->attrs + handle->length - 4;
+ err = ext2fs_get_mem(strlen(key) + 1, &x->name);
+ if (err)
+ return err;
+ strcpy(x->name, key);
+
+ err = ext2fs_get_mem(value_len, &x->value);
+ if (err)
+ return err;
+ memcpy(x->value, value, value_len);
+ x->value_len = value_len;
+ handle->dirty = 1;
+ return 0;
+}
+
+errcode_t ext2fs_xattr_remove(struct ext2_xattr_handle *handle,
+ const char *key)
+{
+ struct ext2_xattr *x;
+ errcode_t err;
+
+ for (x = handle->attrs; x < handle->attrs + handle->length; x++) {
+ if (!x->name)
+ continue;
+
+ if (strcmp(x->name, key) == 0) {
+ ext2fs_free_mem(&x->name);
+ ext2fs_free_mem(&x->value);
+ x->value_len = 0;
+ handle->dirty = 1;
+ return 0;
+ }
+ }
+
+ return EXT2_ET_EA_KEY_NOT_FOUND;
+}
+
+errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_xattr_handle **handle)
+{
+ struct ext2_xattr_handle *h;
+ errcode_t err;
+
+ err = ext2fs_get_memzero(sizeof(*h), &h);
+ if (err)
+ return err;
+
+ h->length = 4;
+ err = ext2fs_get_arrayzero(h->length, sizeof(struct ext2_xattr),
+ &h->attrs);
+ if (err) {
+ ext2fs_free_mem(&h);
+ return err;
+ }
+ h->ino = ino;
+ h->fs = fs;
+ *handle = h;
+ return 0;
+}
+
+errcode_t ext2fs_xattrs_close(struct ext2_xattr_handle **handle)
+{
+ unsigned int i;
+ struct ext2_xattr_handle *h = *handle;
+ struct ext2_xattr *a = h->attrs;
+ errcode_t err;
+
+ if (h->dirty) {
+ err = ext2fs_xattrs_write(h);
+ if (err)
+ return err;
+ }
+
+ for (i = 0; i < h->length; i++) {
+ if (a[i].name)
+ ext2fs_free_mem(&a[i].name);
+ if (a[i].value)
+ ext2fs_free_mem(&a[i].value);
+ }
+
+ ext2fs_free_mem(&h->attrs);
+ ext2fs_free_mem(handle);
+ return 0;
+}
--
1.7.9.7


2013-12-03 12:09:13

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 02/28] libext2fs: various tweaks to the xattr editor APIs

From: "Darrick J. Wong" <[email protected]>

A few tweaks to the extended attribute editing APIs:

* Use size_t, not unsigned int, in the new extended attribute editing
API.

* Don't expose the _expand() call since there should be no external
users.

* Add a function to return the number of attributes.

Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/ext2fs.h | 8 +++-----
lib/ext2fs/ext_attr.c | 16 ++++++++--------
2 files changed, 11 insertions(+), 13 deletions(-)

diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 30bd4cf..57db793 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -1149,20 +1149,18 @@ extern errcode_t ext2fs_adjust_ea_refcount3(ext2_filsys fs, blk64_t blk,
char *block_buf,
int adjust, __u32 *newcount,
ext2_ino_t inum);
-errcode_t ext2fs_xattrs_expand(struct ext2_xattr_handle *h,
- unsigned int expandby);
errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle);
errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle);
errcode_t ext2fs_xattrs_iterate(struct ext2_xattr_handle *h,
int (*func)(char *name, char *value,
- void *data),
+ size_t value_len, void *data),
void *data);
errcode_t ext2fs_xattr_get(struct ext2_xattr_handle *h, const char *key,
- void **value, unsigned int *value_len);
+ void **value, size_t *value_len);
errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *handle,
const char *key,
const void *value,
- unsigned int value_len);
+ size_t value_len);
errcode_t ext2fs_xattr_remove(struct ext2_xattr_handle *handle,
const char *key);
errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
index 4d40149..bfe95f4 100644
--- a/lib/ext2fs/ext_attr.c
+++ b/lib/ext2fs/ext_attr.c
@@ -191,19 +191,19 @@ errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
struct ext2_xattr {
char *name;
void *value;
- unsigned int value_len;
+ size_t value_len;
};

struct ext2_xattr_handle {
ext2_filsys fs;
struct ext2_xattr *attrs;
- unsigned int length;
+ size_t length;
ext2_ino_t ino;
int dirty;
};

-errcode_t ext2fs_xattrs_expand(struct ext2_xattr_handle *h,
- unsigned int expandby)
+static errcode_t ext2fs_xattrs_expand(struct ext2_xattr_handle *h,
+ unsigned int expandby)
{
struct ext2_xattr *new_attrs;
errcode_t err;
@@ -759,7 +759,7 @@ out:
#define XATTR_CHANGED 2
errcode_t ext2fs_xattrs_iterate(struct ext2_xattr_handle *h,
int (*func)(char *name, char *value,
- void *data),
+ size_t value_len, void *data),
void *data)
{
struct ext2_xattr *x;
@@ -770,7 +770,7 @@ errcode_t ext2fs_xattrs_iterate(struct ext2_xattr_handle *h,
if (!x->name)
continue;

- ret = func(x->name, x->value, data);
+ ret = func(x->name, x->value, x->value_len, data);
if (ret & XATTR_CHANGED)
h->dirty = 1;
if (ret & XATTR_ABORT)
@@ -781,7 +781,7 @@ errcode_t ext2fs_xattrs_iterate(struct ext2_xattr_handle *h,
}

errcode_t ext2fs_xattr_get(struct ext2_xattr_handle *h, const char *key,
- void **value, unsigned int *value_len)
+ void **value, size_t *value_len)
{
struct ext2_xattr *x;
void *val;
@@ -808,7 +808,7 @@ errcode_t ext2fs_xattr_get(struct ext2_xattr_handle *h, const char *key,
errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *handle,
const char *key,
const void *value,
- unsigned int value_len)
+ size_t value_len)
{
struct ext2_xattr *x, *last_empty;
char *new_value;
--
1.7.9.7


2013-12-03 12:09:15

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 03/28] libext2fs: extend xattr api to query number of attrs

From: "Darrick J. Wong" <[email protected]>

Add another API to query the number of extended attributes.

Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/ext2fs.h | 1 +
lib/ext2fs/ext_attr.c | 19 +++++++++++++++----
2 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 57db793..e251435 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -1168,6 +1168,7 @@ errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
errcode_t ext2fs_xattrs_close(struct ext2_xattr_handle **handle);
errcode_t ext2fs_free_ext_attr(ext2_filsys fs, ext2_ino_t ino,
struct ext2_inode_large *inode);
+size_t ext2fs_xattrs_count(struct ext2_xattr_handle *handle);

/* extent.c */
extern errcode_t ext2fs_extent_header_verify(void *ptr, int size);
diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
index bfe95f4..2fae516 100644
--- a/lib/ext2fs/ext_attr.c
+++ b/lib/ext2fs/ext_attr.c
@@ -197,7 +197,7 @@ struct ext2_xattr {
struct ext2_xattr_handle {
ext2_filsys fs;
struct ext2_xattr *attrs;
- size_t length;
+ size_t length, count;
ext2_ino_t ino;
int dirty;
};
@@ -575,7 +575,8 @@ out:
static errcode_t read_xattrs_from_buffer(struct ext2_xattr_handle *handle,
struct ext2_ext_attr_entry *entries,
unsigned int storage_size,
- void *value_start)
+ void *value_start,
+ size_t *nr_read)
{
struct ext2_xattr *x;
struct ext2_ext_attr_entry *entry;
@@ -648,6 +649,7 @@ static errcode_t read_xattrs_from_buffer(struct ext2_xattr_handle *handle,
memcpy(x->value, value_start + entry->e_value_offs,
entry->e_value_size);
x++;
+ (*nr_read)++;
entry = EXT2_EXT_ATTR_NEXT(entry);
}

@@ -697,7 +699,7 @@ errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle)
inode->i_extra_isize + sizeof(__u32);

err = read_xattrs_from_buffer(handle, start, storage_size,
- start);
+ start, &handle->count);
if (err)
goto out;
}
@@ -737,7 +739,7 @@ read_ea_block:
sizeof(struct ext2_ext_attr_header);
start = block_buf + sizeof(struct ext2_ext_attr_header);
err = read_xattrs_from_buffer(handle, start, storage_size,
- block_buf);
+ block_buf, &handle->count);
if (err)
goto out3;

@@ -848,6 +850,7 @@ errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *handle,
memcpy(last_empty->value, value, value_len);
last_empty->value_len = value_len;
handle->dirty = 1;
+ handle->count++;
return 0;
}

@@ -868,6 +871,7 @@ errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *handle,
memcpy(x->value, value, value_len);
x->value_len = value_len;
handle->dirty = 1;
+ handle->count++;
return 0;
}

@@ -886,6 +890,7 @@ errcode_t ext2fs_xattr_remove(struct ext2_xattr_handle *handle,
ext2fs_free_mem(&x->value);
x->value_len = 0;
handle->dirty = 1;
+ handle->count--;
return 0;
}
}
@@ -910,6 +915,7 @@ errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
ext2fs_free_mem(&h);
return err;
}
+ h->count = 0;
h->ino = ino;
h->fs = fs;
*handle = h;
@@ -940,3 +946,8 @@ errcode_t ext2fs_xattrs_close(struct ext2_xattr_handle **handle)
ext2fs_free_mem(handle);
return 0;
}
+
+size_t ext2fs_xattrs_count(struct ext2_xattr_handle *handle)
+{
+ return handle->count;
+}
--
1.7.9.7


2013-12-03 12:09:17

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 04/28] libext2fs: fix memory leaks in extended attribute code

From: "Darrick J. Wong" <[email protected]>

Fix some memory leaks and data disclosure problems in the extended
attribute writing code.

Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/ext_attr.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
index 2fae516..94bc89b 100644
--- a/lib/ext2fs/ext_attr.c
+++ b/lib/ext2fs/ext_attr.c
@@ -364,7 +364,7 @@ static errcode_t prep_ea_block_for_write(ext2_filsys fs, ext2_ino_t ino,

/* Single-user block. We're done here. */
if (header->h_refcount == 1)
- return 0;
+ goto out2;

/* We need to CoW the block. */
header->h_refcount--;
@@ -384,10 +384,11 @@ static errcode_t prep_ea_block_for_write(ext2_filsys fs, ext2_ino_t ino,
goal = ext2fs_inode_table_loc(fs, grp);
err = ext2fs_alloc_block2(fs, goal, NULL, &blk);
if (err)
- return err;
+ goto out2;
ext2fs_file_acl_block_set(fs, (struct ext2_inode *)inode, blk);
out2:
- ext2fs_free_mem(&block_buf);
+ if (block_buf)
+ ext2fs_free_mem(&block_buf);
out:
return err;
}
@@ -505,7 +506,7 @@ errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle)

write_ea_block:
/* Write the EA block */
- err = ext2fs_get_mem(handle->fs->blocksize, &block_buf);
+ err = ext2fs_get_memzero(handle->fs->blocksize, &block_buf);
if (err)
goto out;

--
1.7.9.7


2013-12-03 12:09:19

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 05/28] libext2fs: fix block leak when releasing xattr block

From: "Darrick J. Wong" <[email protected]>

If in the process of writing EAs to an inode we discover that we no
longer need the EA block, use the helper function to decrement the
block's usage count and release it, instead of (brokenly) open-coding
it. Also we need to decrement i_blocks when freeing the EA block.

Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/ext_attr.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
index 94bc89b..3e09d58 100644
--- a/lib/ext2fs/ext_attr.c
+++ b/lib/ext2fs/ext_attr.c
@@ -315,6 +315,9 @@ errcode_t ext2fs_free_ext_attr(ext2_filsys fs, ext2_ino_t ino,
ext2fs_file_acl_block_set(fs, (struct ext2_inode *)inode, 0);
if (header->h_refcount == 0)
ext2fs_block_alloc_stats2(fs, blk, -1);
+ err = ext2fs_iblk_sub_blocks(fs, (struct ext2_inode *)inode, 1);
+ if (err)
+ goto out2;

/* Write inode? */
if (inode == &i) {
@@ -549,13 +552,9 @@ skip_ea_block:
blk = ext2fs_file_acl_block(handle->fs, (struct ext2_inode *)inode);
if (!block_buf && blk) {
/* xattrs shrunk, free the block */
- ext2fs_file_acl_block_set(handle->fs,
- (struct ext2_inode *)inode, 0);
- err = ext2fs_iblk_sub_blocks(handle->fs,
- (struct ext2_inode *)inode, 1);
+ err = ext2fs_free_ext_attr(handle->fs, handle->ino, inode);
if (err)
goto out;
- ext2fs_block_alloc_stats2(handle->fs, blk, -1);
}

/* Write the inode */
--
1.7.9.7


2013-12-03 12:09:25

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 08/28] debugfs: dump all extended attributes

From: "Darrick J. Wong" <[email protected]>

Use the new extended attribute APIs to display all extended attributes
(current code does not look in the EA block) and display full names
(current code ignores name index too).

Signed-off-by: Darrick J. Wong <[email protected]>
---
debugfs/debugfs.c | 68 +++++++++++++++++++++++++------------------
tests/r_inline_xattr/expect | 6 ++--
2 files changed, 42 insertions(+), 32 deletions(-)

diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index 8c32eff..e489f62 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -540,34 +540,45 @@ static void internal_dump_inode_extra(FILE *out,
inode->i_extra_isize);
return;
}
- storage_size = EXT2_INODE_SIZE(current_fs->super) -
- EXT2_GOOD_OLD_INODE_SIZE -
- inode->i_extra_isize;
- magic = (__u32 *)((char *)inode + EXT2_GOOD_OLD_INODE_SIZE +
- inode->i_extra_isize);
- if (*magic == EXT2_EXT_ATTR_MAGIC) {
- fprintf(out, "Extended attributes stored in inode body: \n");
- end = (char *) inode + EXT2_INODE_SIZE(current_fs->super);
- start = (char *) magic + sizeof(__u32);
- entry = (struct ext2_ext_attr_entry *) start;
- while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
- struct ext2_ext_attr_entry *next =
- EXT2_EXT_ATTR_NEXT(entry);
- if (entry->e_value_size > storage_size ||
- (char *) next >= end) {
- fprintf(out, "invalid EA entry in inode\n");
- return;
- }
- fprintf(out, " ");
- dump_xattr_string(out, EXT2_EXT_ATTR_NAME(entry),
- entry->e_name_len);
- fprintf(out, " = \"");
- dump_xattr_string(out, start + entry->e_value_offs,
- entry->e_value_size);
- fprintf(out, "\" (%u)\n", entry->e_value_size);
- entry = next;
- }
- }
+}
+
+/* Dump extended attributes */
+static int dump_attr(char *name, char *value, size_t value_len, void *data)
+{
+ FILE *out = data;
+
+ fprintf(out, " ");
+ dump_xattr_string(out, name, strlen(name));
+ fprintf(out, " = \"");
+ dump_xattr_string(out, value, value_len);
+ fprintf(out, "\" (%zu)\n", value_len);
+
+ return 0;
+}
+
+static void dump_inode_attributes(FILE *out, ext2_ino_t ino)
+{
+ struct ext2_xattr_handle *h;
+ errcode_t err;
+
+ err = ext2fs_xattrs_open(current_fs, ino, &h);
+ if (err)
+ return;
+
+ err = ext2fs_xattrs_read(h);
+ if (err)
+ goto out;
+
+ if (ext2fs_xattrs_count(h) == 0)
+ goto out;
+
+ fprintf(out, "Extended attributes:\n");
+ err = ext2fs_xattrs_iterate(h, dump_attr, out);
+ if (err)
+ goto out;
+
+out:
+ err = ext2fs_xattrs_close(&h);
}

static void dump_blocks(FILE *f, const char *prefix, ext2_ino_t inode)
@@ -815,6 +826,7 @@ void internal_dump_inode(FILE *out, const char *prefix,
if (EXT2_INODE_SIZE(current_fs->super) > EXT2_GOOD_OLD_INODE_SIZE)
internal_dump_inode_extra(out, prefix, inode_num,
(struct ext2_inode_large *) inode);
+ dump_inode_attributes(out, inode_num);
if (current_fs->super->s_creator_os == EXT2_OS_LINUX &&
current_fs->super->s_feature_ro_compat &
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) {
diff --git a/tests/r_inline_xattr/expect b/tests/r_inline_xattr/expect
index 9e71264..c7aa088 100644
--- a/tests/r_inline_xattr/expect
+++ b/tests/r_inline_xattr/expect
@@ -1,8 +1,7 @@
resize2fs test
debugfs -R ''stat file'' test.img 2>&1 | grep ''^Inode\|in inode body\|name = ''
Inode: 1550 Type: regular Mode: 0644 Flags: 0x0
-Extended attributes stored in inode body:
- name = "propervalue" (11)
+ user.name = "propervalue" (11)
Exit status is 0
resize2fs test.img 5M
Resizing the filesystem on test.img to 5120 (1k) blocks.
@@ -11,6 +10,5 @@ The filesystem on test.img is now 5120 blocks long.
Exit status is 0
debugfs -R ''stat file'' test.img 2>&1 | grep ''^Inode\|in inode body\|name = ''
Inode: 12 Type: regular Mode: 0644 Flags: 0x0
-Extended attributes stored in inode body:
- name = "propervalue" (11)
+ user.name = "propervalue" (11)
Exit status is 0
--
1.7.9.7


2013-12-03 12:09:23

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 07/28] libext2fs: free key/value pairs before reading

From: "Darrick J. Wong" <[email protected]>

Before loading extended attributes, free any key/value pairs that
might already be associated with the file.

Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/ext_attr.c | 26 +++++++++++++++++---------
1 file changed, 17 insertions(+), 9 deletions(-)

diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
index 934ad6f..0e9a9ab 100644
--- a/lib/ext2fs/ext_attr.c
+++ b/lib/ext2fs/ext_attr.c
@@ -654,6 +654,20 @@ static errcode_t read_xattrs_from_buffer(struct ext2_xattr_handle *handle,
return 0;
}

+static void xattrs_free_keys(struct ext2_xattr_handle *h)
+{
+ struct ext2_xattr *a = h->attrs;
+ size_t i;
+
+ for (i = 0; i < h->length; i++) {
+ if (a[i].name)
+ ext2fs_free_mem(&a[i].name);
+ if (a[i].value)
+ ext2fs_free_mem(&a[i].value);
+ }
+ h->count = 0;
+}
+
errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle)
{
struct ext2_xattr *attrs = NULL, *x;
@@ -679,6 +693,8 @@ errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle)
if (err)
goto out;

+ xattrs_free_keys(handle);
+
/* Does the inode have size for EA? */
if (EXT2_INODE_SIZE(handle->fs->super) <= EXT2_GOOD_OLD_INODE_SIZE +
inode->i_extra_isize +
@@ -919,9 +935,7 @@ errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,

errcode_t ext2fs_xattrs_close(struct ext2_xattr_handle **handle)
{
- unsigned int i;
struct ext2_xattr_handle *h = *handle;
- struct ext2_xattr *a = h->attrs;
errcode_t err;

if (h->dirty) {
@@ -930,13 +944,7 @@ errcode_t ext2fs_xattrs_close(struct ext2_xattr_handle **handle)
return err;
}

- for (i = 0; i < h->length; i++) {
- if (a[i].name)
- ext2fs_free_mem(&a[i].name);
- if (a[i].value)
- ext2fs_free_mem(&a[i].value);
- }

2013-12-03 12:09:21

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 06/28] libext2fs: remove redundant code

From: "Darrick J. Wong" <[email protected]>

Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/ext_attr.c | 39 +++++++++++++++++----------------------
1 file changed, 17 insertions(+), 22 deletions(-)

diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
index 3e09d58..934ad6f 100644
--- a/lib/ext2fs/ext_attr.c
+++ b/lib/ext2fs/ext_attr.c
@@ -466,7 +466,7 @@ errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle)
__u32 ea_inode_magic;
blk64_t blk;
unsigned int storage_size;
- unsigned int i, written;
+ unsigned int i;
errcode_t err;

i = EXT2_INODE_SIZE(handle->fs->super);
@@ -527,26 +527,24 @@ write_ea_block:
goto out2;
}

- if (block_buf) {
- /* Write a header on the EA block */
- header = block_buf;
- header->h_magic = EXT2_EXT_ATTR_MAGIC;
- header->h_refcount = 1;
- header->h_blocks = 1;
+ /* Write a header on the EA block */
+ header = block_buf;
+ header->h_magic = EXT2_EXT_ATTR_MAGIC;
+ header->h_refcount = 1;
+ header->h_blocks = 1;

- /* Get a new block for writing */
- err = prep_ea_block_for_write(handle->fs, handle->ino, inode);
- if (err)
- goto out2;
+ /* Get a new block for writing */
+ err = prep_ea_block_for_write(handle->fs, handle->ino, inode);
+ if (err)
+ goto out2;

- /* Finally, write the new EA block */
- blk = ext2fs_file_acl_block(handle->fs,
- (struct ext2_inode *)inode);
- err = ext2fs_write_ext_attr3(handle->fs, blk, block_buf,
- handle->ino);
- if (err)
- goto out2;
- }
+ /* Finally, write the new EA block */
+ blk = ext2fs_file_acl_block(handle->fs,
+ (struct ext2_inode *)inode);
+ err = ext2fs_write_ext_attr3(handle->fs, blk, block_buf,
+ handle->ino);
+ if (err)
+ goto out2;

skip_ea_block:
blk = ext2fs_file_acl_block(handle->fs, (struct ext2_inode *)inode);
@@ -659,7 +657,6 @@ static errcode_t read_xattrs_from_buffer(struct ext2_xattr_handle *handle,
errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle)
{
struct ext2_xattr *attrs = NULL, *x;
- unsigned int attrs_len;
struct ext2_inode_large *inode;
struct ext2_ext_attr_header *header;
__u32 ea_inode_magic;
@@ -757,8 +754,6 @@ out:
return err;
}

-#define XATTR_ABORT 1
-#define XATTR_CHANGED 2
errcode_t ext2fs_xattrs_iterate(struct ext2_xattr_handle *h,
int (*func)(char *name, char *value,
size_t value_len, void *data),
--
1.7.9.7


2013-12-03 12:09:30

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 10/28] libext2fs: handle inline_data in block iterator function

From: Zheng Liu <[email protected]>

After applied this commit (a7f4c635), we have banned to traverse blocks
for an inode which has inline data because no block belongs to it. But
before calling this function, we need to check inline data flag. This
commit add a sanity check ext2fs_inode_has_valid_blocks2() to fix them
except that ext2fs_expand_dir because it will be fixed by another patch.

Signed-off-by: Zheng Liu <[email protected]>
---
debugfs/filefrag.c | 12 +++++++-----
debugfs/lsdel.c | 17 ++++++++++-------
lib/ext2fs/valid_blk.c | 7 +++++++
misc/tune2fs.c | 3 ++-
4 files changed, 26 insertions(+), 13 deletions(-)

diff --git a/debugfs/filefrag.c b/debugfs/filefrag.c
index e82d133..7a82e8d 100644
--- a/debugfs/filefrag.c
+++ b/debugfs/filefrag.c
@@ -154,11 +154,13 @@ static void filefrag(ext2_ino_t ino, struct ext2_inode *inode,
fs->name, num_blocks, EXT2_I_SIZE(inode));
}
print_header(fs);
- retval = ext2fs_block_iterate3(current_fs, ino,
- BLOCK_FLAG_READ_ONLY, NULL,
- filefrag_blocks_proc, fs);
- if (retval)
- com_err("ext2fs_block_iterate3", retval, 0);
+ if (ext2fs_inode_has_valid_blocks2(current_fs, inode)) {
+ retval = ext2fs_block_iterate3(current_fs, ino,
+ BLOCK_FLAG_READ_ONLY, NULL,
+ filefrag_blocks_proc, fs);
+ if (retval)
+ com_err("ext2fs_block_iterate3", retval, 0);
+ }

report_filefrag(fs);
fprintf(fs->f, "%s: %d contiguous extents%s\n", fs->name, fs->ext,
diff --git a/debugfs/lsdel.c b/debugfs/lsdel.c
index bed0ce6..ba84611 100644
--- a/debugfs/lsdel.c
+++ b/debugfs/lsdel.c
@@ -141,13 +141,16 @@ void do_lsdel(int argc, char **argv)
lsd.free_blocks = 0;
lsd.bad_blocks = 0;

- retval = ext2fs_block_iterate3(current_fs, ino,
- BLOCK_FLAG_READ_ONLY, block_buf,
- lsdel_proc, &lsd);
- if (retval) {
- com_err("ls_deleted_inodes", retval,
- "while calling ext2fs_block_iterate2");
- goto next;
+ if (ext2fs_inode_has_valid_blocks2(current_fs, &inode)) {
+ retval = ext2fs_block_iterate3(current_fs, ino,
+ BLOCK_FLAG_READ_ONLY,
+ block_buf,
+ lsdel_proc, &lsd);
+ if (retval) {
+ com_err("ls_deleted_inodes", retval,
+ "while calling ext2fs_block_iterate2");
+ goto next;
+ }
}
if (lsd.free_blocks && !lsd.bad_blocks) {
if (num_delarray >= max_delarray) {
diff --git a/lib/ext2fs/valid_blk.c b/lib/ext2fs/valid_blk.c
index 895e36e..db5d90a 100644
--- a/lib/ext2fs/valid_blk.c
+++ b/lib/ext2fs/valid_blk.c
@@ -52,6 +52,13 @@ int ext2fs_inode_has_valid_blocks2(ext2_filsys fs, struct ext2_inode *inode)
return 0; /* Probably a fast symlink */
}
}
+
+ /*
+ * If this inode has inline data, it shouldn't have valid block
+ * entries.
+ */
+ if (inode->i_flags & EXT4_INLINE_DATA_FL)
+ return 0;
return 1;
}

diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 1ae0ee6..95c1886 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -688,7 +688,8 @@ static void rewrite_inodes(ext2_filsys fs)
exit(1);
}

- if (LINUX_S_ISDIR(inode->i_mode)) {
+ if (LINUX_S_ISDIR(inode->i_mode) &&
+ ext2fs_inode_has_valid_blocks2(fs, inode)) {
retval = rewrite_directory(fs, ino, inode);
if (retval) {
com_err("rewrite_directory", retval,
--
1.7.9.7


2013-12-03 12:09:32

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 11/28] debugfs: make stat command support inline data

From: Zheng Liu <[email protected]>

If there is an inode with inline data, we just print the size of inline
data in stat command.

Signed-off-by: Theodore Ts'o <[email protected]>
Signed-off-by: Zheng Liu <[email protected]>
---
debugfs/debugfs.c | 12 ++++++++++++
lib/ext2fs/ext2fsP.h | 2 ++
lib/ext2fs/inline_data.c | 23 +++++++++++++++++++++++
3 files changed, 37 insertions(+)

diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index e489f62..d1c81f1 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -729,6 +729,16 @@ static void dump_extents(FILE *f, const char *prefix, ext2_ino_t ino,
fprintf(f, "\n");
}

+static void dump_inline_data(FILE *out, const char *prefix, ext2_ino_t inode_num)
+{
+ errcode_t retval;
+ size_t size;
+
+ retval = ext2fs_inline_data_size(current_fs, inode_num, &size);
+ if (!retval)
+ fprintf(out, "%sSize of inline data: %d", prefix, size);
+}
+
void internal_dump_inode(FILE *out, const char *prefix,
ext2_ino_t inode_num, struct ext2_inode *inode,
int do_dump_blocks)
@@ -863,6 +873,8 @@ void internal_dump_inode(FILE *out, const char *prefix,
if (inode->i_flags & EXT4_EXTENTS_FL)
dump_extents(out, prefix, inode_num,
DUMP_LEAF_EXTENTS|DUMP_NODE_EXTENTS, 0, 0);
+ else if (inode->i_flags & EXT4_INLINE_DATA_FL)
+ dump_inline_data(out, prefix, inode_num);
else
dump_blocks(out, prefix, inode_num);
}
diff --git a/lib/ext2fs/ext2fsP.h b/lib/ext2fs/ext2fsP.h
index 5ab6084..aae55b9 100644
--- a/lib/ext2fs/ext2fsP.h
+++ b/lib/ext2fs/ext2fsP.h
@@ -88,6 +88,8 @@ extern int ext2fs_process_dir_block(ext2_filsys fs,
int ref_offset,
void *priv_data);

+extern errcode_t ext2fs_inline_data_size(ext2_filsys fs, ext2_ino_t ino,
+ size_t *size);
extern errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
ext2_ino_t ino,
int (*func)(ext2_filsys fs,
diff --git a/lib/ext2fs/inline_data.c b/lib/ext2fs/inline_data.c
index cc2954d..7dc3f24 100644
--- a/lib/ext2fs/inline_data.c
+++ b/lib/ext2fs/inline_data.c
@@ -78,6 +78,29 @@ err:
}


+errcode_t ext2fs_inline_data_size(ext2_filsys fs, ext2_ino_t ino, size_t *size)
+{
+ struct ext2_inode inode;
+ struct ext2_inline_data data;
+ errcode_t retval;
+
+ retval = ext2fs_read_inode(fs, ino, &inode);
+ if (retval)
+ return retval;
+
+ if (!(inode.i_flags & EXT4_INLINE_DATA_FL))
+ return EXT2_ET_NO_INLINE_DATA;
+
+ data.fs = fs;
+ data.ino = ino;
+ retval = ext2fs_inline_data_ea_get(&data);
+ if (retval)
+ return retval;
+
+ *size = EXT4_MIN_INLINE_DATA_SIZE + data.ea_size;
+ return ext2fs_free_mem(&data.ea_data);
+}
+
errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
ext2_ino_t ino,
int (*func)(ext2_filsys fs,
--
1.7.9.7


2013-12-03 12:09:27

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 09/28] libext2fs: handle inline data in dir iterator function

From: Zheng Liu <[email protected]>

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:
- cd
- chroot
- link*
- ls
- ncheck
- pwd
- unlink

* TODO: Inline_data doesn't expand to ibody extended attribute because
link command doesn't handle DIR_NO_SPACE error until now. But if we
have already expanded inline data to ibody ea area, link command can
occupy this space.

Signed-off-by: Theodore Ts'o <[email protected]>
Signed-off-by: Zheng Liu <[email protected]>
---
lib/ext2fs/Makefile.in | 8 +++
lib/ext2fs/Makefile.pq | 1 +
lib/ext2fs/dir_iterate.c | 62 +++++++++++-----
lib/ext2fs/ext2_err.et.in | 3 +
lib/ext2fs/ext2_fs.h | 10 +++
lib/ext2fs/ext2fs.h | 1 +
lib/ext2fs/ext2fsP.h | 11 +++
lib/ext2fs/inline_data.c | 175 +++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/swapfs.c | 12 +++-
9 files changed, 265 insertions(+), 18 deletions(-)
create mode 100644 lib/ext2fs/inline_data.c

diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
index e1e6216..4d011c9 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 \
@@ -758,6 +760,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 2f7b654..89082a7 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 8be0ac2..1624371 100644
--- a/lib/ext2fs/dir_iterate.c
+++ b/lib/ext2fs/dir_iterate.c
@@ -127,6 +127,12 @@ 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;
+ retval = ext2fs_inline_data_dir_iterate(fs, dir,
+ ext2fs_process_dir_block,
+ &ctx);
+ }
if (retval)
return retval;
return ctx.errcode;
@@ -189,30 +195,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 +236,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 +256,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 +294,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_CHANGED to notify caller that inline
+ * data has been changed
+ */
+ retval = BLOCK_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 b819a90..0781145 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -500,4 +500,7 @@ ec EXT2_ET_EA_KEY_NOT_FOUND,
ec EXT2_ET_EA_NO_SPACE,
"Insufficient space to store extended attribute data"

+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 6a28d55..5ab16ae 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -914,4 +914,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 e251435..c5b73fd 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -438,6 +438,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 80d2d0a..5ab6084 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,16 @@ extern int ext2fs_process_dir_block(ext2_filsys fs,
int ref_offset,
void *priv_data);

+extern errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
+ ext2_ino_t ino,
+ int (*func)(ext2_filsys fs,
+ blk64_t *blocknr,
+ e2_blkcnt_t blockcnt,
+ blk64_t ref_blk,
+ int ref_offset,
+ void *priv_data),
+ 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 0000000..cc2954d
--- /dev/null
+++ b/lib/ext2fs/inline_data.c
@@ -0,0 +1,175 @@
+/*
+ * inline_data.c --- data in inode
+ *
+ * Copyright (C) 2012 Zheng Liu <[email protected]>
+ *
+ * %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 <stdio.h>
+#include <time.h>
+
+#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 */
+ char *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;
+}
+
+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;
+}
+
+
+errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
+ ext2_ino_t ino,
+ int (*func)(ext2_filsys fs,
+ blk64_t *blocknr,
+ e2_blkcnt_t blockcnt,
+ blk64_t ref_blk,
+ int ref_offset,
+ void *priv_data),
+ void *priv_data)
+{
+ struct dir_context *ctx;
+ struct ext2_inode inode;
+ struct ext2_dir_entry dirent;
+ struct ext2_inline_data data;
+ errcode_t retval = 0;
+ e2_blkcnt_t blockcnt = 0;
+
+ ctx = (struct dir_context *)priv_data;
+
+ retval = ext2fs_read_inode(fs, ino, &inode);
+ if (retval)
+ goto out;
+
+ if (!(inode.i_flags & EXT4_INLINE_DATA_FL))
+ return EXT2_ET_NO_INLINE_DATA;
+
+ if (!LINUX_S_ISDIR(inode.i_mode)) {
+ retval = EXT2_ET_NO_DIRECTORY;
+ 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';
+ ctx->buf = (char *)&dirent;
+ ext2fs_get_rec_len(fs, &dirent, &ctx->buflen);
+ retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
+ if (retval & 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);
+ retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
+ if (retval & BLOCK_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;
+ }
+ if (retval & 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;
+ retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
+ if (retval & BLOCK_CHANGED) {
+ errcode_t err;
+
+ err = ext2fs_write_inode(fs, ino, &inode);
+ if (err)
+ goto out;
+ }
+ if (retval & BLOCK_ABORT)
+ goto out;
+
+ data.fs = fs;
+ data.ino = ino;
+ retval = ext2fs_inline_data_ea_get(&data);
+ if (retval)
+ goto out;
+ if (data.ea_size > 0) {
+ ctx->buf = data.ea_data;
+ ctx->buflen = data.ea_size;
+ retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
+ if (retval & BLOCK_CHANGED)
+ ext2fs_inline_data_ea_set(&data);
+ ext2fs_free_mem(&data.ea_data);
+ ctx->buf = 0;
+ }
+
+out:
+ retval |= BLOCK_ERROR;
+ return retval & BLOCK_ERROR ? ctx->errcode : 0;
+}
diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c
index 1295e81..cfbe5dd 100644
--- a/lib/ext2fs/swapfs.c
+++ b/lib/ext2fs/swapfs.c
@@ -207,6 +207,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;

@@ -233,12 +234,19 @@ 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 are swapped on access, not here
+ * Inline data are not swapped beside parent ino is accessed
+ */
+ 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) {
--
1.7.9.7


2013-12-03 12:09:36

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 13/28] debugfs: make mkdir command support inline data

From: Zheng Liu <[email protected]>

This commit tries to make mkdir command in debugfs support inline data.

Signed-off-by: Zheng Liu <[email protected]>
---
lib/ext2fs/ext2fs.h | 2 ++
lib/ext2fs/ext2fsP.h | 1 +
lib/ext2fs/inline_data.c | 10 ++++++
lib/ext2fs/mkdir.c | 77 +++++++++++++++++++++++++++++++++-------------
lib/ext2fs/newdir.c | 25 +++++++++++++++
5 files changed, 93 insertions(+), 22 deletions(-)

diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index c5b73fd..1481a3d 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -1444,6 +1444,8 @@ int ext2fs_native_flag(void);
/* newdir.c */
extern errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
ext2_ino_t parent_ino, char **block);
+extern errcode_t ext2fs_new_dir_inline_data(ext2_filsys fs, ext2_ino_t dir_ino,
+ ext2_ino_t parent_ino, __u32 *iblock);

/* mkdir.c */
extern errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
diff --git a/lib/ext2fs/ext2fsP.h b/lib/ext2fs/ext2fsP.h
index 4dfa983..b2da21a 100644
--- a/lib/ext2fs/ext2fsP.h
+++ b/lib/ext2fs/ext2fsP.h
@@ -88,6 +88,7 @@ extern int ext2fs_process_dir_block(ext2_filsys fs,
int ref_offset,
void *priv_data);

+extern errcode_t ext2fs_inline_data_init(ext2_filsys fs, ext2_ino_t ino);
extern errcode_t ext2fs_inline_data_size(ext2_filsys fs, ext2_ino_t ino,
size_t *size);
extern errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino);
diff --git a/lib/ext2fs/inline_data.c b/lib/ext2fs/inline_data.c
index 02bb470..527a52d 100644
--- a/lib/ext2fs/inline_data.c
+++ b/lib/ext2fs/inline_data.c
@@ -77,6 +77,16 @@ err:
return retval;
}

+errcode_t ext2fs_inline_data_init(ext2_filsys fs, ext2_ino_t ino)
+{
+ struct ext2_inline_data data;
+
+ data.fs = fs;
+ data.ino = ino;
+ data.ea_size = 0;
+ data.ea_data = "";
+ return ext2fs_inline_data_ea_set(&data);
+}

errcode_t ext2fs_inline_data_size(ext2_filsys fs, ext2_ino_t ino, size_t *size)
{
diff --git a/lib/ext2fs/mkdir.c b/lib/ext2fs/mkdir.c
index 4a85439..06c2c7e 100644
--- a/lib/ext2fs/mkdir.c
+++ b/lib/ext2fs/mkdir.c
@@ -41,10 +41,20 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
ext2_ino_t scratch_ino;
blk64_t blk;
char *block = 0;
+ int inline_data = 0;

EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

/*
+ * Create a new dir with inline data iff this feature is enabled
+ * and ino >= EXT2_FIRST_INO.
+ */
+ if ((!ino || ino >= EXT2_FIRST_INO(fs->super)) &&
+ EXT2_HAS_INCOMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA))
+ inline_data = 1;
+
+ /*
* Allocate an inode, if necessary
*/
if (!ino) {
@@ -57,14 +67,21 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
/*
* Allocate a data block for the directory
*/
- retval = ext2fs_new_block2(fs, 0, 0, &blk);
- if (retval)
- goto cleanup;
+ if (!inline_data) {
+ retval = ext2fs_new_block2(fs, 0, 0, &blk);
+ if (retval)
+ goto cleanup;
+ }

/*
* Create a scratch template for the directory
*/
- retval = ext2fs_new_dir_block(fs, ino, parent, &block);
+ memset(&inode, 0, sizeof(struct ext2_inode));
+ if (inline_data)
+ retval = ext2fs_new_dir_inline_data(fs, ino, parent,
+ inode.i_block);
+ else
+ retval = ext2fs_new_dir_block(fs, ino, parent, &block);
if (retval)
goto cleanup;

@@ -81,16 +98,21 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
/*
* Create the inode structure....
*/
- memset(&inode, 0, sizeof(struct ext2_inode));
inode.i_mode = LINUX_S_IFDIR | (0777 & ~fs->umask);
inode.i_uid = inode.i_gid = 0;
- ext2fs_iblk_set(fs, &inode, 1);
- if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS)
- inode.i_flags |= EXT4_EXTENTS_FL;
- else
- inode.i_block[0] = blk;
+ if (inline_data) {
+ inode.i_flags |= EXT4_INLINE_DATA_FL;
+ inode.i_size = EXT4_MIN_INLINE_DATA_SIZE;
+ } else {
+ if (fs->super->s_feature_incompat &
+ EXT3_FEATURE_INCOMPAT_EXTENTS)
+ inode.i_flags |= EXT4_EXTENTS_FL;
+ else
+ inode.i_block[0] = blk;
+ inode.i_size = fs->blocksize;
+ ext2fs_iblk_set(fs, &inode, 1);
+ }
inode.i_links_count = 2;
- inode.i_size = fs->blocksize;

/*
* Write out the inode and inode data block. The inode generation
@@ -100,18 +122,24 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
retval = ext2fs_write_new_inode(fs, ino, &inode);
if (retval)
goto cleanup;
- retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino);
- if (retval)
- goto cleanup;
-
- if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS) {
- retval = ext2fs_extent_open2(fs, ino, &inode, &handle);
- if (retval)
- goto cleanup;
- retval = ext2fs_extent_set_bmap(handle, 0, blk, 0);
- ext2fs_extent_free(handle);
+ if (inline_data) {
+ /* init "system.data" for new dir */
+ retval = ext2fs_inline_data_init(fs, ino);
+ } else {
+ retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino);
if (retval)
goto cleanup;
+
+ if (fs->super->s_feature_incompat &
+ EXT3_FEATURE_INCOMPAT_EXTENTS) {
+ retval = ext2fs_extent_open2(fs, ino, &inode, &handle);
+ if (retval)
+ goto cleanup;
+ retval = ext2fs_extent_set_bmap(handle, 0, blk, 0);
+ ext2fs_extent_free(handle);
+ if (retval)
+ goto cleanup;
+ }
}

/*
@@ -136,6 +164,10 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
* Update parent inode's counts
*/
if (parent != ino) {
+ /* reload parent inode due to inline data */
+ retval = ext2fs_read_inode(fs, parent, &parent_inode);
+ if (retval)
+ goto cleanup;
parent_inode.i_links_count++;
retval = ext2fs_write_inode(fs, parent, &parent_inode);
if (retval)
@@ -145,7 +177,8 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
/*
* Update accounting....
*/
- ext2fs_block_alloc_stats2(fs, blk, +1);
+ if (!inline_data)
+ ext2fs_block_alloc_stats2(fs, blk, +1);
ext2fs_inode_alloc_stats2(fs, ino, +1, 1);

cleanup:
diff --git a/lib/ext2fs/newdir.c b/lib/ext2fs/newdir.c
index d134bdf..5358c74 100644
--- a/lib/ext2fs/newdir.c
+++ b/lib/ext2fs/newdir.c
@@ -89,3 +89,28 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
*block = buf;
return 0;
}
+
+/*
+ * Create new directory on inline data
+ */
+errcode_t ext2fs_new_dir_inline_data(ext2_filsys fs, ext2_ino_t dir_ino,
+ ext2_ino_t parent_ino, __u32 *iblock)
+{
+ struct ext2_dir_entry *dir = NULL;
+ errcode_t retval;
+ char *buf;
+ int rec_len;
+ int filetype = 0;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ iblock[0] = ext2fs_cpu_to_le32(parent_ino);
+
+ dir = (struct ext2_dir_entry *)((char *)iblock +
+ EXT4_INLINE_DATA_DOTDOT_SIZE);
+ dir->inode = 0;
+ rec_len = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DATA_DOTDOT_SIZE;
+ retval = ext2fs_set_rec_len(fs, rec_len, dir);
+
+ return retval;
+}
--
1.7.9.7


2013-12-03 12:09:34

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 12/28] debugfs: make expand command support inline data

From: Zheng Liu <[email protected]>

This commit defines a ext2fs_inline_data_expand() to expand an inode with
inline data.

Signed-off-by: Zheng Liu <[email protected]>
---
lib/ext2fs/expanddir.c | 2 +
lib/ext2fs/ext2fsP.h | 1 +
lib/ext2fs/inline_data.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 186 insertions(+)

diff --git a/lib/ext2fs/expanddir.c b/lib/ext2fs/expanddir.c
index 22558d6..baad782 100644
--- a/lib/ext2fs/expanddir.c
+++ b/lib/ext2fs/expanddir.c
@@ -116,6 +116,8 @@ errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir)

retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_APPEND,
0, expand_dir_proc, &es);
+ if (retval == EXT2_ET_INLINE_DATA_CANT_ITERATE)
+ return ext2fs_inline_data_expand(fs, dir);

if (es.err)
return es.err;
diff --git a/lib/ext2fs/ext2fsP.h b/lib/ext2fs/ext2fsP.h
index aae55b9..4dfa983 100644
--- a/lib/ext2fs/ext2fsP.h
+++ b/lib/ext2fs/ext2fsP.h
@@ -90,6 +90,7 @@ extern int ext2fs_process_dir_block(ext2_filsys fs,

extern errcode_t ext2fs_inline_data_size(ext2_filsys fs, ext2_ino_t ino,
size_t *size);
+extern errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino);
extern errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
ext2_ino_t ino,
int (*func)(ext2_filsys fs,
diff --git a/lib/ext2fs/inline_data.c b/lib/ext2fs/inline_data.c
index 7dc3f24..02bb470 100644
--- a/lib/ext2fs/inline_data.c
+++ b/lib/ext2fs/inline_data.c
@@ -196,3 +196,186 @@ out:
retval |= BLOCK_ERROR;
return retval & BLOCK_ERROR ? ctx->errcode : 0;
}
+
+static errcode_t ext2fs_inline_data_ea_remove(ext2_filsys fs, ext2_ino_t ino)
+{
+ struct ext2_xattr_handle *handle;
+ errcode_t retval;
+
+ retval = ext2fs_xattrs_open(fs, ino, &handle);
+ if (retval)
+ return retval;
+
+ retval = ext2fs_xattrs_read(handle);
+ if (retval)
+ goto err;
+
+ retval = ext2fs_xattr_remove(handle, "system.data");
+ if (retval)
+ goto err;
+
+ retval = ext2fs_xattrs_write(handle);
+
+err:
+ (void) ext2fs_xattrs_close(&handle);
+ return retval;
+}
+
+static errcode_t ext2fs_inline_data_convert_dir(ext2_filsys fs, ext2_ino_t ino,
+ char *bbuf, char *ibuf, int size)
+{
+ struct ext2_dir_entry *dir, *dir2;
+ struct ext2_dir_entry_tail *t;
+ errcode_t retval;
+ unsigned int offset;
+ int csum_size = 0;
+ int filetype = 0;
+ int rec_len;
+
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dir_entry_tail);
+
+ /* Create '.' and '..' */
+ if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
+ EXT2_FEATURE_INCOMPAT_FILETYPE))
+ filetype = EXT2_FT_DIR << 8;
+
+ /*
+ * Set up entry for '.'
+ */
+ dir = (struct ext2_dir_entry *) bbuf;
+ dir->inode = ino;
+ ext2fs_dirent_set_name_len(dir, 1);
+ ext2fs_dirent_set_file_type(dir, filetype);
+ dir->name[0] = '.';
+ rec_len = (fs->blocksize - csum_size) - EXT2_DIR_REC_LEN(1);
+ dir->rec_len = EXT2_DIR_REC_LEN(1);
+
+ /*
+ * Set up entry for '..'
+ */
+ dir = (struct ext2_dir_entry *) (bbuf + dir->rec_len);
+ dir->rec_len = EXT2_DIR_REC_LEN(2);
+ dir->inode = ext2fs_le32_to_cpu(((__u32 *)ibuf)[0]);
+ ext2fs_dirent_set_name_len(dir, 2);
+ ext2fs_dirent_set_file_type(dir, filetype);
+ dir->name[0] = '.';
+ dir->name[1] = '.';
+
+ /*
+ * Ajust the last rec_len
+ */
+ offset = EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2);
+ dir = (struct ext2_dir_entry *) (bbuf + offset);
+ memcpy(bbuf + offset, ibuf + EXT4_INLINE_DATA_DOTDOT_SIZE,
+ size - EXT4_INLINE_DATA_DOTDOT_SIZE);
+
+ do {
+ dir2 = dir;
+ retval = ext2fs_get_rec_len(fs, dir, &rec_len);
+ if (retval)
+ goto err;
+ offset += rec_len;
+ dir = (struct ext2_dir_entry *) (bbuf + offset);
+ } while (offset < size);
+ rec_len += fs->blocksize - csum_size - offset;
+ retval = ext2fs_set_rec_len(fs, rec_len, dir2);
+ if (retval)
+ goto err;
+
+ if (csum_size) {
+ t = EXT2_DIRENT_TAIL(bbuf, fs->blocksize);
+ ext2fs_initialize_dirent_tail(fs, t);
+ }
+
+err:
+ return retval;
+}
+
+errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino)
+{
+ struct ext2_inode inode;
+ struct ext2_inline_data data;
+ errcode_t retval;
+ blk64_t blk;
+ char *inline_buf = 0;
+ char *blk_buf = 0;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ retval = ext2fs_read_inode(fs, ino, &inode);
+ if (retval)
+ return retval;
+
+ if (!(inode.i_flags & EXT4_INLINE_DATA_FL))
+ return EXT2_ET_NO_INLINE_DATA;
+
+ /* Get inline data first */
+ data.fs = fs;
+ data.ino = ino;
+ retval = ext2fs_inline_data_ea_get(&data);
+ if (retval)
+ return retval;
+ retval = ext2fs_get_mem(EXT4_MIN_INLINE_DATA_SIZE + data.ea_size,
+ &inline_buf);
+ if (retval)
+ goto errout;
+
+ memcpy(inline_buf, (void *)inode.i_block, EXT4_MIN_INLINE_DATA_SIZE);
+ if (data.ea_size > 0) {
+ memcpy(inline_buf + EXT4_MIN_INLINE_DATA_SIZE,
+ data.ea_data, data.ea_size);
+ }
+
+ memset((void *)inode.i_block, 0, EXT4_MIN_INLINE_DATA_SIZE);
+ retval = ext2fs_inline_data_ea_remove(fs, ino);
+ if (retval)
+ goto errout;
+
+ retval = ext2fs_get_mem(fs->blocksize, &blk_buf);
+ if (retval)
+ goto errout;
+
+ /* Adjust the rec_len */
+ retval = ext2fs_inline_data_convert_dir(fs, ino, blk_buf, inline_buf,
+ EXT4_MIN_INLINE_DATA_SIZE +
+ data.ea_size);
+ if (retval)
+ goto errout;
+
+ /* Allocate a new block */
+ retval = ext2fs_new_block2(fs, 0, 0, &blk);
+ if (retval)
+ goto errout;
+ if (EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT3_FEATURE_INCOMPAT_EXTENTS))
+ inode.i_flags |= EXT4_EXTENTS_FL;
+ else
+ inode.i_block[0] = blk;
+ inode.i_flags &= ~EXT4_INLINE_DATA_FL;
+ ext2fs_iblk_set(fs, &inode, 1);
+ inode.i_size = fs->blocksize;
+ retval = ext2fs_write_inode(fs, ino, &inode);
+ if (retval)
+ goto errout;
+ retval = ext2fs_write_dir_block4(fs, blk, blk_buf, 0, ino);
+ if (retval)
+ goto errout;
+ if (EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT3_FEATURE_INCOMPAT_EXTENTS)) {
+ ext2_extent_handle_t handle;
+
+ retval = ext2fs_extent_open2(fs, ino, &inode, &handle);
+ if (retval)
+ goto errout;
+ retval = ext2fs_extent_set_bmap(handle, 0, blk, 0);
+ ext2fs_extent_free(handle);
+ }
+
+errout:
+ if (blk_buf)
+ ext2fs_free_mem(&blk_buf);
+ if (inline_buf)
+ ext2fs_free_mem(&inline_buf);
+ ext2fs_free_mem(&data.ea_data);
+ return retval;
+}
--
1.7.9.7


2013-12-03 12:09:38

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 14/28] debugfs: make lsdel command support inline data

From: Zheng Liu <[email protected]>

After checking inline data in ext2fs_inode_have_valid_blocks2() we won't
traverse the block in do_lsdel() function. But if an inode has inline
data we also need to report it.

Signed-off-by: Theodore Ts'o <[email protected]>
Signed-off-by: Zheng Liu <[email protected]>
---
debugfs/lsdel.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/debugfs/lsdel.c b/debugfs/lsdel.c
index ba84611..b61951f 100644
--- a/debugfs/lsdel.c
+++ b/debugfs/lsdel.c
@@ -152,7 +152,8 @@ void do_lsdel(int argc, char **argv)
goto next;
}
}
- if (lsd.free_blocks && !lsd.bad_blocks) {
+ if (lsd.free_blocks && !lsd.bad_blocks ||
+ inode.i_flags & EXT4_INLINE_DATA_FL) {
if (num_delarray >= max_delarray) {
max_delarray += 50;
delarray = realloc(delarray,
--
1.7.9.7


2013-12-03 12:09:45

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 17/28] libext2fs: handle inline data in read/write function

From: Zheng Liu <[email protected]>

Currently ext2fs_file_read/write are used to copy data from/to a file.
But they manipulate data by blocksize. For supporting inline data, we
handle it in two new fucntions called ext2fs_file_read/write_inline_data.

In read path the implementation is straightforward. But in write path
things get more complicated because if the size of data is greater than
the maximum size of inline data we will expand this file. So now we
will check this in ext2fs_xattr_set(). If this inode doesn't have
enough space, it will return EXT2_ET_INLINE_DATA_NO_SPACE error. Then
the caller will check this error and tries to expand the file.

The following commands in debugfs can handle inline_data feature after
applying this patch:
- dump
- cat
- rdump
- write

Signed-off-by: Theodore Ts'o <[email protected]>
Signed-off-by: Zheng Liu <[email protected]>
---
debugfs/debugfs.c | 13 ++++--
lib/ext2fs/ext2_err.et.in | 3 ++
lib/ext2fs/ext2fsP.h | 6 +++
lib/ext2fs/ext_attr.c | 76 +++++++++++++++++++++++++++++++
lib/ext2fs/fileio.c | 108 +++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/inline_data.c | 90 ++++++++++++++++++++++++++++++++++---
6 files changed, 287 insertions(+), 9 deletions(-)

diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index d1c81f1..0903c8e 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -1684,7 +1684,6 @@ fail:
return retval;
}

-
void do_write(int argc, char *argv[])
{
int fd;
@@ -1750,8 +1749,11 @@ void do_write(int argc, char *argv[])
current_fs->now ? current_fs->now : time(0);
inode.i_links_count = 1;
inode.i_size = statbuf.st_size;
- if (current_fs->super->s_feature_incompat &
- EXT3_FEATURE_INCOMPAT_EXTENTS) {
+ if (EXT2_HAS_INCOMPAT_FEATURE(current_fs->super,
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
+ inode.i_flags |= EXT4_INLINE_DATA_FL;
+ } else if (current_fs->super->s_feature_incompat &
+ EXT3_FEATURE_INCOMPAT_EXTENTS) {
int i;
struct ext3_extent_header *eh;

@@ -1768,6 +1770,11 @@ void do_write(int argc, char *argv[])
close(fd);
return;
}
+ if (inode.i_flags & EXT4_INLINE_DATA_FL) {
+ retval = ext2fs_inline_data_init(current_fs, newfile);
+ if (retval)
+ return;
+ }
if (LINUX_S_ISREG(inode.i_mode)) {
if (statbuf.st_blocks < statbuf.st_size / S_BLKSIZE) {
make_holes = 1;
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index 0781145..68aa3e0 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_NO_INLINE_DATA,
"Inode doesn't have inline data"

+ec EXT2_ET_INLINE_DATA_NO_SPACE,
+ "No free space in inline data"
+
end
diff --git a/lib/ext2fs/ext2fsP.h b/lib/ext2fs/ext2fsP.h
index bfc321c..e159128 100644
--- a/lib/ext2fs/ext2fsP.h
+++ b/lib/ext2fs/ext2fsP.h
@@ -102,6 +102,12 @@ extern errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
int ref_offset,
void *priv_data),
void *priv_data);
+extern errcode_t ext2fs_inline_data_get(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ void *buf, size_t *size);
+extern errcode_t ext2fs_inline_data_set(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ void *buf, size_t size);

/* Generic numeric progress meter */

diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
index 0e9a9ab..98f0fbb 100644
--- a/lib/ext2fs/ext_attr.c
+++ b/lib/ext2fs/ext_attr.c
@@ -818,6 +818,71 @@ errcode_t ext2fs_xattr_get(struct ext2_xattr_handle *h, const char *key,
return EXT2_ET_EA_KEY_NOT_FOUND;
}

+static errcode_t ext2fs_xattr_max_size(struct ext2_xattr_handle *handle, size_t *size)
+{
+ struct ext2_ext_attr_header *header;
+ struct ext2_ext_attr_entry *entry;
+ struct ext2_inode_large *inode;
+ __u32 ea_inode_magic;
+ unsigned int storage_size, freesize, minoff;
+ void *start;
+ int i;
+ errcode_t err;
+
+ i = EXT2_INODE_SIZE(handle->fs->super);
+ if (i < sizeof(*inode))
+ i = sizeof(*inode);
+ err = ext2fs_get_memzero(i, &inode);
+ if (err)
+ return err;
+
+ err = ext2fs_read_inode_full(handle->fs, handle->ino,
+ (struct ext2_inode *)inode,
+ EXT2_INODE_SIZE(handle->fs->super));
+ if (err)
+ goto out;
+
+ /* Does the inode have size for EA? */
+ if (EXT2_INODE_SIZE(handle->fs->super) <= EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize +
+ sizeof(__u32)) {
+ err = EXT2_ET_INLINE_DATA_NO_SPACE;
+ goto out;
+ }
+
+ minoff = EXT2_INODE_SIZE(handle->fs->super) -
+ sizeof(*inode) - sizeof(__u32);
+ memcpy(&ea_inode_magic, ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize, sizeof(__u32));
+ if (ea_inode_magic == EXT2_EXT_ATTR_MAGIC) {
+ /* has xattrs. calculate the size */
+ storage_size = EXT2_INODE_SIZE(handle->fs->super) -
+ EXT2_GOOD_OLD_INODE_SIZE - inode->i_extra_isize -
+ sizeof(__u32);
+ start= ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize + sizeof(__u32);
+ entry = start;
+ while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
+ if (!entry->e_value_block && entry->e_value_size) {
+ unsigned int offs = entry->e_value_offs;
+ if (offs < minoff)
+ minoff = offs;
+ }
+ entry = EXT2_EXT_ATTR_NEXT(entry);
+ }
+ *size = minoff - ((char *)entry - (char *)start) - sizeof(__u32);
+ } else {
+ /* no xattr. return a maximum size */
+ *size = EXT2_EXT_ATTR_SIZE(minoff -
+ EXT2_EXT_ATTR_LEN(strlen("data")) -
+ EXT2_EXT_ATTR_ROUND - sizeof(__u32));
+ }
+
+out:
+ ext2fs_free_mem(&inode);
+ return err;
+}
+
errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *handle,
const char *key,
const void *value,
@@ -827,6 +892,17 @@ errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *handle,
char *new_value;
errcode_t err;

+ /* we need to check inline data space */
+ if (strcmp(key, "system.data") == 0) {
+ size_t max_size;
+
+ err = ext2fs_xattr_max_size(handle, &max_size);
+ if (err)
+ return err;
+ if (value_len > max_size)
+ return EXT2_ET_INLINE_DATA_NO_SPACE;
+ }
+
last_empty = NULL;
for (x = handle->attrs; x < handle->attrs + handle->length; x++) {
if (!x->name) {
diff --git a/lib/ext2fs/fileio.c b/lib/ext2fs/fileio.c
index 02e6263..acb705d 100644
--- a/lib/ext2fs/fileio.c
+++ b/lib/ext2fs/fileio.c
@@ -224,6 +224,39 @@ errcode_t ext2fs_file_close(ext2_file_t file)
}


+static errcode_t
+ext2fs_file_read_inline_data(ext2_file_t file, void *buf,
+ unsigned int wanted, unsigned int *got)
+{
+ ext2_filsys fs;
+ errcode_t retval;
+ unsigned int start, count = 0;
+ size_t size;
+
+ fs = file->fs;
+ retval = ext2fs_inline_data_get(fs, file->ino, &file->inode,
+ file->buf, &size);
+ if (retval)
+ return retval;
+
+ if (file->pos >= size)
+ goto out;
+
+ start = file->pos % size;
+ count = size - start;
+ if (count > wanted)
+ count = wanted;
+ memcpy(buf, file->buf + start, count);
+ file->pos += count;
+ buf += count;
+
+out:
+ if (got)
+ *got = count;
+ return retval;
+}
+
+
errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
unsigned int wanted, unsigned int *got)
{
@@ -236,6 +269,10 @@ errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
fs = file->fs;

+ /* If an inode has inline data, things get complicated. */
+ if (file->inode.i_flags & EXT4_INLINE_DATA_FL)
+ return ext2fs_file_read_inline_data(file, buf, wanted, got);
+
while ((file->pos < EXT2_I_SIZE(&file->inode)) && (wanted > 0)) {
retval = sync_buffer_position(file);
if (retval)
@@ -266,6 +303,67 @@ fail:
}


+static errcode_t
+ext2fs_file_write_inline_data(ext2_file_t file, const void *buf,
+ unsigned int nbytes, unsigned int *written)
+{
+ ext2_filsys fs;
+ errcode_t retval;
+ unsigned int start, count = 0;
+ size_t size;
+
+ fs = file->fs;
+ retval = ext2fs_inline_data_get(fs, file->ino, &file->inode,
+ file->buf, &size);
+ if (retval)
+ return retval;
+
+ if (file->pos < size) {
+ start = file->pos % fs->blocksize;
+ count = nbytes - start;
+ memcpy(file->buf + start, buf, count);
+
+ retval = ext2fs_inline_data_set(fs, file->ino, &file->inode,
+ file->buf, count);
+ if (retval == EXT2_ET_INLINE_DATA_NO_SPACE)
+ goto expand;
+ if (retval)
+ return retval;
+
+ file->pos += count;
+
+ /* Update inode size */
+ if (count != 0 && EXT2_I_SIZE(&file->inode) < file->pos) {
+ errcode_t rc;
+
+ rc = ext2fs_file_set_size2(file, file->pos);
+ if (retval == 0)
+ retval = rc;
+ }
+
+ if (written)
+ *written = count;
+ return 0;
+ }
+
+expand:
+ retval = ext2fs_inline_data_expand(fs, file->ino);
+ if (retval)
+ return retval;
+ /*
+ * reload inode and return no space error
+ *
+ * XXX: file->inode could be copied from the outside
+ * in ext2fs_file_open2(). We have no way to modify
+ * the outside inode.
+ */
+ retval = ext2fs_read_inode(fs, file->ino, &file->inode);
+ if (retval)
+ return retval;
+ return EXT2_ET_INLINE_DATA_NO_SPACE;
+}
+
+
errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
unsigned int nbytes, unsigned int *written)
{
@@ -280,6 +378,16 @@ errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
if (!(file->flags & EXT2_FILE_WRITE))
return EXT2_ET_FILE_RO;

+ /* If an inode has inline data, things get complicated. */
+ if (file->inode.i_flags & EXT4_INLINE_DATA_FL) {
+ retval = ext2fs_file_write_inline_data(file, buf, nbytes,
+ written);
+ if (retval != EXT2_ET_INLINE_DATA_NO_SPACE)
+ return retval;
+ /* fall through to read data from the block */
+ retval = 0;
+ }
+
while (nbytes > 0) {
retval = sync_buffer_position(file);
if (retval)
diff --git a/lib/ext2fs/inline_data.c b/lib/ext2fs/inline_data.c
index 16af814..4a35209 100644
--- a/lib/ext2fs/inline_data.c
+++ b/lib/ext2fs/inline_data.c
@@ -309,6 +309,7 @@ errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino)
struct ext2_inline_data data;
errcode_t retval;
blk64_t blk;
+ size_t inline_size;
char *inline_buf = 0;
char *blk_buf = 0;

@@ -327,8 +328,8 @@ errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino)
retval = ext2fs_inline_data_ea_get(&data);
if (retval)
return retval;
- retval = ext2fs_get_mem(EXT4_MIN_INLINE_DATA_SIZE + data.ea_size,
- &inline_buf);
+ inline_size = data.ea_size + EXT4_MIN_INLINE_DATA_SIZE;
+ retval = ext2fs_get_mem(inline_size, &inline_buf);
if (retval)
goto errout;

@@ -347,10 +348,14 @@ errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino)
if (retval)
goto errout;

- /* Adjust the rec_len */
- retval = ext2fs_inline_data_convert_dir(fs, ino, blk_buf, inline_buf,
- EXT4_MIN_INLINE_DATA_SIZE +
- data.ea_size);
+ if (LINUX_S_ISDIR(inode.i_mode)) {
+ /* Adjust the rec_len */
+ retval = ext2fs_inline_data_convert_dir(fs, ino, blk_buf,
+ inline_buf, inline_size);
+ } else {
+ /* Copy data for a regular inode */
+ memcpy(blk_buf, inline_buf, inline_size);
+ }
if (retval)
goto errout;

@@ -389,3 +394,76 @@ errout:
ext2fs_free_mem(&data.ea_data);
return retval;
}
+
+/*
+ * When caller uses this function to retrieve the inline data, it must
+ * allocate a buffer which has the size of inline data. The size of
+ * inline data can be know by ext2fs_inline_data_get_size().
+ */
+errcode_t ext2fs_inline_data_get(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ void *buf, size_t *size)
+{
+ struct ext2_inode inode_buf;
+ struct ext2_inline_data data;
+ errcode_t retval;
+
+ if (!inode) {
+ retval = ext2fs_read_inode(fs, ino, &inode_buf);
+ if (retval)
+ return retval;
+ inode = &inode_buf;
+ }
+
+ data.fs = fs;
+ data.ino = ino;
+ retval = ext2fs_inline_data_ea_get(&data);
+ if (retval)
+ return retval;
+
+ memcpy(buf, (void *)inode->i_block, EXT4_MIN_INLINE_DATA_SIZE);
+ if (data.ea_size > 0)
+ memcpy(buf + EXT4_MIN_INLINE_DATA_SIZE,
+ data.ea_data, data.ea_size);
+
+ if (size)
+ *size = EXT4_MIN_INLINE_DATA_SIZE + data.ea_size;
+ ext2fs_free_mem(&data.ea_data);
+ return 0;
+}
+
+errcode_t ext2fs_inline_data_set(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ void *buf, size_t size)
+{
+ struct ext2_inode inode_buf;
+ struct ext2_inline_data data;
+ unsigned int max_size = 0;
+ errcode_t retval;
+
+ if (!inode) {
+ retval = ext2fs_read_inode(fs, ino, &inode_buf);
+ if (retval)
+ return retval;
+ inode = &inode_buf;
+ }
+
+ /* simple case */
+ if (size <= EXT4_MIN_INLINE_DATA_SIZE) {
+ memcpy((void *)inode->i_block, buf, size);
+ return ext2fs_write_inode(fs, ino, inode);
+ }
+
+ /*
+ * complicated case
+ */
+ memcpy((void *)inode->i_block, buf, EXT4_MIN_INLINE_DATA_SIZE);
+ retval = ext2fs_write_inode(fs, ino, inode);
+ if (retval)
+ return retval;
+ data.fs = fs;
+ data.ino = ino;
+ data.ea_size = size - EXT4_MIN_INLINE_DATA_SIZE;
+ data.ea_data = buf + EXT4_MIN_INLINE_DATA_SIZE;
+ return ext2fs_inline_data_ea_set(&data);
+}
--
1.7.9.7


2013-12-03 12:09:47

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 18/28] libext2fs: add inline_data feature into EXT2_LIB_FEATURE_INCOMPAT_SUPP

From: Zheng Liu <[email protected]>

Let e2fsprogs support inline_data feature.

Signed-off-by: Theodore Ts'o <[email protected]>
Signed-off-by: Zheng Liu <[email protected]>
---
lib/ext2fs/ext2fs.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 1481a3d..bf0ef26 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -595,7 +595,8 @@ typedef struct ext2_icount *ext2_icount_t;
EXT3_FEATURE_INCOMPAT_EXTENTS|\
EXT4_FEATURE_INCOMPAT_FLEX_BG|\
EXT4_LIB_INCOMPAT_MMP|\
- EXT4_FEATURE_INCOMPAT_64BIT)
+ EXT4_FEATURE_INCOMPAT_64BIT|\
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA)

#define EXT2_LIB_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\
EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\
--
1.7.9.7


2013-12-03 12:09:40

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 15/28] debugfs: handle inline_data feature in bmap command

From: Zheng Liu <[email protected]>

No physical block mapping if an inode has inline data.

Signed-off-by: Theodore Ts'o <[email protected]>
Signed-off-by: Zheng Liu <[email protected]>
---
lib/ext2fs/bmap.c | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/lib/ext2fs/bmap.c b/lib/ext2fs/bmap.c
index 5074587..0d043bd 100644
--- a/lib/ext2fs/bmap.c
+++ b/lib/ext2fs/bmap.c
@@ -273,6 +273,13 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
block_buf = buf;
}

+ /*
+ * If an inode has inline data, that means that it doesn't have
+ * any blocks and we shouldn't map any blocks for it.
+ */
+ if (inode->i_flags & EXT4_INLINE_DATA_FL)
+ goto done;
+
if (inode->i_flags & EXT4_EXTENTS_FL) {
retval = ext2fs_extent_open2(fs, ino, inode, &handle);
if (retval)
--
1.7.9.7


2013-12-03 12:09:50

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 19/28] mke2fs: add inline_data support in mke2fs

From: Zheng Liu <[email protected]>

Now inline_data doesn't depend on ext_attr. Hence we don't need to do
this sanity check. But if the inode size is too small (128 bytes),
inline_data will be useless because we couldn't save data in ibody
extented attribute. So we need to report this error.

Signed-off-by: Theodore Ts'o <[email protected]>
Signed-off-by: Zheng Liu <[email protected]>
---
misc/mke2fs.8.in | 3 +++
misc/mke2fs.c | 18 ++++++++++++++++--
2 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/misc/mke2fs.8.in b/misc/mke2fs.8.in
index 7b89296..c5e1abb 100644
--- a/misc/mke2fs.8.in
+++ b/misc/mke2fs.8.in
@@ -587,6 +587,9 @@ option).
@JDEV@must be created with the same
@JDEV@block size as the filesystems that will be using it.
.TP
+.B inline_data
+Allow data to be stored in the inode and extented attribute area
+.TP
.B large_file
Filesystem can contain files that are greater than 2GB. (Modern kernels
set this feature automatically when a file > 2GB is created.)
diff --git a/misc/mke2fs.c b/misc/mke2fs.c
index 67c9225..0a3880f 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -929,7 +929,8 @@ static __u32 ok_features[3] = {
EXT2_FEATURE_INCOMPAT_META_BG|
EXT4_FEATURE_INCOMPAT_FLEX_BG|
EXT4_FEATURE_INCOMPAT_MMP |
- EXT4_FEATURE_INCOMPAT_64BIT,
+ EXT4_FEATURE_INCOMPAT_64BIT|
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA,
/* R/O compat */
EXT2_FEATURE_RO_COMPAT_LARGE_FILE|
EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
@@ -2069,7 +2070,8 @@ profile_error:
"See https://ext4.wiki.kernel.org/"
"index.php/Quota for more information\n\n"));

- /* Since sparse_super is the default, we would only have a problem
+ /*
+ * Since sparse_super is the default, we would only have a problem
* here if it was explicitly disabled.
*/
if ((fs_param.s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) &&
@@ -2125,6 +2127,18 @@ profile_error:
blocksize);
exit(1);
}
+ /*
+ * If inode size is 128 and inline data is enable, we need to
+ * notify users that inline data will never be useful.
+ */
+ if ((fs_param.s_feature_incompat &
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA) &&
+ inode_size == EXT2_GOOD_OLD_INODE_SIZE) {
+ com_err(program_name, 0,
+ _("inode size is %d, inline data is useless"),
+ inode_size);
+ exit(1);
+ }
fs_param.s_inode_size = inode_size;
}

--
1.7.9.7


2013-12-03 12:09:54

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 21/28] e2fsck: add problem descriptions and check inline data feature

From: Zheng Liu <[email protected]>

Signed-off-by: Theodore Ts'o <[email protected]>
Signed-off-by: Zheng Liu <[email protected]>
---
e2fsck/pass1.c | 22 +++++++++++++++++++++-
e2fsck/problem.c | 9 +++++++++
e2fsck/problem.h | 7 +++++++
3 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index ab23e42..56411f1 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -592,7 +592,7 @@ void e2fsck_pass1(e2fsck_t ctx)
struct ext2_super_block *sb = ctx->fs->super;
const char *old_op;
unsigned int save_type;
- int imagic_fs, extent_fs;
+ int imagic_fs, extent_fs, inlinedata_fs;
int busted_fs_time = 0;
int inode_size;
int failed_csum = 0;
@@ -626,6 +626,8 @@ void e2fsck_pass1(e2fsck_t ctx)

imagic_fs = (sb->s_feature_compat & EXT2_FEATURE_COMPAT_IMAGIC_INODES);
extent_fs = (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS);
+ inlinedata_fs = (sb->s_feature_incompat &
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA);

/*
* Allocate bitmaps structures
@@ -808,6 +810,24 @@ void e2fsck_pass1(e2fsck_t ctx)
}
}

+ /* Test for incorrect inline_data flags settings. */
+ if ((inode->i_flags & EXT4_INLINE_DATA_FL) && !inlinedata_fs &&
+ (ino >= EXT2_FIRST_INODE(fs->super))) {
+ size_t size = 0;
+
+ pctx.errcode = ext2fs_inline_data_size(fs, ino, &size);
+ if (!pctx.errcode && size &&
+ !fix_problem(ctx, PR_1_INLINE_DATA_FEATURE, &pctx)) {
+ sb->s_feature_incompat |=
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA;
+ ext2fs_mark_super_dirty(fs);
+ inlinedata_fs = 1;
+ } else if (!fix_problem(ctx, PR_1_INLINE_DATA_SET, &pctx)) {
+ e2fsck_clear_inode(ctx, ino, inode, 0, "pass1");
+ continue;
+ }
+ }
+
/*
* Test for incorrect extent flag settings.
*
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 897693a..a4f7749 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1018,6 +1018,15 @@ static struct e2fsck_problem problem_table[] = {
N_("@i %i, end of extent exceeds allowed value\n\t(logical @b %c, physical @b %b, len %N)\n"),
PROMPT_CLEAR, 0 },

+ /* Inode has inline data, but superblock is missing INLINE_DATA feature. */
+ { PR_1_INLINE_DATA_FEATURE,
+ N_("@i %i has inline data, but @S is missing INLINE_DATA feature\n"),
+ PROMPT_CLEAR, PR_PREEN_OK },
+
+ /* INLINE_DATA feature is set in a non-inline-data filesystem */
+ { PR_1_INLINE_DATA_SET,
+ N_("@i %i has INLINE_DATA_FL flag on @f without inline data support.\n"),
+ PROMPT_CLEAR, 0 },

/* Pass 1b errors */

diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index ae1ed26..9d41cea 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -593,6 +593,13 @@ struct problem_context {
#define PR_1_EXTENT_INDEX_START_INVALID 0x01006D

#define PR_1_EXTENT_END_OUT_OF_BOUNDS 0x01006E
+
+/* Inode has inline data, but superblock is missing INLINE_DATA feature. */
+#define PR_1_INLINE_DATA_FEATURE 0x01006F
+
+/* INLINE_DATA feature is set in a non-inline-data filesystem */
+#define PR_1_INLINE_DATA_SET 0x010070
+
/*
* Pass 1b errors
*/
--
1.7.9.7


2013-12-03 12:09:42

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 16/28] debugfs: handle inline data feature in punch command

From: Zheng Liu <[email protected]>

Now punch command only can remove all inline data because now
punch command is based on block unit and the size of inline data is
never beyond a block size.

Signed-off-by: Zheng Liu <[email protected]>
---
lib/ext2fs/ext2fsP.h | 1 +
lib/ext2fs/inline_data.c | 2 +-
lib/ext2fs/punch.c | 28 +++++++++++++++++++++++++++-
3 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/lib/ext2fs/ext2fsP.h b/lib/ext2fs/ext2fsP.h
index b2da21a..bfc321c 100644
--- a/lib/ext2fs/ext2fsP.h
+++ b/lib/ext2fs/ext2fsP.h
@@ -91,6 +91,7 @@ extern int ext2fs_process_dir_block(ext2_filsys fs,
extern errcode_t ext2fs_inline_data_init(ext2_filsys fs, ext2_ino_t ino);
extern errcode_t ext2fs_inline_data_size(ext2_filsys fs, ext2_ino_t ino,
size_t *size);
+extern errcode_t ext2fs_inline_data_ea_remove(ext2_filsys fs, ext2_ino_t ino);
extern errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino);
extern errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
ext2_ino_t ino,
diff --git a/lib/ext2fs/inline_data.c b/lib/ext2fs/inline_data.c
index 527a52d..16af814 100644
--- a/lib/ext2fs/inline_data.c
+++ b/lib/ext2fs/inline_data.c
@@ -207,7 +207,7 @@ out:
return retval & BLOCK_ERROR ? ctx->errcode : 0;
}

-static errcode_t ext2fs_inline_data_ea_remove(ext2_filsys fs, ext2_ino_t ino)
+errcode_t ext2fs_inline_data_ea_remove(ext2_filsys fs, ext2_ino_t ino)
{
struct ext2_xattr_handle *handle;
errcode_t retval;
diff --git a/lib/ext2fs/punch.c b/lib/ext2fs/punch.c
index 4471f46..d6c9004 100644
--- a/lib/ext2fs/punch.c
+++ b/lib/ext2fs/punch.c
@@ -300,6 +300,30 @@ errout:
return retval;
}

+static errcode_t ext2fs_punch_inline_data(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ blk64_t start, blk64_t end)
+{
+ errcode_t retval;
+
+ /*
+ * In libext2fs ext2fs_punch is based on block unit. So that
+ * means that if start > 0 we don't need to do nothing. Due
+ * to this we will remove all inline data in ext2fs_punch()
+ * now.
+ */
+ if (start > 0)
+ return 0;
+
+ memset((char *)inode->i_block, 0, EXT4_MIN_INLINE_DATA_SIZE);
+ inode->i_size = 0;
+ retval = ext2fs_write_inode(fs, ino, inode);
+ if (retval)
+ return retval;
+
+ return ext2fs_inline_data_ea_remove(fs, ino);
+}
+
/*
* Deallocate all logical blocks starting at start to end, inclusive.
* If end is ~0, then this is effectively truncate.
@@ -322,7 +346,9 @@ extern errcode_t ext2fs_punch(ext2_filsys fs, ext2_ino_t ino,
return retval;
inode = &inode_buf;
}
- if (inode->i_flags & EXT4_EXTENTS_FL)
+ if (inode->i_flags & EXT4_INLINE_DATA_FL)
+ return ext2fs_punch_inline_data(fs, ino, inode, start, end);
+ else if (inode->i_flags & EXT4_EXTENTS_FL)
retval = ext2fs_punch_extent(fs, ino, inode, start, end);
else {
blk_t count;
--
1.7.9.7


2013-12-03 12:09:52

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 20/28] tune2fs: add inline_data feature in tune2fs

From: Zheng Liu <[email protected]>

Inline_data can be enabled without ext_attr. Hence we don't check it.
As doing in mke2fs we need to check inode size.

Now this feature only can be enabled because we may be out of space when
disabling it. If this feature is disabled, we need to allocate a block
for every file and directory, and it might exhaust all space.

Signed-off-by: Theodore Ts'o <[email protected]>
Signed-off-by: Zheng Liu <[email protected]>
---
misc/tune2fs.8.in | 5 +++++
misc/tune2fs.c | 16 +++++++++++++++-
2 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/misc/tune2fs.8.in b/misc/tune2fs.8.in
index 55c6dd9..78f965b 100644
--- a/misc/tune2fs.8.in
+++ b/misc/tune2fs.8.in
@@ -531,6 +531,11 @@ Setting the filesystem feature is equivalent to using the
.B \-j
option.
.TP
+.B inline_data
+Allow data to be stored in the inode and extented attribute area.
+.B Tune2fs
+only supports setting this filesystem feature.
+.TP
.B large_file
Filesystem can contain files that are greater than 2GB. (Modern kernels
set this feature automatically when a file > 2GB is created.)
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 95c1886..1952081 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -140,7 +140,8 @@ static __u32 ok_features[3] = {
EXT2_FEATURE_INCOMPAT_FILETYPE |
EXT3_FEATURE_INCOMPAT_EXTENTS |
EXT4_FEATURE_INCOMPAT_FLEX_BG |
- EXT4_FEATURE_INCOMPAT_MMP,
+ EXT4_FEATURE_INCOMPAT_MMP |
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA,
/* R/O compat */
EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
@@ -1083,6 +1084,19 @@ mmp_error:
disable_uninit_bg(fs,
EXT4_FEATURE_RO_COMPAT_GDT_CSUM);

+ if (FEATURE_ON(E2P_FEATURE_INCOMPAT,
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
+ /*
+ * Check inode size. If inode size is 128, tell user that
+ * inline data is useless.
+ */
+ if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE) {
+ fputs(_("The inode size is too small to "
+ "store inline data.\n"), stderr);
+ return 1;
+ }
+ }
+
if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
EXT4_FEATURE_RO_COMPAT_QUOTA)) {
/*
--
1.7.9.7


2013-12-03 12:09:59

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 23/28] e2fsck: check inline_data in pass2

From: Zheng Liu <[email protected]>

Signed-off-by: Theodore Ts'o <[email protected]>
Signed-off-by: Zheng Liu <[email protected]>
---
e2fsck/pass2.c | 128 +++++++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 100 insertions(+), 28 deletions(-)

diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c
index 3c0bf49..aa996bf 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -730,6 +730,15 @@ static void salvage_directory(ext2_filsys fs,
}
}

+static int is_last_entry(ext2_filsys fs, int inline_data_size,
+ unsigned int offset, int csum_size)
+{
+ if (inline_data_size)
+ return (offset < inline_data_size);
+ else
+ return (offset < fs->blocksize - csum_size);
+}
+
static int check_dir_block(ext2_filsys fs,
struct ext2_db_entry2 *db,
void *priv_data)
@@ -738,7 +747,7 @@ static int check_dir_block(ext2_filsys fs,
#ifdef ENABLE_HTREE
struct dx_dirblock_info *dx_db = 0;
#endif /* ENABLE_HTREE */
- struct ext2_dir_entry *dirent, *prev;
+ struct ext2_dir_entry *dirent, *prev, dot, dotdot;
ext2_dirhash_t hash;
unsigned int offset = 0;
int dir_modified = 0;
@@ -761,6 +770,8 @@ static int check_dir_block(ext2_filsys fs,
int dx_csum_size = 0, de_csum_size = 0;
int failed_csum = 0;
int is_leaf = 1;
+ int inline_data_size = 0;
+ int filetype = 0;

cd = (struct check_dir_struct *) priv_data;
buf = cd->buf;
@@ -778,6 +789,10 @@ static int check_dir_block(ext2_filsys fs,
de_csum_size = sizeof(struct ext2_dir_entry_tail);
}

+ if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
+ EXT2_FEATURE_INCOMPAT_FILETYPE))
+ filetype = EXT2_FT_DIR << 8;
+
/*
* Make sure the inode is still in use (could have been
* deleted in the duplicate/bad blocks pass.
@@ -792,7 +807,16 @@ static int check_dir_block(ext2_filsys fs,
cd->pctx.dirent = 0;
cd->pctx.num = 0;

- if (db->blk == 0) {
+ if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
+ errcode_t ec;
+
+ ec = ext2fs_inline_data_size(fs, ino, &inline_data_size);
+ if (ec && ec != EXT2_ET_NO_INLINE_DATA)
+ return DIRENT_ABORT;
+ }
+
+ if (db->blk == 0 && !inline_data_size) {
if (allocate_dir_block(ctx, db, buf, &cd->pctx))
return 0;
block_nr = db->blk;
@@ -813,7 +837,11 @@ static int check_dir_block(ext2_filsys fs,
#endif

ehandler_operation(_("reading directory block"));
- cd->pctx.errcode = ext2fs_read_dir_block4(fs, block_nr, buf, 0, ino);
+ if (inline_data_size)
+ cd->pctx.errcode = ext2fs_inline_data_get(fs, ino, 0, buf, 0);
+ else
+ cd->pctx.errcode = ext2fs_read_dir_block4(fs, block_nr,
+ buf, 0, ino);
ehandler_operation(0);
if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
cd->pctx.errcode = 0; /* We'll handle this ourselves */
@@ -884,7 +912,7 @@ out_htree:
#endif /* ENABLE_HTREE */

/* Verify checksum. */
- if (is_leaf && de_csum_size) {
+ if (is_leaf && de_csum_size && !inline_data_size) {
/* No space for csum? Rebuild dirs in pass 3A. */
if (!ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) {
de_csum_size = 0;
@@ -923,20 +951,44 @@ skip_checksum:
unsigned int name_len;

problem = 0;
- dirent = (struct ext2_dir_entry *) (buf + offset);
- (void) ext2fs_get_rec_len(fs, dirent, &rec_len);
- cd->pctx.dirent = dirent;
- cd->pctx.num = offset;
- if (((offset + rec_len) > fs->blocksize) ||
- (rec_len < 12) ||
- ((rec_len % 4) != 0) ||
- ((ext2fs_dirent_name_len(dirent) + 8) > rec_len)) {
- if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
- salvage_directory(fs, dirent, prev, &offset);
- dir_modified++;
- continue;
- } else
- goto abort_free_dict;
+ if (!inline_data_size || dot_state > 1) {
+ dirent = (struct ext2_dir_entry *) (buf + offset);
+ (void) ext2fs_get_rec_len(fs, dirent, &rec_len);
+ cd->pctx.dirent = dirent;
+ cd->pctx.num = offset;
+ if (((offset + rec_len) > fs->blocksize) ||
+ (rec_len < 12) ||
+ ((rec_len % 4) != 0) ||
+ ((ext2fs_dirent_name_len(dirent) + 8) > rec_len)) {
+ if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
+ salvage_directory(fs, dirent, prev, &offset);
+ dir_modified++;
+ continue;
+ } else
+ goto abort_free_dict;
+ }
+ } else {
+ if (dot_state == 0) {
+ memset(&dot, 0, sizeof(dot));
+ dirent = &dot;
+ dirent->inode = ino;
+ dirent->rec_len = EXT2_DIR_REC_LEN(1);
+ dirent->name_len = 1 | filetype;
+ dirent->name[0] = '.';
+ } else if (dot_state == 1) {
+ memset(&dotdot, 0, sizeof(dotdot));
+ dirent = &dotdot;
+ dirent->inode =
+ ((struct ext2_dir_entry *)buf)->inode;
+ dirent->rec_len = EXT2_DIR_REC_LEN(2);
+ dirent->name_len = 2 | filetype;
+ dirent->name[0] = '.';
+ dirent->name[1] = '.';
+ } else {
+ fatal_error(ctx, _("Can not continue."));
+ }
+ cd->pctx.dirent = dirent;
+ cd->pctx.num = offset;
}

if (dot_state == 0) {
@@ -1175,9 +1227,14 @@ skip_checksum:
prev = dirent;
if (dir_modified)
(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
- offset += rec_len;
+ if (!inline_data_size || dot_state > 1) {
+ offset += rec_len;
+ } else {
+ if (dot_state == 1)
+ offset = 4;
+ }
dot_state++;
- } while (offset < fs->blocksize - de_csum_size);
+ } while (is_last_entry(fs, inline_data_size, offset, de_csum_size));
#if 0
printf("\n");
#endif
@@ -1195,12 +1252,22 @@ skip_checksum:
}
#endif /* ENABLE_HTREE */

- if (offset != fs->blocksize - de_csum_size) {
- cd->pctx.num = rec_len - (fs->blocksize - de_csum_size) +
- offset;
- if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
- dirent->rec_len = cd->pctx.num;
- dir_modified++;
+ if (inline_data_size) {
+ if (offset != inline_data_size) {
+ cd->pctx.num = rec_len + offset - inline_data_size;
+ if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
+ dirent->rec_len = cd->pctx.num;
+ dir_modified++;
+ }
+ }
+ } else {
+ if (offset != fs->blocksize - de_csum_size) {
+ cd->pctx.num = rec_len - (fs->blocksize - de_csum_size) +
+ offset;
+ if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
+ dirent->rec_len = cd->pctx.num;
+ dir_modified++;
+ }
}
}
if (dir_modified) {
@@ -1214,8 +1281,13 @@ skip_checksum:
write_and_fix:
if (e2fsck_dir_will_be_rehashed(ctx, ino))
ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
- cd->pctx.errcode = ext2fs_write_dir_block4(fs, block_nr, buf,
- 0, ino);
+ if (inline_data_size) {
+ cd->pctx.errcode =
+ ext2fs_inline_data_set(fs, ino, 0, buf,
+ inline_data_size);
+ } else
+ cd->pctx.errcode = ext2fs_write_dir_block4(fs, block_nr,
+ buf, 0, ino);
if (e2fsck_dir_will_be_rehashed(ctx, ino))
ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (cd->pctx.errcode) {
--
1.7.9.7


2013-12-03 12:10:08

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 27/28] libext2fs: export inode cahce creation function

From: Zheng Liu <[email protected]>

Currently we have already exported inode cache flush and free functions
for users. This commit exports inode cache creation function. Later
we will use this function to initialize inode cache and do some unit
tests for inline data.

Signed-off-by: Zheng Liu <[email protected]>
---
lib/ext2fs/ext2fs.h | 1 +
lib/ext2fs/inode.c | 8 ++++----
2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index bf0ef26..a47bb40 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -1369,6 +1369,7 @@ extern errcode_t ext2fs_get_memalign(unsigned long size,
unsigned long align, void *ptr);

/* inode.c */
+extern errcode_t ext2fs_create_inode_cache(ext2_filsys fs, int cache_size);
extern void ext2fs_free_inode_cache(struct ext2_inode_cache *icache);
extern errcode_t ext2fs_flush_icache(ext2_filsys fs);
extern errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan,
diff --git a/lib/ext2fs/inode.c b/lib/ext2fs/inode.c
index 46c1c58..8cf76b0 100644
--- a/lib/ext2fs/inode.c
+++ b/lib/ext2fs/inode.c
@@ -91,7 +91,7 @@ void ext2fs_free_inode_cache(struct ext2_inode_cache *icache)
ext2fs_free_mem(&icache);
}

-static errcode_t create_icache(ext2_filsys fs)
+errcode_t ext2fs_create_inode_cache(ext2_filsys fs, int cache_size)
{
int i;
errcode_t retval;
@@ -109,7 +109,7 @@ static errcode_t create_icache(ext2_filsys fs)

fs->icache->buffer_blk = 0;
fs->icache->cache_last = -1;
- fs->icache->cache_size = 4;
+ fs->icache->cache_size = cache_size;
fs->icache->refcount = 1;
retval = ext2fs_get_array(fs->icache->cache_size,
sizeof(struct ext2_inode_cache_ent),
@@ -596,7 +596,7 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
return EXT2_ET_BAD_INODE_NUM;
/* Create inode cache if not present */
if (!fs->icache) {
- retval = create_icache(fs);
+ retval = ext2fs_create_inode_cache(fs, 4);
if (retval)
return retval;
}
@@ -732,7 +732,7 @@ errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
}
}
} else {
- retval = create_icache(fs);
+ retval = ext2fs_create_inode_cache(fs, 4);
if (retval)
goto errout;
}
--
1.7.9.7


2013-12-03 12:09:56

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 22/28] e2fsck: check inline_data in pass1

From: Zheng Liu <[email protected]>

Signed-off-by: Theodore Ts'o <[email protected]>
Signed-off-by: Zheng Liu <[email protected]>
---
e2fsck/pass1.c | 62 +++++++++++++++++++++++++++++++++++++++++++----
lib/ext2fs/dblist_dir.c | 16 ++++++++++--
2 files changed, 71 insertions(+), 7 deletions(-)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 56411f1..d5dca9d 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -177,7 +177,8 @@ int e2fsck_pass1_check_symlink(ext2_filsys fs, ext2_ino_t ino,
struct ext2fs_extent extent;

if ((inode->i_size_high || inode->i_size == 0) ||
- (inode->i_flags & EXT2_INDEX_FL))
+ (inode->i_flags & EXT2_INDEX_FL) ||
+ (inode->i_flags & EXT4_INLINE_DATA_FL))
return 0;

if (inode->i_flags & EXT4_EXTENTS_FL) {
@@ -407,6 +408,7 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx,
blk64_t blk;
unsigned int i, rec_len, not_device = 0;
int extent_fs;
+ int inlinedata_fs;

/*
* If the mode looks OK, we believe it. If the first block in
@@ -434,11 +436,23 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx,
* For extent mapped files, we don't do any sanity checking:
* just try to get the phys block of logical block 0 and run
* with it.
+ *
+ * For inline data files, we just try to get the size of inline
+ * data. If it's true, we will treat it as a directory.
*/

extent_fs = (ctx->fs->super->s_feature_incompat &
EXT3_FEATURE_INCOMPAT_EXTENTS);
- if (extent_fs && (inode->i_flags & EXT4_EXTENTS_FL)) {
+ inlinedata_fs = (ctx->fs->super->s_feature_incompat &
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA);
+ if (inlinedata_fs && (inode->i_flags & EXT4_INLINE_DATA_FL)) {
+ unsigned int size;
+
+ if (ext2fs_inline_data_size(ctx->fs, pctx->ino, &size))
+ return;
+ /* device files never have a "system.data" entry */
+ goto isdir;
+ } else if (extent_fs && (inode->i_flags & EXT4_EXTENTS_FL)) {
/* extent mapped */
if (ext2fs_bmap2(ctx->fs, pctx->ino, inode, 0, 0, 0, 0,
&blk))
@@ -501,6 +515,7 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx,
(rec_len % 4))
return;

+isdir:
if (fix_problem(ctx, PR_1_TREAT_AS_DIRECTORY, pctx)) {
inode->i_mode = (inode->i_mode & 07777) | LINUX_S_IFDIR;
e2fsck_write_inode_full(ctx, pctx->ino, inode,
@@ -1194,7 +1209,8 @@ void e2fsck_pass1(e2fsck_t ctx)
ctx->fs_sockets_count++;
} else
mark_inode_bad(ctx, ino);
- if (!(inode->i_flags & EXT4_EXTENTS_FL)) {
+ if (!(inode->i_flags & EXT4_EXTENTS_FL) &&
+ !(inode->i_flags & EXT4_INLINE_DATA_FL)) {
if (inode->i_block[EXT2_IND_BLOCK])
ctx->fs_ind_count++;
if (inode->i_block[EXT2_DIND_BLOCK])
@@ -1203,6 +1219,7 @@ void e2fsck_pass1(e2fsck_t ctx)
ctx->fs_tind_count++;
}
if (!(inode->i_flags & EXT4_EXTENTS_FL) &&
+ !(inode->i_flags & EXT4_INLINE_DATA_FL) &&
(inode->i_block[EXT2_IND_BLOCK] ||
inode->i_block[EXT2_DIND_BLOCK] ||
inode->i_block[EXT2_TIND_BLOCK] ||
@@ -2132,6 +2149,26 @@ static void check_blocks_extents(e2fsck_t ctx, struct problem_context *pctx,
}

/*
+ * In fact we don't need to check blocks for an inode with inline data
+ * because this inode doesn't have any blocks. In this function all
+ * we need to do is add this inode into dblist when it is a directory.
+ */
+static void check_blocks_inline_data(e2fsck_t ctx, struct problem_context *pctx,
+ struct process_block_struct *pb)
+{
+ if (!pb->is_dir)
+ return;
+
+ pctx->errcode = ext2fs_add_dir_block2(ctx->fs->dblist, pb->ino, 0, 0);
+ if (pctx->errcode) {
+ pctx->blk = 0;
+ pctx->num = 0;
+ fix_problem(ctx, PR_1_ADD_DBLOCK, pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ }
+}
+
+/*
* This subroutine is called on each inode to account for all of the
* blocks used by that inode.
*/
@@ -2145,6 +2182,7 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
int bad_size = 0;
int dirty_inode = 0;
int extent_fs;
+ int inlinedata_fs;
__u64 size;

pb.ino = ino;
@@ -2168,6 +2206,8 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,

extent_fs = (ctx->fs->super->s_feature_incompat &
EXT3_FEATURE_INCOMPAT_EXTENTS);
+ inlinedata_fs = (ctx->fs->super->s_feature_incompat &
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA);

if (inode->i_flags & EXT2_COMPRBLK_FL) {
if (fs->super->s_feature_incompat &
@@ -2201,6 +2241,10 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
*/
pb.last_init_lblock = pb.last_block;
}
+ } else {
+ /* check inline data */
+ if (inlinedata_fs && (inode->i_flags & EXT4_INLINE_DATA_FL))
+ check_blocks_inline_data(ctx, pctx, &pb);
}
end_problem_latch(ctx, PR_LATCH_BLOCK);
end_problem_latch(ctx, PR_LATCH_TOOBIG);
@@ -2233,7 +2277,8 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
}
}

- if (!pb.num_blocks && pb.is_dir) {
+ if (!pb.num_blocks && pb.is_dir &&
+ !(inode->i_flags & EXT4_INLINE_DATA_FL)) {
if (fix_problem(ctx, PR_1_ZERO_LENGTH_DIR, pctx)) {
e2fsck_clear_inode(ctx, ino, inode, 0, "check_blocks");
ctx->fs_directory_count--;
@@ -2259,7 +2304,14 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
#endif
if (pb.is_dir) {
int nblock = inode->i_size >> EXT2_BLOCK_SIZE_BITS(fs->super);
- if (inode->i_size & (fs->blocksize - 1))
+ if (inode->i_flags & EXT4_INLINE_DATA_FL) {
+ size_t size;
+
+ if (ext2fs_inline_data_size(ctx->fs, pctx->ino, &size))
+ bad_size = 5;
+ if (size != inode->i_size)
+ bad_size = 5;
+ } else if (inode->i_size & (fs->blocksize - 1))
bad_size = 5;
else if (nblock > (pb.last_block + 1))
bad_size = 1;
diff --git a/lib/ext2fs/dblist_dir.c b/lib/ext2fs/dblist_dir.c
index d4d5111..1e36584 100644
--- a/lib/ext2fs/dblist_dir.c
+++ b/lib/ext2fs/dblist_dir.c
@@ -65,6 +65,7 @@ errcode_t ext2fs_dblist_dir_iterate(ext2_dblist dblist,
static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry2 *db_info,
void *priv_data)
{
+ struct ext2_inode inode;
struct dir_context *ctx;
int ret;

@@ -72,8 +73,19 @@ static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry2 *db_info,
ctx->dir = db_info->ino;
ctx->errcode = 0;

- ret = ext2fs_process_dir_block(fs, &db_info->blk,
- db_info->blockcnt, 0, 0, priv_data);
+ ctx->errcode = ext2fs_read_inode(fs, ctx->dir, &inode);
+ if (ctx->errcode)
+ return DBLIST_ABORT;
+ if (inode.i_flags & EXT4_INLINE_DATA_FL) {
+ ctx->flags = DIRENT_FLAG_INCLUDE_INLINE_DATA;
+ ext2fs_inline_data_dir_iterate(fs, ctx->dir,
+ ext2fs_process_dir_block,
+ ctx);
+ } else {
+ ret = ext2fs_process_dir_block(fs, &db_info->blk,
+ db_info->blockcnt, 0, 0,
+ priv_data);
+ }
if ((ret & BLOCK_ABORT) && !ctx->errcode)
return DBLIST_ABORT;
return 0;
--
1.7.9.7


2013-12-03 12:10:06

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 26/28] mke2fs: enable inline_data feature on ext4dev filesystem

From: Zheng Liu <[email protected]>

Signed-off-by: Theodore Ts'o <[email protected]>
Signed-off-by: Zheng Liu <[email protected]>
---
misc/mke2fs.conf.in | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/misc/mke2fs.conf.in b/misc/mke2fs.conf.in
index 178733f..4c5dba7 100644
--- a/misc/mke2fs.conf.in
+++ b/misc/mke2fs.conf.in
@@ -16,7 +16,7 @@
inode_size = 256
}
ext4dev = {
- features = has_journal,extent,huge_file,flex_bg,metadata_csum,64bit,dir_nlink,extra_isize
+ features = has_journal,extent,huge_file,flex_bg,metadata_csum,inline_data,64bit,dir_nlink,extra_isize
inode_size = 256
options = test_fs=1
}
--
1.7.9.7


2013-12-03 12:10:11

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 28/28] libext2fs: add a unit test for inline data

From: Zheng Liu <[email protected]>

In this unit test, we will test the interface of inline data and make
sure it is fine.

Signed-off-by: Theodore Ts'o <[email protected]>
Signed-off-by: Zheng Liu <[email protected]>
---
lib/ext2fs/Makefile.in | 8 +-
lib/ext2fs/inline_data.c | 260 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 267 insertions(+), 1 deletion(-)

diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
index 4d011c9..3419778 100644
--- a/lib/ext2fs/Makefile.in
+++ b/lib/ext2fs/Makefile.in
@@ -391,6 +391,11 @@ tst_inline: $(srcdir)/inline.c $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
$(Q) $(CC) -o tst_inline $(srcdir)/inline.c $(ALL_CFLAGS) -DDEBUG \
$(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR)

+tst_inline_data: inline_data.c $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_inline_data $(srcdir)/inline_data.c $(ALL_CFLAGS) \
+ -DDEBUG $(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR)
+
tst_csum: csum.c $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR) $(STATIC_LIBE2P) \
$(top_srcdir)/lib/e2p/e2p.h
$(E) " LD $@"
@@ -408,7 +413,7 @@ mkjournal: mkjournal.c $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR)

check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount \
tst_super_size tst_types tst_inode_size tst_csum tst_crc32c tst_bitmaps \
- tst_inline
+ tst_inline tst_inline_data
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_bitops
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_badblocks
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_iscan
@@ -418,6 +423,7 @@ check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount \
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_inode_size
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_csum
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_inline
+ LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_inline_data
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_crc32c
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) \
./tst_bitmaps -f $(srcdir)/tst_bitmaps_cmds > tst_bitmaps_out
diff --git a/lib/ext2fs/inline_data.c b/lib/ext2fs/inline_data.c
index 4a35209..199cdde 100644
--- a/lib/ext2fs/inline_data.c
+++ b/lib/ext2fs/inline_data.c
@@ -467,3 +467,263 @@ errcode_t ext2fs_inline_data_set(ext2_filsys fs, ext2_ino_t ino,
data.ea_data = buf + EXT4_MIN_INLINE_DATA_SIZE;
return ext2fs_inline_data_ea_set(&data);
}
+
+#ifdef DEBUG
+#include "e2p/e2p.h"
+
+/*
+ * The length of buffer is set to 64 because in inode's i_block member it only
+ * can save 60 bytes. Thus this value can let the data being saved in extra
+ * space.
+ */
+#define BUFF_SIZE (64)
+
+static errcode_t file_test(ext2_filsys fs)
+{
+ struct ext2_inode inode;
+ ext2_ino_t newfile;
+ errcode_t retval;
+ size_t size;
+ char *buf = 0, *cmpbuf = 0;
+ int i;
+
+ /* create a new file */
+ retval = ext2fs_new_inode(fs, 2, 010755, 0, &newfile);
+ if (retval) {
+ com_err("file_test", retval, "while allocaing a new inode");
+ return 1;
+ }
+
+ memset(&inode, 0, sizeof(inode));
+ inode.i_flags |= EXT4_INLINE_DATA_FL;
+ inode.i_size = EXT4_MIN_INLINE_DATA_SIZE;
+ inode.i_mode = LINUX_S_IFREG;
+ retval = ext2fs_write_new_inode(fs, newfile, &inode);
+ if (retval) {
+ com_err("file_test", retval, "while writting a new inode");
+ return 1;
+ }
+
+ retval = ext2fs_inline_data_init(fs, newfile);
+ if (retval) {
+ com_err("file_test", retval, "while init 'system.data'");
+ return 1;
+ }
+
+ retval = ext2fs_inline_data_size(fs, newfile, &size);
+ if (retval) {
+ com_err("file_test", retval, "while getting size");
+ return 1;
+ }
+
+ if (size != EXT4_MIN_INLINE_DATA_SIZE) {
+ fprintf(stderr,
+ "tst_inline_data: size of inline data is wrong\n");
+ return 1;
+ }
+
+ ext2fs_get_mem(BUFF_SIZE, &buf);
+ memset(buf, 'a', BUFF_SIZE);
+ retval = ext2fs_inline_data_set(fs, newfile, 0, buf, BUFF_SIZE);
+ if (retval) {
+ com_err("file_test", retval,
+ "while setting inline data %s", buf);
+ goto err;
+ }
+
+ ext2fs_get_mem(BUFF_SIZE, &cmpbuf);
+ retval = ext2fs_inline_data_get(fs, newfile, 0, cmpbuf, &size);
+ if (retval) {
+ com_err("file_test", retval, "while getting inline data");
+ goto err;
+ }
+
+ if (size != BUFF_SIZE) {
+ fprintf(stderr,
+ "tst_inline_data: size %lu != buflen %lu\n",
+ size, BUFF_SIZE);
+ retval = 1;
+ goto err;
+ }
+
+ if (memcmp(buf, cmpbuf, BUFF_SIZE)) {
+ fprintf(stderr, "tst_inline_data: buf != cmpbuf\n");
+ retval = 1;
+ goto err;
+ }
+
+ retval = ext2fs_punch(fs, newfile, 0, 0, 0, ~0ULL);
+ if (retval) {
+ com_err("file_test", retval, "while truncating inode");
+ goto err;
+ }
+
+ /* reload inode and check isize */
+ ext2fs_read_inode(fs, newfile, &inode);
+ if (inode.i_size != 0) {
+ fprintf(stderr, "tst_inline_data: i_size should be 0\n");
+ retval = 1;
+ }
+
+err:
+ if (cmpbuf)
+ ext2fs_free_mem(&cmpbuf);
+ if (buf)
+ ext2fs_free_mem(&buf);
+ return retval;
+}
+
+static errcode_t dir_test(ext2_filsys fs)
+{
+ const char *dot_name = ".";
+ const char *stub_name = "stub";
+ const char *parent_name = "test";
+ ext2_ino_t parent, dir, tmp;
+ errcode_t retval;
+ char dirname[PATH_MAX];
+ int i;
+
+ retval = ext2fs_mkdir(fs, 11, 11, stub_name);
+ if (retval) {
+ com_err("dir_test", retval, "while creating %s dir", stub_name);
+ return retval;
+ }
+
+ retval = ext2fs_mkdir(fs, 11, 0, parent_name);
+ if (retval) {
+ com_err("dir_test", retval,
+ "while creating %s dir", parent_name);
+ return retval;
+ }
+
+ retval = ext2fs_lookup(fs, 11, parent_name, strlen(parent_name),
+ 0, &parent);
+ if (retval) {
+ com_err("dir_test", retval,
+ "while looking up %s dir", parent_name);
+ return retval;
+ }
+
+ retval = ext2fs_lookup(fs, parent, dot_name, strlen(dot_name),
+ 0, &tmp);
+ if (retval) {
+ com_err("dir_test", retval,
+ "while looking up %s dir", parent_name);
+ return retval;
+ }
+
+ if (parent != tmp) {
+ fprintf(stderr, "tst_inline_data: parent (%lu) != tmp (%lu)\n",
+ parent, tmp);
+ return 1;
+ }
+
+ for (i = 0, dir = 13; i < 4; i++, dir++) {
+ tmp = 0;
+ snprintf(dirname, PATH_MAX, "%d", i);
+ retval = ext2fs_mkdir(fs, parent, 0, dirname);
+ if (retval) {
+ com_err("dir_test", retval,
+ "while creating %s dir", dirname);
+ return retval;
+ }
+
+ retval = ext2fs_lookup(fs, parent, dirname, strlen(dirname),
+ 0, &tmp);
+ if (retval) {
+ com_err("dir_test", retval,
+ "while looking up %s dir", parent_name);
+ return retval;
+ }
+
+ if (dir != tmp) {
+ fprintf(stderr, "tst_inline_data: dir (%lu) != tmp (%lu)\n",
+ dir, tmp);
+ return 1;
+ }
+ }
+
+ snprintf(dirname, PATH_MAX, "%d", i);
+ retval = ext2fs_mkdir(fs, parent, 0, dirname);
+ if (retval && retval != EXT2_ET_DIR_NO_SPACE) {
+ com_err("dir_test", retval, "while creating %s dir", dirname);
+ return retval;
+ }
+
+ retval = ext2fs_expand_dir(fs, parent);
+ if (retval) {
+ com_err("dir_test", retval, "while expanding %s dir", parent_name);
+ return retval;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ ext2_filsys fs;
+ struct ext2_super_block param;
+ errcode_t retval;
+ int i;
+
+ /* setup */
+ initialize_ext2_error_table();
+
+ memset(&param, 0, sizeof(param));
+ ext2fs_blocks_count_set(&param, 32768);
+ param.s_inodes_count = 100;
+
+ param.s_feature_incompat |= EXT4_FEATURE_INCOMPAT_INLINE_DATA;
+ param.s_rev_level = EXT2_DYNAMIC_REV;
+ param.s_inode_size = 256;
+
+ retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, &param,
+ test_io_manager, &fs);
+ if (retval) {
+ com_err("setup", retval,
+ "while initializing filesystem");
+ exit(1);
+ }
+
+ retval = ext2fs_allocate_tables(fs);
+ if (retval) {
+ com_err("setup", retval,
+ "while allocating tables for test filesysmte");
+ exit(1);
+ }
+
+ /* initialize inode cache */
+ if (!fs->icache) {
+ struct ext2_inode inode;
+ ext2_ino_t first_ino = EXT2_FIRST_INO(fs->super);
+ int i;
+
+ /* we just want to init inode cache. So ignore error */
+ ext2fs_create_inode_cache(fs, 16);
+ if (!fs->icache) {
+ fprintf(stderr,
+ "tst_inline_data: init inode cache failed\n");
+ exit(1);
+ }
+
+ /* setup inode cache */
+ for (i = 0; i < fs->icache->cache_size; i++)
+ fs->icache->cache[i].ino = first_ino++;
+ }
+
+ /* test */
+ if (file_test(fs)) {
+ fprintf(stderr, "tst_inline_data(FILE): FAILED\n");
+ return 1;
+ }
+ printf("tst_inline_data(FILE): OK\n");
+
+ if (dir_test(fs)) {
+ fprintf(stderr, "tst_inline_data(DIR): FAILED\n");
+ return 1;
+ }
+ printf("tst_inline_data(DIR): OK\n");
+
+ return 0;
+}
+#endif
--
1.7.9.7


2013-12-03 12:10:03

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 25/28] tests: change result in f_bad_disconnected_inode

From: Zheng Liu <[email protected]>

In this test, inode flag is some random data, and after we apply inline
data patch set we should need to handle it.

Signed-off-by: Theodore Ts'o <[email protected]>
Signed-off-by: Zheng Liu <[email protected]>
---
tests/f_bad_disconnected_inode/expect.1 | 9 +++++++++
1 file changed, 9 insertions(+)

diff --git a/tests/f_bad_disconnected_inode/expect.1 b/tests/f_bad_disconnected_inode/expect.1
index 11862f6..9f9b447 100644
--- a/tests/f_bad_disconnected_inode/expect.1
+++ b/tests/f_bad_disconnected_inode/expect.1
@@ -2,9 +2,18 @@ Pass 1: Checking inodes, blocks, and sizes
Inode 1 has EXTENTS_FL flag set on filesystem without extents support.
Clear? yes

+Inode 14 has INLINE_DATA_FL flag on filesystem without inline data support.
+Clear? yes
+
+Inode 15 has INLINE_DATA_FL flag on filesystem without inline data support.
+Clear? yes
+
Inode 15 has EXTENTS_FL flag set on filesystem without extents support.
Clear? yes

+Inode 16 has INLINE_DATA_FL flag on filesystem without inline data support.
+Clear? yes
+
Inode 16 has EXTENTS_FL flag set on filesystem without extents support.
Clear? yes

--
1.7.9.7


2013-12-03 12:10:01

by Zheng Liu

[permalink] [raw]
Subject: [PATCH v2 24/28] e2fsck: check inline_data in pass3

From: Zheng Liu <[email protected]>

In e2fsck_expand_directory() we don't handle a dir with inline data
because when this function is called the directory inode shouldn't
contains inline data.

Signed-off-by: Theodore Ts'o <[email protected]>
Signed-off-by: Zheng Liu <[email protected]>
---
e2fsck/pass3.c | 12 ++++++++++++
e2fsck/problem.c | 5 +++++
e2fsck/problem.h | 3 +++
e2fsck/rehash.c | 2 ++
4 files changed, 22 insertions(+)

diff --git a/e2fsck/pass3.c b/e2fsck/pass3.c
index 2dd414b..2cab41d 100644
--- a/e2fsck/pass3.c
+++ b/e2fsck/pass3.c
@@ -375,6 +375,17 @@ ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix)
if (retval && !fix)
return 0;
if (!retval) {
+ /* Lost+found shouldn't have inline data */
+ retval = ext2fs_read_inode(fs, ino, &inode);
+ if (fix && retval)
+ return 0;
+
+ if (fix && (inode.i_flags & EXT4_INLINE_DATA_FL)) {
+ if (!fix_problem(ctx, PR_3_LPF_INLINE_DATA, &pctx))
+ return 0;
+ goto unlink;
+ }
+
if (ext2fs_check_directory(fs, ino) == 0) {
ctx->lost_and_found = ino;
return ino;
@@ -387,6 +398,7 @@ ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix)
if (!fix_problem(ctx, PR_3_LPF_NOTDIR, &pctx))
return 0;

+unlink:
/* OK, unlink the old /lost+found file. */
pctx.errcode = ext2fs_unlink(fs, EXT2_ROOT_INO, name, ino, 0);
if (pctx.errcode) {
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index a4f7749..471cec7 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1597,6 +1597,11 @@ static struct e2fsck_problem problem_table[] = {
N_("/@l is not a @d (ino=%i)\n"),
PROMPT_UNLINK, 0 },

+ /* Lost+found has inline data */
+ { PR_3_LPF_INLINE_DATA,
+ N_("/@l has inline data\n"),
+ PROMPT_CLEAR, 0 },
+
/* Pass 3A Directory Optimization */

/* Pass 3A: Optimizing directories */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 9d41cea..8219b84 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -955,6 +955,9 @@ struct problem_context {
/* Lost+found is not a directory */
#define PR_3_LPF_NOTDIR 0x030017

+/* Lost+found has inline data */
+#define PR_3_LPF_INLINE_DATA 0x030018
+
/*
* Pass 3a --- rehashing diretories
*/
diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c
index 6ef3568..b254e81 100644
--- a/e2fsck/rehash.c
+++ b/e2fsck/rehash.c
@@ -814,6 +814,8 @@ retry_nohash:
/* Read in the entire directory into memory */
retval = ext2fs_block_iterate3(fs, ino, 0, 0,
fill_dir_block, &fd);
+ if (retval == EXT2_ET_INLINE_DATA_CANT_ITERATE)
+ goto errout;
if (fd.err) {
retval = fd.err;
goto errout;
--
1.7.9.7


2013-12-03 19:55:07

by Darrick J. Wong

[permalink] [raw]
Subject: Re: [PATCH v2 01/28] libext2fs: support modifying arbitrary extended attributes

On Tue, Dec 03, 2013 at 08:11:28PM +0800, Zheng Liu wrote:
> From: "Darrick J. Wong" <[email protected]>
>
> Add functions to allow clients to get, set, and remove extended
> attributes from any file. It also supports modifying EAs living in
> i_file_acl.
>
> v2: Put the header declarations in the correct part of ext2fs.h,
> provide a function to release an EA block from an inode, and check
> i_extra_isize to make sure we actually have space for in-inode EAs.
>
> [Modified by Zheng]
> Ext_attr feature check in ext2fs_xattrs_read/write() is removed because
> inline_data feature can be enabled without ext_attr.

Ok, so inline_data implies ext_attr. I think we should change the test to
check for either feature flag, because with no test, we could accidentally
read/write EAs on a FS with ^ext_attr,^inline_data. Or maybe provide a helper
predicate to hide those details.

Also, I'd ask you to send broken out changes to my patches... but I haven't
posted my monster patchset recently. :)

(I think I might work up the courage to patchbomb the mailing list tonight.)

--D
>
> Signed-off-by: Darrick J. Wong <[email protected]>
> Signed-off-by: Zheng Liu <[email protected]>
> ---
> lib/ext2fs/ext2_err.et.in | 18 ++
> lib/ext2fs/ext2fs.h | 28 ++
> lib/ext2fs/ext_attr.c | 754 +++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 800 insertions(+)
>
> diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
> index 9cc1bd1..b819a90 100644
> --- a/lib/ext2fs/ext2_err.et.in
> +++ b/lib/ext2fs/ext2_err.et.in
> @@ -482,4 +482,22 @@ ec EXT2_ET_BLOCK_BITMAP_CSUM_INVALID,
> ec EXT2_ET_INLINE_DATA_CANT_ITERATE,
> "Cannot block iterate on an inode containing inline data"
>
> +ec EXT2_ET_EA_BAD_NAME_LEN,
> + "Extended attribute has an invalid name length"
> +
> +ec EXT2_ET_EA_BAD_VALUE_SIZE,
> + "Extended attribute has an invalid value length"
> +
> +ec EXT2_ET_BAD_EA_HASH,
> + "Extended attribute has an incorrect hash"
> +
> +ec EXT2_ET_BAD_EA_HEADER,
> + "Extended attribute block has a bad header"
> +
> +ec EXT2_ET_EA_KEY_NOT_FOUND,
> + "Extended attribute key not found"
> +
> +ec EXT2_ET_EA_NO_SPACE,
> + "Insufficient space to store extended attribute data"
> +
> end
> diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
> index 67876ad..30bd4cf 100644
> --- a/lib/ext2fs/ext2fs.h
> +++ b/lib/ext2fs/ext2fs.h
> @@ -637,6 +637,13 @@ typedef struct stat ext2fs_struct_stat;
> #define EXT2_FLAG_FLUSH_NO_SYNC 1
>
> /*
> + * Modify and iterate extended attributes
> + */
> +struct ext2_xattr_handle;
> +#define XATTR_ABORT 1
> +#define XATTR_CHANGED 2
> +
> +/*
> * function prototypes
> */
> static inline int ext2fs_has_group_desc_csum(ext2_filsys fs)
> @@ -1142,6 +1149,27 @@ extern errcode_t ext2fs_adjust_ea_refcount3(ext2_filsys fs, blk64_t blk,
> char *block_buf,
> int adjust, __u32 *newcount,
> ext2_ino_t inum);
> +errcode_t ext2fs_xattrs_expand(struct ext2_xattr_handle *h,
> + unsigned int expandby);
> +errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle);
> +errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle);
> +errcode_t ext2fs_xattrs_iterate(struct ext2_xattr_handle *h,
> + int (*func)(char *name, char *value,
> + void *data),
> + void *data);
> +errcode_t ext2fs_xattr_get(struct ext2_xattr_handle *h, const char *key,
> + void **value, unsigned int *value_len);
> +errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *handle,
> + const char *key,
> + const void *value,
> + unsigned int value_len);
> +errcode_t ext2fs_xattr_remove(struct ext2_xattr_handle *handle,
> + const char *key);
> +errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
> + struct ext2_xattr_handle **handle);
> +errcode_t ext2fs_xattrs_close(struct ext2_xattr_handle **handle);
> +errcode_t ext2fs_free_ext_attr(ext2_filsys fs, ext2_ino_t ino,
> + struct ext2_inode_large *inode);
>
> /* extent.c */
> extern errcode_t ext2fs_extent_header_verify(void *ptr, int size);
> diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
> index 9649a14..4d40149 100644
> --- a/lib/ext2fs/ext_attr.c
> +++ b/lib/ext2fs/ext_attr.c
> @@ -186,3 +186,757 @@ errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
> return ext2fs_adjust_ea_refcount2(fs, blk, block_buf, adjust,
> newcount);
> }
> +
> +/* Manipulate the contents of extended attribute regions */
> +struct ext2_xattr {
> + char *name;
> + void *value;
> + unsigned int value_len;
> +};
> +
> +struct ext2_xattr_handle {
> + ext2_filsys fs;
> + struct ext2_xattr *attrs;
> + unsigned int length;
> + ext2_ino_t ino;
> + int dirty;
> +};
> +
> +errcode_t ext2fs_xattrs_expand(struct ext2_xattr_handle *h,
> + unsigned int expandby)
> +{
> + struct ext2_xattr *new_attrs;
> + errcode_t err;
> +
> + err = ext2fs_get_arrayzero(h->length + expandby,
> + sizeof(struct ext2_xattr), &new_attrs);
> + if (err)
> + return err;
> +
> + memcpy(new_attrs, h->attrs, h->length * sizeof(struct ext2_xattr));
> + ext2fs_free_mem(&h->attrs);
> + h->length += expandby;
> + h->attrs = new_attrs;
> +
> + return 0;
> +}
> +
> +struct ea_name_index {
> + int index;
> + const char *name;
> +};
> +
> +static struct ea_name_index ea_names[] = {
> + {1, "user."},
> + {2, "system.posix_acl_access"},
> + {3, "system.posix_acl_default"},
> + {4, "trusted."},
> + {6, "security."},
> + {7, "system."},
> + {0, NULL},
> +};
> +
> +static const char *find_ea_prefix(int index)
> +{
> + struct ea_name_index *e;
> +
> + for (e = ea_names; e->name; e++)
> + if (e->index == index)
> + return e->name;
> +
> + return NULL;
> +}
> +
> +static int find_ea_index(const char *fullname, char **name, int *index)
> +{
> + struct ea_name_index *e;
> +
> + for (e = ea_names; e->name; e++) {
> + if (memcmp(fullname, e->name, strlen(e->name)) == 0) {
> + *name = (char *)fullname + strlen(e->name);
> + *index = e->index;
> + return 1;
> + }
> + }
> + return 0;
> +}
> +
> +errcode_t ext2fs_free_ext_attr(ext2_filsys fs, ext2_ino_t ino,
> + struct ext2_inode_large *inode)
> +{
> + struct ext2_ext_attr_header *header;
> + void *block_buf = NULL;
> + dgrp_t grp;
> + blk64_t blk, goal;
> + errcode_t err;
> + struct ext2_inode_large i;
> +
> + /* Read inode? */
> + if (inode == NULL) {
> + err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&i,
> + sizeof(struct ext2_inode_large));
> + if (err)
> + return err;
> + inode = &i;
> + }
> +
> + /* Do we already have an EA block? */
> + blk = ext2fs_file_acl_block(fs, (struct ext2_inode *)inode);
> + if (blk == 0)
> + return 0;
> +
> + /* Find block, zero it, write back */
> + if ((blk < fs->super->s_first_data_block) ||
> + (blk >= ext2fs_blocks_count(fs->super))) {
> + err = EXT2_ET_BAD_EA_BLOCK_NUM;
> + goto out;
> + }
> +
> + err = ext2fs_get_mem(fs->blocksize, &block_buf);
> + if (err)
> + goto out;
> +
> + err = ext2fs_read_ext_attr3(fs, blk, block_buf, ino);
> + if (err)
> + goto out2;
> +
> + header = (struct ext2_ext_attr_header *) block_buf;
> + if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
> + err = EXT2_ET_BAD_EA_HEADER;
> + goto out2;
> + }
> +
> + header->h_refcount--;
> + err = ext2fs_write_ext_attr3(fs, blk, block_buf, ino);
> + if (err)
> + goto out2;
> +
> + /* Erase link to block */
> + ext2fs_file_acl_block_set(fs, (struct ext2_inode *)inode, 0);
> + if (header->h_refcount == 0)
> + ext2fs_block_alloc_stats2(fs, blk, -1);
> +
> + /* Write inode? */
> + if (inode == &i) {
> + err = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&i,
> + sizeof(struct ext2_inode_large));
> + if (err)
> + goto out2;
> + }
> +
> +out2:
> + ext2fs_free_mem(&block_buf);
> +out:
> + return err;
> +}
> +
> +static errcode_t prep_ea_block_for_write(ext2_filsys fs, ext2_ino_t ino,
> + struct ext2_inode_large *inode)
> +{
> + struct ext2_ext_attr_header *header;
> + void *block_buf = NULL;
> + dgrp_t grp;
> + blk64_t blk, goal;
> + errcode_t err;
> +
> + /* Do we already have an EA block? */
> + blk = ext2fs_file_acl_block(fs, (struct ext2_inode *)inode);
> + if (blk != 0) {
> + if ((blk < fs->super->s_first_data_block) ||
> + (blk >= ext2fs_blocks_count(fs->super))) {
> + err = EXT2_ET_BAD_EA_BLOCK_NUM;
> + goto out;
> + }
> +
> + err = ext2fs_get_mem(fs->blocksize, &block_buf);
> + if (err)
> + goto out;
> +
> + err = ext2fs_read_ext_attr3(fs, blk, block_buf, ino);
> + if (err)
> + goto out2;
> +
> + header = (struct ext2_ext_attr_header *) block_buf;
> + if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
> + err = EXT2_ET_BAD_EA_HEADER;
> + goto out2;
> + }
> +
> + /* Single-user block. We're done here. */
> + if (header->h_refcount == 1)
> + return 0;
> +
> + /* We need to CoW the block. */
> + header->h_refcount--;
> + err = ext2fs_write_ext_attr3(fs, blk, block_buf, ino);
> + if (err)
> + goto out2;
> + } else {
> + /* No block, we must increment i_blocks */
> + err = ext2fs_iblk_add_blocks(fs, (struct ext2_inode *)inode,
> + 1);
> + if (err)
> + goto out;
> + }
> +
> + /* Allocate a block */
> + grp = ext2fs_group_of_ino(fs, ino);
> + goal = ext2fs_inode_table_loc(fs, grp);
> + err = ext2fs_alloc_block2(fs, goal, NULL, &blk);
> + if (err)
> + return err;
> + ext2fs_file_acl_block_set(fs, (struct ext2_inode *)inode, blk);
> +out2:
> + ext2fs_free_mem(&block_buf);
> +out:
> + return err;
> +}
> +
> +
> +static errcode_t write_xattrs_to_buffer(struct ext2_xattr_handle *handle,
> + struct ext2_xattr **pos,
> + void *entries_start,
> + unsigned int storage_size,
> + unsigned int value_offset_correction)
> +{
> + struct ext2_xattr *x = *pos;
> + struct ext2_ext_attr_entry *e = entries_start;
> + void *end = entries_start + storage_size;
> + char *shortname;
> + unsigned int entry_size, value_size;
> + int idx, ret;
> +
> + /* For all remaining x... */
> + for (; x < handle->attrs + handle->length; x++) {
> + if (!x->name)
> + continue;
> +
> + /* Calculate index and shortname position */
> + shortname = x->name;
> + ret = find_ea_index(x->name, &shortname, &idx);
> +
> + /* Calculate entry and value size */
> + entry_size = (sizeof(*e) + strlen(shortname) +
> + EXT2_EXT_ATTR_PAD - 1) &
> + ~(EXT2_EXT_ATTR_PAD - 1);
> + value_size = ((x->value_len + EXT2_EXT_ATTR_PAD - 1) /
> + EXT2_EXT_ATTR_PAD) * EXT2_EXT_ATTR_PAD;
> +
> + /*
> + * Would entry collide with value?
> + * Note that we must leave sufficient room for a (u32)0 to
> + * mark the end of the entries.
> + */
> + if ((void *)e + entry_size + sizeof(__u32) > end - value_size)
> + break;
> +
> + /* Fill out e appropriately */
> + e->e_name_len = strlen(shortname);
> + e->e_name_index = (ret ? idx : 0);
> + e->e_value_offs = end - value_size - (void *)entries_start +
> + value_offset_correction;
> + e->e_value_block = 0;
> + e->e_value_size = x->value_len;
> +
> + /* Store name and value */
> + end -= value_size;
> + memcpy((void *)e + sizeof(*e), shortname, e->e_name_len);
> + memcpy(end, x->value, e->e_value_size);
> +
> + e->e_hash = ext2fs_ext_attr_hash_entry(e, end);
> +
> + e = EXT2_EXT_ATTR_NEXT(e);
> + *(__u32 *)e = 0;
> + }
> + *pos = x;
> +
> + return 0;
> +}
> +
> +errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle)
> +{
> + struct ext2_xattr *x;
> + struct ext2_inode_large *inode;
> + void *start, *block_buf = NULL;
> + struct ext2_ext_attr_header *header;
> + __u32 ea_inode_magic;
> + blk64_t blk;
> + unsigned int storage_size;
> + unsigned int i, written;
> + errcode_t err;
> +
> + i = EXT2_INODE_SIZE(handle->fs->super);
> + if (i < sizeof(*inode))
> + i = sizeof(*inode);
> + err = ext2fs_get_memzero(i, &inode);
> + if (err)
> + return err;
> +
> + err = ext2fs_read_inode_full(handle->fs, handle->ino,
> + (struct ext2_inode *)inode,
> + EXT2_INODE_SIZE(handle->fs->super));
> + if (err)
> + goto out;
> +
> + x = handle->attrs;
> + /* Does the inode have size for EA? */
> + if (EXT2_INODE_SIZE(handle->fs->super) <= EXT2_GOOD_OLD_INODE_SIZE +
> + inode->i_extra_isize +
> + sizeof(__u32))
> + goto write_ea_block;
> +
> + /* Write the inode EA */
> + ea_inode_magic = EXT2_EXT_ATTR_MAGIC;
> + memcpy(((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
> + inode->i_extra_isize, &ea_inode_magic, sizeof(__u32));
> + storage_size = EXT2_INODE_SIZE(handle->fs->super) -
> + EXT2_GOOD_OLD_INODE_SIZE - inode->i_extra_isize -
> + sizeof(__u32);
> + start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
> + inode->i_extra_isize + sizeof(__u32);
> +
> + err = write_xattrs_to_buffer(handle, &x, start, storage_size, 0);
> + if (err)
> + goto out;
> +
> + /* Are we done? */
> + if (x == handle->attrs + handle->length)
> + goto skip_ea_block;
> +
> +write_ea_block:
> + /* Write the EA block */
> + err = ext2fs_get_mem(handle->fs->blocksize, &block_buf);
> + if (err)
> + goto out;
> +
> + storage_size = handle->fs->blocksize -
> + sizeof(struct ext2_ext_attr_header);
> + start = block_buf + sizeof(struct ext2_ext_attr_header);
> +
> + err = write_xattrs_to_buffer(handle, &x, start, storage_size,
> + (void *)start - block_buf);
> + if (err)
> + goto out2;
> +
> + if (x < handle->attrs + handle->length) {
> + err = EXT2_ET_EA_NO_SPACE;
> + goto out2;
> + }
> +
> + if (block_buf) {
> + /* Write a header on the EA block */
> + header = block_buf;
> + header->h_magic = EXT2_EXT_ATTR_MAGIC;
> + header->h_refcount = 1;
> + header->h_blocks = 1;
> +
> + /* Get a new block for writing */
> + err = prep_ea_block_for_write(handle->fs, handle->ino, inode);
> + if (err)
> + goto out2;
> +
> + /* Finally, write the new EA block */
> + blk = ext2fs_file_acl_block(handle->fs,
> + (struct ext2_inode *)inode);
> + err = ext2fs_write_ext_attr3(handle->fs, blk, block_buf,
> + handle->ino);
> + if (err)
> + goto out2;
> + }
> +
> +skip_ea_block:
> + blk = ext2fs_file_acl_block(handle->fs, (struct ext2_inode *)inode);
> + if (!block_buf && blk) {
> + /* xattrs shrunk, free the block */
> + ext2fs_file_acl_block_set(handle->fs,
> + (struct ext2_inode *)inode, 0);
> + err = ext2fs_iblk_sub_blocks(handle->fs,
> + (struct ext2_inode *)inode, 1);
> + if (err)
> + goto out;
> + ext2fs_block_alloc_stats2(handle->fs, blk, -1);
> + }
> +
> + /* Write the inode */
> + err = ext2fs_write_inode_full(handle->fs, handle->ino,
> + (struct ext2_inode *)inode,
> + EXT2_INODE_SIZE(handle->fs->super));
> + if (err)
> + goto out2;
> +
> +out2:
> + ext2fs_free_mem(&block_buf);
> +out:
> + ext2fs_free_mem(&inode);
> + handle->dirty = 0;
> + return err;
> +}
> +
> +static errcode_t read_xattrs_from_buffer(struct ext2_xattr_handle *handle,
> + struct ext2_ext_attr_entry *entries,
> + unsigned int storage_size,
> + void *value_start)
> +{
> + struct ext2_xattr *x;
> + struct ext2_ext_attr_entry *entry;
> + const char *prefix;
> + void *ptr;
> + unsigned int remain, prefix_len;
> + errcode_t err;
> +
> + x = handle->attrs;
> + while (x->name)
> + x++;
> +
> + entry = entries;
> + while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
> + __u32 hash;
> +
> + /* header eats this space */
> + remain -= sizeof(struct ext2_ext_attr_entry);
> +
> + /* is attribute name valid? */
> + if (EXT2_EXT_ATTR_SIZE(entry->e_name_len) > remain)
> + return EXT2_ET_EA_BAD_NAME_LEN;
> +
> + /* attribute len eats this space */
> + remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
> +
> + /* check value size */
> + if (entry->e_value_size > remain)
> + return EXT2_ET_EA_BAD_VALUE_SIZE;
> +
> + /* e_value_block must be 0 in inode's ea */
> + if (entry->e_value_block != 0)
> + return EXT2_ET_BAD_EA_BLOCK_NUM;
> +
> + hash = ext2fs_ext_attr_hash_entry(entry, value_start +
> + entry->e_value_offs);
> +
> + /* e_hash may be 0 in older inode's ea */
> + if (entry->e_hash != 0 && entry->e_hash != hash)
> + return EXT2_ET_BAD_EA_HASH;
> +
> + remain -= entry->e_value_size;
> +
> + /* Allocate space for more attrs? */
> + if (x == handle->attrs + handle->length) {
> + err = ext2fs_xattrs_expand(handle, 4);
> + if (err)
> + return err;
> + x = handle->attrs + handle->length - 4;
> + }
> +
> + /* Extract name/value */
> + prefix = find_ea_prefix(entry->e_name_index);
> + prefix_len = (prefix ? strlen(prefix) : 0);
> + err = ext2fs_get_memzero(entry->e_name_len + prefix_len + 1,
> + &x->name);
> + if (err)
> + return err;
> + if (prefix)
> + memcpy(x->name, prefix, prefix_len);
> + if (entry->e_name_len)
> + memcpy(x->name + prefix_len,
> + (void *)entry + sizeof(*entry),
> + entry->e_name_len);
> +
> + err = ext2fs_get_mem(entry->e_value_size, &x->value);
> + if (err)
> + return err;
> + x->value_len = entry->e_value_size;
> + memcpy(x->value, value_start + entry->e_value_offs,
> + entry->e_value_size);
> + x++;
> + entry = EXT2_EXT_ATTR_NEXT(entry);
> + }
> +
> + return 0;
> +}
> +
> +errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle)
> +{
> + struct ext2_xattr *attrs = NULL, *x;
> + unsigned int attrs_len;
> + struct ext2_inode_large *inode;
> + struct ext2_ext_attr_header *header;
> + __u32 ea_inode_magic;
> + unsigned int storage_size;
> + void *start, *block_buf = NULL;
> + blk64_t blk;
> + int i;
> + errcode_t err;
> +
> + i = EXT2_INODE_SIZE(handle->fs->super);
> + if (i < sizeof(*inode))
> + i = sizeof(*inode);
> + err = ext2fs_get_memzero(i, &inode);
> + if (err)
> + return err;
> +
> + err = ext2fs_read_inode_full(handle->fs, handle->ino,
> + (struct ext2_inode *)inode,
> + EXT2_INODE_SIZE(handle->fs->super));
> + if (err)
> + goto out;
> +
> + /* Does the inode have size for EA? */
> + if (EXT2_INODE_SIZE(handle->fs->super) <= EXT2_GOOD_OLD_INODE_SIZE +
> + inode->i_extra_isize +
> + sizeof(__u32))
> + goto read_ea_block;
> +
> + /* Look for EA in the inode */
> + memcpy(&ea_inode_magic, ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
> + inode->i_extra_isize, sizeof(__u32));
> + if (ea_inode_magic == EXT2_EXT_ATTR_MAGIC) {
> + storage_size = EXT2_INODE_SIZE(handle->fs->super) -
> + EXT2_GOOD_OLD_INODE_SIZE - inode->i_extra_isize -
> + sizeof(__u32);
> + start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
> + inode->i_extra_isize + sizeof(__u32);
> +
> + err = read_xattrs_from_buffer(handle, start, storage_size,
> + start);
> + if (err)
> + goto out;
> + }
> +
> +read_ea_block:
> + /* Look for EA in a separate EA block */
> + blk = ext2fs_file_acl_block(handle->fs, (struct ext2_inode *)inode);
> + if (blk != 0) {
> + if ((blk < handle->fs->super->s_first_data_block) ||
> + (blk >= ext2fs_blocks_count(handle->fs->super))) {
> + err = EXT2_ET_BAD_EA_BLOCK_NUM;
> + goto out;
> + }
> +
> + err = ext2fs_get_mem(handle->fs->blocksize, &block_buf);
> + if (err)
> + goto out;
> +
> + err = ext2fs_read_ext_attr3(handle->fs, blk, block_buf,
> + handle->ino);
> + if (err)
> + goto out3;
> +
> + header = (struct ext2_ext_attr_header *) block_buf;
> + if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
> + err = EXT2_ET_BAD_EA_HEADER;
> + goto out3;
> + }
> +
> + if (header->h_blocks != 1) {
> + err = EXT2_ET_BAD_EA_HEADER;
> + goto out3;
> + }
> +
> + /* Read EAs */
> + storage_size = handle->fs->blocksize -
> + sizeof(struct ext2_ext_attr_header);
> + start = block_buf + sizeof(struct ext2_ext_attr_header);
> + err = read_xattrs_from_buffer(handle, start, storage_size,
> + block_buf);
> + if (err)
> + goto out3;
> +
> + ext2fs_free_mem(&block_buf);
> + }
> +
> + ext2fs_free_mem(&block_buf);
> + ext2fs_free_mem(&inode);
> + return 0;
> +
> +out3:
> + ext2fs_free_mem(&block_buf);
> +out:
> + ext2fs_free_mem(&inode);
> + return err;
> +}
> +
> +#define XATTR_ABORT 1
> +#define XATTR_CHANGED 2
> +errcode_t ext2fs_xattrs_iterate(struct ext2_xattr_handle *h,
> + int (*func)(char *name, char *value,
> + void *data),
> + void *data)
> +{
> + struct ext2_xattr *x;
> + errcode_t err;
> + int ret;
> +
> + for (x = h->attrs; x < h->attrs + h->length; x++) {
> + if (!x->name)
> + continue;
> +
> + ret = func(x->name, x->value, data);
> + if (ret & XATTR_CHANGED)
> + h->dirty = 1;
> + if (ret & XATTR_ABORT)
> + return 0;
> + }
> +
> + return 0;
> +}
> +
> +errcode_t ext2fs_xattr_get(struct ext2_xattr_handle *h, const char *key,
> + void **value, unsigned int *value_len)
> +{
> + struct ext2_xattr *x;
> + void *val;
> + errcode_t err;
> +
> + for (x = h->attrs; x < h->attrs + h->length; x++) {
> + if (!x->name)
> + continue;
> +
> + if (strcmp(x->name, key) == 0) {
> + err = ext2fs_get_mem(x->value_len, &val);
> + if (err)
> + return err;
> + memcpy(val, x->value, x->value_len);
> + *value = val;
> + *value_len = x->value_len;
> + return 0;
> + }
> + }
> +
> + return EXT2_ET_EA_KEY_NOT_FOUND;
> +}
> +
> +errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *handle,
> + const char *key,
> + const void *value,
> + unsigned int value_len)
> +{
> + struct ext2_xattr *x, *last_empty;
> + char *new_value;
> + errcode_t err;
> +
> + last_empty = NULL;
> + for (x = handle->attrs; x < handle->attrs + handle->length; x++) {
> + if (!x->name) {
> + last_empty = x;
> + continue;
> + }
> +
> + /* Replace xattr */
> + if (strcmp(x->name, key) == 0) {
> + err = ext2fs_get_mem(value_len, &new_value);
> + if (err)
> + return err;
> + memcpy(new_value, value, value_len);
> + ext2fs_free_mem(&x->value);
> + x->value = new_value;
> + x->value_len = value_len;
> + handle->dirty = 1;
> + return 0;
> + }
> + }
> +
> + /* Add attr to empty slot */
> + if (last_empty) {
> + err = ext2fs_get_mem(strlen(key) + 1, &last_empty->name);
> + if (err)
> + return err;
> + strcpy(last_empty->name, key);
> +
> + err = ext2fs_get_mem(value_len, &last_empty->value);
> + if (err)
> + return err;
> + memcpy(last_empty->value, value, value_len);
> + last_empty->value_len = value_len;
> + handle->dirty = 1;
> + return 0;
> + }
> +
> + /* Expand array, append slot */
> + err = ext2fs_xattrs_expand(handle, 4);
> + if (err)
> + return err;
> +
> + x = handle->attrs + handle->length - 4;
> + err = ext2fs_get_mem(strlen(key) + 1, &x->name);
> + if (err)
> + return err;
> + strcpy(x->name, key);
> +
> + err = ext2fs_get_mem(value_len, &x->value);
> + if (err)
> + return err;
> + memcpy(x->value, value, value_len);
> + x->value_len = value_len;
> + handle->dirty = 1;
> + return 0;
> +}
> +
> +errcode_t ext2fs_xattr_remove(struct ext2_xattr_handle *handle,
> + const char *key)
> +{
> + struct ext2_xattr *x;
> + errcode_t err;
> +
> + for (x = handle->attrs; x < handle->attrs + handle->length; x++) {
> + if (!x->name)
> + continue;
> +
> + if (strcmp(x->name, key) == 0) {
> + ext2fs_free_mem(&x->name);
> + ext2fs_free_mem(&x->value);
> + x->value_len = 0;
> + handle->dirty = 1;
> + return 0;
> + }
> + }
> +
> + return EXT2_ET_EA_KEY_NOT_FOUND;
> +}
> +
> +errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
> + struct ext2_xattr_handle **handle)
> +{
> + struct ext2_xattr_handle *h;
> + errcode_t err;
> +
> + err = ext2fs_get_memzero(sizeof(*h), &h);
> + if (err)
> + return err;
> +
> + h->length = 4;
> + err = ext2fs_get_arrayzero(h->length, sizeof(struct ext2_xattr),
> + &h->attrs);
> + if (err) {
> + ext2fs_free_mem(&h);
> + return err;
> + }
> + h->ino = ino;
> + h->fs = fs;
> + *handle = h;
> + return 0;
> +}
> +
> +errcode_t ext2fs_xattrs_close(struct ext2_xattr_handle **handle)
> +{
> + unsigned int i;
> + struct ext2_xattr_handle *h = *handle;
> + struct ext2_xattr *a = h->attrs;
> + errcode_t err;
> +
> + if (h->dirty) {
> + err = ext2fs_xattrs_write(h);
> + if (err)
> + return err;
> + }
> +
> + for (i = 0; i < h->length; i++) {
> + if (a[i].name)
> + ext2fs_free_mem(&a[i].name);
> + if (a[i].value)
> + ext2fs_free_mem(&a[i].value);
> + }
> +
> + ext2fs_free_mem(&h->attrs);
> + ext2fs_free_mem(handle);
> + return 0;
> +}
> --
> 1.7.9.7
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-12-03 21:20:04

by Darrick J. Wong

[permalink] [raw]
Subject: Re: [PATCH v2 12/28] debugfs: make expand command support inline data

On Tue, Dec 03, 2013 at 08:11:39PM +0800, Zheng Liu wrote:
> From: Zheng Liu <[email protected]>
>
> This commit defines a ext2fs_inline_data_expand() to expand an inode with
> inline data.
>
> Signed-off-by: Zheng Liu <[email protected]>
> ---
> lib/ext2fs/expanddir.c | 2 +
> lib/ext2fs/ext2fsP.h | 1 +
> lib/ext2fs/inline_data.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 186 insertions(+)
>
> diff --git a/lib/ext2fs/expanddir.c b/lib/ext2fs/expanddir.c
> index 22558d6..baad782 100644
> --- a/lib/ext2fs/expanddir.c
> +++ b/lib/ext2fs/expanddir.c
> @@ -116,6 +116,8 @@ errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir)
>
> retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_APPEND,
> 0, expand_dir_proc, &es);
> + if (retval == EXT2_ET_INLINE_DATA_CANT_ITERATE)
> + return ext2fs_inline_data_expand(fs, dir);
>
> if (es.err)
> return es.err;
> diff --git a/lib/ext2fs/ext2fsP.h b/lib/ext2fs/ext2fsP.h
> index aae55b9..4dfa983 100644
> --- a/lib/ext2fs/ext2fsP.h
> +++ b/lib/ext2fs/ext2fsP.h
> @@ -90,6 +90,7 @@ extern int ext2fs_process_dir_block(ext2_filsys fs,
>
> extern errcode_t ext2fs_inline_data_size(ext2_filsys fs, ext2_ino_t ino,
> size_t *size);
> +extern errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino);
> extern errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
> ext2_ino_t ino,
> int (*func)(ext2_filsys fs,
> diff --git a/lib/ext2fs/inline_data.c b/lib/ext2fs/inline_data.c
> index 7dc3f24..02bb470 100644
> --- a/lib/ext2fs/inline_data.c
> +++ b/lib/ext2fs/inline_data.c
> @@ -196,3 +196,186 @@ out:
> retval |= BLOCK_ERROR;
> return retval & BLOCK_ERROR ? ctx->errcode : 0;
> }
> +
> +static errcode_t ext2fs_inline_data_ea_remove(ext2_filsys fs, ext2_ino_t ino)
> +{
> + struct ext2_xattr_handle *handle;
> + errcode_t retval;
> +
> + retval = ext2fs_xattrs_open(fs, ino, &handle);
> + if (retval)
> + return retval;
> +
> + retval = ext2fs_xattrs_read(handle);
> + if (retval)
> + goto err;
> +
> + retval = ext2fs_xattr_remove(handle, "system.data");
> + if (retval)
> + goto err;
> +
> + retval = ext2fs_xattrs_write(handle);
> +
> +err:
> + (void) ext2fs_xattrs_close(&handle);
> + return retval;
> +}
> +
> +static errcode_t ext2fs_inline_data_convert_dir(ext2_filsys fs, ext2_ino_t ino,
> + char *bbuf, char *ibuf, int size)
> +{
> + struct ext2_dir_entry *dir, *dir2;
> + struct ext2_dir_entry_tail *t;
> + errcode_t retval;
> + unsigned int offset;
> + int csum_size = 0;
> + int filetype = 0;
> + int rec_len;
> +
> + if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
> + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
> + csum_size = sizeof(struct ext2_dir_entry_tail);
> +
> + /* Create '.' and '..' */
> + if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
> + EXT2_FEATURE_INCOMPAT_FILETYPE))
> + filetype = EXT2_FT_DIR << 8;
> +
> + /*
> + * Set up entry for '.'
> + */
> + dir = (struct ext2_dir_entry *) bbuf;
> + dir->inode = ino;
> + ext2fs_dirent_set_name_len(dir, 1);
> + ext2fs_dirent_set_file_type(dir, filetype);
> + dir->name[0] = '.';
> + rec_len = (fs->blocksize - csum_size) - EXT2_DIR_REC_LEN(1);
> + dir->rec_len = EXT2_DIR_REC_LEN(1);
> +
> + /*
> + * Set up entry for '..'
> + */
> + dir = (struct ext2_dir_entry *) (bbuf + dir->rec_len);
> + dir->rec_len = EXT2_DIR_REC_LEN(2);
> + dir->inode = ext2fs_le32_to_cpu(((__u32 *)ibuf)[0]);
> + ext2fs_dirent_set_name_len(dir, 2);
> + ext2fs_dirent_set_file_type(dir, filetype);
> + dir->name[0] = '.';
> + dir->name[1] = '.';
> +
> + /*
> + * Ajust the last rec_len
> + */
> + offset = EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2);
> + dir = (struct ext2_dir_entry *) (bbuf + offset);
> + memcpy(bbuf + offset, ibuf + EXT4_INLINE_DATA_DOTDOT_SIZE,
> + size - EXT4_INLINE_DATA_DOTDOT_SIZE);
> +
> + do {
> + dir2 = dir;
> + retval = ext2fs_get_rec_len(fs, dir, &rec_len);
> + if (retval)
> + goto err;
> + offset += rec_len;
> + dir = (struct ext2_dir_entry *) (bbuf + offset);
> + } while (offset < size);
> + rec_len += fs->blocksize - csum_size - offset;
> + retval = ext2fs_set_rec_len(fs, rec_len, dir2);
> + if (retval)
> + goto err;
> +
> + if (csum_size) {
> + t = EXT2_DIRENT_TAIL(bbuf, fs->blocksize);
> + ext2fs_initialize_dirent_tail(fs, t);
> + }
> +
> +err:
> + return retval;
> +}
> +
> +errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino)

I couldn't tell from the name that this function only expands inline
directories. A name like ext2fs_inline_dir_expand() would make that more
immediately clear; initially I thought this could be a method that also expands
files.

> +{
> + struct ext2_inode inode;
> + struct ext2_inline_data data;
> + errcode_t retval;
> + blk64_t blk;
> + char *inline_buf = 0;
> + char *blk_buf = 0;
> +
> + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
> +
> + retval = ext2fs_read_inode(fs, ino, &inode);
> + if (retval)
> + return retval;
> +
> + if (!(inode.i_flags & EXT4_INLINE_DATA_FL))
> + return EXT2_ET_NO_INLINE_DATA;
> +
> + /* Get inline data first */
> + data.fs = fs;
> + data.ino = ino;
> + retval = ext2fs_inline_data_ea_get(&data);
> + if (retval)
> + return retval;
> + retval = ext2fs_get_mem(EXT4_MIN_INLINE_DATA_SIZE + data.ea_size,
> + &inline_buf);
> + if (retval)
> + goto errout;
> +
> + memcpy(inline_buf, (void *)inode.i_block, EXT4_MIN_INLINE_DATA_SIZE);
> + if (data.ea_size > 0) {
> + memcpy(inline_buf + EXT4_MIN_INLINE_DATA_SIZE,
> + data.ea_data, data.ea_size);
> + }
> +
> + memset((void *)inode.i_block, 0, EXT4_MIN_INLINE_DATA_SIZE);
> + retval = ext2fs_inline_data_ea_remove(fs, ino);
> + if (retval)
> + goto errout;
> +
> + retval = ext2fs_get_mem(fs->blocksize, &blk_buf);
> + if (retval)
> + goto errout;
> +
> + /* Adjust the rec_len */
> + retval = ext2fs_inline_data_convert_dir(fs, ino, blk_buf, inline_buf,
> + EXT4_MIN_INLINE_DATA_SIZE +
> + data.ea_size);
> + if (retval)
> + goto errout;
> +
> + /* Allocate a new block */
> + retval = ext2fs_new_block2(fs, 0, 0, &blk);
> + if (retval)
> + goto errout;
> + if (EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT3_FEATURE_INCOMPAT_EXTENTS))
> + inode.i_flags |= EXT4_EXTENTS_FL;
> + else
> + inode.i_block[0] = blk;
> + inode.i_flags &= ~EXT4_INLINE_DATA_FL;
> + ext2fs_iblk_set(fs, &inode, 1);
> + inode.i_size = fs->blocksize;
> + retval = ext2fs_write_inode(fs, ino, &inode);
> + if (retval)
> + goto errout;
> + retval = ext2fs_write_dir_block4(fs, blk, blk_buf, 0, ino);
> + if (retval)
> + goto errout;
> + if (EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT3_FEATURE_INCOMPAT_EXTENTS)) {
> + ext2_extent_handle_t handle;
> +
> + retval = ext2fs_extent_open2(fs, ino, &inode, &handle);
> + if (retval)
> + goto errout;
> + retval = ext2fs_extent_set_bmap(handle, 0, blk, 0);
> + ext2fs_extent_free(handle);
> + }

Would it be simpler to call ext2fs_bmap2() with bmap_flags = BMAP_SET here?
Then you wouldn't need to open code the blockmap and extent setting cases.
Then all you have to do is:

if (fs_has_extents)
inode.i_flags |= EXT4_EXTENTS_FL;
ext2fs_bmap2(fs, ino, &inode, NULL, BMAP_SET, 0, NULL, &blk);
ext2fs_write_inode(...);

--D
> +
> +errout:
> + if (blk_buf)
> + ext2fs_free_mem(&blk_buf);
> + if (inline_buf)
> + ext2fs_free_mem(&inline_buf);
> + ext2fs_free_mem(&data.ea_data);
> + return retval;
> +}
> --
> 1.7.9.7
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-12-03 22:13:58

by Darrick J. Wong

[permalink] [raw]
Subject: Re: [PATCH v2 09/28] libext2fs: handle inline data in dir iterator function

On Tue, Dec 03, 2013 at 08:11:36PM +0800, Zheng Liu wrote:
> From: Zheng Liu <[email protected]>
>
> 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:
> - cd
> - chroot
> - link*
> - ls
> - ncheck
> - pwd
> - unlink
>
> * TODO: Inline_data doesn't expand to ibody extended attribute because
> link command doesn't handle DIR_NO_SPACE error until now. But if we
> have already expanded inline data to ibody ea area, link command can
> occupy this space.

A patch for this TODO is coming, right?

> Signed-off-by: Theodore Ts'o <[email protected]>
> Signed-off-by: Zheng Liu <[email protected]>
> ---
> lib/ext2fs/Makefile.in | 8 +++
> lib/ext2fs/Makefile.pq | 1 +
> lib/ext2fs/dir_iterate.c | 62 +++++++++++-----
> lib/ext2fs/ext2_err.et.in | 3 +
> lib/ext2fs/ext2_fs.h | 10 +++
> lib/ext2fs/ext2fs.h | 1 +
> lib/ext2fs/ext2fsP.h | 11 +++
> lib/ext2fs/inline_data.c | 175 +++++++++++++++++++++++++++++++++++++++++++++
> lib/ext2fs/swapfs.c | 12 +++-
> 9 files changed, 265 insertions(+), 18 deletions(-)
> create mode 100644 lib/ext2fs/inline_data.c
>
> diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
> index e1e6216..4d011c9 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 \
> @@ -758,6 +760,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 2f7b654..89082a7 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 8be0ac2..1624371 100644
> --- a/lib/ext2fs/dir_iterate.c
> +++ b/lib/ext2fs/dir_iterate.c
> @@ -127,6 +127,12 @@ 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;
> + retval = ext2fs_inline_data_dir_iterate(fs, dir,
> + ext2fs_process_dir_block,
> + &ctx);
> + }
> if (retval)
> return retval;
> return ctx.errcode;
> @@ -189,30 +195,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;

Since we don't swap i_block[] when we're reading in the inode, who calls
ext2fs_dirent_swab_in()?

> + }
>
> 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 +236,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 +256,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 +294,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_CHANGED to notify caller that inline
> + * data has been changed
> + */

I don't think I like overloading the meaning of BLOCK_CHANGED here. For a
non-inlinedata file, the iterator function returning BLOCK_CHANGED means that
the lblk->pblk mapping changed, whereas for an inline data file, it means that
the block *contents* have changed.

At best, the two meanings ought to be documented wherever BLOCK_CHANGED is
defined, though I think I'd prefer a new flag BLOCK_INLINE_DATA_CHANGED to
handle this situation, because otherwise the meaning of the flag is
context-dependent and therefore more difficult to reason about.

Separate flags also gives us the ability to check for programming errors, such
as someone returning BLOCK_CHANGED on inlinedata files or returning
BLOCK_INLINE_DATA_CHANGED on !inlinedata files.

> + retval = BLOCK_CHANGED;

Who calls ext2fs_dirent_swab_out()?

> + }
> }
> 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 b819a90..0781145 100644
> --- a/lib/ext2fs/ext2_err.et.in
> +++ b/lib/ext2fs/ext2_err.et.in
> @@ -500,4 +500,7 @@ ec EXT2_ET_EA_KEY_NOT_FOUND,
> ec EXT2_ET_EA_NO_SPACE,
> "Insufficient space to store extended attribute data"
>
> +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 6a28d55..5ab16ae 100644
> --- a/lib/ext2fs/ext2_fs.h
> +++ b/lib/ext2fs/ext2_fs.h
> @@ -914,4 +914,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 e251435..c5b73fd 100644
> --- a/lib/ext2fs/ext2fs.h
> +++ b/lib/ext2fs/ext2fs.h
> @@ -438,6 +438,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 80d2d0a..5ab6084 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,16 @@ extern int ext2fs_process_dir_block(ext2_filsys fs,
> int ref_offset,
> void *priv_data);
>
> +extern errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
> + ext2_ino_t ino,
> + int (*func)(ext2_filsys fs,
> + blk64_t *blocknr,
> + e2_blkcnt_t blockcnt,
> + blk64_t ref_blk,
> + int ref_offset,
> + void *priv_data),
> + 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 0000000..cc2954d
> --- /dev/null
> +++ b/lib/ext2fs/inline_data.c
> @@ -0,0 +1,175 @@
> +/*
> + * inline_data.c --- data in inode
> + *
> + * Copyright (C) 2012 Zheng Liu <[email protected]>
> + *
> + * %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 <stdio.h>
> +#include <time.h>
> +
> +#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 */
> + char *ea_data;

Should this be a void* since the file data type is opaque to the library?

> +};
> +
> +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;
> +}
> +
> +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;
> +}
> +
> +
> +errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
> + ext2_ino_t ino,
> + int (*func)(ext2_filsys fs,
> + blk64_t *blocknr,
> + e2_blkcnt_t blockcnt,
> + blk64_t ref_blk,
> + int ref_offset,
> + void *priv_data),
> + void *priv_data)
> +{
> + struct dir_context *ctx;
> + struct ext2_inode inode;
> + struct ext2_dir_entry dirent;
> + struct ext2_inline_data data;
> + errcode_t retval = 0;
> + e2_blkcnt_t blockcnt = 0;
> +
> + ctx = (struct dir_context *)priv_data;
> +
> + retval = ext2fs_read_inode(fs, ino, &inode);
> + if (retval)
> + goto out;
> +
> + if (!(inode.i_flags & EXT4_INLINE_DATA_FL))
> + return EXT2_ET_NO_INLINE_DATA;
> +
> + if (!LINUX_S_ISDIR(inode.i_mode)) {
> + retval = EXT2_ET_NO_DIRECTORY;
> + 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';
> + ctx->buf = (char *)&dirent;
> + ext2fs_get_rec_len(fs, &dirent, &ctx->buflen);
> + retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
> + if (retval & 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);
> + retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
> + if (retval & BLOCK_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;
> + }
> + if (retval & 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;
> + retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
> + if (retval & BLOCK_CHANGED) {
> + errcode_t err;
> +
> + err = ext2fs_write_inode(fs, ino, &inode);
> + if (err)
> + goto out;
> + }
> + if (retval & BLOCK_ABORT)
> + goto out;
> +
> + data.fs = fs;
> + data.ino = ino;
> + retval = ext2fs_inline_data_ea_get(&data);
> + if (retval)
> + goto out;
> + if (data.ea_size > 0) {
> + ctx->buf = data.ea_data;
> + ctx->buflen = data.ea_size;
> + retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
> + if (retval & BLOCK_CHANGED)
> + ext2fs_inline_data_ea_set(&data);
> + ext2fs_free_mem(&data.ea_data);
> + ctx->buf = 0;
> + }
> +
> +out:
> + retval |= BLOCK_ERROR;
> + return retval & BLOCK_ERROR ? ctx->errcode : 0;
> +}
> diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c
> index 1295e81..cfbe5dd 100644
> --- a/lib/ext2fs/swapfs.c
> +++ b/lib/ext2fs/swapfs.c
> @@ -207,6 +207,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;
>
> @@ -233,12 +234,19 @@ 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 are swapped on access, not here
> + * Inline data are not swapped beside parent ino is accessed

>From the other patches it looks like the parent ino is swapped on access too.

I'm confused about what this comment is trying to say, though the code here
says that inline data is swapped on access (if at all).

--D
> + */
> + 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) {
> --
> 1.7.9.7
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-12-03 22:22:58

by Darrick J. Wong

[permalink] [raw]
Subject: Re: [PATCH v2 27/28] libext2fs: export inode cahce creation function

On Tue, Dec 03, 2013 at 08:11:54PM +0800, Zheng Liu wrote:
> From: Zheng Liu <[email protected]>
>
> Currently we have already exported inode cache flush and free functions
> for users. This commit exports inode cache creation function. Later
> we will use this function to initialize inode cache and do some unit
> tests for inline data.
>
> Signed-off-by: Zheng Liu <[email protected]>
> ---
> lib/ext2fs/ext2fs.h | 1 +
> lib/ext2fs/inode.c | 8 ++++----
> 2 files changed, 5 insertions(+), 4 deletions(-)
>
> diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
> index bf0ef26..a47bb40 100644
> --- a/lib/ext2fs/ext2fs.h
> +++ b/lib/ext2fs/ext2fs.h
> @@ -1369,6 +1369,7 @@ extern errcode_t ext2fs_get_memalign(unsigned long size,
> unsigned long align, void *ptr);
>
> /* inode.c */
> +extern errcode_t ext2fs_create_inode_cache(ext2_filsys fs, int cache_size);
> extern void ext2fs_free_inode_cache(struct ext2_inode_cache *icache);
> extern errcode_t ext2fs_flush_icache(ext2_filsys fs);
> extern errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan,
> diff --git a/lib/ext2fs/inode.c b/lib/ext2fs/inode.c
> index 46c1c58..8cf76b0 100644
> --- a/lib/ext2fs/inode.c
> +++ b/lib/ext2fs/inode.c
> @@ -91,7 +91,7 @@ void ext2fs_free_inode_cache(struct ext2_inode_cache *icache)
> ext2fs_free_mem(&icache);
> }
>
> -static errcode_t create_icache(ext2_filsys fs)
> +errcode_t ext2fs_create_inode_cache(ext2_filsys fs, int cache_size)

cache_size should unsigned int; there's not much point in letting people pass
us a negative size.

Possibly ext2_inode_cache.cache_size should be unsigned too...

--D

> {
> int i;
> errcode_t retval;
> @@ -109,7 +109,7 @@ static errcode_t create_icache(ext2_filsys fs)
>
> fs->icache->buffer_blk = 0;
> fs->icache->cache_last = -1;
> - fs->icache->cache_size = 4;
> + fs->icache->cache_size = cache_size;
> fs->icache->refcount = 1;
> retval = ext2fs_get_array(fs->icache->cache_size,
> sizeof(struct ext2_inode_cache_ent),
> @@ -596,7 +596,7 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
> return EXT2_ET_BAD_INODE_NUM;
> /* Create inode cache if not present */
> if (!fs->icache) {
> - retval = create_icache(fs);
> + retval = ext2fs_create_inode_cache(fs, 4);
> if (retval)
> return retval;
> }
> @@ -732,7 +732,7 @@ errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
> }
> }
> } else {
> - retval = create_icache(fs);
> + retval = ext2fs_create_inode_cache(fs, 4);
> if (retval)
> goto errout;
> }
> --
> 1.7.9.7
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-12-03 22:29:54

by Darrick J. Wong

[permalink] [raw]
Subject: Re: [PATCH v2 20/28] tune2fs: add inline_data feature in tune2fs

On Tue, Dec 03, 2013 at 08:11:47PM +0800, Zheng Liu wrote:
> From: Zheng Liu <[email protected]>
>
> Inline_data can be enabled without ext_attr. Hence we don't check it.
> As doing in mke2fs we need to check inode size.
>
> Now this feature only can be enabled because we may be out of space when
> disabling it. If this feature is disabled, we need to allocate a block
> for every file and directory, and it might exhaust all space.

Could we at least try to turn off inlinedata and decline to clear the flag if
we ENOSPC?

(Yes, that's more of a TODO feature request...)

> Signed-off-by: Theodore Ts'o <[email protected]>
> Signed-off-by: Zheng Liu <[email protected]>
> ---
> misc/tune2fs.8.in | 5 +++++
> misc/tune2fs.c | 16 +++++++++++++++-
> 2 files changed, 20 insertions(+), 1 deletion(-)
>
> diff --git a/misc/tune2fs.8.in b/misc/tune2fs.8.in
> index 55c6dd9..78f965b 100644
> --- a/misc/tune2fs.8.in
> +++ b/misc/tune2fs.8.in
> @@ -531,6 +531,11 @@ Setting the filesystem feature is equivalent to using the
> .B \-j
> option.
> .TP
> +.B inline_data
> +Allow data to be stored in the inode and extented attribute area.

"extended"

--D

> +.B Tune2fs
> +only supports setting this filesystem feature.
> +.TP
> .B large_file
> Filesystem can contain files that are greater than 2GB. (Modern kernels
> set this feature automatically when a file > 2GB is created.)
> diff --git a/misc/tune2fs.c b/misc/tune2fs.c
> index 95c1886..1952081 100644
> --- a/misc/tune2fs.c
> +++ b/misc/tune2fs.c
> @@ -140,7 +140,8 @@ static __u32 ok_features[3] = {
> EXT2_FEATURE_INCOMPAT_FILETYPE |
> EXT3_FEATURE_INCOMPAT_EXTENTS |
> EXT4_FEATURE_INCOMPAT_FLEX_BG |
> - EXT4_FEATURE_INCOMPAT_MMP,
> + EXT4_FEATURE_INCOMPAT_MMP |
> + EXT4_FEATURE_INCOMPAT_INLINE_DATA,
> /* R/O compat */
> EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
> EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
> @@ -1083,6 +1084,19 @@ mmp_error:
> disable_uninit_bg(fs,
> EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
>
> + if (FEATURE_ON(E2P_FEATURE_INCOMPAT,
> + EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
> + /*
> + * Check inode size. If inode size is 128, tell user that
> + * inline data is useless.
> + */
> + if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE) {
> + fputs(_("The inode size is too small to "
> + "store inline data.\n"), stderr);
> + return 1;
> + }
> + }
> +
> if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
> EXT4_FEATURE_RO_COMPAT_QUOTA)) {
> /*
> --
> 1.7.9.7
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-12-03 22:30:34

by Darrick J. Wong

[permalink] [raw]
Subject: Re: [PATCH v2 19/28] mke2fs: add inline_data support in mke2fs

On Tue, Dec 03, 2013 at 08:11:46PM +0800, Zheng Liu wrote:
> From: Zheng Liu <[email protected]>
>
> Now inline_data doesn't depend on ext_attr. Hence we don't need to do
> this sanity check. But if the inode size is too small (128 bytes),
> inline_data will be useless because we couldn't save data in ibody
> extented attribute. So we need to report this error.
>
> Signed-off-by: Theodore Ts'o <[email protected]>
> Signed-off-by: Zheng Liu <[email protected]>
> ---
> misc/mke2fs.8.in | 3 +++
> misc/mke2fs.c | 18 ++++++++++++++++--
> 2 files changed, 19 insertions(+), 2 deletions(-)
>
> diff --git a/misc/mke2fs.8.in b/misc/mke2fs.8.in
> index 7b89296..c5e1abb 100644
> --- a/misc/mke2fs.8.in
> +++ b/misc/mke2fs.8.in
> @@ -587,6 +587,9 @@ option).
> @JDEV@must be created with the same
> @JDEV@block size as the filesystems that will be using it.
> .TP
> +.B inline_data
> +Allow data to be stored in the inode and extented attribute area

"extended"

> +.TP
> .B large_file
> Filesystem can contain files that are greater than 2GB. (Modern kernels
> set this feature automatically when a file > 2GB is created.)
> diff --git a/misc/mke2fs.c b/misc/mke2fs.c
> index 67c9225..0a3880f 100644
> --- a/misc/mke2fs.c
> +++ b/misc/mke2fs.c
> @@ -929,7 +929,8 @@ static __u32 ok_features[3] = {
> EXT2_FEATURE_INCOMPAT_META_BG|
> EXT4_FEATURE_INCOMPAT_FLEX_BG|
> EXT4_FEATURE_INCOMPAT_MMP |
> - EXT4_FEATURE_INCOMPAT_64BIT,
> + EXT4_FEATURE_INCOMPAT_64BIT|
> + EXT4_FEATURE_INCOMPAT_INLINE_DATA,
> /* R/O compat */
> EXT2_FEATURE_RO_COMPAT_LARGE_FILE|
> EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
> @@ -2069,7 +2070,8 @@ profile_error:
> "See https://ext4.wiki.kernel.org/"
> "index.php/Quota for more information\n\n"));
>
> - /* Since sparse_super is the default, we would only have a problem
> + /*
> + * Since sparse_super is the default, we would only have a problem
> * here if it was explicitly disabled.
> */
> if ((fs_param.s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) &&
> @@ -2125,6 +2127,18 @@ profile_error:
> blocksize);
> exit(1);
> }
> + /*
> + * If inode size is 128 and inline data is enable, we need to

"is enabled"

> + * notify users that inline data will never be useful.
> + */
> + if ((fs_param.s_feature_incompat &
> + EXT4_FEATURE_INCOMPAT_INLINE_DATA) &&
> + inode_size == EXT2_GOOD_OLD_INODE_SIZE) {

Perhaps I'm missing something here, but why is it impossible to use i_blocks
for inline data even if there's no space for EAs?

--D

> + com_err(program_name, 0,
> + _("inode size is %d, inline data is useless"),
> + inode_size);
> + exit(1);
> + }
> fs_param.s_inode_size = inode_size;
> }
>
> --
> 1.7.9.7
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-12-04 03:02:50

by Darrick J. Wong

[permalink] [raw]
Subject: Re: [PATCH v2 17/28] libext2fs: handle inline data in read/write function

On Tue, Dec 03, 2013 at 08:11:44PM +0800, Zheng Liu wrote:
> From: Zheng Liu <[email protected]>
>
> Currently ext2fs_file_read/write are used to copy data from/to a file.
> But they manipulate data by blocksize. For supporting inline data, we
> handle it in two new fucntions called ext2fs_file_read/write_inline_data.
>
> In read path the implementation is straightforward. But in write path
> things get more complicated because if the size of data is greater than
> the maximum size of inline data we will expand this file. So now we
> will check this in ext2fs_xattr_set(). If this inode doesn't have
> enough space, it will return EXT2_ET_INLINE_DATA_NO_SPACE error. Then
> the caller will check this error and tries to expand the file.
>
> The following commands in debugfs can handle inline_data feature after
> applying this patch:
> - dump
> - cat
> - rdump
> - write
>
> Signed-off-by: Theodore Ts'o <[email protected]>
> Signed-off-by: Zheng Liu <[email protected]>
> ---
> debugfs/debugfs.c | 13 ++++--
> lib/ext2fs/ext2_err.et.in | 3 ++
> lib/ext2fs/ext2fsP.h | 6 +++
> lib/ext2fs/ext_attr.c | 76 +++++++++++++++++++++++++++++++
> lib/ext2fs/fileio.c | 108 +++++++++++++++++++++++++++++++++++++++++++++
> lib/ext2fs/inline_data.c | 90 ++++++++++++++++++++++++++++++++++---
> 6 files changed, 287 insertions(+), 9 deletions(-)
>
> diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
> index d1c81f1..0903c8e 100644
> --- a/debugfs/debugfs.c
> +++ b/debugfs/debugfs.c
> @@ -1684,7 +1684,6 @@ fail:
> return retval;
> }
>
> -
> void do_write(int argc, char *argv[])
> {
> int fd;
> @@ -1750,8 +1749,11 @@ void do_write(int argc, char *argv[])
> current_fs->now ? current_fs->now : time(0);
> inode.i_links_count = 1;
> inode.i_size = statbuf.st_size;
> - if (current_fs->super->s_feature_incompat &
> - EXT3_FEATURE_INCOMPAT_EXTENTS) {
> + if (EXT2_HAS_INCOMPAT_FEATURE(current_fs->super,
> + EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
> + inode.i_flags |= EXT4_INLINE_DATA_FL;
> + } else if (current_fs->super->s_feature_incompat &
> + EXT3_FEATURE_INCOMPAT_EXTENTS) {
> int i;
> struct ext3_extent_header *eh;
>
> @@ -1768,6 +1770,11 @@ void do_write(int argc, char *argv[])
> close(fd);
> return;
> }
> + if (inode.i_flags & EXT4_INLINE_DATA_FL) {
> + retval = ext2fs_inline_data_init(current_fs, newfile);
> + if (retval)
> + return;
> + }
> if (LINUX_S_ISREG(inode.i_mode)) {
> if (statbuf.st_blocks < statbuf.st_size / S_BLKSIZE) {
> make_holes = 1;
> diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
> index 0781145..68aa3e0 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_NO_INLINE_DATA,
> "Inode doesn't have inline data"
>
> +ec EXT2_ET_INLINE_DATA_NO_SPACE,
> + "No free space in inline data"
> +
> end
> diff --git a/lib/ext2fs/ext2fsP.h b/lib/ext2fs/ext2fsP.h
> index bfc321c..e159128 100644
> --- a/lib/ext2fs/ext2fsP.h
> +++ b/lib/ext2fs/ext2fsP.h
> @@ -102,6 +102,12 @@ extern errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
> int ref_offset,
> void *priv_data),
> void *priv_data);
> +extern errcode_t ext2fs_inline_data_get(ext2_filsys fs, ext2_ino_t ino,
> + struct ext2_inode *inode,
> + void *buf, size_t *size);
> +extern errcode_t ext2fs_inline_data_set(ext2_filsys fs, ext2_ino_t ino,
> + struct ext2_inode *inode,
> + void *buf, size_t size);
>
> /* Generic numeric progress meter */
>
> diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
> index 0e9a9ab..98f0fbb 100644
> --- a/lib/ext2fs/ext_attr.c
> +++ b/lib/ext2fs/ext_attr.c
> @@ -818,6 +818,71 @@ errcode_t ext2fs_xattr_get(struct ext2_xattr_handle *h, const char *key,
> return EXT2_ET_EA_KEY_NOT_FOUND;
> }
>
> +static errcode_t ext2fs_xattr_max_size(struct ext2_xattr_handle *handle, size_t *size)
> +{
> + struct ext2_ext_attr_header *header;
> + struct ext2_ext_attr_entry *entry;
> + struct ext2_inode_large *inode;
> + __u32 ea_inode_magic;
> + unsigned int storage_size, freesize, minoff;
> + void *start;
> + int i;
> + errcode_t err;
> +
> + i = EXT2_INODE_SIZE(handle->fs->super);
> + if (i < sizeof(*inode))
> + i = sizeof(*inode);
> + err = ext2fs_get_memzero(i, &inode);
> + if (err)
> + return err;
> +
> + err = ext2fs_read_inode_full(handle->fs, handle->ino,
> + (struct ext2_inode *)inode,
> + EXT2_INODE_SIZE(handle->fs->super));
> + if (err)
> + goto out;
> +
> + /* Does the inode have size for EA? */
> + if (EXT2_INODE_SIZE(handle->fs->super) <= EXT2_GOOD_OLD_INODE_SIZE +
> + inode->i_extra_isize +
> + sizeof(__u32)) {
> + err = EXT2_ET_INLINE_DATA_NO_SPACE;
> + goto out;
> + }
> +
> + minoff = EXT2_INODE_SIZE(handle->fs->super) -
> + sizeof(*inode) - sizeof(__u32);
> + memcpy(&ea_inode_magic, ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
> + inode->i_extra_isize, sizeof(__u32));
> + if (ea_inode_magic == EXT2_EXT_ATTR_MAGIC) {
> + /* has xattrs. calculate the size */
> + storage_size = EXT2_INODE_SIZE(handle->fs->super) -
> + EXT2_GOOD_OLD_INODE_SIZE - inode->i_extra_isize -
> + sizeof(__u32);
> + start= ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
> + inode->i_extra_isize + sizeof(__u32);
> + entry = start;
> + while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
> + if (!entry->e_value_block && entry->e_value_size) {
> + unsigned int offs = entry->e_value_offs;
> + if (offs < minoff)
> + minoff = offs;
> + }
> + entry = EXT2_EXT_ATTR_NEXT(entry);
> + }
> + *size = minoff - ((char *)entry - (char *)start) - sizeof(__u32);
> + } else {
> + /* no xattr. return a maximum size */
> + *size = EXT2_EXT_ATTR_SIZE(minoff -
> + EXT2_EXT_ATTR_LEN(strlen("data")) -
> + EXT2_EXT_ATTR_ROUND - sizeof(__u32));
> + }
> +
> +out:
> + ext2fs_free_mem(&inode);
> + return err;
> +}
> +
> errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *handle,
> const char *key,
> const void *value,
> @@ -827,6 +892,17 @@ errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *handle,
> char *new_value;
> errcode_t err;
>
> + /* we need to check inline data space */
> + if (strcmp(key, "system.data") == 0) {
> + size_t max_size;
> +
> + err = ext2fs_xattr_max_size(handle, &max_size);
> + if (err)
> + return err;
> + if (value_len > max_size)
> + return EXT2_ET_INLINE_DATA_NO_SPACE;
> + }
> +
> last_empty = NULL;
> for (x = handle->attrs; x < handle->attrs + handle->length; x++) {
> if (!x->name) {
> diff --git a/lib/ext2fs/fileio.c b/lib/ext2fs/fileio.c
> index 02e6263..acb705d 100644
> --- a/lib/ext2fs/fileio.c
> +++ b/lib/ext2fs/fileio.c
> @@ -224,6 +224,39 @@ errcode_t ext2fs_file_close(ext2_file_t file)
> }
>
>
> +static errcode_t
> +ext2fs_file_read_inline_data(ext2_file_t file, void *buf,
> + unsigned int wanted, unsigned int *got)
> +{
> + ext2_filsys fs;
> + errcode_t retval;
> + unsigned int start, count = 0;
> + size_t size;
> +
> + fs = file->fs;
> + retval = ext2fs_inline_data_get(fs, file->ino, &file->inode,
> + file->buf, &size);
> + if (retval)
> + return retval;
> +
> + if (file->pos >= size)
> + goto out;
> +
> + start = file->pos % size;

If file->pos can never be larger than size, this is unnecessary.

> + count = size - start;
> + if (count > wanted)
> + count = wanted;
> + memcpy(buf, file->buf + start, count);
> + file->pos += count;
> + buf += count;
> +
> +out:
> + if (got)
> + *got = count;
> + return retval;
> +}
> +
> +
> errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
> unsigned int wanted, unsigned int *got)
> {
> @@ -236,6 +269,10 @@ errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
> EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
> fs = file->fs;
>
> + /* If an inode has inline data, things get complicated. */
> + if (file->inode.i_flags & EXT4_INLINE_DATA_FL)
> + return ext2fs_file_read_inline_data(file, buf, wanted, got);
> +
> while ((file->pos < EXT2_I_SIZE(&file->inode)) && (wanted > 0)) {
> retval = sync_buffer_position(file);
> if (retval)
> @@ -266,6 +303,67 @@ fail:
> }
>
>
> +static errcode_t
> +ext2fs_file_write_inline_data(ext2_file_t file, const void *buf,
> + unsigned int nbytes, unsigned int *written)
> +{
> + ext2_filsys fs;
> + errcode_t retval;
> + unsigned int start, count = 0;
> + size_t size;
> +
> + fs = file->fs;
> + retval = ext2fs_inline_data_get(fs, file->ino, &file->inode,
> + file->buf, &size);
> + if (retval)
> + return retval;
> +
> + if (file->pos < size) {
> + start = file->pos % fs->blocksize;

Since we're not dealing with blocks, I don't think you need to '%' with the
block size.

--D

> + count = nbytes - start;
> + memcpy(file->buf + start, buf, count);
> +
> + retval = ext2fs_inline_data_set(fs, file->ino, &file->inode,
> + file->buf, count);
> + if (retval == EXT2_ET_INLINE_DATA_NO_SPACE)
> + goto expand;
> + if (retval)
> + return retval;
> +
> + file->pos += count;
> +
> + /* Update inode size */
> + if (count != 0 && EXT2_I_SIZE(&file->inode) < file->pos) {
> + errcode_t rc;
> +
> + rc = ext2fs_file_set_size2(file, file->pos);
> + if (retval == 0)
> + retval = rc;
> + }
> +
> + if (written)
> + *written = count;
> + return 0;
> + }
> +
> +expand:
> + retval = ext2fs_inline_data_expand(fs, file->ino);
> + if (retval)
> + return retval;
> + /*
> + * reload inode and return no space error
> + *
> + * XXX: file->inode could be copied from the outside
> + * in ext2fs_file_open2(). We have no way to modify
> + * the outside inode.
> + */
> + retval = ext2fs_read_inode(fs, file->ino, &file->inode);
> + if (retval)
> + return retval;
> + return EXT2_ET_INLINE_DATA_NO_SPACE;
> +}
> +
> +
> errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
> unsigned int nbytes, unsigned int *written)
> {
> @@ -280,6 +378,16 @@ errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
> if (!(file->flags & EXT2_FILE_WRITE))
> return EXT2_ET_FILE_RO;
>
> + /* If an inode has inline data, things get complicated. */
> + if (file->inode.i_flags & EXT4_INLINE_DATA_FL) {
> + retval = ext2fs_file_write_inline_data(file, buf, nbytes,
> + written);
> + if (retval != EXT2_ET_INLINE_DATA_NO_SPACE)
> + return retval;
> + /* fall through to read data from the block */
> + retval = 0;
> + }
> +
> while (nbytes > 0) {
> retval = sync_buffer_position(file);
> if (retval)
> diff --git a/lib/ext2fs/inline_data.c b/lib/ext2fs/inline_data.c
> index 16af814..4a35209 100644
> --- a/lib/ext2fs/inline_data.c
> +++ b/lib/ext2fs/inline_data.c
> @@ -309,6 +309,7 @@ errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino)
> struct ext2_inline_data data;
> errcode_t retval;
> blk64_t blk;
> + size_t inline_size;
> char *inline_buf = 0;
> char *blk_buf = 0;
>
> @@ -327,8 +328,8 @@ errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino)
> retval = ext2fs_inline_data_ea_get(&data);
> if (retval)
> return retval;
> - retval = ext2fs_get_mem(EXT4_MIN_INLINE_DATA_SIZE + data.ea_size,
> - &inline_buf);
> + inline_size = data.ea_size + EXT4_MIN_INLINE_DATA_SIZE;
> + retval = ext2fs_get_mem(inline_size, &inline_buf);
> if (retval)
> goto errout;
>
> @@ -347,10 +348,14 @@ errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino)
> if (retval)
> goto errout;
>
> - /* Adjust the rec_len */
> - retval = ext2fs_inline_data_convert_dir(fs, ino, blk_buf, inline_buf,
> - EXT4_MIN_INLINE_DATA_SIZE +
> - data.ea_size);
> + if (LINUX_S_ISDIR(inode.i_mode)) {
> + /* Adjust the rec_len */
> + retval = ext2fs_inline_data_convert_dir(fs, ino, blk_buf,
> + inline_buf, inline_size);
> + } else {
> + /* Copy data for a regular inode */
> + memcpy(blk_buf, inline_buf, inline_size);
> + }
> if (retval)
> goto errout;
>
> @@ -389,3 +394,76 @@ errout:
> ext2fs_free_mem(&data.ea_data);
> return retval;
> }
> +
> +/*
> + * When caller uses this function to retrieve the inline data, it must
> + * allocate a buffer which has the size of inline data. The size of
> + * inline data can be know by ext2fs_inline_data_get_size().
> + */
> +errcode_t ext2fs_inline_data_get(ext2_filsys fs, ext2_ino_t ino,
> + struct ext2_inode *inode,
> + void *buf, size_t *size)
> +{
> + struct ext2_inode inode_buf;
> + struct ext2_inline_data data;
> + errcode_t retval;
> +
> + if (!inode) {
> + retval = ext2fs_read_inode(fs, ino, &inode_buf);
> + if (retval)
> + return retval;
> + inode = &inode_buf;
> + }
> +
> + data.fs = fs;
> + data.ino = ino;
> + retval = ext2fs_inline_data_ea_get(&data);
> + if (retval)
> + return retval;
> +
> + memcpy(buf, (void *)inode->i_block, EXT4_MIN_INLINE_DATA_SIZE);
> + if (data.ea_size > 0)
> + memcpy(buf + EXT4_MIN_INLINE_DATA_SIZE,
> + data.ea_data, data.ea_size);
> +
> + if (size)
> + *size = EXT4_MIN_INLINE_DATA_SIZE + data.ea_size;
> + ext2fs_free_mem(&data.ea_data);
> + return 0;
> +}
> +
> +errcode_t ext2fs_inline_data_set(ext2_filsys fs, ext2_ino_t ino,
> + struct ext2_inode *inode,
> + void *buf, size_t size)
> +{
> + struct ext2_inode inode_buf;
> + struct ext2_inline_data data;
> + unsigned int max_size = 0;
> + errcode_t retval;
> +
> + if (!inode) {
> + retval = ext2fs_read_inode(fs, ino, &inode_buf);
> + if (retval)
> + return retval;
> + inode = &inode_buf;
> + }
> +
> + /* simple case */
> + if (size <= EXT4_MIN_INLINE_DATA_SIZE) {
> + memcpy((void *)inode->i_block, buf, size);
> + return ext2fs_write_inode(fs, ino, inode);
> + }
> +
> + /*
> + * complicated case
> + */
> + memcpy((void *)inode->i_block, buf, EXT4_MIN_INLINE_DATA_SIZE);
> + retval = ext2fs_write_inode(fs, ino, inode);
> + if (retval)
> + return retval;
> + data.fs = fs;
> + data.ino = ino;
> + data.ea_size = size - EXT4_MIN_INLINE_DATA_SIZE;
> + data.ea_data = buf + EXT4_MIN_INLINE_DATA_SIZE;
> + return ext2fs_inline_data_ea_set(&data);
> +}
> --
> 1.7.9.7
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-12-04 03:11:34

by Darrick J. Wong

[permalink] [raw]
Subject: Re: [PATCH v2 15/28] debugfs: handle inline_data feature in bmap command

On Tue, Dec 03, 2013 at 08:11:42PM +0800, Zheng Liu wrote:
> From: Zheng Liu <[email protected]>
>
> No physical block mapping if an inode has inline data.
>
> Signed-off-by: Theodore Ts'o <[email protected]>
> Signed-off-by: Zheng Liu <[email protected]>
> ---
> lib/ext2fs/bmap.c | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/lib/ext2fs/bmap.c b/lib/ext2fs/bmap.c
> index 5074587..0d043bd 100644
> --- a/lib/ext2fs/bmap.c
> +++ b/lib/ext2fs/bmap.c
> @@ -273,6 +273,13 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
> block_buf = buf;
> }
>
> + /*
> + * If an inode has inline data, that means that it doesn't have
> + * any blocks and we shouldn't map any blocks for it.
> + */
> + if (inode->i_flags & EXT4_INLINE_DATA_FL)
> + goto done;

If you move this to before the ext2fs_get_array() chunk above it, you could
return straight out of the function.

This should return an error code of some kind, because otherwise the client
will think that something happened when the function returns 0, especially if
BMAP_{ALLOC,SET,UNINIT} are passed in.

--D

> +
> if (inode->i_flags & EXT4_EXTENTS_FL) {
> retval = ext2fs_extent_open2(fs, ino, inode, &handle);
> if (retval)
> --
> 1.7.9.7
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-12-04 03:18:04

by Zheng Liu

[permalink] [raw]
Subject: Re: [PATCH v2 01/28] libext2fs: support modifying arbitrary extended attributes

On Tue, Dec 03, 2013 at 11:54:57AM -0800, Darrick J. Wong wrote:
> On Tue, Dec 03, 2013 at 08:11:28PM +0800, Zheng Liu wrote:
> > From: "Darrick J. Wong" <[email protected]>
> >
> > Add functions to allow clients to get, set, and remove extended
> > attributes from any file. It also supports modifying EAs living in
> > i_file_acl.
> >
> > v2: Put the header declarations in the correct part of ext2fs.h,
> > provide a function to release an EA block from an inode, and check
> > i_extra_isize to make sure we actually have space for in-inode EAs.
> >
> > [Modified by Zheng]
> > Ext_attr feature check in ext2fs_xattrs_read/write() is removed because
> > inline_data feature can be enabled without ext_attr.
>
> Ok, so inline_data implies ext_attr.

No. I try this command to create a ext4 filesystem with inline_data:

% sudo mke2fs -t ext4 -O inline_data,^ext_attr ${DEV}

Then I mount it and create/delete/modify some files. Everything is
fine. So that means that we can use inline_data even without ext_attr.
That is why I modify the sanity check in mke2fs and tune2fs. Now we
only check the inode size to ensure that we have enough space to store a
xattr entry in inode extended attribute space. I will explain why we
must ensure the inode has a 'system.data' xattr entry.

> I think we should change the test to
> check for either feature flag, because with no test, we could accidentally
> read/write EAs on a FS with ^ext_attr,^inline_data. Or maybe provide a helper
> predicate to hide those details.

Agree. I will add a sanity check for inline_data feature.

>
> Also, I'd ask you to send broken out changes to my patches... but I haven't
> posted my monster patchset recently. :)

No problem. I will split this patch and send them out.

Thanks,
- Zheng

>
> (I think I might work up the courage to patchbomb the mailing list tonight.)
>
> --D
> >
> > Signed-off-by: Darrick J. Wong <[email protected]>
> > Signed-off-by: Zheng Liu <[email protected]>
> > ---
> > lib/ext2fs/ext2_err.et.in | 18 ++
> > lib/ext2fs/ext2fs.h | 28 ++
> > lib/ext2fs/ext_attr.c | 754 +++++++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 800 insertions(+)
> >
> > diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
> > index 9cc1bd1..b819a90 100644
> > --- a/lib/ext2fs/ext2_err.et.in
> > +++ b/lib/ext2fs/ext2_err.et.in
> > @@ -482,4 +482,22 @@ ec EXT2_ET_BLOCK_BITMAP_CSUM_INVALID,
> > ec EXT2_ET_INLINE_DATA_CANT_ITERATE,
> > "Cannot block iterate on an inode containing inline data"
> >
> > +ec EXT2_ET_EA_BAD_NAME_LEN,
> > + "Extended attribute has an invalid name length"
> > +
> > +ec EXT2_ET_EA_BAD_VALUE_SIZE,
> > + "Extended attribute has an invalid value length"
> > +
> > +ec EXT2_ET_BAD_EA_HASH,
> > + "Extended attribute has an incorrect hash"
> > +
> > +ec EXT2_ET_BAD_EA_HEADER,
> > + "Extended attribute block has a bad header"
> > +
> > +ec EXT2_ET_EA_KEY_NOT_FOUND,
> > + "Extended attribute key not found"
> > +
> > +ec EXT2_ET_EA_NO_SPACE,
> > + "Insufficient space to store extended attribute data"
> > +
> > end
> > diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
> > index 67876ad..30bd4cf 100644
> > --- a/lib/ext2fs/ext2fs.h
> > +++ b/lib/ext2fs/ext2fs.h
> > @@ -637,6 +637,13 @@ typedef struct stat ext2fs_struct_stat;
> > #define EXT2_FLAG_FLUSH_NO_SYNC 1
> >
> > /*
> > + * Modify and iterate extended attributes
> > + */
> > +struct ext2_xattr_handle;
> > +#define XATTR_ABORT 1
> > +#define XATTR_CHANGED 2
> > +
> > +/*
> > * function prototypes
> > */
> > static inline int ext2fs_has_group_desc_csum(ext2_filsys fs)
> > @@ -1142,6 +1149,27 @@ extern errcode_t ext2fs_adjust_ea_refcount3(ext2_filsys fs, blk64_t blk,
> > char *block_buf,
> > int adjust, __u32 *newcount,
> > ext2_ino_t inum);
> > +errcode_t ext2fs_xattrs_expand(struct ext2_xattr_handle *h,
> > + unsigned int expandby);
> > +errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle);
> > +errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle);
> > +errcode_t ext2fs_xattrs_iterate(struct ext2_xattr_handle *h,
> > + int (*func)(char *name, char *value,
> > + void *data),
> > + void *data);
> > +errcode_t ext2fs_xattr_get(struct ext2_xattr_handle *h, const char *key,
> > + void **value, unsigned int *value_len);
> > +errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *handle,
> > + const char *key,
> > + const void *value,
> > + unsigned int value_len);
> > +errcode_t ext2fs_xattr_remove(struct ext2_xattr_handle *handle,
> > + const char *key);
> > +errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
> > + struct ext2_xattr_handle **handle);
> > +errcode_t ext2fs_xattrs_close(struct ext2_xattr_handle **handle);
> > +errcode_t ext2fs_free_ext_attr(ext2_filsys fs, ext2_ino_t ino,
> > + struct ext2_inode_large *inode);
> >
> > /* extent.c */
> > extern errcode_t ext2fs_extent_header_verify(void *ptr, int size);
> > diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
> > index 9649a14..4d40149 100644
> > --- a/lib/ext2fs/ext_attr.c
> > +++ b/lib/ext2fs/ext_attr.c
> > @@ -186,3 +186,757 @@ errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
> > return ext2fs_adjust_ea_refcount2(fs, blk, block_buf, adjust,
> > newcount);
> > }
> > +
> > +/* Manipulate the contents of extended attribute regions */
> > +struct ext2_xattr {
> > + char *name;
> > + void *value;
> > + unsigned int value_len;
> > +};
> > +
> > +struct ext2_xattr_handle {
> > + ext2_filsys fs;
> > + struct ext2_xattr *attrs;
> > + unsigned int length;
> > + ext2_ino_t ino;
> > + int dirty;
> > +};
> > +
> > +errcode_t ext2fs_xattrs_expand(struct ext2_xattr_handle *h,
> > + unsigned int expandby)
> > +{
> > + struct ext2_xattr *new_attrs;
> > + errcode_t err;
> > +
> > + err = ext2fs_get_arrayzero(h->length + expandby,
> > + sizeof(struct ext2_xattr), &new_attrs);
> > + if (err)
> > + return err;
> > +
> > + memcpy(new_attrs, h->attrs, h->length * sizeof(struct ext2_xattr));
> > + ext2fs_free_mem(&h->attrs);
> > + h->length += expandby;
> > + h->attrs = new_attrs;
> > +
> > + return 0;
> > +}
> > +
> > +struct ea_name_index {
> > + int index;
> > + const char *name;
> > +};
> > +
> > +static struct ea_name_index ea_names[] = {
> > + {1, "user."},
> > + {2, "system.posix_acl_access"},
> > + {3, "system.posix_acl_default"},
> > + {4, "trusted."},
> > + {6, "security."},
> > + {7, "system."},
> > + {0, NULL},
> > +};
> > +
> > +static const char *find_ea_prefix(int index)
> > +{
> > + struct ea_name_index *e;
> > +
> > + for (e = ea_names; e->name; e++)
> > + if (e->index == index)
> > + return e->name;
> > +
> > + return NULL;
> > +}
> > +
> > +static int find_ea_index(const char *fullname, char **name, int *index)
> > +{
> > + struct ea_name_index *e;
> > +
> > + for (e = ea_names; e->name; e++) {
> > + if (memcmp(fullname, e->name, strlen(e->name)) == 0) {
> > + *name = (char *)fullname + strlen(e->name);
> > + *index = e->index;
> > + return 1;
> > + }
> > + }
> > + return 0;
> > +}
> > +
> > +errcode_t ext2fs_free_ext_attr(ext2_filsys fs, ext2_ino_t ino,
> > + struct ext2_inode_large *inode)
> > +{
> > + struct ext2_ext_attr_header *header;
> > + void *block_buf = NULL;
> > + dgrp_t grp;
> > + blk64_t blk, goal;
> > + errcode_t err;
> > + struct ext2_inode_large i;
> > +
> > + /* Read inode? */
> > + if (inode == NULL) {
> > + err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&i,
> > + sizeof(struct ext2_inode_large));
> > + if (err)
> > + return err;
> > + inode = &i;
> > + }
> > +
> > + /* Do we already have an EA block? */
> > + blk = ext2fs_file_acl_block(fs, (struct ext2_inode *)inode);
> > + if (blk == 0)
> > + return 0;
> > +
> > + /* Find block, zero it, write back */
> > + if ((blk < fs->super->s_first_data_block) ||
> > + (blk >= ext2fs_blocks_count(fs->super))) {
> > + err = EXT2_ET_BAD_EA_BLOCK_NUM;
> > + goto out;
> > + }
> > +
> > + err = ext2fs_get_mem(fs->blocksize, &block_buf);
> > + if (err)
> > + goto out;
> > +
> > + err = ext2fs_read_ext_attr3(fs, blk, block_buf, ino);
> > + if (err)
> > + goto out2;
> > +
> > + header = (struct ext2_ext_attr_header *) block_buf;
> > + if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
> > + err = EXT2_ET_BAD_EA_HEADER;
> > + goto out2;
> > + }
> > +
> > + header->h_refcount--;
> > + err = ext2fs_write_ext_attr3(fs, blk, block_buf, ino);
> > + if (err)
> > + goto out2;
> > +
> > + /* Erase link to block */
> > + ext2fs_file_acl_block_set(fs, (struct ext2_inode *)inode, 0);
> > + if (header->h_refcount == 0)
> > + ext2fs_block_alloc_stats2(fs, blk, -1);
> > +
> > + /* Write inode? */
> > + if (inode == &i) {
> > + err = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&i,
> > + sizeof(struct ext2_inode_large));
> > + if (err)
> > + goto out2;
> > + }
> > +
> > +out2:
> > + ext2fs_free_mem(&block_buf);
> > +out:
> > + return err;
> > +}
> > +
> > +static errcode_t prep_ea_block_for_write(ext2_filsys fs, ext2_ino_t ino,
> > + struct ext2_inode_large *inode)
> > +{
> > + struct ext2_ext_attr_header *header;
> > + void *block_buf = NULL;
> > + dgrp_t grp;
> > + blk64_t blk, goal;
> > + errcode_t err;
> > +
> > + /* Do we already have an EA block? */
> > + blk = ext2fs_file_acl_block(fs, (struct ext2_inode *)inode);
> > + if (blk != 0) {
> > + if ((blk < fs->super->s_first_data_block) ||
> > + (blk >= ext2fs_blocks_count(fs->super))) {
> > + err = EXT2_ET_BAD_EA_BLOCK_NUM;
> > + goto out;
> > + }
> > +
> > + err = ext2fs_get_mem(fs->blocksize, &block_buf);
> > + if (err)
> > + goto out;
> > +
> > + err = ext2fs_read_ext_attr3(fs, blk, block_buf, ino);
> > + if (err)
> > + goto out2;
> > +
> > + header = (struct ext2_ext_attr_header *) block_buf;
> > + if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
> > + err = EXT2_ET_BAD_EA_HEADER;
> > + goto out2;
> > + }
> > +
> > + /* Single-user block. We're done here. */
> > + if (header->h_refcount == 1)
> > + return 0;
> > +
> > + /* We need to CoW the block. */
> > + header->h_refcount--;
> > + err = ext2fs_write_ext_attr3(fs, blk, block_buf, ino);
> > + if (err)
> > + goto out2;
> > + } else {
> > + /* No block, we must increment i_blocks */
> > + err = ext2fs_iblk_add_blocks(fs, (struct ext2_inode *)inode,
> > + 1);
> > + if (err)
> > + goto out;
> > + }
> > +
> > + /* Allocate a block */
> > + grp = ext2fs_group_of_ino(fs, ino);
> > + goal = ext2fs_inode_table_loc(fs, grp);
> > + err = ext2fs_alloc_block2(fs, goal, NULL, &blk);
> > + if (err)
> > + return err;
> > + ext2fs_file_acl_block_set(fs, (struct ext2_inode *)inode, blk);
> > +out2:
> > + ext2fs_free_mem(&block_buf);
> > +out:
> > + return err;
> > +}
> > +
> > +
> > +static errcode_t write_xattrs_to_buffer(struct ext2_xattr_handle *handle,
> > + struct ext2_xattr **pos,
> > + void *entries_start,
> > + unsigned int storage_size,
> > + unsigned int value_offset_correction)
> > +{
> > + struct ext2_xattr *x = *pos;
> > + struct ext2_ext_attr_entry *e = entries_start;
> > + void *end = entries_start + storage_size;
> > + char *shortname;
> > + unsigned int entry_size, value_size;
> > + int idx, ret;
> > +
> > + /* For all remaining x... */
> > + for (; x < handle->attrs + handle->length; x++) {
> > + if (!x->name)
> > + continue;
> > +
> > + /* Calculate index and shortname position */
> > + shortname = x->name;
> > + ret = find_ea_index(x->name, &shortname, &idx);
> > +
> > + /* Calculate entry and value size */
> > + entry_size = (sizeof(*e) + strlen(shortname) +
> > + EXT2_EXT_ATTR_PAD - 1) &
> > + ~(EXT2_EXT_ATTR_PAD - 1);
> > + value_size = ((x->value_len + EXT2_EXT_ATTR_PAD - 1) /
> > + EXT2_EXT_ATTR_PAD) * EXT2_EXT_ATTR_PAD;
> > +
> > + /*
> > + * Would entry collide with value?
> > + * Note that we must leave sufficient room for a (u32)0 to
> > + * mark the end of the entries.
> > + */
> > + if ((void *)e + entry_size + sizeof(__u32) > end - value_size)
> > + break;
> > +
> > + /* Fill out e appropriately */
> > + e->e_name_len = strlen(shortname);
> > + e->e_name_index = (ret ? idx : 0);
> > + e->e_value_offs = end - value_size - (void *)entries_start +
> > + value_offset_correction;
> > + e->e_value_block = 0;
> > + e->e_value_size = x->value_len;
> > +
> > + /* Store name and value */
> > + end -= value_size;
> > + memcpy((void *)e + sizeof(*e), shortname, e->e_name_len);
> > + memcpy(end, x->value, e->e_value_size);
> > +
> > + e->e_hash = ext2fs_ext_attr_hash_entry(e, end);
> > +
> > + e = EXT2_EXT_ATTR_NEXT(e);
> > + *(__u32 *)e = 0;
> > + }
> > + *pos = x;
> > +
> > + return 0;
> > +}
> > +
> > +errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle)
> > +{
> > + struct ext2_xattr *x;
> > + struct ext2_inode_large *inode;
> > + void *start, *block_buf = NULL;
> > + struct ext2_ext_attr_header *header;
> > + __u32 ea_inode_magic;
> > + blk64_t blk;
> > + unsigned int storage_size;
> > + unsigned int i, written;
> > + errcode_t err;
> > +
> > + i = EXT2_INODE_SIZE(handle->fs->super);
> > + if (i < sizeof(*inode))
> > + i = sizeof(*inode);
> > + err = ext2fs_get_memzero(i, &inode);
> > + if (err)
> > + return err;
> > +
> > + err = ext2fs_read_inode_full(handle->fs, handle->ino,
> > + (struct ext2_inode *)inode,
> > + EXT2_INODE_SIZE(handle->fs->super));
> > + if (err)
> > + goto out;
> > +
> > + x = handle->attrs;
> > + /* Does the inode have size for EA? */
> > + if (EXT2_INODE_SIZE(handle->fs->super) <= EXT2_GOOD_OLD_INODE_SIZE +
> > + inode->i_extra_isize +
> > + sizeof(__u32))
> > + goto write_ea_block;
> > +
> > + /* Write the inode EA */
> > + ea_inode_magic = EXT2_EXT_ATTR_MAGIC;
> > + memcpy(((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
> > + inode->i_extra_isize, &ea_inode_magic, sizeof(__u32));
> > + storage_size = EXT2_INODE_SIZE(handle->fs->super) -
> > + EXT2_GOOD_OLD_INODE_SIZE - inode->i_extra_isize -
> > + sizeof(__u32);
> > + start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
> > + inode->i_extra_isize + sizeof(__u32);
> > +
> > + err = write_xattrs_to_buffer(handle, &x, start, storage_size, 0);
> > + if (err)
> > + goto out;
> > +
> > + /* Are we done? */
> > + if (x == handle->attrs + handle->length)
> > + goto skip_ea_block;
> > +
> > +write_ea_block:
> > + /* Write the EA block */
> > + err = ext2fs_get_mem(handle->fs->blocksize, &block_buf);
> > + if (err)
> > + goto out;
> > +
> > + storage_size = handle->fs->blocksize -
> > + sizeof(struct ext2_ext_attr_header);
> > + start = block_buf + sizeof(struct ext2_ext_attr_header);
> > +
> > + err = write_xattrs_to_buffer(handle, &x, start, storage_size,
> > + (void *)start - block_buf);
> > + if (err)
> > + goto out2;
> > +
> > + if (x < handle->attrs + handle->length) {
> > + err = EXT2_ET_EA_NO_SPACE;
> > + goto out2;
> > + }
> > +
> > + if (block_buf) {
> > + /* Write a header on the EA block */
> > + header = block_buf;
> > + header->h_magic = EXT2_EXT_ATTR_MAGIC;
> > + header->h_refcount = 1;
> > + header->h_blocks = 1;
> > +
> > + /* Get a new block for writing */
> > + err = prep_ea_block_for_write(handle->fs, handle->ino, inode);
> > + if (err)
> > + goto out2;
> > +
> > + /* Finally, write the new EA block */
> > + blk = ext2fs_file_acl_block(handle->fs,
> > + (struct ext2_inode *)inode);
> > + err = ext2fs_write_ext_attr3(handle->fs, blk, block_buf,
> > + handle->ino);
> > + if (err)
> > + goto out2;
> > + }
> > +
> > +skip_ea_block:
> > + blk = ext2fs_file_acl_block(handle->fs, (struct ext2_inode *)inode);
> > + if (!block_buf && blk) {
> > + /* xattrs shrunk, free the block */
> > + ext2fs_file_acl_block_set(handle->fs,
> > + (struct ext2_inode *)inode, 0);
> > + err = ext2fs_iblk_sub_blocks(handle->fs,
> > + (struct ext2_inode *)inode, 1);
> > + if (err)
> > + goto out;
> > + ext2fs_block_alloc_stats2(handle->fs, blk, -1);
> > + }
> > +
> > + /* Write the inode */
> > + err = ext2fs_write_inode_full(handle->fs, handle->ino,
> > + (struct ext2_inode *)inode,
> > + EXT2_INODE_SIZE(handle->fs->super));
> > + if (err)
> > + goto out2;
> > +
> > +out2:
> > + ext2fs_free_mem(&block_buf);
> > +out:
> > + ext2fs_free_mem(&inode);
> > + handle->dirty = 0;
> > + return err;
> > +}
> > +
> > +static errcode_t read_xattrs_from_buffer(struct ext2_xattr_handle *handle,
> > + struct ext2_ext_attr_entry *entries,
> > + unsigned int storage_size,
> > + void *value_start)
> > +{
> > + struct ext2_xattr *x;
> > + struct ext2_ext_attr_entry *entry;
> > + const char *prefix;
> > + void *ptr;
> > + unsigned int remain, prefix_len;
> > + errcode_t err;
> > +
> > + x = handle->attrs;
> > + while (x->name)
> > + x++;
> > +
> > + entry = entries;
> > + while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
> > + __u32 hash;
> > +
> > + /* header eats this space */
> > + remain -= sizeof(struct ext2_ext_attr_entry);
> > +
> > + /* is attribute name valid? */
> > + if (EXT2_EXT_ATTR_SIZE(entry->e_name_len) > remain)
> > + return EXT2_ET_EA_BAD_NAME_LEN;
> > +
> > + /* attribute len eats this space */
> > + remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
> > +
> > + /* check value size */
> > + if (entry->e_value_size > remain)
> > + return EXT2_ET_EA_BAD_VALUE_SIZE;
> > +
> > + /* e_value_block must be 0 in inode's ea */
> > + if (entry->e_value_block != 0)
> > + return EXT2_ET_BAD_EA_BLOCK_NUM;
> > +
> > + hash = ext2fs_ext_attr_hash_entry(entry, value_start +
> > + entry->e_value_offs);
> > +
> > + /* e_hash may be 0 in older inode's ea */
> > + if (entry->e_hash != 0 && entry->e_hash != hash)
> > + return EXT2_ET_BAD_EA_HASH;
> > +
> > + remain -= entry->e_value_size;
> > +
> > + /* Allocate space for more attrs? */
> > + if (x == handle->attrs + handle->length) {
> > + err = ext2fs_xattrs_expand(handle, 4);
> > + if (err)
> > + return err;
> > + x = handle->attrs + handle->length - 4;
> > + }
> > +
> > + /* Extract name/value */
> > + prefix = find_ea_prefix(entry->e_name_index);
> > + prefix_len = (prefix ? strlen(prefix) : 0);
> > + err = ext2fs_get_memzero(entry->e_name_len + prefix_len + 1,
> > + &x->name);
> > + if (err)
> > + return err;
> > + if (prefix)
> > + memcpy(x->name, prefix, prefix_len);
> > + if (entry->e_name_len)
> > + memcpy(x->name + prefix_len,
> > + (void *)entry + sizeof(*entry),
> > + entry->e_name_len);
> > +
> > + err = ext2fs_get_mem(entry->e_value_size, &x->value);
> > + if (err)
> > + return err;
> > + x->value_len = entry->e_value_size;
> > + memcpy(x->value, value_start + entry->e_value_offs,
> > + entry->e_value_size);
> > + x++;
> > + entry = EXT2_EXT_ATTR_NEXT(entry);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle)
> > +{
> > + struct ext2_xattr *attrs = NULL, *x;
> > + unsigned int attrs_len;
> > + struct ext2_inode_large *inode;
> > + struct ext2_ext_attr_header *header;
> > + __u32 ea_inode_magic;
> > + unsigned int storage_size;
> > + void *start, *block_buf = NULL;
> > + blk64_t blk;
> > + int i;
> > + errcode_t err;
> > +
> > + i = EXT2_INODE_SIZE(handle->fs->super);
> > + if (i < sizeof(*inode))
> > + i = sizeof(*inode);
> > + err = ext2fs_get_memzero(i, &inode);
> > + if (err)
> > + return err;
> > +
> > + err = ext2fs_read_inode_full(handle->fs, handle->ino,
> > + (struct ext2_inode *)inode,
> > + EXT2_INODE_SIZE(handle->fs->super));
> > + if (err)
> > + goto out;
> > +
> > + /* Does the inode have size for EA? */
> > + if (EXT2_INODE_SIZE(handle->fs->super) <= EXT2_GOOD_OLD_INODE_SIZE +
> > + inode->i_extra_isize +
> > + sizeof(__u32))
> > + goto read_ea_block;
> > +
> > + /* Look for EA in the inode */
> > + memcpy(&ea_inode_magic, ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
> > + inode->i_extra_isize, sizeof(__u32));
> > + if (ea_inode_magic == EXT2_EXT_ATTR_MAGIC) {
> > + storage_size = EXT2_INODE_SIZE(handle->fs->super) -
> > + EXT2_GOOD_OLD_INODE_SIZE - inode->i_extra_isize -
> > + sizeof(__u32);
> > + start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
> > + inode->i_extra_isize + sizeof(__u32);
> > +
> > + err = read_xattrs_from_buffer(handle, start, storage_size,
> > + start);
> > + if (err)
> > + goto out;
> > + }
> > +
> > +read_ea_block:
> > + /* Look for EA in a separate EA block */
> > + blk = ext2fs_file_acl_block(handle->fs, (struct ext2_inode *)inode);
> > + if (blk != 0) {
> > + if ((blk < handle->fs->super->s_first_data_block) ||
> > + (blk >= ext2fs_blocks_count(handle->fs->super))) {
> > + err = EXT2_ET_BAD_EA_BLOCK_NUM;
> > + goto out;
> > + }
> > +
> > + err = ext2fs_get_mem(handle->fs->blocksize, &block_buf);
> > + if (err)
> > + goto out;
> > +
> > + err = ext2fs_read_ext_attr3(handle->fs, blk, block_buf,
> > + handle->ino);
> > + if (err)
> > + goto out3;
> > +
> > + header = (struct ext2_ext_attr_header *) block_buf;
> > + if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
> > + err = EXT2_ET_BAD_EA_HEADER;
> > + goto out3;
> > + }
> > +
> > + if (header->h_blocks != 1) {
> > + err = EXT2_ET_BAD_EA_HEADER;
> > + goto out3;
> > + }
> > +
> > + /* Read EAs */
> > + storage_size = handle->fs->blocksize -
> > + sizeof(struct ext2_ext_attr_header);
> > + start = block_buf + sizeof(struct ext2_ext_attr_header);
> > + err = read_xattrs_from_buffer(handle, start, storage_size,
> > + block_buf);
> > + if (err)
> > + goto out3;
> > +
> > + ext2fs_free_mem(&block_buf);
> > + }
> > +
> > + ext2fs_free_mem(&block_buf);
> > + ext2fs_free_mem(&inode);
> > + return 0;
> > +
> > +out3:
> > + ext2fs_free_mem(&block_buf);
> > +out:
> > + ext2fs_free_mem(&inode);
> > + return err;
> > +}
> > +
> > +#define XATTR_ABORT 1
> > +#define XATTR_CHANGED 2
> > +errcode_t ext2fs_xattrs_iterate(struct ext2_xattr_handle *h,
> > + int (*func)(char *name, char *value,
> > + void *data),
> > + void *data)
> > +{
> > + struct ext2_xattr *x;
> > + errcode_t err;
> > + int ret;
> > +
> > + for (x = h->attrs; x < h->attrs + h->length; x++) {
> > + if (!x->name)
> > + continue;
> > +
> > + ret = func(x->name, x->value, data);
> > + if (ret & XATTR_CHANGED)
> > + h->dirty = 1;
> > + if (ret & XATTR_ABORT)
> > + return 0;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +errcode_t ext2fs_xattr_get(struct ext2_xattr_handle *h, const char *key,
> > + void **value, unsigned int *value_len)
> > +{
> > + struct ext2_xattr *x;
> > + void *val;
> > + errcode_t err;
> > +
> > + for (x = h->attrs; x < h->attrs + h->length; x++) {
> > + if (!x->name)
> > + continue;
> > +
> > + if (strcmp(x->name, key) == 0) {
> > + err = ext2fs_get_mem(x->value_len, &val);
> > + if (err)
> > + return err;
> > + memcpy(val, x->value, x->value_len);
> > + *value = val;
> > + *value_len = x->value_len;
> > + return 0;
> > + }
> > + }
> > +
> > + return EXT2_ET_EA_KEY_NOT_FOUND;
> > +}
> > +
> > +errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *handle,
> > + const char *key,
> > + const void *value,
> > + unsigned int value_len)
> > +{
> > + struct ext2_xattr *x, *last_empty;
> > + char *new_value;
> > + errcode_t err;
> > +
> > + last_empty = NULL;
> > + for (x = handle->attrs; x < handle->attrs + handle->length; x++) {
> > + if (!x->name) {
> > + last_empty = x;
> > + continue;
> > + }
> > +
> > + /* Replace xattr */
> > + if (strcmp(x->name, key) == 0) {
> > + err = ext2fs_get_mem(value_len, &new_value);
> > + if (err)
> > + return err;
> > + memcpy(new_value, value, value_len);
> > + ext2fs_free_mem(&x->value);
> > + x->value = new_value;
> > + x->value_len = value_len;
> > + handle->dirty = 1;
> > + return 0;
> > + }
> > + }
> > +
> > + /* Add attr to empty slot */
> > + if (last_empty) {
> > + err = ext2fs_get_mem(strlen(key) + 1, &last_empty->name);
> > + if (err)
> > + return err;
> > + strcpy(last_empty->name, key);
> > +
> > + err = ext2fs_get_mem(value_len, &last_empty->value);
> > + if (err)
> > + return err;
> > + memcpy(last_empty->value, value, value_len);
> > + last_empty->value_len = value_len;
> > + handle->dirty = 1;
> > + return 0;
> > + }
> > +
> > + /* Expand array, append slot */
> > + err = ext2fs_xattrs_expand(handle, 4);
> > + if (err)
> > + return err;
> > +
> > + x = handle->attrs + handle->length - 4;
> > + err = ext2fs_get_mem(strlen(key) + 1, &x->name);
> > + if (err)
> > + return err;
> > + strcpy(x->name, key);
> > +
> > + err = ext2fs_get_mem(value_len, &x->value);
> > + if (err)
> > + return err;
> > + memcpy(x->value, value, value_len);
> > + x->value_len = value_len;
> > + handle->dirty = 1;
> > + return 0;
> > +}
> > +
> > +errcode_t ext2fs_xattr_remove(struct ext2_xattr_handle *handle,
> > + const char *key)
> > +{
> > + struct ext2_xattr *x;
> > + errcode_t err;
> > +
> > + for (x = handle->attrs; x < handle->attrs + handle->length; x++) {
> > + if (!x->name)
> > + continue;
> > +
> > + if (strcmp(x->name, key) == 0) {
> > + ext2fs_free_mem(&x->name);
> > + ext2fs_free_mem(&x->value);
> > + x->value_len = 0;
> > + handle->dirty = 1;
> > + return 0;
> > + }
> > + }
> > +
> > + return EXT2_ET_EA_KEY_NOT_FOUND;
> > +}
> > +
> > +errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
> > + struct ext2_xattr_handle **handle)
> > +{
> > + struct ext2_xattr_handle *h;
> > + errcode_t err;
> > +
> > + err = ext2fs_get_memzero(sizeof(*h), &h);
> > + if (err)
> > + return err;
> > +
> > + h->length = 4;
> > + err = ext2fs_get_arrayzero(h->length, sizeof(struct ext2_xattr),
> > + &h->attrs);
> > + if (err) {
> > + ext2fs_free_mem(&h);
> > + return err;
> > + }
> > + h->ino = ino;
> > + h->fs = fs;
> > + *handle = h;
> > + return 0;
> > +}
> > +
> > +errcode_t ext2fs_xattrs_close(struct ext2_xattr_handle **handle)
> > +{
> > + unsigned int i;
> > + struct ext2_xattr_handle *h = *handle;
> > + struct ext2_xattr *a = h->attrs;
> > + errcode_t err;
> > +
> > + if (h->dirty) {
> > + err = ext2fs_xattrs_write(h);
> > + if (err)
> > + return err;
> > + }
> > +
> > + for (i = 0; i < h->length; i++) {
> > + if (a[i].name)
> > + ext2fs_free_mem(&a[i].name);
> > + if (a[i].value)
> > + ext2fs_free_mem(&a[i].value);
> > + }
> > +
> > + ext2fs_free_mem(&h->attrs);
> > + ext2fs_free_mem(handle);
> > + return 0;
> > +}
> > --
> > 1.7.9.7
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> > the body of a message to [email protected]
> > More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-12-04 03:21:30

by Darrick J. Wong

[permalink] [raw]
Subject: Re: [PATCH v2 01/28] libext2fs: support modifying arbitrary extended attributes

On Wed, Dec 04, 2013 at 11:20:59AM +0800, Zheng Liu wrote:
> On Tue, Dec 03, 2013 at 11:54:57AM -0800, Darrick J. Wong wrote:
> > On Tue, Dec 03, 2013 at 08:11:28PM +0800, Zheng Liu wrote:
> > > From: "Darrick J. Wong" <[email protected]>
> > >
> > > Add functions to allow clients to get, set, and remove extended
> > > attributes from any file. It also supports modifying EAs living in
> > > i_file_acl.
> > >
> > > v2: Put the header declarations in the correct part of ext2fs.h,
> > > provide a function to release an EA block from an inode, and check
> > > i_extra_isize to make sure we actually have space for in-inode EAs.
> > >
> > > [Modified by Zheng]
> > > Ext_attr feature check in ext2fs_xattrs_read/write() is removed because
> > > inline_data feature can be enabled without ext_attr.
> >
> > Ok, so inline_data implies ext_attr.
>
> No. I try this command to create a ext4 filesystem with inline_data:
>
> % sudo mke2fs -t ext4 -O inline_data,^ext_attr ${DEV}
>
> Then I mount it and create/delete/modify some files. Everything is
> fine. So that means that we can use inline_data even without ext_attr.
> That is why I modify the sanity check in mke2fs and tune2fs. Now we
> only check the inode size to ensure that we have enough space to store a
> xattr entry in inode extended attribute space. I will explain why we
> must ensure the inode has a 'system.data' xattr entry.

<nod> My mistake. With inlinedata,^extattr we (userland) shouldn't be able to
store attrs, even if inline_data is using those functions.
>
> > I think we should change the test to
> > check for either feature flag, because with no test, we could accidentally
> > read/write EAs on a FS with ^ext_attr,^inline_data. Or maybe provide a helper
> > predicate to hide those details.
>
> Agree. I will add a sanity check for inline_data feature.

Already did. I'll send it out in my ... growing nightmare of a patch stack.
>
> >
> > Also, I'd ask you to send broken out changes to my patches... but I haven't
> > posted my monster patchset recently. :)
>
> No problem. I will split this patch and send them out.

Ok, I look forward to seeing that.

--D
>
> Thanks,
> - Zheng
>
> >
> > (I think I might work up the courage to patchbomb the mailing list tonight.)
> >
> > --D
> > >
> > > Signed-off-by: Darrick J. Wong <[email protected]>
> > > Signed-off-by: Zheng Liu <[email protected]>
> > > ---
> > > lib/ext2fs/ext2_err.et.in | 18 ++
> > > lib/ext2fs/ext2fs.h | 28 ++
> > > lib/ext2fs/ext_attr.c | 754 +++++++++++++++++++++++++++++++++++++++++++++
> > > 3 files changed, 800 insertions(+)
> > >
> > > diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
> > > index 9cc1bd1..b819a90 100644
> > > --- a/lib/ext2fs/ext2_err.et.in
> > > +++ b/lib/ext2fs/ext2_err.et.in
> > > @@ -482,4 +482,22 @@ ec EXT2_ET_BLOCK_BITMAP_CSUM_INVALID,
> > > ec EXT2_ET_INLINE_DATA_CANT_ITERATE,
> > > "Cannot block iterate on an inode containing inline data"
> > >
> > > +ec EXT2_ET_EA_BAD_NAME_LEN,
> > > + "Extended attribute has an invalid name length"
> > > +
> > > +ec EXT2_ET_EA_BAD_VALUE_SIZE,
> > > + "Extended attribute has an invalid value length"
> > > +
> > > +ec EXT2_ET_BAD_EA_HASH,
> > > + "Extended attribute has an incorrect hash"
> > > +
> > > +ec EXT2_ET_BAD_EA_HEADER,
> > > + "Extended attribute block has a bad header"
> > > +
> > > +ec EXT2_ET_EA_KEY_NOT_FOUND,
> > > + "Extended attribute key not found"
> > > +
> > > +ec EXT2_ET_EA_NO_SPACE,
> > > + "Insufficient space to store extended attribute data"
> > > +
> > > end
> > > diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
> > > index 67876ad..30bd4cf 100644
> > > --- a/lib/ext2fs/ext2fs.h
> > > +++ b/lib/ext2fs/ext2fs.h
> > > @@ -637,6 +637,13 @@ typedef struct stat ext2fs_struct_stat;
> > > #define EXT2_FLAG_FLUSH_NO_SYNC 1
> > >
> > > /*
> > > + * Modify and iterate extended attributes
> > > + */
> > > +struct ext2_xattr_handle;
> > > +#define XATTR_ABORT 1
> > > +#define XATTR_CHANGED 2
> > > +
> > > +/*
> > > * function prototypes
> > > */
> > > static inline int ext2fs_has_group_desc_csum(ext2_filsys fs)
> > > @@ -1142,6 +1149,27 @@ extern errcode_t ext2fs_adjust_ea_refcount3(ext2_filsys fs, blk64_t blk,
> > > char *block_buf,
> > > int adjust, __u32 *newcount,
> > > ext2_ino_t inum);
> > > +errcode_t ext2fs_xattrs_expand(struct ext2_xattr_handle *h,
> > > + unsigned int expandby);
> > > +errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle);
> > > +errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle);
> > > +errcode_t ext2fs_xattrs_iterate(struct ext2_xattr_handle *h,
> > > + int (*func)(char *name, char *value,
> > > + void *data),
> > > + void *data);
> > > +errcode_t ext2fs_xattr_get(struct ext2_xattr_handle *h, const char *key,
> > > + void **value, unsigned int *value_len);
> > > +errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *handle,
> > > + const char *key,
> > > + const void *value,
> > > + unsigned int value_len);
> > > +errcode_t ext2fs_xattr_remove(struct ext2_xattr_handle *handle,
> > > + const char *key);
> > > +errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
> > > + struct ext2_xattr_handle **handle);
> > > +errcode_t ext2fs_xattrs_close(struct ext2_xattr_handle **handle);
> > > +errcode_t ext2fs_free_ext_attr(ext2_filsys fs, ext2_ino_t ino,
> > > + struct ext2_inode_large *inode);
> > >
> > > /* extent.c */
> > > extern errcode_t ext2fs_extent_header_verify(void *ptr, int size);
> > > diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
> > > index 9649a14..4d40149 100644
> > > --- a/lib/ext2fs/ext_attr.c
> > > +++ b/lib/ext2fs/ext_attr.c
> > > @@ -186,3 +186,757 @@ errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
> > > return ext2fs_adjust_ea_refcount2(fs, blk, block_buf, adjust,
> > > newcount);
> > > }
> > > +
> > > +/* Manipulate the contents of extended attribute regions */
> > > +struct ext2_xattr {
> > > + char *name;
> > > + void *value;
> > > + unsigned int value_len;
> > > +};
> > > +
> > > +struct ext2_xattr_handle {
> > > + ext2_filsys fs;
> > > + struct ext2_xattr *attrs;
> > > + unsigned int length;
> > > + ext2_ino_t ino;
> > > + int dirty;
> > > +};
> > > +
> > > +errcode_t ext2fs_xattrs_expand(struct ext2_xattr_handle *h,
> > > + unsigned int expandby)
> > > +{
> > > + struct ext2_xattr *new_attrs;
> > > + errcode_t err;
> > > +
> > > + err = ext2fs_get_arrayzero(h->length + expandby,
> > > + sizeof(struct ext2_xattr), &new_attrs);
> > > + if (err)
> > > + return err;
> > > +
> > > + memcpy(new_attrs, h->attrs, h->length * sizeof(struct ext2_xattr));
> > > + ext2fs_free_mem(&h->attrs);
> > > + h->length += expandby;
> > > + h->attrs = new_attrs;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +struct ea_name_index {
> > > + int index;
> > > + const char *name;
> > > +};
> > > +
> > > +static struct ea_name_index ea_names[] = {
> > > + {1, "user."},
> > > + {2, "system.posix_acl_access"},
> > > + {3, "system.posix_acl_default"},
> > > + {4, "trusted."},
> > > + {6, "security."},
> > > + {7, "system."},
> > > + {0, NULL},
> > > +};
> > > +
> > > +static const char *find_ea_prefix(int index)
> > > +{
> > > + struct ea_name_index *e;
> > > +
> > > + for (e = ea_names; e->name; e++)
> > > + if (e->index == index)
> > > + return e->name;
> > > +
> > > + return NULL;
> > > +}
> > > +
> > > +static int find_ea_index(const char *fullname, char **name, int *index)
> > > +{
> > > + struct ea_name_index *e;
> > > +
> > > + for (e = ea_names; e->name; e++) {
> > > + if (memcmp(fullname, e->name, strlen(e->name)) == 0) {
> > > + *name = (char *)fullname + strlen(e->name);
> > > + *index = e->index;
> > > + return 1;
> > > + }
> > > + }
> > > + return 0;
> > > +}
> > > +
> > > +errcode_t ext2fs_free_ext_attr(ext2_filsys fs, ext2_ino_t ino,
> > > + struct ext2_inode_large *inode)
> > > +{
> > > + struct ext2_ext_attr_header *header;
> > > + void *block_buf = NULL;
> > > + dgrp_t grp;
> > > + blk64_t blk, goal;
> > > + errcode_t err;
> > > + struct ext2_inode_large i;
> > > +
> > > + /* Read inode? */
> > > + if (inode == NULL) {
> > > + err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&i,
> > > + sizeof(struct ext2_inode_large));
> > > + if (err)
> > > + return err;
> > > + inode = &i;
> > > + }
> > > +
> > > + /* Do we already have an EA block? */
> > > + blk = ext2fs_file_acl_block(fs, (struct ext2_inode *)inode);
> > > + if (blk == 0)
> > > + return 0;
> > > +
> > > + /* Find block, zero it, write back */
> > > + if ((blk < fs->super->s_first_data_block) ||
> > > + (blk >= ext2fs_blocks_count(fs->super))) {
> > > + err = EXT2_ET_BAD_EA_BLOCK_NUM;
> > > + goto out;
> > > + }
> > > +
> > > + err = ext2fs_get_mem(fs->blocksize, &block_buf);
> > > + if (err)
> > > + goto out;
> > > +
> > > + err = ext2fs_read_ext_attr3(fs, blk, block_buf, ino);
> > > + if (err)
> > > + goto out2;
> > > +
> > > + header = (struct ext2_ext_attr_header *) block_buf;
> > > + if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
> > > + err = EXT2_ET_BAD_EA_HEADER;
> > > + goto out2;
> > > + }
> > > +
> > > + header->h_refcount--;
> > > + err = ext2fs_write_ext_attr3(fs, blk, block_buf, ino);
> > > + if (err)
> > > + goto out2;
> > > +
> > > + /* Erase link to block */
> > > + ext2fs_file_acl_block_set(fs, (struct ext2_inode *)inode, 0);
> > > + if (header->h_refcount == 0)
> > > + ext2fs_block_alloc_stats2(fs, blk, -1);
> > > +
> > > + /* Write inode? */
> > > + if (inode == &i) {
> > > + err = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&i,
> > > + sizeof(struct ext2_inode_large));
> > > + if (err)
> > > + goto out2;
> > > + }
> > > +
> > > +out2:
> > > + ext2fs_free_mem(&block_buf);
> > > +out:
> > > + return err;
> > > +}
> > > +
> > > +static errcode_t prep_ea_block_for_write(ext2_filsys fs, ext2_ino_t ino,
> > > + struct ext2_inode_large *inode)
> > > +{
> > > + struct ext2_ext_attr_header *header;
> > > + void *block_buf = NULL;
> > > + dgrp_t grp;
> > > + blk64_t blk, goal;
> > > + errcode_t err;
> > > +
> > > + /* Do we already have an EA block? */
> > > + blk = ext2fs_file_acl_block(fs, (struct ext2_inode *)inode);
> > > + if (blk != 0) {
> > > + if ((blk < fs->super->s_first_data_block) ||
> > > + (blk >= ext2fs_blocks_count(fs->super))) {
> > > + err = EXT2_ET_BAD_EA_BLOCK_NUM;
> > > + goto out;
> > > + }
> > > +
> > > + err = ext2fs_get_mem(fs->blocksize, &block_buf);
> > > + if (err)
> > > + goto out;
> > > +
> > > + err = ext2fs_read_ext_attr3(fs, blk, block_buf, ino);
> > > + if (err)
> > > + goto out2;
> > > +
> > > + header = (struct ext2_ext_attr_header *) block_buf;
> > > + if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
> > > + err = EXT2_ET_BAD_EA_HEADER;
> > > + goto out2;
> > > + }
> > > +
> > > + /* Single-user block. We're done here. */
> > > + if (header->h_refcount == 1)
> > > + return 0;
> > > +
> > > + /* We need to CoW the block. */
> > > + header->h_refcount--;
> > > + err = ext2fs_write_ext_attr3(fs, blk, block_buf, ino);
> > > + if (err)
> > > + goto out2;
> > > + } else {
> > > + /* No block, we must increment i_blocks */
> > > + err = ext2fs_iblk_add_blocks(fs, (struct ext2_inode *)inode,
> > > + 1);
> > > + if (err)
> > > + goto out;
> > > + }
> > > +
> > > + /* Allocate a block */
> > > + grp = ext2fs_group_of_ino(fs, ino);
> > > + goal = ext2fs_inode_table_loc(fs, grp);
> > > + err = ext2fs_alloc_block2(fs, goal, NULL, &blk);
> > > + if (err)
> > > + return err;
> > > + ext2fs_file_acl_block_set(fs, (struct ext2_inode *)inode, blk);
> > > +out2:
> > > + ext2fs_free_mem(&block_buf);
> > > +out:
> > > + return err;
> > > +}
> > > +
> > > +
> > > +static errcode_t write_xattrs_to_buffer(struct ext2_xattr_handle *handle,
> > > + struct ext2_xattr **pos,
> > > + void *entries_start,
> > > + unsigned int storage_size,
> > > + unsigned int value_offset_correction)
> > > +{
> > > + struct ext2_xattr *x = *pos;
> > > + struct ext2_ext_attr_entry *e = entries_start;
> > > + void *end = entries_start + storage_size;
> > > + char *shortname;
> > > + unsigned int entry_size, value_size;
> > > + int idx, ret;
> > > +
> > > + /* For all remaining x... */
> > > + for (; x < handle->attrs + handle->length; x++) {
> > > + if (!x->name)
> > > + continue;
> > > +
> > > + /* Calculate index and shortname position */
> > > + shortname = x->name;
> > > + ret = find_ea_index(x->name, &shortname, &idx);
> > > +
> > > + /* Calculate entry and value size */
> > > + entry_size = (sizeof(*e) + strlen(shortname) +
> > > + EXT2_EXT_ATTR_PAD - 1) &
> > > + ~(EXT2_EXT_ATTR_PAD - 1);
> > > + value_size = ((x->value_len + EXT2_EXT_ATTR_PAD - 1) /
> > > + EXT2_EXT_ATTR_PAD) * EXT2_EXT_ATTR_PAD;
> > > +
> > > + /*
> > > + * Would entry collide with value?
> > > + * Note that we must leave sufficient room for a (u32)0 to
> > > + * mark the end of the entries.
> > > + */
> > > + if ((void *)e + entry_size + sizeof(__u32) > end - value_size)
> > > + break;
> > > +
> > > + /* Fill out e appropriately */
> > > + e->e_name_len = strlen(shortname);
> > > + e->e_name_index = (ret ? idx : 0);
> > > + e->e_value_offs = end - value_size - (void *)entries_start +
> > > + value_offset_correction;
> > > + e->e_value_block = 0;
> > > + e->e_value_size = x->value_len;
> > > +
> > > + /* Store name and value */
> > > + end -= value_size;
> > > + memcpy((void *)e + sizeof(*e), shortname, e->e_name_len);
> > > + memcpy(end, x->value, e->e_value_size);
> > > +
> > > + e->e_hash = ext2fs_ext_attr_hash_entry(e, end);
> > > +
> > > + e = EXT2_EXT_ATTR_NEXT(e);
> > > + *(__u32 *)e = 0;
> > > + }
> > > + *pos = x;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle)
> > > +{
> > > + struct ext2_xattr *x;
> > > + struct ext2_inode_large *inode;
> > > + void *start, *block_buf = NULL;
> > > + struct ext2_ext_attr_header *header;
> > > + __u32 ea_inode_magic;
> > > + blk64_t blk;
> > > + unsigned int storage_size;
> > > + unsigned int i, written;
> > > + errcode_t err;
> > > +
> > > + i = EXT2_INODE_SIZE(handle->fs->super);
> > > + if (i < sizeof(*inode))
> > > + i = sizeof(*inode);
> > > + err = ext2fs_get_memzero(i, &inode);
> > > + if (err)
> > > + return err;
> > > +
> > > + err = ext2fs_read_inode_full(handle->fs, handle->ino,
> > > + (struct ext2_inode *)inode,
> > > + EXT2_INODE_SIZE(handle->fs->super));
> > > + if (err)
> > > + goto out;
> > > +
> > > + x = handle->attrs;
> > > + /* Does the inode have size for EA? */
> > > + if (EXT2_INODE_SIZE(handle->fs->super) <= EXT2_GOOD_OLD_INODE_SIZE +
> > > + inode->i_extra_isize +
> > > + sizeof(__u32))
> > > + goto write_ea_block;
> > > +
> > > + /* Write the inode EA */
> > > + ea_inode_magic = EXT2_EXT_ATTR_MAGIC;
> > > + memcpy(((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
> > > + inode->i_extra_isize, &ea_inode_magic, sizeof(__u32));
> > > + storage_size = EXT2_INODE_SIZE(handle->fs->super) -
> > > + EXT2_GOOD_OLD_INODE_SIZE - inode->i_extra_isize -
> > > + sizeof(__u32);
> > > + start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
> > > + inode->i_extra_isize + sizeof(__u32);
> > > +
> > > + err = write_xattrs_to_buffer(handle, &x, start, storage_size, 0);
> > > + if (err)
> > > + goto out;
> > > +
> > > + /* Are we done? */
> > > + if (x == handle->attrs + handle->length)
> > > + goto skip_ea_block;
> > > +
> > > +write_ea_block:
> > > + /* Write the EA block */
> > > + err = ext2fs_get_mem(handle->fs->blocksize, &block_buf);
> > > + if (err)
> > > + goto out;
> > > +
> > > + storage_size = handle->fs->blocksize -
> > > + sizeof(struct ext2_ext_attr_header);
> > > + start = block_buf + sizeof(struct ext2_ext_attr_header);
> > > +
> > > + err = write_xattrs_to_buffer(handle, &x, start, storage_size,
> > > + (void *)start - block_buf);
> > > + if (err)
> > > + goto out2;
> > > +
> > > + if (x < handle->attrs + handle->length) {
> > > + err = EXT2_ET_EA_NO_SPACE;
> > > + goto out2;
> > > + }
> > > +
> > > + if (block_buf) {
> > > + /* Write a header on the EA block */
> > > + header = block_buf;
> > > + header->h_magic = EXT2_EXT_ATTR_MAGIC;
> > > + header->h_refcount = 1;
> > > + header->h_blocks = 1;
> > > +
> > > + /* Get a new block for writing */
> > > + err = prep_ea_block_for_write(handle->fs, handle->ino, inode);
> > > + if (err)
> > > + goto out2;
> > > +
> > > + /* Finally, write the new EA block */
> > > + blk = ext2fs_file_acl_block(handle->fs,
> > > + (struct ext2_inode *)inode);
> > > + err = ext2fs_write_ext_attr3(handle->fs, blk, block_buf,
> > > + handle->ino);
> > > + if (err)
> > > + goto out2;
> > > + }
> > > +
> > > +skip_ea_block:
> > > + blk = ext2fs_file_acl_block(handle->fs, (struct ext2_inode *)inode);
> > > + if (!block_buf && blk) {
> > > + /* xattrs shrunk, free the block */
> > > + ext2fs_file_acl_block_set(handle->fs,
> > > + (struct ext2_inode *)inode, 0);
> > > + err = ext2fs_iblk_sub_blocks(handle->fs,
> > > + (struct ext2_inode *)inode, 1);
> > > + if (err)
> > > + goto out;
> > > + ext2fs_block_alloc_stats2(handle->fs, blk, -1);
> > > + }
> > > +
> > > + /* Write the inode */
> > > + err = ext2fs_write_inode_full(handle->fs, handle->ino,
> > > + (struct ext2_inode *)inode,
> > > + EXT2_INODE_SIZE(handle->fs->super));
> > > + if (err)
> > > + goto out2;
> > > +
> > > +out2:
> > > + ext2fs_free_mem(&block_buf);
> > > +out:
> > > + ext2fs_free_mem(&inode);
> > > + handle->dirty = 0;
> > > + return err;
> > > +}
> > > +
> > > +static errcode_t read_xattrs_from_buffer(struct ext2_xattr_handle *handle,
> > > + struct ext2_ext_attr_entry *entries,
> > > + unsigned int storage_size,
> > > + void *value_start)
> > > +{
> > > + struct ext2_xattr *x;
> > > + struct ext2_ext_attr_entry *entry;
> > > + const char *prefix;
> > > + void *ptr;
> > > + unsigned int remain, prefix_len;
> > > + errcode_t err;
> > > +
> > > + x = handle->attrs;
> > > + while (x->name)
> > > + x++;
> > > +
> > > + entry = entries;
> > > + while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
> > > + __u32 hash;
> > > +
> > > + /* header eats this space */
> > > + remain -= sizeof(struct ext2_ext_attr_entry);
> > > +
> > > + /* is attribute name valid? */
> > > + if (EXT2_EXT_ATTR_SIZE(entry->e_name_len) > remain)
> > > + return EXT2_ET_EA_BAD_NAME_LEN;
> > > +
> > > + /* attribute len eats this space */
> > > + remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
> > > +
> > > + /* check value size */
> > > + if (entry->e_value_size > remain)
> > > + return EXT2_ET_EA_BAD_VALUE_SIZE;
> > > +
> > > + /* e_value_block must be 0 in inode's ea */
> > > + if (entry->e_value_block != 0)
> > > + return EXT2_ET_BAD_EA_BLOCK_NUM;
> > > +
> > > + hash = ext2fs_ext_attr_hash_entry(entry, value_start +
> > > + entry->e_value_offs);
> > > +
> > > + /* e_hash may be 0 in older inode's ea */
> > > + if (entry->e_hash != 0 && entry->e_hash != hash)
> > > + return EXT2_ET_BAD_EA_HASH;
> > > +
> > > + remain -= entry->e_value_size;
> > > +
> > > + /* Allocate space for more attrs? */
> > > + if (x == handle->attrs + handle->length) {
> > > + err = ext2fs_xattrs_expand(handle, 4);
> > > + if (err)
> > > + return err;
> > > + x = handle->attrs + handle->length - 4;
> > > + }
> > > +
> > > + /* Extract name/value */
> > > + prefix = find_ea_prefix(entry->e_name_index);
> > > + prefix_len = (prefix ? strlen(prefix) : 0);
> > > + err = ext2fs_get_memzero(entry->e_name_len + prefix_len + 1,
> > > + &x->name);
> > > + if (err)
> > > + return err;
> > > + if (prefix)
> > > + memcpy(x->name, prefix, prefix_len);
> > > + if (entry->e_name_len)
> > > + memcpy(x->name + prefix_len,
> > > + (void *)entry + sizeof(*entry),
> > > + entry->e_name_len);
> > > +
> > > + err = ext2fs_get_mem(entry->e_value_size, &x->value);
> > > + if (err)
> > > + return err;
> > > + x->value_len = entry->e_value_size;
> > > + memcpy(x->value, value_start + entry->e_value_offs,
> > > + entry->e_value_size);
> > > + x++;
> > > + entry = EXT2_EXT_ATTR_NEXT(entry);
> > > + }
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle)
> > > +{
> > > + struct ext2_xattr *attrs = NULL, *x;
> > > + unsigned int attrs_len;
> > > + struct ext2_inode_large *inode;
> > > + struct ext2_ext_attr_header *header;
> > > + __u32 ea_inode_magic;
> > > + unsigned int storage_size;
> > > + void *start, *block_buf = NULL;
> > > + blk64_t blk;
> > > + int i;
> > > + errcode_t err;
> > > +
> > > + i = EXT2_INODE_SIZE(handle->fs->super);
> > > + if (i < sizeof(*inode))
> > > + i = sizeof(*inode);
> > > + err = ext2fs_get_memzero(i, &inode);
> > > + if (err)
> > > + return err;
> > > +
> > > + err = ext2fs_read_inode_full(handle->fs, handle->ino,
> > > + (struct ext2_inode *)inode,
> > > + EXT2_INODE_SIZE(handle->fs->super));
> > > + if (err)
> > > + goto out;
> > > +
> > > + /* Does the inode have size for EA? */
> > > + if (EXT2_INODE_SIZE(handle->fs->super) <= EXT2_GOOD_OLD_INODE_SIZE +
> > > + inode->i_extra_isize +
> > > + sizeof(__u32))
> > > + goto read_ea_block;
> > > +
> > > + /* Look for EA in the inode */
> > > + memcpy(&ea_inode_magic, ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
> > > + inode->i_extra_isize, sizeof(__u32));
> > > + if (ea_inode_magic == EXT2_EXT_ATTR_MAGIC) {
> > > + storage_size = EXT2_INODE_SIZE(handle->fs->super) -
> > > + EXT2_GOOD_OLD_INODE_SIZE - inode->i_extra_isize -
> > > + sizeof(__u32);
> > > + start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
> > > + inode->i_extra_isize + sizeof(__u32);
> > > +
> > > + err = read_xattrs_from_buffer(handle, start, storage_size,
> > > + start);
> > > + if (err)
> > > + goto out;
> > > + }
> > > +
> > > +read_ea_block:
> > > + /* Look for EA in a separate EA block */
> > > + blk = ext2fs_file_acl_block(handle->fs, (struct ext2_inode *)inode);
> > > + if (blk != 0) {
> > > + if ((blk < handle->fs->super->s_first_data_block) ||
> > > + (blk >= ext2fs_blocks_count(handle->fs->super))) {
> > > + err = EXT2_ET_BAD_EA_BLOCK_NUM;
> > > + goto out;
> > > + }
> > > +
> > > + err = ext2fs_get_mem(handle->fs->blocksize, &block_buf);
> > > + if (err)
> > > + goto out;
> > > +
> > > + err = ext2fs_read_ext_attr3(handle->fs, blk, block_buf,
> > > + handle->ino);
> > > + if (err)
> > > + goto out3;
> > > +
> > > + header = (struct ext2_ext_attr_header *) block_buf;
> > > + if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
> > > + err = EXT2_ET_BAD_EA_HEADER;
> > > + goto out3;
> > > + }
> > > +
> > > + if (header->h_blocks != 1) {
> > > + err = EXT2_ET_BAD_EA_HEADER;
> > > + goto out3;
> > > + }
> > > +
> > > + /* Read EAs */
> > > + storage_size = handle->fs->blocksize -
> > > + sizeof(struct ext2_ext_attr_header);
> > > + start = block_buf + sizeof(struct ext2_ext_attr_header);
> > > + err = read_xattrs_from_buffer(handle, start, storage_size,
> > > + block_buf);
> > > + if (err)
> > > + goto out3;
> > > +
> > > + ext2fs_free_mem(&block_buf);
> > > + }
> > > +
> > > + ext2fs_free_mem(&block_buf);
> > > + ext2fs_free_mem(&inode);
> > > + return 0;
> > > +
> > > +out3:
> > > + ext2fs_free_mem(&block_buf);
> > > +out:
> > > + ext2fs_free_mem(&inode);
> > > + return err;
> > > +}
> > > +
> > > +#define XATTR_ABORT 1
> > > +#define XATTR_CHANGED 2
> > > +errcode_t ext2fs_xattrs_iterate(struct ext2_xattr_handle *h,
> > > + int (*func)(char *name, char *value,
> > > + void *data),
> > > + void *data)
> > > +{
> > > + struct ext2_xattr *x;
> > > + errcode_t err;
> > > + int ret;
> > > +
> > > + for (x = h->attrs; x < h->attrs + h->length; x++) {
> > > + if (!x->name)
> > > + continue;
> > > +
> > > + ret = func(x->name, x->value, data);
> > > + if (ret & XATTR_CHANGED)
> > > + h->dirty = 1;
> > > + if (ret & XATTR_ABORT)
> > > + return 0;
> > > + }
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +errcode_t ext2fs_xattr_get(struct ext2_xattr_handle *h, const char *key,
> > > + void **value, unsigned int *value_len)
> > > +{
> > > + struct ext2_xattr *x;
> > > + void *val;
> > > + errcode_t err;
> > > +
> > > + for (x = h->attrs; x < h->attrs + h->length; x++) {
> > > + if (!x->name)
> > > + continue;
> > > +
> > > + if (strcmp(x->name, key) == 0) {
> > > + err = ext2fs_get_mem(x->value_len, &val);
> > > + if (err)
> > > + return err;
> > > + memcpy(val, x->value, x->value_len);
> > > + *value = val;
> > > + *value_len = x->value_len;
> > > + return 0;
> > > + }
> > > + }
> > > +
> > > + return EXT2_ET_EA_KEY_NOT_FOUND;
> > > +}
> > > +
> > > +errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *handle,
> > > + const char *key,
> > > + const void *value,
> > > + unsigned int value_len)
> > > +{
> > > + struct ext2_xattr *x, *last_empty;
> > > + char *new_value;
> > > + errcode_t err;
> > > +
> > > + last_empty = NULL;
> > > + for (x = handle->attrs; x < handle->attrs + handle->length; x++) {
> > > + if (!x->name) {
> > > + last_empty = x;
> > > + continue;
> > > + }
> > > +
> > > + /* Replace xattr */
> > > + if (strcmp(x->name, key) == 0) {
> > > + err = ext2fs_get_mem(value_len, &new_value);
> > > + if (err)
> > > + return err;
> > > + memcpy(new_value, value, value_len);
> > > + ext2fs_free_mem(&x->value);
> > > + x->value = new_value;
> > > + x->value_len = value_len;
> > > + handle->dirty = 1;
> > > + return 0;
> > > + }
> > > + }
> > > +
> > > + /* Add attr to empty slot */
> > > + if (last_empty) {
> > > + err = ext2fs_get_mem(strlen(key) + 1, &last_empty->name);
> > > + if (err)
> > > + return err;
> > > + strcpy(last_empty->name, key);
> > > +
> > > + err = ext2fs_get_mem(value_len, &last_empty->value);
> > > + if (err)
> > > + return err;
> > > + memcpy(last_empty->value, value, value_len);
> > > + last_empty->value_len = value_len;
> > > + handle->dirty = 1;
> > > + return 0;
> > > + }
> > > +
> > > + /* Expand array, append slot */
> > > + err = ext2fs_xattrs_expand(handle, 4);
> > > + if (err)
> > > + return err;
> > > +
> > > + x = handle->attrs + handle->length - 4;
> > > + err = ext2fs_get_mem(strlen(key) + 1, &x->name);
> > > + if (err)
> > > + return err;
> > > + strcpy(x->name, key);
> > > +
> > > + err = ext2fs_get_mem(value_len, &x->value);
> > > + if (err)
> > > + return err;
> > > + memcpy(x->value, value, value_len);
> > > + x->value_len = value_len;
> > > + handle->dirty = 1;
> > > + return 0;
> > > +}
> > > +
> > > +errcode_t ext2fs_xattr_remove(struct ext2_xattr_handle *handle,
> > > + const char *key)
> > > +{
> > > + struct ext2_xattr *x;
> > > + errcode_t err;
> > > +
> > > + for (x = handle->attrs; x < handle->attrs + handle->length; x++) {
> > > + if (!x->name)
> > > + continue;
> > > +
> > > + if (strcmp(x->name, key) == 0) {
> > > + ext2fs_free_mem(&x->name);
> > > + ext2fs_free_mem(&x->value);
> > > + x->value_len = 0;
> > > + handle->dirty = 1;
> > > + return 0;
> > > + }
> > > + }
> > > +
> > > + return EXT2_ET_EA_KEY_NOT_FOUND;
> > > +}
> > > +
> > > +errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
> > > + struct ext2_xattr_handle **handle)
> > > +{
> > > + struct ext2_xattr_handle *h;
> > > + errcode_t err;
> > > +
> > > + err = ext2fs_get_memzero(sizeof(*h), &h);
> > > + if (err)
> > > + return err;
> > > +
> > > + h->length = 4;
> > > + err = ext2fs_get_arrayzero(h->length, sizeof(struct ext2_xattr),
> > > + &h->attrs);
> > > + if (err) {
> > > + ext2fs_free_mem(&h);
> > > + return err;
> > > + }
> > > + h->ino = ino;
> > > + h->fs = fs;
> > > + *handle = h;
> > > + return 0;
> > > +}
> > > +
> > > +errcode_t ext2fs_xattrs_close(struct ext2_xattr_handle **handle)
> > > +{
> > > + unsigned int i;
> > > + struct ext2_xattr_handle *h = *handle;
> > > + struct ext2_xattr *a = h->attrs;
> > > + errcode_t err;
> > > +
> > > + if (h->dirty) {
> > > + err = ext2fs_xattrs_write(h);
> > > + if (err)
> > > + return err;
> > > + }
> > > +
> > > + for (i = 0; i < h->length; i++) {
> > > + if (a[i].name)
> > > + ext2fs_free_mem(&a[i].name);
> > > + if (a[i].value)
> > > + ext2fs_free_mem(&a[i].value);
> > > + }
> > > +
> > > + ext2fs_free_mem(&h->attrs);
> > > + ext2fs_free_mem(handle);
> > > + return 0;
> > > +}
> > > --
> > > 1.7.9.7
> > >
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> > > the body of a message to [email protected]
> > > More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-12-04 03:25:00

by Zheng Liu

[permalink] [raw]
Subject: Re: [PATCH v2 19/28] mke2fs: add inline_data support in mke2fs

On Tue, Dec 03, 2013 at 02:30:26PM -0800, Darrick J. Wong wrote:
[...]
> > + * notify users that inline data will never be useful.
> > + */
> > + if ((fs_param.s_feature_incompat &
> > + EXT4_FEATURE_INCOMPAT_INLINE_DATA) &&
> > + inode_size == EXT2_GOOD_OLD_INODE_SIZE) {
>
> Perhaps I'm missing something here, but why is it impossible to use i_blocks
> for inline data even if there's no space for EAs?

If I understand correctly, on kernel side, we determine an inode has
inline data according to whether we have 'system.data' xattr entry on
inode extended attribute space. If an inode doesn't have enough space
to store an entry with 'system.data', we just think this inode doesn't
has inline data. So that is why I add this sanity check.

Regards,
- Zheng

2013-12-04 03:39:21

by Zheng Liu

[permalink] [raw]
Subject: Re: [PATCH v2 12/28] debugfs: make expand command support inline data

On Tue, Dec 03, 2013 at 01:19:54PM -0800, Darrick J. Wong wrote:
[...]
> > +errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino)
>
> I couldn't tell from the name that this function only expands inline
> directories. A name like ext2fs_inline_dir_expand() would make that more
> immediately clear; initially I thought this could be a method that also expands
> files.

Your initially thought is right. This function will expand a directory
or a file. In this commit, it just handles directory because it is only
used by ext2fs_expand_dir(). Later commit will let it handle a file.
Sorry, I don't clarify this.

>
> > +{
> > + struct ext2_inode inode;
> > + struct ext2_inline_data data;
> > + errcode_t retval;
> > + blk64_t blk;
> > + char *inline_buf = 0;
> > + char *blk_buf = 0;
> > +
> > + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
> > +
> > + retval = ext2fs_read_inode(fs, ino, &inode);
> > + if (retval)
> > + return retval;
> > +
> > + if (!(inode.i_flags & EXT4_INLINE_DATA_FL))
> > + return EXT2_ET_NO_INLINE_DATA;
> > +
> > + /* Get inline data first */
> > + data.fs = fs;
> > + data.ino = ino;
> > + retval = ext2fs_inline_data_ea_get(&data);
> > + if (retval)
> > + return retval;
> > + retval = ext2fs_get_mem(EXT4_MIN_INLINE_DATA_SIZE + data.ea_size,
> > + &inline_buf);
> > + if (retval)
> > + goto errout;
> > +
> > + memcpy(inline_buf, (void *)inode.i_block, EXT4_MIN_INLINE_DATA_SIZE);
> > + if (data.ea_size > 0) {
> > + memcpy(inline_buf + EXT4_MIN_INLINE_DATA_SIZE,
> > + data.ea_data, data.ea_size);
> > + }
> > +
> > + memset((void *)inode.i_block, 0, EXT4_MIN_INLINE_DATA_SIZE);
> > + retval = ext2fs_inline_data_ea_remove(fs, ino);
> > + if (retval)
> > + goto errout;
> > +
> > + retval = ext2fs_get_mem(fs->blocksize, &blk_buf);
> > + if (retval)
> > + goto errout;
> > +
> > + /* Adjust the rec_len */
> > + retval = ext2fs_inline_data_convert_dir(fs, ino, blk_buf, inline_buf,
> > + EXT4_MIN_INLINE_DATA_SIZE +
> > + data.ea_size);
> > + if (retval)
> > + goto errout;
> > +
> > + /* Allocate a new block */
> > + retval = ext2fs_new_block2(fs, 0, 0, &blk);
> > + if (retval)
> > + goto errout;
> > + if (EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT3_FEATURE_INCOMPAT_EXTENTS))
> > + inode.i_flags |= EXT4_EXTENTS_FL;
> > + else
> > + inode.i_block[0] = blk;
> > + inode.i_flags &= ~EXT4_INLINE_DATA_FL;
> > + ext2fs_iblk_set(fs, &inode, 1);
> > + inode.i_size = fs->blocksize;
> > + retval = ext2fs_write_inode(fs, ino, &inode);
> > + if (retval)
> > + goto errout;
> > + retval = ext2fs_write_dir_block4(fs, blk, blk_buf, 0, ino);
> > + if (retval)
> > + goto errout;
> > + if (EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT3_FEATURE_INCOMPAT_EXTENTS)) {
> > + ext2_extent_handle_t handle;
> > +
> > + retval = ext2fs_extent_open2(fs, ino, &inode, &handle);
> > + if (retval)
> > + goto errout;
> > + retval = ext2fs_extent_set_bmap(handle, 0, blk, 0);
> > + ext2fs_extent_free(handle);
> > + }
>
> Would it be simpler to call ext2fs_bmap2() with bmap_flags = BMAP_SET here?
> Then you wouldn't need to open code the blockmap and extent setting cases.
> Then all you have to do is:
>
> if (fs_has_extents)
> inode.i_flags |= EXT4_EXTENTS_FL;
> ext2fs_bmap2(fs, ino, &inode, NULL, BMAP_SET, 0, NULL, &blk);
> ext2fs_write_inode(...);

Yes, I remember that you have advised me to use this function in first
version. I will fix it.

Thanks,
- Zheng

2013-12-04 03:43:23

by Zheng Liu

[permalink] [raw]
Subject: Re: [PATCH v2 15/28] debugfs: handle inline_data feature in bmap command

On Tue, Dec 03, 2013 at 07:11:25PM -0800, Darrick J. Wong wrote:
> On Tue, Dec 03, 2013 at 08:11:42PM +0800, Zheng Liu wrote:
> > From: Zheng Liu <[email protected]>
> >
> > No physical block mapping if an inode has inline data.
> >
> > Signed-off-by: Theodore Ts'o <[email protected]>
> > Signed-off-by: Zheng Liu <[email protected]>
> > ---
> > lib/ext2fs/bmap.c | 7 +++++++
> > 1 file changed, 7 insertions(+)
> >
> > diff --git a/lib/ext2fs/bmap.c b/lib/ext2fs/bmap.c
> > index 5074587..0d043bd 100644
> > --- a/lib/ext2fs/bmap.c
> > +++ b/lib/ext2fs/bmap.c
> > @@ -273,6 +273,13 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
> > block_buf = buf;
> > }
> >
> > + /*
> > + * If an inode has inline data, that means that it doesn't have
> > + * any blocks and we shouldn't map any blocks for it.
> > + */
> > + if (inode->i_flags & EXT4_INLINE_DATA_FL)
> > + goto done;
>
> If you move this to before the ext2fs_get_array() chunk above it, you could
> return straight out of the function.

Good catch!

>
> This should return an error code of some kind, because otherwise the client
> will think that something happened when the function returns 0, especially if
> BMAP_{ALLOC,SET,UNINIT} are passed in.

Fair enough

- Zheng

>
> --D
>
> > +
> > if (inode->i_flags & EXT4_EXTENTS_FL) {
> > retval = ext2fs_extent_open2(fs, ino, inode, &handle);
> > if (retval)
> > --
> > 1.7.9.7
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> > the body of a message to [email protected]
> > More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-12-04 03:45:01

by Zheng Liu

[permalink] [raw]
Subject: Re: [PATCH v2 17/28] libext2fs: handle inline data in read/write function

On Tue, Dec 03, 2013 at 07:02:42PM -0800, Darrick J. Wong wrote:
[...]
> > +static errcode_t
> > +ext2fs_file_read_inline_data(ext2_file_t file, void *buf,
> > + unsigned int wanted, unsigned int *got)
> > +{
> > + ext2_filsys fs;
> > + errcode_t retval;
> > + unsigned int start, count = 0;
> > + size_t size;
> > +
> > + fs = file->fs;
> > + retval = ext2fs_inline_data_get(fs, file->ino, &file->inode,
> > + file->buf, &size);
> > + if (retval)
> > + return retval;
> > +
> > + if (file->pos >= size)
> > + goto out;
> > +
> > + start = file->pos % size;
>
> If file->pos can never be larger than size, this is unnecessary.

Good catch! I will fix it soon.

Thanks,
- Zheng

>
> > + count = size - start;
> > + if (count > wanted)
> > + count = wanted;
> > + memcpy(buf, file->buf + start, count);
> > + file->pos += count;
> > + buf += count;
> > +
> > +out:
> > + if (got)
> > + *got = count;
> > + return retval;
> > +}
> > +
> > +
> > errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
> > unsigned int wanted, unsigned int *got)
> > {
> > @@ -236,6 +269,10 @@ errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
> > EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
> > fs = file->fs;
> >
> > + /* If an inode has inline data, things get complicated. */
> > + if (file->inode.i_flags & EXT4_INLINE_DATA_FL)
> > + return ext2fs_file_read_inline_data(file, buf, wanted, got);
> > +
> > while ((file->pos < EXT2_I_SIZE(&file->inode)) && (wanted > 0)) {
> > retval = sync_buffer_position(file);
> > if (retval)
> > @@ -266,6 +303,67 @@ fail:
> > }
> >
> >
> > +static errcode_t
> > +ext2fs_file_write_inline_data(ext2_file_t file, const void *buf,
> > + unsigned int nbytes, unsigned int *written)
> > +{
> > + ext2_filsys fs;
> > + errcode_t retval;
> > + unsigned int start, count = 0;
> > + size_t size;
> > +
> > + fs = file->fs;
> > + retval = ext2fs_inline_data_get(fs, file->ino, &file->inode,
> > + file->buf, &size);
> > + if (retval)
> > + return retval;
> > +
> > + if (file->pos < size) {
> > + start = file->pos % fs->blocksize;
>
> Since we're not dealing with blocks, I don't think you need to '%' with the
> block size.
>
> --D
>
> > + count = nbytes - start;
> > + memcpy(file->buf + start, buf, count);
> > +
> > + retval = ext2fs_inline_data_set(fs, file->ino, &file->inode,
> > + file->buf, count);
> > + if (retval == EXT2_ET_INLINE_DATA_NO_SPACE)
> > + goto expand;
> > + if (retval)
> > + return retval;
> > +
> > + file->pos += count;
> > +
> > + /* Update inode size */
> > + if (count != 0 && EXT2_I_SIZE(&file->inode) < file->pos) {
> > + errcode_t rc;
> > +
> > + rc = ext2fs_file_set_size2(file, file->pos);
> > + if (retval == 0)
> > + retval = rc;
> > + }
> > +
> > + if (written)
> > + *written = count;
> > + return 0;
> > + }
> > +
> > +expand:
> > + retval = ext2fs_inline_data_expand(fs, file->ino);
> > + if (retval)
> > + return retval;
> > + /*
> > + * reload inode and return no space error
> > + *
> > + * XXX: file->inode could be copied from the outside
> > + * in ext2fs_file_open2(). We have no way to modify
> > + * the outside inode.
> > + */
> > + retval = ext2fs_read_inode(fs, file->ino, &file->inode);
> > + if (retval)
> > + return retval;
> > + return EXT2_ET_INLINE_DATA_NO_SPACE;
> > +}
> > +
> > +
> > errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
> > unsigned int nbytes, unsigned int *written)
> > {
> > @@ -280,6 +378,16 @@ errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
> > if (!(file->flags & EXT2_FILE_WRITE))
> > return EXT2_ET_FILE_RO;
> >
> > + /* If an inode has inline data, things get complicated. */
> > + if (file->inode.i_flags & EXT4_INLINE_DATA_FL) {
> > + retval = ext2fs_file_write_inline_data(file, buf, nbytes,
> > + written);
> > + if (retval != EXT2_ET_INLINE_DATA_NO_SPACE)
> > + return retval;
> > + /* fall through to read data from the block */
> > + retval = 0;
> > + }
> > +
> > while (nbytes > 0) {
> > retval = sync_buffer_position(file);
> > if (retval)
> > diff --git a/lib/ext2fs/inline_data.c b/lib/ext2fs/inline_data.c
> > index 16af814..4a35209 100644
> > --- a/lib/ext2fs/inline_data.c
> > +++ b/lib/ext2fs/inline_data.c
> > @@ -309,6 +309,7 @@ errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino)
> > struct ext2_inline_data data;
> > errcode_t retval;
> > blk64_t blk;
> > + size_t inline_size;
> > char *inline_buf = 0;
> > char *blk_buf = 0;
> >
> > @@ -327,8 +328,8 @@ errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino)
> > retval = ext2fs_inline_data_ea_get(&data);
> > if (retval)
> > return retval;
> > - retval = ext2fs_get_mem(EXT4_MIN_INLINE_DATA_SIZE + data.ea_size,
> > - &inline_buf);
> > + inline_size = data.ea_size + EXT4_MIN_INLINE_DATA_SIZE;
> > + retval = ext2fs_get_mem(inline_size, &inline_buf);
> > if (retval)
> > goto errout;
> >
> > @@ -347,10 +348,14 @@ errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino)
> > if (retval)
> > goto errout;
> >
> > - /* Adjust the rec_len */
> > - retval = ext2fs_inline_data_convert_dir(fs, ino, blk_buf, inline_buf,
> > - EXT4_MIN_INLINE_DATA_SIZE +
> > - data.ea_size);
> > + if (LINUX_S_ISDIR(inode.i_mode)) {
> > + /* Adjust the rec_len */
> > + retval = ext2fs_inline_data_convert_dir(fs, ino, blk_buf,
> > + inline_buf, inline_size);
> > + } else {
> > + /* Copy data for a regular inode */
> > + memcpy(blk_buf, inline_buf, inline_size);
> > + }
> > if (retval)
> > goto errout;
> >
> > @@ -389,3 +394,76 @@ errout:
> > ext2fs_free_mem(&data.ea_data);
> > return retval;
> > }
> > +
> > +/*
> > + * When caller uses this function to retrieve the inline data, it must
> > + * allocate a buffer which has the size of inline data. The size of
> > + * inline data can be know by ext2fs_inline_data_get_size().
> > + */
> > +errcode_t ext2fs_inline_data_get(ext2_filsys fs, ext2_ino_t ino,
> > + struct ext2_inode *inode,
> > + void *buf, size_t *size)
> > +{
> > + struct ext2_inode inode_buf;
> > + struct ext2_inline_data data;
> > + errcode_t retval;
> > +
> > + if (!inode) {
> > + retval = ext2fs_read_inode(fs, ino, &inode_buf);
> > + if (retval)
> > + return retval;
> > + inode = &inode_buf;
> > + }
> > +
> > + data.fs = fs;
> > + data.ino = ino;
> > + retval = ext2fs_inline_data_ea_get(&data);
> > + if (retval)
> > + return retval;
> > +
> > + memcpy(buf, (void *)inode->i_block, EXT4_MIN_INLINE_DATA_SIZE);
> > + if (data.ea_size > 0)
> > + memcpy(buf + EXT4_MIN_INLINE_DATA_SIZE,
> > + data.ea_data, data.ea_size);
> > +
> > + if (size)
> > + *size = EXT4_MIN_INLINE_DATA_SIZE + data.ea_size;
> > + ext2fs_free_mem(&data.ea_data);
> > + return 0;
> > +}
> > +
> > +errcode_t ext2fs_inline_data_set(ext2_filsys fs, ext2_ino_t ino,
> > + struct ext2_inode *inode,
> > + void *buf, size_t size)
> > +{
> > + struct ext2_inode inode_buf;
> > + struct ext2_inline_data data;
> > + unsigned int max_size = 0;
> > + errcode_t retval;
> > +
> > + if (!inode) {
> > + retval = ext2fs_read_inode(fs, ino, &inode_buf);
> > + if (retval)
> > + return retval;
> > + inode = &inode_buf;
> > + }
> > +
> > + /* simple case */
> > + if (size <= EXT4_MIN_INLINE_DATA_SIZE) {
> > + memcpy((void *)inode->i_block, buf, size);
> > + return ext2fs_write_inode(fs, ino, inode);
> > + }
> > +
> > + /*
> > + * complicated case
> > + */
> > + memcpy((void *)inode->i_block, buf, EXT4_MIN_INLINE_DATA_SIZE);
> > + retval = ext2fs_write_inode(fs, ino, inode);
> > + if (retval)
> > + return retval;
> > + data.fs = fs;
> > + data.ino = ino;
> > + data.ea_size = size - EXT4_MIN_INLINE_DATA_SIZE;
> > + data.ea_data = buf + EXT4_MIN_INLINE_DATA_SIZE;
> > + return ext2fs_inline_data_ea_set(&data);
> > +}
> > --
> > 1.7.9.7
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> > the body of a message to [email protected]
> > More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-12-04 03:52:39

by Zheng Liu

[permalink] [raw]
Subject: Re: [PATCH v2 20/28] tune2fs: add inline_data feature in tune2fs

On Tue, Dec 03, 2013 at 02:29:46PM -0800, Darrick J. Wong wrote:
> On Tue, Dec 03, 2013 at 08:11:47PM +0800, Zheng Liu wrote:
> > From: Zheng Liu <[email protected]>
> >
> > Inline_data can be enabled without ext_attr. Hence we don't check it.
> > As doing in mke2fs we need to check inode size.
> >
> > Now this feature only can be enabled because we may be out of space when
> > disabling it. If this feature is disabled, we need to allocate a block
> > for every file and directory, and it might exhaust all space.
>
> Could we at least try to turn off inlinedata and decline to clear the flag if
> we ENOSPC?

If we want to turn off inline_data, that means that we need to scan all
used inodes and try to expand it. It isn't hard but I am wondering if
someone really want to disable this feature. In my opinion, I really
want to enable inline_data feature by default when we believe it is very
stable because it will benefit for all desktop and Android users.

- Zheng

>
> (Yes, that's more of a TODO feature request...)
>
> > Signed-off-by: Theodore Ts'o <[email protected]>
> > Signed-off-by: Zheng Liu <[email protected]>
> > ---
> > misc/tune2fs.8.in | 5 +++++
> > misc/tune2fs.c | 16 +++++++++++++++-
> > 2 files changed, 20 insertions(+), 1 deletion(-)
> >
> > diff --git a/misc/tune2fs.8.in b/misc/tune2fs.8.in
> > index 55c6dd9..78f965b 100644
> > --- a/misc/tune2fs.8.in
> > +++ b/misc/tune2fs.8.in
> > @@ -531,6 +531,11 @@ Setting the filesystem feature is equivalent to using the
> > .B \-j
> > option.
> > .TP
> > +.B inline_data
> > +Allow data to be stored in the inode and extented attribute area.
>
> "extended"
>
> --D
>
> > +.B Tune2fs
> > +only supports setting this filesystem feature.
> > +.TP
> > .B large_file
> > Filesystem can contain files that are greater than 2GB. (Modern kernels
> > set this feature automatically when a file > 2GB is created.)
> > diff --git a/misc/tune2fs.c b/misc/tune2fs.c
> > index 95c1886..1952081 100644
> > --- a/misc/tune2fs.c
> > +++ b/misc/tune2fs.c
> > @@ -140,7 +140,8 @@ static __u32 ok_features[3] = {
> > EXT2_FEATURE_INCOMPAT_FILETYPE |
> > EXT3_FEATURE_INCOMPAT_EXTENTS |
> > EXT4_FEATURE_INCOMPAT_FLEX_BG |
> > - EXT4_FEATURE_INCOMPAT_MMP,
> > + EXT4_FEATURE_INCOMPAT_MMP |
> > + EXT4_FEATURE_INCOMPAT_INLINE_DATA,
> > /* R/O compat */
> > EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
> > EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
> > @@ -1083,6 +1084,19 @@ mmp_error:
> > disable_uninit_bg(fs,
> > EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
> >
> > + if (FEATURE_ON(E2P_FEATURE_INCOMPAT,
> > + EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
> > + /*
> > + * Check inode size. If inode size is 128, tell user that
> > + * inline data is useless.
> > + */
> > + if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE) {
> > + fputs(_("The inode size is too small to "
> > + "store inline data.\n"), stderr);
> > + return 1;
> > + }
> > + }
> > +
> > if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
> > EXT4_FEATURE_RO_COMPAT_QUOTA)) {
> > /*
> > --
> > 1.7.9.7
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> > the body of a message to [email protected]
> > More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-12-04 03:53:23

by Zheng Liu

[permalink] [raw]
Subject: Re: [PATCH v2 27/28] libext2fs: export inode cahce creation function

On Tue, Dec 03, 2013 at 02:22:49PM -0800, Darrick J. Wong wrote:
> On Tue, Dec 03, 2013 at 08:11:54PM +0800, Zheng Liu wrote:
> > From: Zheng Liu <[email protected]>
> >
> > Currently we have already exported inode cache flush and free functions
> > for users. This commit exports inode cache creation function. Later
> > we will use this function to initialize inode cache and do some unit
> > tests for inline data.
> >
> > Signed-off-by: Zheng Liu <[email protected]>
> > ---
> > lib/ext2fs/ext2fs.h | 1 +
> > lib/ext2fs/inode.c | 8 ++++----
> > 2 files changed, 5 insertions(+), 4 deletions(-)
> >
> > diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
> > index bf0ef26..a47bb40 100644
> > --- a/lib/ext2fs/ext2fs.h
> > +++ b/lib/ext2fs/ext2fs.h
> > @@ -1369,6 +1369,7 @@ extern errcode_t ext2fs_get_memalign(unsigned long size,
> > unsigned long align, void *ptr);
> >
> > /* inode.c */
> > +extern errcode_t ext2fs_create_inode_cache(ext2_filsys fs, int cache_size);
> > extern void ext2fs_free_inode_cache(struct ext2_inode_cache *icache);
> > extern errcode_t ext2fs_flush_icache(ext2_filsys fs);
> > extern errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan,
> > diff --git a/lib/ext2fs/inode.c b/lib/ext2fs/inode.c
> > index 46c1c58..8cf76b0 100644
> > --- a/lib/ext2fs/inode.c
> > +++ b/lib/ext2fs/inode.c
> > @@ -91,7 +91,7 @@ void ext2fs_free_inode_cache(struct ext2_inode_cache *icache)
> > ext2fs_free_mem(&icache);
> > }
> >
> > -static errcode_t create_icache(ext2_filsys fs)
> > +errcode_t ext2fs_create_inode_cache(ext2_filsys fs, int cache_size)
>
> cache_size should unsigned int; there's not much point in letting people pass
> us a negative size.
>
> Possibly ext2_inode_cache.cache_size should be unsigned too...

Fair enough. I will fix it soon.

Thanks,
- Zheng

>
> --D
>
> > {
> > int i;
> > errcode_t retval;
> > @@ -109,7 +109,7 @@ static errcode_t create_icache(ext2_filsys fs)
> >
> > fs->icache->buffer_blk = 0;
> > fs->icache->cache_last = -1;
> > - fs->icache->cache_size = 4;
> > + fs->icache->cache_size = cache_size;
> > fs->icache->refcount = 1;
> > retval = ext2fs_get_array(fs->icache->cache_size,
> > sizeof(struct ext2_inode_cache_ent),
> > @@ -596,7 +596,7 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
> > return EXT2_ET_BAD_INODE_NUM;
> > /* Create inode cache if not present */
> > if (!fs->icache) {
> > - retval = create_icache(fs);
> > + retval = ext2fs_create_inode_cache(fs, 4);
> > if (retval)
> > return retval;
> > }
> > @@ -732,7 +732,7 @@ errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
> > }
> > }
> > } else {
> > - retval = create_icache(fs);
> > + retval = ext2fs_create_inode_cache(fs, 4);
> > if (retval)
> > goto errout;
> > }
> > --
> > 1.7.9.7
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> > the body of a message to [email protected]
> > More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-12-04 04:03:10

by Darrick J. Wong

[permalink] [raw]
Subject: Re: [PATCH v2 20/28] tune2fs: add inline_data feature in tune2fs

On Wed, Dec 04, 2013 at 11:55:36AM +0800, Zheng Liu wrote:
> On Tue, Dec 03, 2013 at 02:29:46PM -0800, Darrick J. Wong wrote:
> > On Tue, Dec 03, 2013 at 08:11:47PM +0800, Zheng Liu wrote:
> > > From: Zheng Liu <[email protected]>
> > >
> > > Inline_data can be enabled without ext_attr. Hence we don't check it.
> > > As doing in mke2fs we need to check inode size.
> > >
> > > Now this feature only can be enabled because we may be out of space when
> > > disabling it. If this feature is disabled, we need to allocate a block
> > > for every file and directory, and it might exhaust all space.
> >
> > Could we at least try to turn off inlinedata and decline to clear the flag if
> > we ENOSPC?
>
> If we want to turn off inline_data, that means that we need to scan all
> used inodes and try to expand it. It isn't hard but I am wondering if
> someone really want to disable this feature. In my opinion, I really
> want to enable inline_data feature by default when we believe it is very
> stable because it will benefit for all desktop and Android users.

I guess that really depends on our testers -- on the one hand, I do most of my
testing with scratch filesystems so on a practical level I will never need the
ability to "undo" these sorts of things.

On the other hand, there's the argument that more people will test code if they
think they can go back to the old system if the new one turns out to be a mess.

Same sort of reasoning applies to "why did you implement converting 64bit ->
32bit filesystems?" :)

--D
>
> - Zheng
>
> >
> > (Yes, that's more of a TODO feature request...)
> >
> > > Signed-off-by: Theodore Ts'o <[email protected]>
> > > Signed-off-by: Zheng Liu <[email protected]>
> > > ---
> > > misc/tune2fs.8.in | 5 +++++
> > > misc/tune2fs.c | 16 +++++++++++++++-
> > > 2 files changed, 20 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/misc/tune2fs.8.in b/misc/tune2fs.8.in
> > > index 55c6dd9..78f965b 100644
> > > --- a/misc/tune2fs.8.in
> > > +++ b/misc/tune2fs.8.in
> > > @@ -531,6 +531,11 @@ Setting the filesystem feature is equivalent to using the
> > > .B \-j
> > > option.
> > > .TP
> > > +.B inline_data
> > > +Allow data to be stored in the inode and extented attribute area.
> >
> > "extended"
> >
> > --D
> >
> > > +.B Tune2fs
> > > +only supports setting this filesystem feature.
> > > +.TP
> > > .B large_file
> > > Filesystem can contain files that are greater than 2GB. (Modern kernels
> > > set this feature automatically when a file > 2GB is created.)
> > > diff --git a/misc/tune2fs.c b/misc/tune2fs.c
> > > index 95c1886..1952081 100644
> > > --- a/misc/tune2fs.c
> > > +++ b/misc/tune2fs.c
> > > @@ -140,7 +140,8 @@ static __u32 ok_features[3] = {
> > > EXT2_FEATURE_INCOMPAT_FILETYPE |
> > > EXT3_FEATURE_INCOMPAT_EXTENTS |
> > > EXT4_FEATURE_INCOMPAT_FLEX_BG |
> > > - EXT4_FEATURE_INCOMPAT_MMP,
> > > + EXT4_FEATURE_INCOMPAT_MMP |
> > > + EXT4_FEATURE_INCOMPAT_INLINE_DATA,
> > > /* R/O compat */
> > > EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
> > > EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
> > > @@ -1083,6 +1084,19 @@ mmp_error:
> > > disable_uninit_bg(fs,
> > > EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
> > >
> > > + if (FEATURE_ON(E2P_FEATURE_INCOMPAT,
> > > + EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
> > > + /*
> > > + * Check inode size. If inode size is 128, tell user that
> > > + * inline data is useless.
> > > + */
> > > + if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE) {
> > > + fputs(_("The inode size is too small to "
> > > + "store inline data.\n"), stderr);
> > > + return 1;
> > > + }
> > > + }
> > > +
> > > if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
> > > EXT4_FEATURE_RO_COMPAT_QUOTA)) {
> > > /*
> > > --
> > > 1.7.9.7
> > >
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> > > the body of a message to [email protected]
> > > 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 [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-12-04 04:08:32

by Darrick J. Wong

[permalink] [raw]
Subject: Re: [PATCH v2 19/28] mke2fs: add inline_data support in mke2fs

On Wed, Dec 04, 2013 at 11:27:57AM +0800, Zheng Liu wrote:
> On Tue, Dec 03, 2013 at 02:30:26PM -0800, Darrick J. Wong wrote:
> [...]
> > > + * notify users that inline data will never be useful.
> > > + */
> > > + if ((fs_param.s_feature_incompat &
> > > + EXT4_FEATURE_INCOMPAT_INLINE_DATA) &&
> > > + inode_size == EXT2_GOOD_OLD_INODE_SIZE) {
> >
> > Perhaps I'm missing something here, but why is it impossible to use i_blocks
> > for inline data even if there's no space for EAs?
>
> If I understand correctly, on kernel side, we determine an inode has
> inline data according to whether we have 'system.data' xattr entry on
> inode extended attribute space. If an inode doesn't have enough space
> to store an entry with 'system.data', we just think this inode doesn't
> has inline data. So that is why I add this sanity check.

Ok. I was curious. Small inode => no inline data seems like an unfortunate
restriction to me, but oh well, it's your feature. I don't plan to go back to
128 byte inodes ever. :)

Also, we could store four more bytes if we created a new e_name_index value (5?
9?) to represent "system.data". Any thoughts about that?

--D

>
> Regards,
> - Zheng
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-12-04 04:54:39

by Zheng Liu

[permalink] [raw]
Subject: Re: [PATCH v2 09/28] libext2fs: handle inline data in dir iterator function

On Tue, Dec 03, 2013 at 02:13:49PM -0800, Darrick J. Wong wrote:
> On Tue, Dec 03, 2013 at 08:11:36PM +0800, Zheng Liu wrote:
> > From: Zheng Liu <[email protected]>
> >
> > 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:
> > - cd
> > - chroot
> > - link*
> > - ls
> > - ncheck
> > - pwd
> > - unlink
> >
> > * TODO: Inline_data doesn't expand to ibody extended attribute because
> > link command doesn't handle DIR_NO_SPACE error until now. But if we
> > have already expanded inline data to ibody ea area, link command can
> > occupy this space.
>
> A patch for this TODO is coming, right?

TBH, I don't have a patch for this because I don't know why ext2fs_link
doesn't handle DIR_NO_SPACE error. So I will try to fix it later.

>
> > Signed-off-by: Theodore Ts'o <[email protected]>
> > Signed-off-by: Zheng Liu <[email protected]>
> > ---
> > lib/ext2fs/Makefile.in | 8 +++
> > lib/ext2fs/Makefile.pq | 1 +
> > lib/ext2fs/dir_iterate.c | 62 +++++++++++-----
> > lib/ext2fs/ext2_err.et.in | 3 +
> > lib/ext2fs/ext2_fs.h | 10 +++
> > lib/ext2fs/ext2fs.h | 1 +
> > lib/ext2fs/ext2fsP.h | 11 +++
> > lib/ext2fs/inline_data.c | 175 +++++++++++++++++++++++++++++++++++++++++++++
> > lib/ext2fs/swapfs.c | 12 +++-
> > 9 files changed, 265 insertions(+), 18 deletions(-)
> > create mode 100644 lib/ext2fs/inline_data.c
> >
> > diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
> > index e1e6216..4d011c9 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 \
> > @@ -758,6 +760,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 2f7b654..89082a7 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 8be0ac2..1624371 100644
> > --- a/lib/ext2fs/dir_iterate.c
> > +++ b/lib/ext2fs/dir_iterate.c
> > @@ -127,6 +127,12 @@ 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;
> > + retval = ext2fs_inline_data_dir_iterate(fs, dir,
> > + ext2fs_process_dir_block,
> > + &ctx);
> > + }
> > if (retval)
> > return retval;
> > return ctx.errcode;
> > @@ -189,30 +195,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;
>
> Since we don't swap i_block[] when we're reading in the inode, who calls
> ext2fs_dirent_swab_in()?

Good catch! I will call this on ext2fs_inline_data_dir_iterate().

>
> > + }
> >
> > 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 +236,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 +256,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 +294,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_CHANGED to notify caller that inline
> > + * data has been changed
> > + */
>
> I don't think I like overloading the meaning of BLOCK_CHANGED here. For a
> non-inlinedata file, the iterator function returning BLOCK_CHANGED means that
> the lblk->pblk mapping changed, whereas for an inline data file, it means that
> the block *contents* have changed.
>
> At best, the two meanings ought to be documented wherever BLOCK_CHANGED is
> defined, though I think I'd prefer a new flag BLOCK_INLINE_DATA_CHANGED to
> handle this situation, because otherwise the meaning of the flag is
> context-dependent and therefore more difficult to reason about.
>
> Separate flags also gives us the ability to check for programming errors, such
> as someone returning BLOCK_CHANGED on inlinedata files or returning
> BLOCK_INLINE_DATA_CHANGED on !inlinedata files.

Fair enough.

>
> > + retval = BLOCK_CHANGED;
>
> Who calls ext2fs_dirent_swab_out()?

Ditto.

>
> > + }
> > }
> > 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 b819a90..0781145 100644
> > --- a/lib/ext2fs/ext2_err.et.in
> > +++ b/lib/ext2fs/ext2_err.et.in
> > @@ -500,4 +500,7 @@ ec EXT2_ET_EA_KEY_NOT_FOUND,
> > ec EXT2_ET_EA_NO_SPACE,
> > "Insufficient space to store extended attribute data"
> >
> > +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 6a28d55..5ab16ae 100644
> > --- a/lib/ext2fs/ext2_fs.h
> > +++ b/lib/ext2fs/ext2_fs.h
> > @@ -914,4 +914,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 e251435..c5b73fd 100644
> > --- a/lib/ext2fs/ext2fs.h
> > +++ b/lib/ext2fs/ext2fs.h
> > @@ -438,6 +438,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 80d2d0a..5ab6084 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,16 @@ extern int ext2fs_process_dir_block(ext2_filsys fs,
> > int ref_offset,
> > void *priv_data);
> >
> > +extern errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
> > + ext2_ino_t ino,
> > + int (*func)(ext2_filsys fs,
> > + blk64_t *blocknr,
> > + e2_blkcnt_t blockcnt,
> > + blk64_t ref_blk,
> > + int ref_offset,
> > + void *priv_data),
> > + 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 0000000..cc2954d
> > --- /dev/null
> > +++ b/lib/ext2fs/inline_data.c
> > @@ -0,0 +1,175 @@
> > +/*
> > + * inline_data.c --- data in inode
> > + *
> > + * Copyright (C) 2012 Zheng Liu <[email protected]>
> > + *
> > + * %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 <stdio.h>
> > +#include <time.h>
> > +
> > +#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 */
> > + char *ea_data;
>
> Should this be a void* since the file data type is opaque to the library?

I will fix it.

>
> > +};
> > +
> > +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;
> > +}
> > +
> > +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;
> > +}
> > +
> > +
> > +errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
> > + ext2_ino_t ino,
> > + int (*func)(ext2_filsys fs,
> > + blk64_t *blocknr,
> > + e2_blkcnt_t blockcnt,
> > + blk64_t ref_blk,
> > + int ref_offset,
> > + void *priv_data),
> > + void *priv_data)
> > +{
> > + struct dir_context *ctx;
> > + struct ext2_inode inode;
> > + struct ext2_dir_entry dirent;
> > + struct ext2_inline_data data;
> > + errcode_t retval = 0;
> > + e2_blkcnt_t blockcnt = 0;
> > +
> > + ctx = (struct dir_context *)priv_data;
> > +
> > + retval = ext2fs_read_inode(fs, ino, &inode);
> > + if (retval)
> > + goto out;
> > +
> > + if (!(inode.i_flags & EXT4_INLINE_DATA_FL))
> > + return EXT2_ET_NO_INLINE_DATA;
> > +
> > + if (!LINUX_S_ISDIR(inode.i_mode)) {
> > + retval = EXT2_ET_NO_DIRECTORY;
> > + 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';
> > + ctx->buf = (char *)&dirent;
> > + ext2fs_get_rec_len(fs, &dirent, &ctx->buflen);
> > + retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
> > + if (retval & 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);
> > + retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
> > + if (retval & BLOCK_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;
> > + }
> > + if (retval & 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;
> > + retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
> > + if (retval & BLOCK_CHANGED) {
> > + errcode_t err;
> > +
> > + err = ext2fs_write_inode(fs, ino, &inode);
> > + if (err)
> > + goto out;
> > + }
> > + if (retval & BLOCK_ABORT)
> > + goto out;
> > +
> > + data.fs = fs;
> > + data.ino = ino;
> > + retval = ext2fs_inline_data_ea_get(&data);
> > + if (retval)
> > + goto out;
> > + if (data.ea_size > 0) {
> > + ctx->buf = data.ea_data;
> > + ctx->buflen = data.ea_size;
> > + retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
> > + if (retval & BLOCK_CHANGED)
> > + ext2fs_inline_data_ea_set(&data);
> > + ext2fs_free_mem(&data.ea_data);
> > + ctx->buf = 0;
> > + }
> > +
> > +out:
> > + retval |= BLOCK_ERROR;
> > + return retval & BLOCK_ERROR ? ctx->errcode : 0;
> > +}
> > diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c
> > index 1295e81..cfbe5dd 100644
> > --- a/lib/ext2fs/swapfs.c
> > +++ b/lib/ext2fs/swapfs.c
> > @@ -207,6 +207,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;
> >
> > @@ -233,12 +234,19 @@ 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 are swapped on access, not here
> > + * Inline data are not swapped beside parent ino is accessed
>
> From the other patches it looks like the parent ino is swapped on access too.
>
> I'm confused about what this comment is trying to say, though the code here
> says that inline data is swapped on access (if at all).

OK. I will clarify this.

Thanks,
- Zheng

2013-12-04 05:10:34

by Darrick J. Wong

[permalink] [raw]
Subject: Re: [PATCH v2 09/28] libext2fs: handle inline data in dir iterator function

On Wed, Dec 04, 2013 at 12:57:36PM +0800, Zheng Liu wrote:
> On Tue, Dec 03, 2013 at 02:13:49PM -0800, Darrick J. Wong wrote:
> > On Tue, Dec 03, 2013 at 08:11:36PM +0800, Zheng Liu wrote:
> > > From: Zheng Liu <[email protected]>
> > >
> > > 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:
> > > - cd
> > > - chroot
> > > - link*
> > > - ls
> > > - ncheck
> > > - pwd
> > > - unlink
> > >
> > > * TODO: Inline_data doesn't expand to ibody extended attribute because
> > > link command doesn't handle DIR_NO_SPACE error until now. But if we
> > > have already expanded inline data to ibody ea area, link command can
> > > occupy this space.
> >
> > A patch for this TODO is coming, right?
>
> TBH, I don't have a patch for this because I don't know why ext2fs_link
> doesn't handle DIR_NO_SPACE error. So I will try to fix it later.

Yeah, it's sort of annoying that it doesn't do that. You might notice that
fuse2fs will detect that error code, call ext2fs_expand_dir(), and try again.
On the other hand, none of the other programs do that...

...it's not difficult to change ext2fs_link() to do that, though.

again:

/* link_proc magic... */

if (!ls.done && !not_again) {
ext2fs_expand_dir(fs, dir...);
not_again = 1;
goto again;
}

Hmm. That /is/ easy to fix. I might as well fix that.

--D

>
> >
> > > Signed-off-by: Theodore Ts'o <[email protected]>
> > > Signed-off-by: Zheng Liu <[email protected]>
> > > ---
> > > lib/ext2fs/Makefile.in | 8 +++
> > > lib/ext2fs/Makefile.pq | 1 +
> > > lib/ext2fs/dir_iterate.c | 62 +++++++++++-----
> > > lib/ext2fs/ext2_err.et.in | 3 +
> > > lib/ext2fs/ext2_fs.h | 10 +++
> > > lib/ext2fs/ext2fs.h | 1 +
> > > lib/ext2fs/ext2fsP.h | 11 +++
> > > lib/ext2fs/inline_data.c | 175 +++++++++++++++++++++++++++++++++++++++++++++
> > > lib/ext2fs/swapfs.c | 12 +++-
> > > 9 files changed, 265 insertions(+), 18 deletions(-)
> > > create mode 100644 lib/ext2fs/inline_data.c
> > >
> > > diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
> > > index e1e6216..4d011c9 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 \
> > > @@ -758,6 +760,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 2f7b654..89082a7 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 8be0ac2..1624371 100644
> > > --- a/lib/ext2fs/dir_iterate.c
> > > +++ b/lib/ext2fs/dir_iterate.c
> > > @@ -127,6 +127,12 @@ 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;
> > > + retval = ext2fs_inline_data_dir_iterate(fs, dir,
> > > + ext2fs_process_dir_block,
> > > + &ctx);
> > > + }
> > > if (retval)
> > > return retval;
> > > return ctx.errcode;
> > > @@ -189,30 +195,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;
> >
> > Since we don't swap i_block[] when we're reading in the inode, who calls
> > ext2fs_dirent_swab_in()?
>
> Good catch! I will call this on ext2fs_inline_data_dir_iterate().
>
> >
> > > + }
> > >
> > > 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 +236,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 +256,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 +294,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_CHANGED to notify caller that inline
> > > + * data has been changed
> > > + */
> >
> > I don't think I like overloading the meaning of BLOCK_CHANGED here. For a
> > non-inlinedata file, the iterator function returning BLOCK_CHANGED means that
> > the lblk->pblk mapping changed, whereas for an inline data file, it means that
> > the block *contents* have changed.
> >
> > At best, the two meanings ought to be documented wherever BLOCK_CHANGED is
> > defined, though I think I'd prefer a new flag BLOCK_INLINE_DATA_CHANGED to
> > handle this situation, because otherwise the meaning of the flag is
> > context-dependent and therefore more difficult to reason about.
> >
> > Separate flags also gives us the ability to check for programming errors, such
> > as someone returning BLOCK_CHANGED on inlinedata files or returning
> > BLOCK_INLINE_DATA_CHANGED on !inlinedata files.
>
> Fair enough.
>
> >
> > > + retval = BLOCK_CHANGED;
> >
> > Who calls ext2fs_dirent_swab_out()?
>
> Ditto.
>
> >
> > > + }
> > > }
> > > 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 b819a90..0781145 100644
> > > --- a/lib/ext2fs/ext2_err.et.in
> > > +++ b/lib/ext2fs/ext2_err.et.in
> > > @@ -500,4 +500,7 @@ ec EXT2_ET_EA_KEY_NOT_FOUND,
> > > ec EXT2_ET_EA_NO_SPACE,
> > > "Insufficient space to store extended attribute data"
> > >
> > > +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 6a28d55..5ab16ae 100644
> > > --- a/lib/ext2fs/ext2_fs.h
> > > +++ b/lib/ext2fs/ext2_fs.h
> > > @@ -914,4 +914,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 e251435..c5b73fd 100644
> > > --- a/lib/ext2fs/ext2fs.h
> > > +++ b/lib/ext2fs/ext2fs.h
> > > @@ -438,6 +438,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 80d2d0a..5ab6084 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,16 @@ extern int ext2fs_process_dir_block(ext2_filsys fs,
> > > int ref_offset,
> > > void *priv_data);
> > >
> > > +extern errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
> > > + ext2_ino_t ino,
> > > + int (*func)(ext2_filsys fs,
> > > + blk64_t *blocknr,
> > > + e2_blkcnt_t blockcnt,
> > > + blk64_t ref_blk,
> > > + int ref_offset,
> > > + void *priv_data),
> > > + 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 0000000..cc2954d
> > > --- /dev/null
> > > +++ b/lib/ext2fs/inline_data.c
> > > @@ -0,0 +1,175 @@
> > > +/*
> > > + * inline_data.c --- data in inode
> > > + *
> > > + * Copyright (C) 2012 Zheng Liu <[email protected]>
> > > + *
> > > + * %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 <stdio.h>
> > > +#include <time.h>
> > > +
> > > +#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 */
> > > + char *ea_data;
> >
> > Should this be a void* since the file data type is opaque to the library?
>
> I will fix it.
>
> >
> > > +};
> > > +
> > > +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;
> > > +}
> > > +
> > > +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;
> > > +}
> > > +
> > > +
> > > +errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
> > > + ext2_ino_t ino,
> > > + int (*func)(ext2_filsys fs,
> > > + blk64_t *blocknr,
> > > + e2_blkcnt_t blockcnt,
> > > + blk64_t ref_blk,
> > > + int ref_offset,
> > > + void *priv_data),
> > > + void *priv_data)
> > > +{
> > > + struct dir_context *ctx;
> > > + struct ext2_inode inode;
> > > + struct ext2_dir_entry dirent;
> > > + struct ext2_inline_data data;
> > > + errcode_t retval = 0;
> > > + e2_blkcnt_t blockcnt = 0;
> > > +
> > > + ctx = (struct dir_context *)priv_data;
> > > +
> > > + retval = ext2fs_read_inode(fs, ino, &inode);
> > > + if (retval)
> > > + goto out;
> > > +
> > > + if (!(inode.i_flags & EXT4_INLINE_DATA_FL))
> > > + return EXT2_ET_NO_INLINE_DATA;
> > > +
> > > + if (!LINUX_S_ISDIR(inode.i_mode)) {
> > > + retval = EXT2_ET_NO_DIRECTORY;
> > > + 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';
> > > + ctx->buf = (char *)&dirent;
> > > + ext2fs_get_rec_len(fs, &dirent, &ctx->buflen);
> > > + retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
> > > + if (retval & 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);
> > > + retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
> > > + if (retval & BLOCK_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;
> > > + }
> > > + if (retval & 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;
> > > + retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
> > > + if (retval & BLOCK_CHANGED) {
> > > + errcode_t err;
> > > +
> > > + err = ext2fs_write_inode(fs, ino, &inode);
> > > + if (err)
> > > + goto out;
> > > + }
> > > + if (retval & BLOCK_ABORT)
> > > + goto out;
> > > +
> > > + data.fs = fs;
> > > + data.ino = ino;
> > > + retval = ext2fs_inline_data_ea_get(&data);
> > > + if (retval)
> > > + goto out;
> > > + if (data.ea_size > 0) {
> > > + ctx->buf = data.ea_data;
> > > + ctx->buflen = data.ea_size;
> > > + retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
> > > + if (retval & BLOCK_CHANGED)
> > > + ext2fs_inline_data_ea_set(&data);
> > > + ext2fs_free_mem(&data.ea_data);
> > > + ctx->buf = 0;
> > > + }
> > > +
> > > +out:
> > > + retval |= BLOCK_ERROR;
> > > + return retval & BLOCK_ERROR ? ctx->errcode : 0;
> > > +}
> > > diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c
> > > index 1295e81..cfbe5dd 100644
> > > --- a/lib/ext2fs/swapfs.c
> > > +++ b/lib/ext2fs/swapfs.c
> > > @@ -207,6 +207,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;
> > >
> > > @@ -233,12 +234,19 @@ 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 are swapped on access, not here
> > > + * Inline data are not swapped beside parent ino is accessed
> >
> > From the other patches it looks like the parent ino is swapped on access too.
> >
> > I'm confused about what this comment is trying to say, though the code here
> > says that inline data is swapped on access (if at all).
>
> OK. I will clarify this.
>
> Thanks,
> - Zheng

2013-12-04 05:18:55

by Zheng Liu

[permalink] [raw]
Subject: Re: [PATCH v2 19/28] mke2fs: add inline_data support in mke2fs

On Tue, Dec 03, 2013 at 08:08:19PM -0800, Darrick J. Wong wrote:
> On Wed, Dec 04, 2013 at 11:27:57AM +0800, Zheng Liu wrote:
> > On Tue, Dec 03, 2013 at 02:30:26PM -0800, Darrick J. Wong wrote:
> > [...]
> > > > + * notify users that inline data will never be useful.
> > > > + */
> > > > + if ((fs_param.s_feature_incompat &
> > > > + EXT4_FEATURE_INCOMPAT_INLINE_DATA) &&
> > > > + inode_size == EXT2_GOOD_OLD_INODE_SIZE) {
> > >
> > > Perhaps I'm missing something here, but why is it impossible to use i_blocks
> > > for inline data even if there's no space for EAs?
> >
> > If I understand correctly, on kernel side, we determine an inode has
> > inline data according to whether we have 'system.data' xattr entry on
> > inode extended attribute space. If an inode doesn't have enough space
> > to store an entry with 'system.data', we just think this inode doesn't
> > has inline data. So that is why I add this sanity check.
>
> Ok. I was curious. Small inode => no inline data seems like an unfortunate
> restriction to me, but oh well, it's your feature. I don't plan to go back to
> 128 byte inodes ever. :)
>
> Also, we could store four more bytes if we created a new e_name_index value (5?
> 9?) to represent "system.data". Any thoughts about that?

Sorry, I don't get your point. Do you want to create a new e_name_index?
Any reason lets you want to do this?

- Zheng

>
> --D
>
> >
> > Regards,
> > - Zheng
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> > the body of a message to [email protected]
> > 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 [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-12-04 05:23:22

by Zheng Liu

[permalink] [raw]
Subject: Re: [PATCH v2 09/28] libext2fs: handle inline data in dir iterator function

On Tue, Dec 03, 2013 at 09:10:25PM -0800, Darrick J. Wong wrote:
> On Wed, Dec 04, 2013 at 12:57:36PM +0800, Zheng Liu wrote:
> > On Tue, Dec 03, 2013 at 02:13:49PM -0800, Darrick J. Wong wrote:
> > > On Tue, Dec 03, 2013 at 08:11:36PM +0800, Zheng Liu wrote:
> > > > From: Zheng Liu <[email protected]>
> > > >
> > > > 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:
> > > > - cd
> > > > - chroot
> > > > - link*
> > > > - ls
> > > > - ncheck
> > > > - pwd
> > > > - unlink
> > > >
> > > > * TODO: Inline_data doesn't expand to ibody extended attribute because
> > > > link command doesn't handle DIR_NO_SPACE error until now. But if we
> > > > have already expanded inline data to ibody ea area, link command can
> > > > occupy this space.
> > >
> > > A patch for this TODO is coming, right?
> >
> > TBH, I don't have a patch for this because I don't know why ext2fs_link
> > doesn't handle DIR_NO_SPACE error. So I will try to fix it later.
>
> Yeah, it's sort of annoying that it doesn't do that. You might notice that
> fuse2fs will detect that error code, call ext2fs_expand_dir(), and try again.
> On the other hand, none of the other programs do that...
>
> ...it's not difficult to change ext2fs_link() to do that, though.
>
> again:
>
> /* link_proc magic... */
>
> if (!ls.done && !not_again) {
> ext2fs_expand_dir(fs, dir...);
> not_again = 1;
> goto again;
> }
>
> Hmm. That /is/ easy to fix. I might as well fix that.

I don't think we should fix it in ext2fs_link because I am afraid that
we could break the client that has handled this problem. So I am plan
to fix it in make_link(). What do you think?

- Zheng

2013-12-04 05:25:02

by Zheng Liu

[permalink] [raw]
Subject: Re: [PATCH v2 20/28] tune2fs: add inline_data feature in tune2fs

On Tue, Dec 03, 2013 at 08:03:00PM -0800, Darrick J. Wong wrote:
> On Wed, Dec 04, 2013 at 11:55:36AM +0800, Zheng Liu wrote:
> > On Tue, Dec 03, 2013 at 02:29:46PM -0800, Darrick J. Wong wrote:
> > > On Tue, Dec 03, 2013 at 08:11:47PM +0800, Zheng Liu wrote:
> > > > From: Zheng Liu <[email protected]>
> > > >
> > > > Inline_data can be enabled without ext_attr. Hence we don't check it.
> > > > As doing in mke2fs we need to check inode size.
> > > >
> > > > Now this feature only can be enabled because we may be out of space when
> > > > disabling it. If this feature is disabled, we need to allocate a block
> > > > for every file and directory, and it might exhaust all space.
> > >
> > > Could we at least try to turn off inlinedata and decline to clear the flag if
> > > we ENOSPC?
> >
> > If we want to turn off inline_data, that means that we need to scan all
> > used inodes and try to expand it. It isn't hard but I am wondering if
> > someone really want to disable this feature. In my opinion, I really
> > want to enable inline_data feature by default when we believe it is very
> > stable because it will benefit for all desktop and Android users.
>
> I guess that really depends on our testers -- on the one hand, I do most of my
> testing with scratch filesystems so on a practical level I will never need the
> ability to "undo" these sorts of things.
>
> On the other hand, there's the argument that more people will test code if they
> think they can go back to the old system if the new one turns out to be a mess.
>
> Same sort of reasoning applies to "why did you implement converting 64bit ->
> 32bit filesystems?" :)

Fair enough. You convince me. :)

- Zheng

>
> --D
> >
> > - Zheng
> >
> > >
> > > (Yes, that's more of a TODO feature request...)
> > >
> > > > Signed-off-by: Theodore Ts'o <[email protected]>
> > > > Signed-off-by: Zheng Liu <[email protected]>
> > > > ---
> > > > misc/tune2fs.8.in | 5 +++++
> > > > misc/tune2fs.c | 16 +++++++++++++++-
> > > > 2 files changed, 20 insertions(+), 1 deletion(-)
> > > >
> > > > diff --git a/misc/tune2fs.8.in b/misc/tune2fs.8.in
> > > > index 55c6dd9..78f965b 100644
> > > > --- a/misc/tune2fs.8.in
> > > > +++ b/misc/tune2fs.8.in
> > > > @@ -531,6 +531,11 @@ Setting the filesystem feature is equivalent to using the
> > > > .B \-j
> > > > option.
> > > > .TP
> > > > +.B inline_data
> > > > +Allow data to be stored in the inode and extented attribute area.
> > >
> > > "extended"
> > >
> > > --D
> > >
> > > > +.B Tune2fs
> > > > +only supports setting this filesystem feature.
> > > > +.TP
> > > > .B large_file
> > > > Filesystem can contain files that are greater than 2GB. (Modern kernels
> > > > set this feature automatically when a file > 2GB is created.)
> > > > diff --git a/misc/tune2fs.c b/misc/tune2fs.c
> > > > index 95c1886..1952081 100644
> > > > --- a/misc/tune2fs.c
> > > > +++ b/misc/tune2fs.c
> > > > @@ -140,7 +140,8 @@ static __u32 ok_features[3] = {
> > > > EXT2_FEATURE_INCOMPAT_FILETYPE |
> > > > EXT3_FEATURE_INCOMPAT_EXTENTS |
> > > > EXT4_FEATURE_INCOMPAT_FLEX_BG |
> > > > - EXT4_FEATURE_INCOMPAT_MMP,
> > > > + EXT4_FEATURE_INCOMPAT_MMP |
> > > > + EXT4_FEATURE_INCOMPAT_INLINE_DATA,
> > > > /* R/O compat */
> > > > EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
> > > > EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
> > > > @@ -1083,6 +1084,19 @@ mmp_error:
> > > > disable_uninit_bg(fs,
> > > > EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
> > > >
> > > > + if (FEATURE_ON(E2P_FEATURE_INCOMPAT,
> > > > + EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
> > > > + /*
> > > > + * Check inode size. If inode size is 128, tell user that
> > > > + * inline data is useless.
> > > > + */
> > > > + if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE) {
> > > > + fputs(_("The inode size is too small to "
> > > > + "store inline data.\n"), stderr);
> > > > + return 1;
> > > > + }
> > > > + }
> > > > +
> > > > if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
> > > > EXT4_FEATURE_RO_COMPAT_QUOTA)) {
> > > > /*
> > > > --
> > > > 1.7.9.7
> > > >
> > > > --
> > > > To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> > > > the body of a message to [email protected]
> > > > 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 [email protected]
> > 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 [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-12-04 05:31:16

by Darrick J. Wong

[permalink] [raw]
Subject: Re: [PATCH v2 19/28] mke2fs: add inline_data support in mke2fs

On Wed, Dec 04, 2013 at 01:21:50PM +0800, Zheng Liu wrote:
> On Tue, Dec 03, 2013 at 08:08:19PM -0800, Darrick J. Wong wrote:
> > On Wed, Dec 04, 2013 at 11:27:57AM +0800, Zheng Liu wrote:
> > > On Tue, Dec 03, 2013 at 02:30:26PM -0800, Darrick J. Wong wrote:
> > > [...]
> > > > > + * notify users that inline data will never be useful.
> > > > > + */
> > > > > + if ((fs_param.s_feature_incompat &
> > > > > + EXT4_FEATURE_INCOMPAT_INLINE_DATA) &&
> > > > > + inode_size == EXT2_GOOD_OLD_INODE_SIZE) {
> > > >
> > > > Perhaps I'm missing something here, but why is it impossible to use i_blocks
> > > > for inline data even if there's no space for EAs?
> > >
> > > If I understand correctly, on kernel side, we determine an inode has
> > > inline data according to whether we have 'system.data' xattr entry on
> > > inode extended attribute space. If an inode doesn't have enough space
> > > to store an entry with 'system.data', we just think this inode doesn't
> > > has inline data. So that is why I add this sanity check.
> >
> > Ok. I was curious. Small inode => no inline data seems like an unfortunate
> > restriction to me, but oh well, it's your feature. I don't plan to go back to
> > 128 byte inodes ever. :)
> >
> > Also, we could store four more bytes if we created a new e_name_index value (5?
> > 9?) to represent "system.data". Any thoughts about that?
>
> Sorry, I don't get your point. Do you want to create a new e_name_index?
> Any reason lets you want to do this?

Yep, that's exactly what I propose to do, so we can cram four more bytes into
the inline data.

--D
>
> - Zheng
>
> >
> > --D
> >
> > >
> > > Regards,
> > > - Zheng
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> > > the body of a message to [email protected]
> > > 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 [email protected]
> > 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 [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-12-04 05:47:55

by Zheng Liu

[permalink] [raw]
Subject: Re: [PATCH v2 19/28] mke2fs: add inline_data support in mke2fs

[Cc Tao to get some comments]

On Tue, Dec 03, 2013 at 09:26:08PM -0800, Darrick J. Wong wrote:
> On Wed, Dec 04, 2013 at 01:21:50PM +0800, Zheng Liu wrote:
> > On Tue, Dec 03, 2013 at 08:08:19PM -0800, Darrick J. Wong wrote:
> > > On Wed, Dec 04, 2013 at 11:27:57AM +0800, Zheng Liu wrote:
> > > > On Tue, Dec 03, 2013 at 02:30:26PM -0800, Darrick J. Wong wrote:
> > > > [...]
> > > > > > + * notify users that inline data will never be useful.
> > > > > > + */
> > > > > > + if ((fs_param.s_feature_incompat &
> > > > > > + EXT4_FEATURE_INCOMPAT_INLINE_DATA) &&
> > > > > > + inode_size == EXT2_GOOD_OLD_INODE_SIZE) {
> > > > >
> > > > > Perhaps I'm missing something here, but why is it impossible to use i_blocks
> > > > > for inline data even if there's no space for EAs?
> > > >
> > > > If I understand correctly, on kernel side, we determine an inode has
> > > > inline data according to whether we have 'system.data' xattr entry on
> > > > inode extended attribute space. If an inode doesn't have enough space
> > > > to store an entry with 'system.data', we just think this inode doesn't
> > > > has inline data. So that is why I add this sanity check.
> > >
> > > Ok. I was curious. Small inode => no inline data seems like an unfortunate
> > > restriction to me, but oh well, it's your feature. I don't plan to go back to
> > > 128 byte inodes ever. :)
> > >
> > > Also, we could store four more bytes if we created a new e_name_index value (5?
> > > 9?) to represent "system.data". Any thoughts about that?
> >
> > Sorry, I don't get your point. Do you want to create a new e_name_index?
> > Any reason lets you want to do this?
>
> Yep, that's exactly what I propose to do, so we can cram four more bytes into
> the inline data.

Agree. I believe it is fine. But I am wondering if it will break the
file system that inline data has been enabled.

- Zheng

2013-12-04 06:02:34

by Darrick J. Wong

[permalink] [raw]
Subject: Re: [PATCH v2 19/28] mke2fs: add inline_data support in mke2fs

On Wed, Dec 04, 2013 at 01:50:52PM +0800, Zheng Liu wrote:
> [Cc Tao to get some comments]
>
> On Tue, Dec 03, 2013 at 09:26:08PM -0800, Darrick J. Wong wrote:
> > On Wed, Dec 04, 2013 at 01:21:50PM +0800, Zheng Liu wrote:
> > > On Tue, Dec 03, 2013 at 08:08:19PM -0800, Darrick J. Wong wrote:
> > > > On Wed, Dec 04, 2013 at 11:27:57AM +0800, Zheng Liu wrote:
> > > > > On Tue, Dec 03, 2013 at 02:30:26PM -0800, Darrick J. Wong wrote:
> > > > > [...]
> > > > > > > + * notify users that inline data will never be useful.
> > > > > > > + */
> > > > > > > + if ((fs_param.s_feature_incompat &
> > > > > > > + EXT4_FEATURE_INCOMPAT_INLINE_DATA) &&
> > > > > > > + inode_size == EXT2_GOOD_OLD_INODE_SIZE) {
> > > > > >
> > > > > > Perhaps I'm missing something here, but why is it impossible to use i_blocks
> > > > > > for inline data even if there's no space for EAs?
> > > > >
> > > > > If I understand correctly, on kernel side, we determine an inode has
> > > > > inline data according to whether we have 'system.data' xattr entry on
> > > > > inode extended attribute space. If an inode doesn't have enough space
> > > > > to store an entry with 'system.data', we just think this inode doesn't
> > > > > has inline data. So that is why I add this sanity check.
> > > >
> > > > Ok. I was curious. Small inode => no inline data seems like an unfortunate
> > > > restriction to me, but oh well, it's your feature. I don't plan to go back to
> > > > 128 byte inodes ever. :)
> > > >
> > > > Also, we could store four more bytes if we created a new e_name_index value (5?
> > > > 9?) to represent "system.data". Any thoughts about that?
> > >
> > > Sorry, I don't get your point. Do you want to create a new e_name_index?
> > > Any reason lets you want to do this?
> >
> > Yep, that's exactly what I propose to do, so we can cram four more bytes into
> > the inline data.
>
> Agree. I believe it is fine. But I am wondering if it will break the
> file system that inline data has been enabled.

There's a fair amount of work needed for fs/ext4/inline.c. My e2fsprogs thing
should handle it fine, though I think the inlinedata_max_size function
somewhere in your patchset might also be broken.

I suspect a lot depends on how widely deployed inlinedata is inside Taobao, or
anyone else who's actually running it right now.

--D
>
> - Zheng
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-12-04 06:07:49

by Darrick J. Wong

[permalink] [raw]
Subject: Re: [PATCH v2 09/28] libext2fs: handle inline data in dir iterator function

On Wed, Dec 04, 2013 at 01:26:19PM +0800, Zheng Liu wrote:
> On Tue, Dec 03, 2013 at 09:10:25PM -0800, Darrick J. Wong wrote:
> > On Wed, Dec 04, 2013 at 12:57:36PM +0800, Zheng Liu wrote:
> > > On Tue, Dec 03, 2013 at 02:13:49PM -0800, Darrick J. Wong wrote:
> > > > On Tue, Dec 03, 2013 at 08:11:36PM +0800, Zheng Liu wrote:
> > > > > From: Zheng Liu <[email protected]>
> > > > >
> > > > > 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:
> > > > > - cd
> > > > > - chroot
> > > > > - link*
> > > > > - ls
> > > > > - ncheck
> > > > > - pwd
> > > > > - unlink
> > > > >
> > > > > * TODO: Inline_data doesn't expand to ibody extended attribute because
> > > > > link command doesn't handle DIR_NO_SPACE error until now. But if we
> > > > > have already expanded inline data to ibody ea area, link command can
> > > > > occupy this space.
> > > >
> > > > A patch for this TODO is coming, right?
> > >
> > > TBH, I don't have a patch for this because I don't know why ext2fs_link
> > > doesn't handle DIR_NO_SPACE error. So I will try to fix it later.
> >
> > Yeah, it's sort of annoying that it doesn't do that. You might notice that
> > fuse2fs will detect that error code, call ext2fs_expand_dir(), and try again.
> > On the other hand, none of the other programs do that...
> >
> > ...it's not difficult to change ext2fs_link() to do that, though.
> >
> > again:
> >
> > /* link_proc magic... */
> >
> > if (!ls.done && !not_again) {
> > ext2fs_expand_dir(fs, dir...);
> > not_again = 1;
> > goto again;
> > }
> >
> > Hmm. That /is/ easy to fix. I might as well fix that.
>
> I don't think we should fix it in ext2fs_link because I am afraid that
> we could break the client that has handled this problem. So I am plan
> to fix it in make_link(). What do you think?

I think the clients will be fine. Most likely, if the dir can't be expanded,
then ext2fs_link will return ext2fs_expand_directory's failure code.

On the off chance there's a weird bug somewhere such that we successfully
expand the dir but _link's built-in retry fails again, then the client will see
the "no dir space" error, expand the dir (again!), and retry the link, which
will again fail. Assuming the client doesn't madly retry in a loop, all that
means is that we bang our heads against the wall a few more times than we need
to.

Luckily, most of the _link clients in the e2fsprogs source are too stupid even
to expand-and-retry.

--D

>
> - Zheng
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-12-04 06:15:09

by Zheng Liu

[permalink] [raw]
Subject: Re: [PATCH v2 19/28] mke2fs: add inline_data support in mke2fs

On Tue, Dec 03, 2013 at 10:02:24PM -0800, Darrick J. Wong wrote:
> On Wed, Dec 04, 2013 at 01:50:52PM +0800, Zheng Liu wrote:
> > [Cc Tao to get some comments]
> >
> > On Tue, Dec 03, 2013 at 09:26:08PM -0800, Darrick J. Wong wrote:
> > > On Wed, Dec 04, 2013 at 01:21:50PM +0800, Zheng Liu wrote:
> > > > On Tue, Dec 03, 2013 at 08:08:19PM -0800, Darrick J. Wong wrote:
> > > > > On Wed, Dec 04, 2013 at 11:27:57AM +0800, Zheng Liu wrote:
> > > > > > On Tue, Dec 03, 2013 at 02:30:26PM -0800, Darrick J. Wong wrote:
> > > > > > [...]
> > > > > > > > + * notify users that inline data will never be useful.
> > > > > > > > + */
> > > > > > > > + if ((fs_param.s_feature_incompat &
> > > > > > > > + EXT4_FEATURE_INCOMPAT_INLINE_DATA) &&
> > > > > > > > + inode_size == EXT2_GOOD_OLD_INODE_SIZE) {
> > > > > > >
> > > > > > > Perhaps I'm missing something here, but why is it impossible to use i_blocks
> > > > > > > for inline data even if there's no space for EAs?
> > > > > >
> > > > > > If I understand correctly, on kernel side, we determine an inode has
> > > > > > inline data according to whether we have 'system.data' xattr entry on
> > > > > > inode extended attribute space. If an inode doesn't have enough space
> > > > > > to store an entry with 'system.data', we just think this inode doesn't
> > > > > > has inline data. So that is why I add this sanity check.
> > > > >
> > > > > Ok. I was curious. Small inode => no inline data seems like an unfortunate
> > > > > restriction to me, but oh well, it's your feature. I don't plan to go back to
> > > > > 128 byte inodes ever. :)
> > > > >
> > > > > Also, we could store four more bytes if we created a new e_name_index value (5?
> > > > > 9?) to represent "system.data". Any thoughts about that?
> > > >
> > > > Sorry, I don't get your point. Do you want to create a new e_name_index?
> > > > Any reason lets you want to do this?
> > >
> > > Yep, that's exactly what I propose to do, so we can cram four more bytes into
> > > the inline data.
> >
> > Agree. I believe it is fine. But I am wondering if it will break the
> > file system that inline data has been enabled.
>
> There's a fair amount of work needed for fs/ext4/inline.c. My e2fsprogs thing
> should handle it fine, though I think the inlinedata_max_size function
> somewhere in your patchset might also be broken.
>
> I suspect a lot depends on how widely deployed inlinedata is inside Taobao, or
> anyone else who's actually running it right now.

That is my concern because at Taobao we have already used inline data on
our own servers. So obviously it will break our file system. :(

- Zheng

2013-12-04 06:18:49

by Zheng Liu

[permalink] [raw]
Subject: Re: [PATCH v2 09/28] libext2fs: handle inline data in dir iterator function

On Tue, Dec 03, 2013 at 10:07:42PM -0800, Darrick J. Wong wrote:
> On Wed, Dec 04, 2013 at 01:26:19PM +0800, Zheng Liu wrote:
> > On Tue, Dec 03, 2013 at 09:10:25PM -0800, Darrick J. Wong wrote:
> > > On Wed, Dec 04, 2013 at 12:57:36PM +0800, Zheng Liu wrote:
> > > > On Tue, Dec 03, 2013 at 02:13:49PM -0800, Darrick J. Wong wrote:
> > > > > On Tue, Dec 03, 2013 at 08:11:36PM +0800, Zheng Liu wrote:
> > > > > > From: Zheng Liu <[email protected]>
> > > > > >
> > > > > > 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:
> > > > > > - cd
> > > > > > - chroot
> > > > > > - link*
> > > > > > - ls
> > > > > > - ncheck
> > > > > > - pwd
> > > > > > - unlink
> > > > > >
> > > > > > * TODO: Inline_data doesn't expand to ibody extended attribute because
> > > > > > link command doesn't handle DIR_NO_SPACE error until now. But if we
> > > > > > have already expanded inline data to ibody ea area, link command can
> > > > > > occupy this space.
> > > > >
> > > > > A patch for this TODO is coming, right?
> > > >
> > > > TBH, I don't have a patch for this because I don't know why ext2fs_link
> > > > doesn't handle DIR_NO_SPACE error. So I will try to fix it later.
> > >
> > > Yeah, it's sort of annoying that it doesn't do that. You might notice that
> > > fuse2fs will detect that error code, call ext2fs_expand_dir(), and try again.
> > > On the other hand, none of the other programs do that...
> > >
> > > ...it's not difficult to change ext2fs_link() to do that, though.
> > >
> > > again:
> > >
> > > /* link_proc magic... */
> > >
> > > if (!ls.done && !not_again) {
> > > ext2fs_expand_dir(fs, dir...);
> > > not_again = 1;
> > > goto again;
> > > }
> > >
> > > Hmm. That /is/ easy to fix. I might as well fix that.
> >
> > I don't think we should fix it in ext2fs_link because I am afraid that
> > we could break the client that has handled this problem. So I am plan
> > to fix it in make_link(). What do you think?
>
> I think the clients will be fine. Most likely, if the dir can't be expanded,
> then ext2fs_link will return ext2fs_expand_directory's failure code.
>
> On the off chance there's a weird bug somewhere such that we successfully
> expand the dir but _link's built-in retry fails again, then the client will see
> the "no dir space" error, expand the dir (again!), and retry the link, which
> will again fail. Assuming the client doesn't madly retry in a loop, all that
> means is that we bang our heads against the wall a few more times than we need
> to.
>
> Luckily, most of the _link clients in the e2fsprogs source are too stupid even
> to expand-and-retry.

OK. So let me fix it. :)

- Zheng

2013-12-04 07:33:30

by Darrick J. Wong

[permalink] [raw]
Subject: Re: [PATCH v2 09/28] libext2fs: handle inline data in dir iterator function

On Wed, Dec 04, 2013 at 02:21:42PM +0800, Zheng Liu wrote:
> On Tue, Dec 03, 2013 at 10:07:42PM -0800, Darrick J. Wong wrote:
> > On Wed, Dec 04, 2013 at 01:26:19PM +0800, Zheng Liu wrote:
> > > On Tue, Dec 03, 2013 at 09:10:25PM -0800, Darrick J. Wong wrote:
> > > > On Wed, Dec 04, 2013 at 12:57:36PM +0800, Zheng Liu wrote:
> > > > > On Tue, Dec 03, 2013 at 02:13:49PM -0800, Darrick J. Wong wrote:
> > > > > > On Tue, Dec 03, 2013 at 08:11:36PM +0800, Zheng Liu wrote:
> > > > > > > From: Zheng Liu <[email protected]>
> > > > > > >
> > > > > > > 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:
> > > > > > > - cd
> > > > > > > - chroot
> > > > > > > - link*
> > > > > > > - ls
> > > > > > > - ncheck
> > > > > > > - pwd
> > > > > > > - unlink
> > > > > > >
> > > > > > > * TODO: Inline_data doesn't expand to ibody extended attribute because
> > > > > > > link command doesn't handle DIR_NO_SPACE error until now. But if we
> > > > > > > have already expanded inline data to ibody ea area, link command can
> > > > > > > occupy this space.
> > > > > >
> > > > > > A patch for this TODO is coming, right?
> > > > >
> > > > > TBH, I don't have a patch for this because I don't know why ext2fs_link
> > > > > doesn't handle DIR_NO_SPACE error. So I will try to fix it later.
> > > >
> > > > Yeah, it's sort of annoying that it doesn't do that. You might notice that
> > > > fuse2fs will detect that error code, call ext2fs_expand_dir(), and try again.
> > > > On the other hand, none of the other programs do that...
> > > >
> > > > ...it's not difficult to change ext2fs_link() to do that, though.
> > > >
> > > > again:
> > > >
> > > > /* link_proc magic... */
> > > >
> > > > if (!ls.done && !not_again) {
> > > > ext2fs_expand_dir(fs, dir...);
> > > > not_again = 1;
> > > > goto again;
> > > > }
> > > >
> > > > Hmm. That /is/ easy to fix. I might as well fix that.
> > >
> > > I don't think we should fix it in ext2fs_link because I am afraid that
> > > we could break the client that has handled this problem. So I am plan
> > > to fix it in make_link(). What do you think?
> >
> > I think the clients will be fine. Most likely, if the dir can't be expanded,
> > then ext2fs_link will return ext2fs_expand_directory's failure code.
> >
> > On the off chance there's a weird bug somewhere such that we successfully
> > expand the dir but _link's built-in retry fails again, then the client will see
> > the "no dir space" error, expand the dir (again!), and retry the link, which
> > will again fail. Assuming the client doesn't madly retry in a loop, all that
> > means is that we bang our heads against the wall a few more times than we need
> > to.
> >
> > Luckily, most of the _link clients in the e2fsprogs source are too stupid even
> > to expand-and-retry.
>
> OK. So let me fix it. :)

Well, I need to fix up fuse2fs too anyway... something like this?

--D

diff --git a/lib/ext2fs/link.c b/lib/ext2fs/link.c
index 09e6cb4..2cbf335 100644
--- a/lib/ext2fs/link.c
+++ b/lib/ext2fs/link.c
@@ -158,12 +158,14 @@ errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name,
errcode_t retval;
struct link_struct ls;
struct ext2_inode inode;
+ int tries = 0;

EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

if (!(fs->flags & EXT2_FLAG_RW))
return EXT2_ET_RO_FILSYS;

+again:
ls.fs = fs;
ls.name = name;
ls.namelen = name ? strlen(name) : 0;
@@ -181,8 +183,16 @@ errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name,
if (ls.err)
return ls.err;

- if (!ls.done)
- return EXT2_ET_DIR_NO_SPACE;
+ /* If we didn't find space, expand the dir and try again */
+ if (!ls.done) {
+ if (tries)
+ return EXT2_ET_DIR_NO_SPACE;
+ retval = ext2fs_expand_dir(fs, dir);
+ if (retval)
+ return retval;
+ tries++;
+ goto again;
+ }

if ((retval = ext2fs_read_inode(fs, dir, &inode)) != 0)
return retval;

2013-12-04 14:53:49

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH v2 01/28] libext2fs: support modifying arbitrary extended attributes

On Tue, Dec 03, 2013 at 11:54:57AM -0800, Darrick J. Wong wrote:
> Ok, so inline_data implies ext_attr. I think we should change the test to
> check for either feature flag, because with no test, we could accidentally
> read/write EAs on a FS with ^ext_attr,^inline_data. Or maybe provide a helper
> predicate to hide those details.
>
> Also, I'd ask you to send broken out changes to my patches... but I haven't
> posted my monster patchset recently. :)
>
> (I think I might work up the courage to patchbomb the mailing list tonight.)

Zheng's patchset includes some of your patches. Does it include all
of them? If you are going to patchbomb the mailing list, do you have
any suggestions for the best way for me to figure out which patches
I should take from him and which from you?

Thanks,

- Ted

2013-12-04 20:30:36

by Darrick J. Wong

[permalink] [raw]
Subject: Re: [PATCH v2 01/28] libext2fs: support modifying arbitrary extended attributes

On Wed, Dec 04, 2013 at 09:53:43AM -0500, Theodore Ts'o wrote:
> On Tue, Dec 03, 2013 at 11:54:57AM -0800, Darrick J. Wong wrote:
> > Ok, so inline_data implies ext_attr. I think we should change the test to
> > check for either feature flag, because with no test, we could accidentally
> > read/write EAs on a FS with ^ext_attr,^inline_data. Or maybe provide a helper
> > predicate to hide those details.
> >
> > Also, I'd ask you to send broken out changes to my patches... but I haven't
> > posted my monster patchset recently. :)
> >
> > (I think I might work up the courage to patchbomb the mailing list tonight.)
>
> Zheng's patchset includes some of your patches. Does it include all
> of them? If you are going to patchbomb the mailing list, do you have
> any suggestions for the best way for me to figure out which patches
> I should take from him and which from you?

It doesn't include all of them. At the moment there are 83 of them(!) which I
haven't figured out how to release in a reviewable manner. Roughly speaking,
the patches can be grouped:

1. The unapplied patches from the October submission (fuse2fs, 64bit
conversion, xattr editing part 1, metadata checksumming tests)
2. The xattr-editing patches
3. Fixes for uninit extent handling
4. Patches to test and turn on block_validity
5. A bunch of coverity fixes
6. Fixes for bugs I found by running xfstests atop fuse2fs
7. Fixes for that thing about block_uninit groups that Akira Fujita and I were
discussing
8. Fixes for 1k block filesystems with meta_bg
9. Patches to prevent too-high lblk mapping

I could collapse all the post-October fuse2fs and xattr-editing patches into
the original submissions, but that seems like it would be harder for people to
review the new changes.

As for resolving the inlinedata vs djwong-patchbomb sets, I'm trying to
maintain the situation that you should be able to apply them at any point after
applying the original xattr patch + group 2.

--D
>
> Thanks,
>
> - Ted
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-12-04 22:43:20

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH v2 01/28] libext2fs: support modifying arbitrary extended attributes

On Wed, Dec 04, 2013 at 12:30:28PM -0800, Darrick J. Wong wrote:
> It doesn't include all of them. At the moment there are 83 of them(!) which I
> haven't figured out how to release in a reviewable manner. Roughly speaking,
> the patches can be grouped:
>
> 1. The unapplied patches from the October submission (fuse2fs, 64bit
> conversion, xattr editing part 1, metadata checksumming tests)
> 2. The xattr-editing patches
> 3. Fixes for uninit extent handling
> 4. Patches to test and turn on block_validity
> 5. A bunch of coverity fixes
> 6. Fixes for bugs I found by running xfstests atop fuse2fs
> 7. Fixes for that thing about block_uninit groups that Akira Fujita and I were
> discussing
> 8. Fixes for 1k block filesystems with meta_bg
> 9. Patches to prevent too-high lblk mapping
>
> I could collapse all the post-October fuse2fs and xattr-editing patches into
> the original submissions, but that seems like it would be harder for people to
> review the new changes.

Can you send at least some of the fixes independent of patches that
have API/ABI or functionality impacts, or are dependent on patches
that will require more careful review?

In particular, if there are any fixes that might lead to data loss or
be significant in some way (potential security issues, etc.) sending
them independently, preferably against the maint branch, would be a
good idea, so we can get that into a 1.42.x maintenance release.

Thanks,

- Ted

2013-12-04 22:55:42

by Darrick J. Wong

[permalink] [raw]
Subject: Re: [PATCH v2 01/28] libext2fs: support modifying arbitrary extended attributes

On Wed, Dec 04, 2013 at 05:43:14PM -0500, Theodore Ts'o wrote:
> On Wed, Dec 04, 2013 at 12:30:28PM -0800, Darrick J. Wong wrote:
> > It doesn't include all of them. At the moment there are 83 of them(!) which I
> > haven't figured out how to release in a reviewable manner. Roughly speaking,
> > the patches can be grouped:
> >
> > 1. The unapplied patches from the October submission (fuse2fs, 64bit
> > conversion, xattr editing part 1, metadata checksumming tests)
> > 2. The xattr-editing patches
> > 3. Fixes for uninit extent handling
> > 4. Patches to test and turn on block_validity
> > 5. A bunch of coverity fixes
> > 6. Fixes for bugs I found by running xfstests atop fuse2fs
> > 7. Fixes for that thing about block_uninit groups that Akira Fujita and I were
> > discussing
> > 8. Fixes for 1k block filesystems with meta_bg
> > 9. Patches to prevent too-high lblk mapping
> >
> > I could collapse all the post-October fuse2fs and xattr-editing patches into
> > the original submissions, but that seems like it would be harder for people to
> > review the new changes.
>
> Can you send at least some of the fixes independent of patches that
> have API/ABI or functionality impacts, or are dependent on patches
> that will require more careful review?
>
> In particular, if there are any fixes that might lead to data loss or
> be significant in some way (potential security issues, etc.) sending
> them independently, preferably against the maint branch, would be a
> good idea, so we can get that into a 1.42.x maintenance release.

Shouldn't be too hard, all I have to do is shove the fuse2fs/xattr/64bit-conv
patches towards the end. :)

--D
>
> Thanks,
>
> - Ted