Hi all,
This is the third version of inline data patch set for e2fsprogs. In
this version, I made most changes according to Darrick's comments. As
always any comment or suggestion are welcome.
Ted, patch 01 ~ 08 are taken from Darrick's patchbomb. I don't touch
them. Please let me know if I can do something for your review.
Darrick, I don't pick up your patch that tries to handle _DIR_NO_SPACE
error in ext2fs_link() because after applied that path some tests won't
pass. So I prefer to put this problem into TODO list.
changelog:
v3:
* Check inline_data feature in extended attribute API [patch 09]
* Define a new interface (ext2fs_dirent_swab_in2/out2) [patch 10] for
inline data and use them in ext2fs_inline_data_dir_iterate()
[patch 11, 14]
* Define a new flag (BLOCK_INLINE_DATA_CHANGED) to reflect that inline
data has been changed [patch 17]
* Use ext2fs_bmap2() in ext2fs_inline_data_expand() [patch 17]
* Return EXT2_ET_INLINE_DATA_NO_BLOCK in ext2fs_bmap2() [patch 17]
* Refactor out the code of ext2fs_inline_data_expand() [patch 19]
* Remove useless variable 'start' [patch 19]
* Typo fixes [patch 21,22]
* Turn off inline_data feature in tune2fs [patch 22]
* Change type from 'int' to 'unsigned int' for cache_size [patch 29]
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 (22):
libext2fs: check inline_data in ext2fs_xattrs_read/write
libext2fs: define new dirent_swab interfaces for inline data
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 | 104 ++--
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 | 27 +
lib/ext2fs/ext2_fs.h | 10 +
lib/ext2fs/ext2fs.h | 48 +-
lib/ext2fs/ext2fsP.h | 24 +-
lib/ext2fs/ext_attr.c | 856 +++++++++++++++++++++++++++++++
lib/ext2fs/fileio.c | 106 ++++
lib/ext2fs/inline_data.c | 811 +++++++++++++++++++++++++++++
lib/ext2fs/inode.c | 8 +-
lib/ext2fs/mkdir.c | 77 ++-
lib/ext2fs/newdir.c | 25 +
lib/ext2fs/punch.c | 28 +-
lib/ext2fs/swapfs.c | 27 +-
lib/ext2fs/valid_blk.c | 7 +
misc/mke2fs.8.in | 3 +
misc/mke2fs.c | 18 +-
misc/mke2fs.conf.in | 2 +-
misc/tune2fs.8.in | 3 +
misc/tune2fs.c | 99 +++-
tests/f_bad_disconnected_inode/expect.1 | 9 +
tests/r_inline_xattr/expect | 6 +-
35 files changed, 2535 insertions(+), 149 deletions(-)
create mode 100644 lib/ext2fs/inline_data.c
--
1.7.9.7
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.
Signed-off-by: Darrick J. Wong <[email protected]>
---
lib/ext2fs/ext2_err.et.in | 18 ++
lib/ext2fs/ext2fs.h | 28 ++
lib/ext2fs/ext_attr.c | 762 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 808 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..c54925f 100644
--- a/lib/ext2fs/ext_attr.c
+++ b/lib/ext2fs/ext_attr.c
@@ -186,3 +186,765 @@ 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;
+
+ if (!EXT2_HAS_COMPAT_FEATURE(handle->fs->super,
+ EXT2_FEATURE_COMPAT_EXT_ATTR))
+ return 0;
+
+ 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;
+
+ if (!EXT2_HAS_COMPAT_FEATURE(handle->fs->super,
+ EXT2_FEATURE_COMPAT_EXT_ATTR))
+ return 0;
+
+ 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
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 fa082ad..f7d623a 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;
};
@@ -579,7 +579,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;
@@ -652,6 +653,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);
}
@@ -705,7 +707,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;
}
@@ -745,7 +747,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;
@@ -856,6 +858,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;
}
@@ -876,6 +879,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;
}
@@ -894,6 +898,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;
}
}
@@ -918,6 +923,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;
@@ -948,3 +954,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
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 c54925f..fa082ad 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;
@@ -767,7 +767,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;
@@ -778,7 +778,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)
@@ -789,7 +789,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;
@@ -816,7 +816,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
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 f7d623a..a8a8c93 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;
}
@@ -509,7 +510,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
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 1b65c4b..09e13b2 100644
--- a/lib/ext2fs/ext_attr.c
+++ b/lib/ext2fs/ext_attr.c
@@ -658,6 +658,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;
@@ -687,6 +701,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 +
@@ -927,9 +943,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) {
@@ -938,13 +952,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);
- }
From: Zheng Liu <[email protected]>
Now on kernel side we can enable inline data even without ext_attr. So
we need to adjust sanity check on ext2fs_xattrs_read/write to let we
access extended attribute when filesystem is create with '-O ^ext_attr,
inline_data'.
Signed-off-by: Zheng Liu <[email protected]>
---
lib/ext2fs/ext_attr.c | 28 ++++++++++++++++++++++------
1 file changed, 22 insertions(+), 6 deletions(-)
diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
index 09e13b2..e5e0fa9 100644
--- a/lib/ext2fs/ext_attr.c
+++ b/lib/ext2fs/ext_attr.c
@@ -469,9 +469,17 @@ errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle)
unsigned int i;
errcode_t err;
- if (!EXT2_HAS_COMPAT_FEATURE(handle->fs->super,
- EXT2_FEATURE_COMPAT_EXT_ATTR))
- return 0;
+ /*
+ * If inline_data is enabled, we don't check ext_attr because
+ * inline_data needs to read/write extended attribute even
+ * without ext_attr.
+ */
+ if (!EXT2_HAS_INCOMPAT_FEATURE(handle->fs->super,
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
+ if (!EXT2_HAS_COMPAT_FEATURE(handle->fs->super,
+ EXT2_FEATURE_COMPAT_EXT_ATTR))
+ return 0;
+ }
i = EXT2_INODE_SIZE(handle->fs->super);
if (i < sizeof(*inode))
@@ -684,9 +692,17 @@ errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle)
int i;
errcode_t err;
- if (!EXT2_HAS_COMPAT_FEATURE(handle->fs->super,
- EXT2_FEATURE_COMPAT_EXT_ATTR))
- return 0;
+ /*
+ * If inline_data is enabled, we don't check ext_attr because
+ * inline_data needs to read/write extended attribute even
+ * without ext_attr.
+ */
+ if (!EXT2_HAS_INCOMPAT_FEATURE(handle->fs->super,
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
+ if (!EXT2_HAS_COMPAT_FEATURE(handle->fs->super,
+ EXT2_FEATURE_COMPAT_EXT_ATTR))
+ return 0;
+ }
i = EXT2_INODE_SIZE(handle->fs->super);
if (i < sizeof(*inode))
--
1.7.9.7
From: Zheng Liu <[email protected]>
Later we will use ext2fs_dirent_swab_in/out to handle big-endian problem
for inline data. Now interfaces assume that it handles a block, but it
is not true after adding inline data. So this commit defines a new
interface for inline data.
Signed-off-by: Zheng Liu <[email protected]>
---
lib/ext2fs/ext2fs.h | 4 ++++
lib/ext2fs/swapfs.c | 16 ++++++++++++++--
2 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index e251435..1d5b80c 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -1524,7 +1524,11 @@ extern errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f,
extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs);
/* swapfs.c */
+extern errcode_t ext2fs_dirent_swab_in2(ext2_filsys fs, char *buf, size_t size,
+ int flags);
extern errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags);
+extern errcode_t ext2fs_dirent_swab_out2(ext2_filsys fs, char *buf, size_t size,
+ int flags);
extern errcode_t ext2fs_dirent_swab_out(ext2_filsys fs, char *buf, int flags);
extern void ext2fs_swap_ext_attr(char *to, char *from, int bufsize,
int has_header);
diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c
index 1295e81..8dfdcc9 100644
--- a/lib/ext2fs/swapfs.c
+++ b/lib/ext2fs/swapfs.c
@@ -354,13 +354,19 @@ void ext2fs_swap_mmp(struct mmp_struct *mmp)
errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags)
{
+ return ext2fs_dirent_swab_in(fs, buf, fs->blocksize, flags);
+}
+
+errcode_t ext2fs_dirent_swab_in2(ext2_filsys fs, char *buf,
+ size_t size, int flags)
+{
errcode_t retval;
char *p, *end;
struct ext2_dir_entry *dirent;
unsigned int name_len, rec_len;
p = (char *) buf;
- end = (char *) buf + fs->blocksize;
+ end = (char *) buf + size;
while (p < end-8) {
dirent = (struct ext2_dir_entry *) p;
dirent->inode = ext2fs_swab32(dirent->inode);
@@ -385,13 +391,19 @@ errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags)
errcode_t ext2fs_dirent_swab_out(ext2_filsys fs, char *buf, int flags)
{
+ return ext2fs_dirent_swab_out2(fs, buf, fs->blocksize, flags);
+}
+
+errcode_t ext2fs_dirent_swab_out2(ext2_filsys fs, char *buf,
+ size_t size, int flags)
+{
errcode_t retval;
char *p, *end;
unsigned int rec_len;
struct ext2_dir_entry *dirent;
p = buf;
- end = buf + fs->blocksize;
+ end = buf + size;
while (p < end) {
dirent = (struct ext2_dir_entry *) p;
retval = ext2fs_get_rec_len(fs, dirent, &rec_len);
--
1.7.9.7
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
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 81f60ca..1b65c4b 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;
if (!EXT2_HAS_COMPAT_FEATURE(handle->fs->super,
@@ -531,26 +531,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);
@@ -663,7 +661,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;
@@ -765,8 +762,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
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 | 8 +-
lib/ext2fs/ext2fsP.h | 11 +++
lib/ext2fs/inline_data.c | 206 +++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/swapfs.c | 11 ++-
9 files changed, 299 insertions(+), 21 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..ab816ad 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_INLINE_DATA_CHANGED to notify caller
+ * that inline data has been changed.
+ */
+ retval = BLOCK_INLINE_DATA_CHANGED;
+ }
}
if (do_abort)
- return BLOCK_ABORT;
- return 0;
+ return retval | BLOCK_ABORT;
+ return retval;
}
-
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index 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 1d5b80c..1a267e2 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -298,9 +298,10 @@ struct struct_ext2_filsys {
/*
* Return flags for the block iterator functions
*/
-#define BLOCK_CHANGED 1
-#define BLOCK_ABORT 2
-#define BLOCK_ERROR 4
+#define BLOCK_CHANGED 1
+#define BLOCK_ABORT 2
+#define BLOCK_ERROR 4
+#define BLOCK_INLINE_DATA_CHANGED 8
/*
* Block interate flags
@@ -438,6 +439,7 @@ struct ext2_extent_info {
#define DIRENT_FLAG_INCLUDE_EMPTY 1
#define DIRENT_FLAG_INCLUDE_REMOVED 2
#define DIRENT_FLAG_INCLUDE_CSUM 4
+#define DIRENT_FLAG_INCLUDE_INLINE_DATA 8
#define DIRENT_DOT_FILE 1
#define DIRENT_DOT_DOT_FILE 2
diff --git a/lib/ext2fs/ext2fsP.h b/lib/ext2fs/ext2fsP.h
index 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..b768b9a
--- /dev/null
+++ b/lib/ext2fs/inline_data.c
@@ -0,0 +1,206 @@
+/*
+ * 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 */
+ void *ea_data;
+};
+
+static errcode_t ext2fs_inline_data_ea_set(struct ext2_inline_data *data)
+{
+ struct ext2_xattr_handle *handle;
+ errcode_t retval;
+
+ retval = ext2fs_xattrs_open(data->fs, data->ino, &handle);
+ if (retval)
+ return retval;
+
+ retval = ext2fs_xattrs_read(handle);
+ if (retval)
+ goto err;
+
+ retval = ext2fs_xattr_set(handle, "system.data",
+ data->ea_data, data->ea_size);
+ if (retval)
+ goto err;
+
+ retval = ext2fs_xattrs_write(handle);
+
+err:
+ (void) ext2fs_xattrs_close(&handle);
+ return retval;
+}
+
+static errcode_t ext2fs_inline_data_ea_get(struct ext2_inline_data *data)
+{
+ struct ext2_xattr_handle *handle;
+ errcode_t retval;
+
+ data->ea_size = 0;
+ data->ea_data = 0;
+
+ retval = ext2fs_xattrs_open(data->fs, data->ino, &handle);
+ if (retval)
+ return retval;
+
+ retval = ext2fs_xattrs_read(handle);
+ if (retval)
+ goto err;
+
+ retval = ext2fs_xattr_get(handle, "system.data",
+ (void **)&data->ea_data, &data->ea_size);
+ if (retval)
+ goto err;
+
+err:
+ (void) ext2fs_xattrs_close(&handle);
+ return retval;
+}
+
+
+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_INLINE_DATA_CHANGED) {
+ errcode_t err;
+
+ inode.i_block[0] = ext2fs_cpu_to_le32(dirent.inode);
+ err = ext2fs_write_inode(fs, ino, &inode);
+ if (err)
+ goto out;
+ }
+ 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;
+#ifdef WORDS_BIGENDIAN
+ retval = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
+ if (retval)
+ goto out;
+#endif
+ retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
+ if (retval & BLOCK_INLINE_DATA_CHANGED) {
+ errcode_t err;
+
+#ifdef WORDS_BIGENDIAN
+ err = ext2fs_dirent_swab_out2(fs, ctx->buf, ctx->buflen, 0);
+ if (err)
+ goto out;
+#endif
+ 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)
+ goto out;
+
+ ctx->buf = data.ea_data;
+ ctx->buflen = data.ea_size;
+#ifdef WORDS_BIGENDIAN
+ retval = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
+ if (retval)
+ goto out;
+#endif
+
+ retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
+ if (retval & BLOCK_INLINE_DATA_CHANGED) {
+ errcode_t err;
+
+#ifdef WORDS_BIGENDIAN
+ err = ext2fs_dirent_swab_out2(fs, ctx->buf, ctx->buflen, 0);
+ if (err) {
+ retval = err;
+ goto out1;
+ }
+#endif
+ err = ext2fs_inline_data_ea_set(&data);
+ if (err)
+ retval = err;
+ }
+
+out1:
+ ext2fs_free_mem(&data.ea_data);
+ ctx->buf = 0;
+
+out:
+ retval &= ~(BLOCK_ABORT | BLOCK_INLINE_DATA_CHANGED);
+ return retval & BLOCK_ERROR ? ctx->errcode : retval;
+}
diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c
index 8dfdcc9..88a150e 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,18 @@ void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t,
(struct ext2_inode *) t);
if (hostorder && (f->i_flags & EXT4_EXTENTS_FL))
has_extents = 1;
+ if (hostorder && (f->i_flags & EXT4_INLINE_DATA_FL))
+ has_inline_data = 1;
t->i_flags = ext2fs_swab32(f->i_flags);
if (!hostorder && (t->i_flags & EXT4_EXTENTS_FL))
has_extents = 1;
+ if (!hostorder && (f->i_flags & EXT4_INLINE_DATA_FL))
+ has_inline_data = 1;
t->i_dir_acl = ext2fs_swab32(f->i_dir_acl);
- /* extent data are swapped on access, not here */
- if (!has_extents && (!islnk || has_data_blocks)) {
+ /*
+ * Extent data and inline data are swapped on access, not here
+ */
+ if (!has_extents && !has_inline_data && (!islnk || has_data_blocks)) {
for (i = 0; i < EXT2_N_BLOCKS; i++)
t->i_block[i] = ext2fs_swab32(f->i_block[i]);
} else if (t != f) {
--
1.7.9.7
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.
Meanwhile in this commit it fixes a bug that when we kill a file we
could leak an inode.
Signed-off-by: Zheng Liu <[email protected]>
---
debugfs/debugfs.c | 9 ++++-----
debugfs/filefrag.c | 12 +++++++-----
debugfs/lsdel.c | 17 ++++++++++-------
lib/ext2fs/valid_blk.c | 7 +++++++
misc/tune2fs.c | 3 ++-
5 files changed, 30 insertions(+), 18 deletions(-)
diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index e489f62..e600d58 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -1921,11 +1921,10 @@ static void kill_file_by_inode(ext2_ino_t inode)
inode_buf.i_dtime = current_fs->now ? current_fs->now : time(0);
if (debugfs_write_inode(inode, &inode_buf, 0))
return;
- if (!ext2fs_inode_has_valid_blocks2(current_fs, &inode_buf))
- return;
-
- ext2fs_block_iterate3(current_fs, inode, BLOCK_FLAG_READ_ONLY, NULL,
- release_blocks_proc, NULL);
+ if (ext2fs_inode_has_valid_blocks2(current_fs, &inode_buf)) {
+ ext2fs_block_iterate3(current_fs, inode, BLOCK_FLAG_READ_ONLY,
+ NULL, release_blocks_proc, NULL);
+ }
printf("\n");
ext2fs_inode_alloc_stats2(current_fs, inode, -1,
LINUX_S_ISDIR(inode_buf.i_mode));
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
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 1a267e2..322c39d 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -1445,6 +1445,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 4294a5e..3144427 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
From: Zheng Liu <[email protected]>
This commit defines a ext2fs_inline_data_expand() to expand an inode with
inline data. In this commit this function only can expand a directory.
But later it will expand a file.
Signed-off-by: Zheng Liu <[email protected]>
---
lib/ext2fs/expanddir.c | 2 +
lib/ext2fs/ext2fsP.h | 1 +
lib/ext2fs/inline_data.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 190 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 151a9df..4294a5e 100644
--- a/lib/ext2fs/inline_data.c
+++ b/lib/ext2fs/inline_data.c
@@ -227,3 +227,190 @@ out:
retval &= ~(BLOCK_ABORT | BLOCK_INLINE_DATA_CHANGED);
return retval & BLOCK_ERROR ? ctx->errcode : retval;
}
+
+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;
+
+ /*
+ * 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);
+ size += EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2) -
+ 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);
+ }
+
+#ifdef WORDS_BIGENDIAN
+ retval = ext2fs_dirent_swab_in2(fs, inline_buf,
+ data.ea_size + EXT4_MIN_INLINE_DATA_SIZE);
+ if (retval)
+ goto errout;
+#endif
+
+ 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;
+ retval = ext2fs_write_dir_block4(fs, blk, blk_buf, 0, ino);
+ if (retval)
+ goto errout;
+
+ /* Update inode */
+ if (EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT3_FEATURE_INCOMPAT_EXTENTS))
+ inode.i_flags |= EXT4_EXTENTS_FL;
+ inode.i_flags &= ~EXT4_INLINE_DATA_FL;
+ ext2fs_iblk_set(fs, &inode, 1);
+ inode.i_size = fs->blocksize;
+ retval = ext2fs_bmap2(fs, ino, &inode, 0, BMAP_SET, 0, 0, &blk);
+ if (retval)
+ goto errout;
+ retval = ext2fs_write_inode(fs, ino, &inode);
+ if (retval)
+ goto errout;
+ ext2fs_block_alloc_stats2(fs, blk, +1);
+
+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
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 e600d58..ef5f201 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 b768b9a..151a9df 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
From: Zheng Liu <[email protected]>
No physical block mapping if an inode has inline data.
Meanwhile this commit fixes a minor bug in bmap command when it prints
an error.
Signed-off-by: Theodore Ts'o <[email protected]>
Signed-off-by: Zheng Liu <[email protected]>
---
debugfs/debugfs.c | 2 +-
lib/ext2fs/bmap.c | 7 +++++++
lib/ext2fs/ext2_err.et.in | 3 +++
3 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index ef5f201..bc8413d 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -2135,7 +2135,7 @@ void do_bmap(int argc, char *argv[])
errcode = ext2fs_bmap2(current_fs, ino, 0, 0, 0, blk, 0, &pblk);
if (errcode) {
- com_err("argv[0]", errcode,
+ com_err(argv[0], errcode,
"while mapping logical block %llu\n", blk);
return;
}
diff --git a/lib/ext2fs/bmap.c b/lib/ext2fs/bmap.c
index 5074587..e148dba 100644
--- a/lib/ext2fs/bmap.c
+++ b/lib/ext2fs/bmap.c
@@ -266,6 +266,13 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
}
addr_per_block = (blk_t) fs->blocksize >> 2;
+ /*
+ * 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)
+ return EXT2_ET_INLINE_DATA_NO_BLOCK;
+
if (!block_buf) {
retval = ext2fs_get_array(2, fs->blocksize, &buf);
if (retval)
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index 0781145..f93f0e8 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_BLOCK,
+ "No block for an inode with inline data"
+
end
--
1.7.9.7
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
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 b10d6dd..3237b03 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -596,7 +596,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
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 a8a8c93..81f60ca 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) {
@@ -553,13 +556,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
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 3144427..a25c5bd 100644
--- a/lib/ext2fs/inline_data.c
+++ b/lib/ext2fs/inline_data.c
@@ -238,7 +238,7 @@ out:
return retval & BLOCK_ERROR ? ctx->errcode : retval;
}
-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
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_inline_data_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/ext2fs.h | 2 +
lib/ext2fs/ext2fsP.h | 6 ++
lib/ext2fs/ext_attr.c | 64 +++++++++++++
lib/ext2fs/fileio.c | 106 ++++++++++++++++++++++
lib/ext2fs/inline_data.c | 217 +++++++++++++++++++++++++++++++++++----------
7 files changed, 362 insertions(+), 49 deletions(-)
diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index bc8413d..0908f48 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 f93f0e8..99360da 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -506,4 +506,7 @@ ec EXT2_ET_NO_INLINE_DATA,
ec EXT2_ET_INLINE_DATA_NO_BLOCK,
"No block for an inode with inline data"
+ec EXT2_ET_INLINE_DATA_NO_SPACE,
+ "No free space in inline data"
+
end
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 322c39d..b10d6dd 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -1171,6 +1171,8 @@ 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);
+errcode_t ext2fs_xattr_inode_max_size(ext2_filsys fs, ext2_ino_t ino,
+ size_t *size);
/* extent.c */
extern errcode_t ext2fs_extent_header_verify(void *ptr, int size);
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 e5e0fa9..3942f1c 100644
--- a/lib/ext2fs/ext_attr.c
+++ b/lib/ext2fs/ext_attr.c
@@ -842,6 +842,70 @@ errcode_t ext2fs_xattr_get(struct ext2_xattr_handle *h, const char *key,
return EXT2_ET_EA_KEY_NOT_FOUND;
}
+errcode_t ext2fs_xattr_inode_max_size(ext2_filsys fs, ext2_ino_t ino,
+ 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(fs->super);
+ if (i < sizeof(*inode))
+ i = sizeof(*inode);
+ err = ext2fs_get_memzero(i, &inode);
+ if (err)
+ return err;
+
+ err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)inode,
+ EXT2_INODE_SIZE(fs->super));
+ if (err)
+ goto out;
+
+ /* Does the inode have size for EA? */
+ if (EXT2_INODE_SIZE(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(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(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,
diff --git a/lib/ext2fs/fileio.c b/lib/ext2fs/fileio.c
index 02e6263..a9f25db 100644
--- a/lib/ext2fs/fileio.c
+++ b/lib/ext2fs/fileio.c
@@ -224,6 +224,38 @@ 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 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;
+
+ count = size - file->pos;
+ if (count > wanted)
+ count = wanted;
+ memcpy(buf, file->buf + file->pos, 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 +268,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 +302,66 @@ 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 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) {
+ count = nbytes - file->pos;
+ memcpy(file->buf + file->pos, 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 +376,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 a25c5bd..1905121 100644
--- a/lib/ext2fs/inline_data.c
+++ b/lib/ext2fs/inline_data.c
@@ -336,14 +336,98 @@ err:
return retval;
}
+static errcode_t
+ext2fs_inline_data_dir_expand(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode, char *buf, size_t size)
+{
+ errcode_t retval;
+ blk64_t blk;
+ char *blk_buf;
+
+ retval = ext2fs_get_memzero(fs->blocksize, &blk_buf);
+ if (retval)
+ return retval;
+
+#ifdef WORDS_BIGENDIAN
+ retval = ext2fs_dirent_swab_in2(fs, buf, size);
+ if (retval)
+ goto errout;
+#endif
+
+ /* Adjust the rec_len */
+ retval = ext2fs_inline_data_convert_dir(fs, ino, blk_buf, buf, size);
+ /* Allocate a new block */
+ retval = ext2fs_new_block2(fs, 0, 0, &blk);
+ if (retval)
+ goto errout;
+ retval = ext2fs_write_dir_block4(fs, blk, blk_buf, 0, ino);
+ if (retval)
+ goto errout;
+
+ /* Update inode */
+ if (EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT3_FEATURE_INCOMPAT_EXTENTS))
+ inode->i_flags |= EXT4_EXTENTS_FL;
+ inode->i_flags &= ~EXT4_INLINE_DATA_FL;
+ ext2fs_iblk_set(fs, inode, 1);
+ inode->i_size = fs->blocksize;
+ retval = ext2fs_bmap2(fs, ino, inode, 0, BMAP_SET, 0, 0, &blk);
+ if (retval)
+ goto errout;
+ retval = ext2fs_write_inode(fs, ino, inode);
+ if (retval)
+ goto errout;
+ ext2fs_block_alloc_stats(fs, blk, +1);
+
+errout:
+ ext2fs_free_mem(&blk_buf);
+ return retval;
+}
+
+static errcode_t
+ext2fs_inline_data_file_expand(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode, char *buf, size_t size)
+{
+ ext2_file_t e2_file;
+ errcode_t retval;
+
+ /* Update inode */
+ if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
+ EXT3_FEATURE_INCOMPAT_EXTENTS)) {
+ int i;
+ struct ext3_extent_header *eh;
+
+ eh = (struct ext3_extent_header *) &inode->i_block[0];
+ eh->eh_depth = 0;
+ eh->eh_entries = 0;
+ eh->eh_magic = EXT3_EXT_MAGIC;
+ i = (sizeof(inode->i_block) - sizeof(*eh)) /
+ sizeof(struct ext3_extent);
+ eh->eh_max = ext2fs_cpu_to_le16(i);
+ inode->i_flags |= EXT4_EXTENTS_FL;
+ }
+ inode->i_flags &= ~EXT4_INLINE_DATA_FL;
+ ext2fs_iblk_set(fs, inode, 0);
+ inode->i_size = 0;
+ retval = ext2fs_write_inode(fs, ino, inode);
+ if (retval)
+ return retval;
+
+ /* Write out the block buffer */
+ retval = ext2fs_file_open(fs, ino, EXT2_FILE_WRITE, &e2_file);
+ if (retval)
+ return retval;
+ retval = ext2fs_file_write(e2_file, buf, size, 0);
+ ext2fs_file_close(e2_file);
+ 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;
+ size_t inline_size;
char *inline_buf = 0;
- char *blk_buf = 0;
EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
@@ -354,14 +438,13 @@ errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino)
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);
+ inline_size = data.ea_size + EXT4_MIN_INLINE_DATA_SIZE;
+ retval = ext2fs_get_mem(inline_size, &inline_buf);
if (retval)
goto errout;
@@ -371,56 +454,98 @@ errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino)
data.ea_data, data.ea_size);
}
-#ifdef WORDS_BIGENDIAN
- retval = ext2fs_dirent_swab_in2(fs, inline_buf,
- data.ea_size + EXT4_MIN_INLINE_DATA_SIZE);
- if (retval)
- goto errout;
-#endif
-
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;
- retval = ext2fs_write_dir_block4(fs, blk, blk_buf, 0, ino);
- if (retval)
- goto errout;
-
- /* Update inode */
- if (EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT3_FEATURE_INCOMPAT_EXTENTS))
- inode.i_flags |= EXT4_EXTENTS_FL;
- inode.i_flags &= ~EXT4_INLINE_DATA_FL;
- ext2fs_iblk_set(fs, &inode, 1);
- inode.i_size = fs->blocksize;
- retval = ext2fs_bmap2(fs, ino, &inode, 0, BMAP_SET, 0, 0, &blk);
- if (retval)
- goto errout;
- retval = ext2fs_write_inode(fs, ino, &inode);
- if (retval)
- goto errout;
- ext2fs_block_alloc_stats2(fs, blk, +1);
+ if (LINUX_S_ISDIR(inode.i_mode)) {
+ retval = ext2fs_inline_data_dir_expand(fs, ino, &inode,
+ inline_buf, inline_size);
+ } else {
+ retval = ext2fs_inline_data_file_expand(fs, ino, &inode,
+ inline_buf, inline_size);
+ }
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;
}
+
+/*
+ * 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;
+ errcode_t retval;
+ size_t max_size;
+
+ if (!inode) {
+ retval = ext2fs_read_inode(fs, ino, &inode_buf);
+ if (retval)
+ return retval;
+ inode = &inode_buf;
+ }
+
+ if (size <= EXT4_MIN_INLINE_DATA_SIZE) {
+ memcpy((void *)inode->i_block, buf, size);
+ return ext2fs_write_inode(fs, ino, inode);
+ }
+
+ retval = ext2fs_xattr_inode_max_size(fs, ino, &max_size);
+ if (retval)
+ return retval;
+
+ if (size - EXT4_MIN_INLINE_DATA_SIZE > max_size)
+ return EXT2_ET_INLINE_DATA_NO_SPACE;
+
+ 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
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
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..7b40d31 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 extended 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..fbe1d03 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 enabled, 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
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
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
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
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
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 1905121..57093cf 100644
--- a/lib/ext2fs/inline_data.c
+++ b/lib/ext2fs/inline_data.c
@@ -549,3 +549,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(¶m, 0, sizeof(param));
+ ext2fs_blocks_count_set(¶m, 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, ¶m,
+ 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
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 | 2 ++
lib/ext2fs/ext2fsP.h | 2 +-
lib/ext2fs/inode.c | 8 ++++----
3 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 3237b03..fe6b0d9 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -1372,6 +1372,8 @@ 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,
+ unsigned 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/ext2fsP.h b/lib/ext2fs/ext2fsP.h
index e159128..6f175f7 100644
--- a/lib/ext2fs/ext2fsP.h
+++ b/lib/ext2fs/ext2fsP.h
@@ -69,7 +69,7 @@ struct ext2_inode_cache {
void * buffer;
blk64_t buffer_blk;
int cache_last;
- int cache_size;
+ unsigned int cache_size;
int refcount;
struct ext2_inode_cache_ent *cache;
};
diff --git a/lib/ext2fs/inode.c b/lib/ext2fs/inode.c
index 46c1c58..0cea9f0 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, unsigned 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
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 = ˙
+ 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
From: Zheng Liu <[email protected]>
This commit make us switch on/off inline_data feature in tune2fs. Now
inline_data can be enabled without ext_attr. Hence we don't check it.
As doing in mke2fs we need to check inode size when we want to enable
it.
Signed-off-by: Theodore Ts'o <[email protected]>
Signed-off-by: Zheng Liu <[email protected]>
---
misc/tune2fs.8.in | 3 ++
misc/tune2fs.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 97 insertions(+), 2 deletions(-)
diff --git a/misc/tune2fs.8.in b/misc/tune2fs.8.in
index 55c6dd9..8d56cdd 100644
--- a/misc/tune2fs.8.in
+++ b/misc/tune2fs.8.in
@@ -531,6 +531,9 @@ 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 extended 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/tune2fs.c b/misc/tune2fs.c
index 95c1886..d205df5 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -92,6 +92,7 @@ static unsigned long new_inode_size;
static char *ext_mount_opts;
static int usrquota, grpquota;
static int rewrite_checksums;
+static int disable_inline_data;
int journal_size, journal_flags;
char *journal_device;
@@ -140,7 +141,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|
@@ -162,7 +164,8 @@ static __u32 clear_ok_features[3] = {
/* Incompat */
EXT2_FEATURE_INCOMPAT_FILETYPE |
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|
@@ -745,6 +748,68 @@ static void rewrite_metadata_checksums(ext2_filsys fs)
ext2fs_mark_super_dirty(fs);
}
+/*
+ * Allocate a block for all inodes with inline data.
+ */
+static void expand_inodes(ext2_filsys fs)
+{
+ int inode_size = EXT2_INODE_SIZE(fs->super);
+ struct ext2_inode *inode;
+ ext2_inode_scan scan;
+ errcode_t retval;
+ ext2_ino_t ino;
+
+ retval = ext2fs_open_inode_scan(fs, 0, &scan);
+ if (retval) {
+ com_err("expand_inline_data", retval,
+ "while opening inode scan");
+ exit(1);
+ }
+
+ retval = ext2fs_get_mem(inode_size, &inode);
+ if (retval) {
+ com_err("expand_inline_data", retval,
+ "while allocating memory");
+ exit(1);
+ }
+
+ do {
+ retval = ext2fs_get_next_inode_full(scan, &ino, inode,
+ inode_size);
+ if (retval) {
+ com_err("expand_inline_data", retval,
+ "while getting next inode");
+ exit(1);
+ }
+ if (!ino)
+ break;
+ if (!ext2fs_test_inode_bitmap2(fs->inode_map, ino))
+ continue;
+ if (!(inode->i_flags & EXT4_INLINE_DATA_FL))
+ continue;
+ retval = ext2fs_inline_data_expand(fs, ino);
+ if (retval) {
+ com_err("expand_inline_data", retval,
+ "while expanding inode %lu", ino);
+ exit(1);
+ }
+ } while (ino);
+
+ ext2fs_free_mem(&inode);
+ ext2fs_close_inode_scan(scan);
+}
+
+static void expand_inline_data(ext2_filsys fs)
+{
+ ext2fs_read_bitmaps(fs);
+ expand_inodes(fs);
+ ext2fs_mark_ib_dirty(fs);
+ ext2fs_mark_bb_dirty(fs);
+ ext2fs_mmp_update2(fs, 1);
+ fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+ ext2fs_mark_super_dirty(fs);
+}
+
static void enable_uninit_bg(ext2_filsys fs)
{
struct ext2_group_desc *gd;
@@ -1083,6 +1148,31 @@ 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_OFF(E2P_FEATURE_INCOMPAT,
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
+ if (mount_flags & EXT2_MF_MOUNTED) {
+ fputs(_("The inline data feature can't be clear if "
+ "the filesystem is mounted\n"), stderr);
+ return 1;
+ }
+ if (check_fsck_needed(fs))
+ return 1;
+ disable_inline_data = 1;
+ }
+
if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
EXT4_FEATURE_RO_COMPAT_QUOTA)) {
/*
@@ -2708,6 +2798,8 @@ retry_open:
}
if (rewrite_checksums)
rewrite_metadata_checksums(fs);
+ if (disable_inline_data)
+ expand_inline_data(fs);
if (I_flag) {
if (mount_flags & EXT2_MF_MOUNTED) {
fputs(_("The inode size may only be "
--
1.7.9.7
On Fri, Dec 06, 2013 at 05:57:56PM +0800, Zheng Liu wrote:
> From: Zheng Liu <[email protected]>
>
> Now on kernel side we can enable inline data even without ext_attr. So
> we need to adjust sanity check on ext2fs_xattrs_read/write to let we
> access extended attribute when filesystem is create with '-O ^ext_attr,
> inline_data'.
Oops, I removed both of those checks in favor of looking for ext_attr or
inline_data in ext2fs_xattrs_open(). Oh well, sorry about the
miscommunication.
--D
>
> Signed-off-by: Zheng Liu <[email protected]>
> ---
> lib/ext2fs/ext_attr.c | 28 ++++++++++++++++++++++------
> 1 file changed, 22 insertions(+), 6 deletions(-)
>
> diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
> index 09e13b2..e5e0fa9 100644
> --- a/lib/ext2fs/ext_attr.c
> +++ b/lib/ext2fs/ext_attr.c
> @@ -469,9 +469,17 @@ errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle)
> unsigned int i;
> errcode_t err;
>
> - if (!EXT2_HAS_COMPAT_FEATURE(handle->fs->super,
> - EXT2_FEATURE_COMPAT_EXT_ATTR))
> - return 0;
> + /*
> + * If inline_data is enabled, we don't check ext_attr because
> + * inline_data needs to read/write extended attribute even
> + * without ext_attr.
> + */
> + if (!EXT2_HAS_INCOMPAT_FEATURE(handle->fs->super,
> + EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
> + if (!EXT2_HAS_COMPAT_FEATURE(handle->fs->super,
> + EXT2_FEATURE_COMPAT_EXT_ATTR))
> + return 0;
> + }
>
> i = EXT2_INODE_SIZE(handle->fs->super);
> if (i < sizeof(*inode))
> @@ -684,9 +692,17 @@ errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle)
> int i;
> errcode_t err;
>
> - if (!EXT2_HAS_COMPAT_FEATURE(handle->fs->super,
> - EXT2_FEATURE_COMPAT_EXT_ATTR))
> - return 0;
> + /*
> + * If inline_data is enabled, we don't check ext_attr because
> + * inline_data needs to read/write extended attribute even
> + * without ext_attr.
> + */
> + if (!EXT2_HAS_INCOMPAT_FEATURE(handle->fs->super,
> + EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
> + if (!EXT2_HAS_COMPAT_FEATURE(handle->fs->super,
> + EXT2_FEATURE_COMPAT_EXT_ATTR))
> + return 0;
> + }
>
> i = EXT2_INODE_SIZE(handle->fs->super);
> if (i < sizeof(*inode))
> --
> 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
On Fri, Dec 06, 2013 at 05:57:47PM +0800, Zheng Liu wrote:
> Hi all,
>
> This is the third version of inline data patch set for e2fsprogs. In
> this version, I made most changes according to Darrick's comments. As
> always any comment or suggestion are welcome.
>
> Ted, patch 01 ~ 08 are taken from Darrick's patchbomb. I don't touch
> them. Please let me know if I can do something for your review.
As soon as the last regression tests finish I will blast this out. Probably
tomorrow morning.
> Darrick, I don't pick up your patch that tries to handle _DIR_NO_SPACE
> error in ext2fs_link() because after applied that path some tests won't
> pass. So I prefer to put this problem into TODO list.
That patch turned out to be a disaster so I kicked it out of my patchbomb set.
I'll ... work on it later.
(Also, I probably won't get to reviewing this iteration for a couple of
days...)
--D
>
> changelog:
> v3:
> * Check inline_data feature in extended attribute API [patch 09]
> * Define a new interface (ext2fs_dirent_swab_in2/out2) [patch 10] for
> inline data and use them in ext2fs_inline_data_dir_iterate()
> [patch 11, 14]
> * Define a new flag (BLOCK_INLINE_DATA_CHANGED) to reflect that inline
> data has been changed [patch 17]
> * Use ext2fs_bmap2() in ext2fs_inline_data_expand() [patch 17]
> * Return EXT2_ET_INLINE_DATA_NO_BLOCK in ext2fs_bmap2() [patch 17]
> * Refactor out the code of ext2fs_inline_data_expand() [patch 19]
> * Remove useless variable 'start' [patch 19]
> * Typo fixes [patch 21,22]
> * Turn off inline_data feature in tune2fs [patch 22]
> * Change type from 'int' to 'unsigned int' for cache_size [patch 29]
>
> 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 (22):
> libext2fs: check inline_data in ext2fs_xattrs_read/write
> libext2fs: define new dirent_swab interfaces for inline data
> 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 | 104 ++--
> 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 | 27 +
> lib/ext2fs/ext2_fs.h | 10 +
> lib/ext2fs/ext2fs.h | 48 +-
> lib/ext2fs/ext2fsP.h | 24 +-
> lib/ext2fs/ext_attr.c | 856 +++++++++++++++++++++++++++++++
> lib/ext2fs/fileio.c | 106 ++++
> lib/ext2fs/inline_data.c | 811 +++++++++++++++++++++++++++++
> lib/ext2fs/inode.c | 8 +-
> lib/ext2fs/mkdir.c | 77 ++-
> lib/ext2fs/newdir.c | 25 +
> lib/ext2fs/punch.c | 28 +-
> lib/ext2fs/swapfs.c | 27 +-
> lib/ext2fs/valid_blk.c | 7 +
> misc/mke2fs.8.in | 3 +
> misc/mke2fs.c | 18 +-
> misc/mke2fs.conf.in | 2 +-
> misc/tune2fs.8.in | 3 +
> misc/tune2fs.c | 99 +++-
> tests/f_bad_disconnected_inode/expect.1 | 9 +
> tests/r_inline_xattr/expect | 6 +-
> 35 files changed, 2535 insertions(+), 149 deletions(-)
> create mode 100644 lib/ext2fs/inline_data.c
>
> --
> 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
On Fri, Dec 06, 2013 at 05:53:21PM -0800, Darrick J. Wong wrote:
> On Fri, Dec 06, 2013 at 05:57:47PM +0800, Zheng Liu wrote:
> > Hi all,
> >
> > This is the third version of inline data patch set for e2fsprogs. In
> > this version, I made most changes according to Darrick's comments. As
> > always any comment or suggestion are welcome.
> >
> > Ted, patch 01 ~ 08 are taken from Darrick's patchbomb. I don't touch
> > them. Please let me know if I can do something for your review.
>
> As soon as the last regression tests finish I will blast this out. Probably
> tomorrow morning.
Great!
>
> > Darrick, I don't pick up your patch that tries to handle _DIR_NO_SPACE
> > error in ext2fs_link() because after applied that path some tests won't
> > pass. So I prefer to put this problem into TODO list.
>
> That patch turned out to be a disaster so I kicked it out of my patchbomb set.
> I'll ... work on it later.
Yes, that still needs some more works.
>
> (Also, I probably won't get to reviewing this iteration for a couple of
> days...)
Never mind. I really appreciate your comments. :-)
Thanks,
- Zheng
>
> --D
> >
> > changelog:
> > v3:
> > * Check inline_data feature in extended attribute API [patch 09]
> > * Define a new interface (ext2fs_dirent_swab_in2/out2) [patch 10] for
> > inline data and use them in ext2fs_inline_data_dir_iterate()
> > [patch 11, 14]
> > * Define a new flag (BLOCK_INLINE_DATA_CHANGED) to reflect that inline
> > data has been changed [patch 17]
> > * Use ext2fs_bmap2() in ext2fs_inline_data_expand() [patch 17]
> > * Return EXT2_ET_INLINE_DATA_NO_BLOCK in ext2fs_bmap2() [patch 17]
> > * Refactor out the code of ext2fs_inline_data_expand() [patch 19]
> > * Remove useless variable 'start' [patch 19]
> > * Typo fixes [patch 21,22]
> > * Turn off inline_data feature in tune2fs [patch 22]
> > * Change type from 'int' to 'unsigned int' for cache_size [patch 29]
> >
> > 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 (22):
> > libext2fs: check inline_data in ext2fs_xattrs_read/write
> > libext2fs: define new dirent_swab interfaces for inline data
> > 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 | 104 ++--
> > 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 | 27 +
> > lib/ext2fs/ext2_fs.h | 10 +
> > lib/ext2fs/ext2fs.h | 48 +-
> > lib/ext2fs/ext2fsP.h | 24 +-
> > lib/ext2fs/ext_attr.c | 856 +++++++++++++++++++++++++++++++
> > lib/ext2fs/fileio.c | 106 ++++
> > lib/ext2fs/inline_data.c | 811 +++++++++++++++++++++++++++++
> > lib/ext2fs/inode.c | 8 +-
> > lib/ext2fs/mkdir.c | 77 ++-
> > lib/ext2fs/newdir.c | 25 +
> > lib/ext2fs/punch.c | 28 +-
> > lib/ext2fs/swapfs.c | 27 +-
> > lib/ext2fs/valid_blk.c | 7 +
> > misc/mke2fs.8.in | 3 +
> > misc/mke2fs.c | 18 +-
> > misc/mke2fs.conf.in | 2 +-
> > misc/tune2fs.8.in | 3 +
> > misc/tune2fs.c | 99 +++-
> > tests/f_bad_disconnected_inode/expect.1 | 9 +
> > tests/r_inline_xattr/expect | 6 +-
> > 35 files changed, 2535 insertions(+), 149 deletions(-)
> > create mode 100644 lib/ext2fs/inline_data.c
> >
> > --
> > 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
On Fri, Dec 06, 2013 at 01:38:26PM -0800, Darrick J. Wong wrote:
> On Fri, Dec 06, 2013 at 05:57:56PM +0800, Zheng Liu wrote:
> > From: Zheng Liu <[email protected]>
> >
> > Now on kernel side we can enable inline data even without ext_attr. So
> > we need to adjust sanity check on ext2fs_xattrs_read/write to let we
> > access extended attribute when filesystem is create with '-O ^ext_attr,
> > inline_data'.
>
> Oops, I removed both of those checks in favor of looking for ext_attr or
> inline_data in ext2fs_xattrs_open(). Oh well, sorry about the
> miscommunication.
Got it. I am happy to rebase against your latest patch.
Thanks,
- Zheng
>
> --D
>
> >
> > Signed-off-by: Zheng Liu <[email protected]>
> > ---
> > lib/ext2fs/ext_attr.c | 28 ++++++++++++++++++++++------
> > 1 file changed, 22 insertions(+), 6 deletions(-)
> >
> > diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
> > index 09e13b2..e5e0fa9 100644
> > --- a/lib/ext2fs/ext_attr.c
> > +++ b/lib/ext2fs/ext_attr.c
> > @@ -469,9 +469,17 @@ errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle)
> > unsigned int i;
> > errcode_t err;
> >
> > - if (!EXT2_HAS_COMPAT_FEATURE(handle->fs->super,
> > - EXT2_FEATURE_COMPAT_EXT_ATTR))
> > - return 0;
> > + /*
> > + * If inline_data is enabled, we don't check ext_attr because
> > + * inline_data needs to read/write extended attribute even
> > + * without ext_attr.
> > + */
> > + if (!EXT2_HAS_INCOMPAT_FEATURE(handle->fs->super,
> > + EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
> > + if (!EXT2_HAS_COMPAT_FEATURE(handle->fs->super,
> > + EXT2_FEATURE_COMPAT_EXT_ATTR))
> > + return 0;
> > + }
> >
> > i = EXT2_INODE_SIZE(handle->fs->super);
> > if (i < sizeof(*inode))
> > @@ -684,9 +692,17 @@ errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle)
> > int i;
> > errcode_t err;
> >
> > - if (!EXT2_HAS_COMPAT_FEATURE(handle->fs->super,
> > - EXT2_FEATURE_COMPAT_EXT_ATTR))
> > - return 0;
> > + /*
> > + * If inline_data is enabled, we don't check ext_attr because
> > + * inline_data needs to read/write extended attribute even
> > + * without ext_attr.
> > + */
> > + if (!EXT2_HAS_INCOMPAT_FEATURE(handle->fs->super,
> > + EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
> > + if (!EXT2_HAS_COMPAT_FEATURE(handle->fs->super,
> > + EXT2_FEATURE_COMPAT_EXT_ATTR))
> > + return 0;
> > + }
> >
> > i = EXT2_INODE_SIZE(handle->fs->super);
> > if (i < sizeof(*inode))
> > --
> > 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
On Fri, Dec 06, 2013 at 05:58:09PM +0800, Zheng Liu wrote:
> From: Zheng Liu <[email protected]>
>
> This commit make us switch on/off inline_data feature in tune2fs. Now
> inline_data can be enabled without ext_attr. Hence we don't check it.
> As doing in mke2fs we need to check inode size when we want to enable
> it.
>
> Signed-off-by: Theodore Ts'o <[email protected]>
> Signed-off-by: Zheng Liu <[email protected]>
> ---
> misc/tune2fs.8.in | 3 ++
> misc/tune2fs.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++--
> 2 files changed, 97 insertions(+), 2 deletions(-)
>
> diff --git a/misc/tune2fs.8.in b/misc/tune2fs.8.in
> index 55c6dd9..8d56cdd 100644
> --- a/misc/tune2fs.8.in
> +++ b/misc/tune2fs.8.in
> @@ -531,6 +531,9 @@ 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 extended 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/tune2fs.c b/misc/tune2fs.c
> index 95c1886..d205df5 100644
> --- a/misc/tune2fs.c
> +++ b/misc/tune2fs.c
> @@ -92,6 +92,7 @@ static unsigned long new_inode_size;
> static char *ext_mount_opts;
> static int usrquota, grpquota;
> static int rewrite_checksums;
> +static int disable_inline_data;
>
> int journal_size, journal_flags;
> char *journal_device;
> @@ -140,7 +141,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|
> @@ -162,7 +164,8 @@ static __u32 clear_ok_features[3] = {
> /* Incompat */
> EXT2_FEATURE_INCOMPAT_FILETYPE |
> 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|
> @@ -745,6 +748,68 @@ static void rewrite_metadata_checksums(ext2_filsys fs)
> ext2fs_mark_super_dirty(fs);
> }
>
> +/*
> + * Allocate a block for all inodes with inline data.
> + */
> +static void expand_inodes(ext2_filsys fs)
> +{
> + int inode_size = EXT2_INODE_SIZE(fs->super);
> + struct ext2_inode *inode;
> + ext2_inode_scan scan;
> + errcode_t retval;
> + ext2_ino_t ino;
> +
> + retval = ext2fs_open_inode_scan(fs, 0, &scan);
> + if (retval) {
> + com_err("expand_inline_data", retval,
> + "while opening inode scan");
> + exit(1);
> + }
> +
> + retval = ext2fs_get_mem(inode_size, &inode);
> + if (retval) {
> + com_err("expand_inline_data", retval,
> + "while allocating memory");
> + exit(1);
> + }
> +
> + do {
> + retval = ext2fs_get_next_inode_full(scan, &ino, inode,
> + inode_size);
> + if (retval) {
> + com_err("expand_inline_data", retval,
> + "while getting next inode");
> + exit(1);
> + }
> + if (!ino)
> + break;
> + if (!ext2fs_test_inode_bitmap2(fs->inode_map, ino))
> + continue;
> + if (!(inode->i_flags & EXT4_INLINE_DATA_FL))
> + continue;
> + retval = ext2fs_inline_data_expand(fs, ino);
> + if (retval) {
> + com_err("expand_inline_data", retval,
> + "while expanding inode %lu", ino);
> + exit(1);
> + }
> + } while (ino);
> +
> + ext2fs_free_mem(&inode);
> + ext2fs_close_inode_scan(scan);
> +}
> +
> +static void expand_inline_data(ext2_filsys fs)
> +{
> + ext2fs_read_bitmaps(fs);
> + expand_inodes(fs);
> + ext2fs_mark_ib_dirty(fs);
> + ext2fs_mark_bb_dirty(fs);
> + ext2fs_mmp_update2(fs, 1);
> + fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
> + ext2fs_mark_super_dirty(fs);
> +}
> +
> static void enable_uninit_bg(ext2_filsys fs)
> {
> struct ext2_group_desc *gd;
> @@ -1083,6 +1148,31 @@ 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_OFF(E2P_FEATURE_INCOMPAT,
> + EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
> + if (mount_flags & EXT2_MF_MOUNTED) {
> + fputs(_("The inline data feature can't be clear if "
"can't be cleared"
--D
> + "the filesystem is mounted\n"), stderr);
> + return 1;
> + }
> + if (check_fsck_needed(fs))
> + return 1;
> + disable_inline_data = 1;
> + }
> +
> if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
> EXT4_FEATURE_RO_COMPAT_QUOTA)) {
> /*
> @@ -2708,6 +2798,8 @@ retry_open:
> }
> if (rewrite_checksums)
> rewrite_metadata_checksums(fs);
> + if (disable_inline_data)
> + expand_inline_data(fs);
> if (I_flag) {
> if (mount_flags & EXT2_MF_MOUNTED) {
> fputs(_("The inode size may only be "
> --
> 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
On Fri, Dec 06, 2013 at 05:57:57PM +0800, Zheng Liu wrote:
> From: Zheng Liu <[email protected]>
>
> Later we will use ext2fs_dirent_swab_in/out to handle big-endian problem
> for inline data. Now interfaces assume that it handles a block, but it
> is not true after adding inline data. So this commit defines a new
> interface for inline data.
>
> Signed-off-by: Zheng Liu <[email protected]>
Thanks, applied.
- Ted
As I mentioend on today's call, there are some pretty serious issues
with ext2fs_inline_data_dir_iterate(). In some places, it is
returning an errcode_t, and in some cases, it was returning the
BLOCK_ABORT, et. al flags (which would be an int).
Also, all of the callers of ext2fs_inline_data_dir_iterate() pass in
ext2fs_process_dir_block(), and given that
ext2fs_inline-data_dir_iterate() is a non-public function, it makes
the code simpler and more maintable to call ext2fs_process_dir_block()
directly. I think we would be better having a
ext2fs_process_dir_buffer() function, instead of complicating the
ext2fs_process_dir_block() interface, but that's a cleanup that we can
do in a different commit.
Here are the diffs that I had to apply to make everything be
consistent. It was a pretty significant change, so I'd appreciate a
second pair of eyes to sanity check what I changed. This diff is
versus the state of the tree after applying your PATCH v3 11/30 diff.
- Ted
diff --git a/lib/ext2fs/dir_iterate.c b/lib/ext2fs/dir_iterate.c
index 6bdfae5..8afccbe 100644
--- a/lib/ext2fs/dir_iterate.c
+++ b/lib/ext2fs/dir_iterate.c
@@ -129,9 +129,7 @@ errcode_t ext2fs_dir_iterate2(ext2_filsys fs,
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);
+ (void) ext2fs_inline_data_dir_iterate(fs, dir, &ctx);
}
if (retval)
return retval;
diff --git a/lib/ext2fs/ext2fsP.h b/lib/ext2fs/ext2fsP.h
index d910f42..b2afd15 100644
--- a/lib/ext2fs/ext2fsP.h
+++ b/lib/ext2fs/ext2fsP.h
@@ -88,15 +88,9 @@ 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);
+extern int ext2fs_inline_data_dir_iterate(ext2_filsys fs,
+ ext2_ino_t ino,
+ void *priv_data);
/* Generic numeric progress meter */
diff --git a/lib/ext2fs/inline_data.c b/lib/ext2fs/inline_data.c
index b768b9a..db1bc03 100644
--- a/lib/ext2fs/inline_data.c
+++ b/lib/ext2fs/inline_data.c
@@ -78,36 +78,32 @@ err:
}
-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)
+int ext2fs_inline_data_dir_iterate(ext2_filsys fs, ext2_ino_t ino,
+ void *priv_data)
{
struct dir_context *ctx;
struct ext2_inode inode;
struct ext2_dir_entry dirent;
struct ext2_inline_data data;
- errcode_t retval = 0;
+ int ret = BLOCK_ABORT;
e2_blkcnt_t blockcnt = 0;
ctx = (struct dir_context *)priv_data;
- retval = ext2fs_read_inode(fs, ino, &inode);
- if (retval)
+ ctx->errcode = ext2fs_read_inode(fs, ino, &inode);
+ if (ctx->errcode)
goto out;
- if (!(inode.i_flags & EXT4_INLINE_DATA_FL))
- return EXT2_ET_NO_INLINE_DATA;
+ if (!(inode.i_flags & EXT4_INLINE_DATA_FL)) {
+ ctx->errcode = EXT2_ET_NO_INLINE_DATA;
+ goto out;
+ }
if (!LINUX_S_ISDIR(inode.i_mode)) {
- retval = EXT2_ET_NO_DIRECTORY;
+ ctx->errcode = EXT2_ET_NO_DIRECTORY;
goto out;
}
+ ret = 0;
/* we first check '.' and '..' dir */
dirent.inode = ino;
@@ -117,8 +113,8 @@ errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
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)
+ ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
+ if (ret & BLOCK_ABORT)
goto out;
dirent.inode = ext2fs_le32_to_cpu(inode.i_block[0]);
@@ -129,8 +125,8 @@ errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
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_INLINE_DATA_CHANGED) {
+ ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
+ if (ret & BLOCK_INLINE_DATA_CHANGED) {
errcode_t err;
inode.i_block[0] = ext2fs_cpu_to_le32(dirent.inode);
@@ -138,62 +134,68 @@ errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
if (err)
goto out;
}
- if (retval & BLOCK_ABORT)
+ if (ret & BLOCK_ABORT)
goto out;
ctx->buf = (char *)inode.i_block + EXT4_INLINE_DATA_DOTDOT_SIZE;
ctx->buflen = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DATA_DOTDOT_SIZE;
#ifdef WORDS_BIGENDIAN
- retval = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
- if (retval)
+ ctx->errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
+ if (ctx->errcode) {
+ ret |= BLOCK_ABORT;
goto out;
+ }
#endif
- retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
- if (retval & BLOCK_INLINE_DATA_CHANGED) {
- errcode_t err;
-
+ ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
+ if (ret & BLOCK_INLINE_DATA_CHANGED) {
#ifdef WORDS_BIGENDIAN
- err = ext2fs_dirent_swab_out2(fs, ctx->buf, ctx->buflen, 0);
- if (err)
+ ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf,
+ ctx->buflen, 0);
+ if (ctx->errcode) {
+ ret |= BLOCK_ABORT;
goto out;
+ }
#endif
- err = ext2fs_write_inode(fs, ino, &inode);
- if (err)
- goto out;
+ ctx->errcode = ext2fs_write_inode(fs, ino, &inode);
+ if (ctx->errcode)
+ ret |= BLOCK_ABORT;
}
- if (retval & BLOCK_ABORT)
+ if (ret & BLOCK_ABORT)
goto out;
data.fs = fs;
data.ino = ino;
- retval = ext2fs_inline_data_ea_get(&data);
- if (retval)
+ ctx->errcode = ext2fs_inline_data_ea_get(&data);
+ if (ctx->errcode) {
+ ret |= BLOCK_ABORT;
goto out;
+ }
if (data.ea_size <= 0)
goto out;
ctx->buf = data.ea_data;
ctx->buflen = data.ea_size;
#ifdef WORDS_BIGENDIAN
- retval = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
- if (retval)
+ ctx.errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
+ if (ctx.errcode) {
+ ret |= BLOCK_ABORT;
goto out;
+ }
#endif
- retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
- if (retval & BLOCK_INLINE_DATA_CHANGED) {
- errcode_t err;
-
+ ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
+ if (ret & BLOCK_INLINE_DATA_CHANGED) {
#ifdef WORDS_BIGENDIAN
- err = ext2fs_dirent_swab_out2(fs, ctx->buf, ctx->buflen, 0);
- if (err) {
- retval = err;
+ ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf,
+ ctx->buflen, 0);
+ if (ctx->errcode) {
+ ret |= BLOCK_ABORT;
goto out1;
}
#endif
- err = ext2fs_inline_data_ea_set(&data);
- if (err)
- retval = err;
+ ctx->errcode = ext2fs_inline_data_ea_set(&data);
+ if (ctx->errcode)
+ ret |= BLOCK_ABORT;
}
out1:
@@ -201,6 +203,6 @@ out1:
ctx->buf = 0;
out:
- retval &= ~(BLOCK_ABORT | BLOCK_INLINE_DATA_CHANGED);
- return retval & BLOCK_ERROR ? ctx->errcode : retval;
+ ret &= ~(BLOCK_ABORT | BLOCK_INLINE_DATA_CHANGED);
+ return ret;
}
On Fri, Dec 06, 2013 at 05:58:11PM +0800, Zheng Liu wrote:
> From: Zheng Liu <[email protected]>
>
> Signed-off-by: Theodore Ts'o <[email protected]>
> Signed-off-by: Zheng Liu <[email protected]>
With my change to patch 11/30, the following change is needed to this
patch.
- Ted
diff --git a/lib/ext2fs/dblist_dir.c b/lib/ext2fs/dblist_dir.c
index 1e36584..2fbb772 100644
--- a/lib/ext2fs/dblist_dir.c
+++ b/lib/ext2fs/dblist_dir.c
@@ -78,9 +78,7 @@ static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry2 *db_info,
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);
+ ret = ext2fs_inline_data_dir_iterate(fs, ctx->dir, ctx);
} else {
ret = ext2fs_process_dir_block(fs, &db_info->blk,
db_info->blockcnt, 0, 0,
On Mon, Mar 03, 2014 at 05:16:53PM -0500, Theodore Ts'o wrote:
> As I mentioend on today's call, there are some pretty serious issues
> with ext2fs_inline_data_dir_iterate(). In some places, it is
> returning an errcode_t, and in some cases, it was returning the
> BLOCK_ABORT, et. al flags (which would be an int).
Sorry I missed this week's call, I wasn't feeling well.
> Also, all of the callers of ext2fs_inline_data_dir_iterate() pass in
> ext2fs_process_dir_block(), and given that
> ext2fs_inline-data_dir_iterate() is a non-public function, it makes
> the code simpler and more maintable to call ext2fs_process_dir_block()
> directly. I think we would be better having a
> ext2fs_process_dir_buffer() function, instead of complicating the
> ext2fs_process_dir_block() interface, but that's a cleanup that we can
> do in a different commit.
>
> Here are the diffs that I had to apply to make everything be
> consistent. It was a pretty significant change, so I'd appreciate a
> second pair of eyes to sanity check what I changed. This diff is
> versus the state of the tree after applying your PATCH v3 11/30 diff.
>
> - Ted
>
> diff --git a/lib/ext2fs/dir_iterate.c b/lib/ext2fs/dir_iterate.c
> index 6bdfae5..8afccbe 100644
> --- a/lib/ext2fs/dir_iterate.c
> +++ b/lib/ext2fs/dir_iterate.c
> @@ -129,9 +129,7 @@ errcode_t ext2fs_dir_iterate2(ext2_filsys fs,
> 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);
> + (void) ext2fs_inline_data_dir_iterate(fs, dir, &ctx);
> }
> if (retval)
> return retval;
Hmm. In order to get into ext2fs_inline_data_dir_iterate(), it has to be the
case that retval == EXT2_ET_INLINE_DATA_CANT_ITERATE. But then we immediately
return that error code, which means that anything interesting in ctx.errcode
will be lost. I /think/ you want to set retval = 0 in that if clause?
> diff --git a/lib/ext2fs/ext2fsP.h b/lib/ext2fs/ext2fsP.h
> index d910f42..b2afd15 100644
> --- a/lib/ext2fs/ext2fsP.h
> +++ b/lib/ext2fs/ext2fsP.h
> @@ -88,15 +88,9 @@ 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);
> +extern int ext2fs_inline_data_dir_iterate(ext2_filsys fs,
> + ext2_ino_t ino,
> + void *priv_data);
>
> /* Generic numeric progress meter */
>
> diff --git a/lib/ext2fs/inline_data.c b/lib/ext2fs/inline_data.c
> index b768b9a..db1bc03 100644
> --- a/lib/ext2fs/inline_data.c
> +++ b/lib/ext2fs/inline_data.c
> @@ -78,36 +78,32 @@ err:
> }
>
>
> -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)
> +int ext2fs_inline_data_dir_iterate(ext2_filsys fs, ext2_ino_t ino,
> + void *priv_data)
> {
> struct dir_context *ctx;
> struct ext2_inode inode;
> struct ext2_dir_entry dirent;
> struct ext2_inline_data data;
> - errcode_t retval = 0;
> + int ret = BLOCK_ABORT;
> e2_blkcnt_t blockcnt = 0;
>
> ctx = (struct dir_context *)priv_data;
>
> - retval = ext2fs_read_inode(fs, ino, &inode);
> - if (retval)
> + ctx->errcode = ext2fs_read_inode(fs, ino, &inode);
> + if (ctx->errcode)
> goto out;
>
> - if (!(inode.i_flags & EXT4_INLINE_DATA_FL))
> - return EXT2_ET_NO_INLINE_DATA;
> + if (!(inode.i_flags & EXT4_INLINE_DATA_FL)) {
> + ctx->errcode = EXT2_ET_NO_INLINE_DATA;
> + goto out;
> + }
>
> if (!LINUX_S_ISDIR(inode.i_mode)) {
> - retval = EXT2_ET_NO_DIRECTORY;
> + ctx->errcode = EXT2_ET_NO_DIRECTORY;
> goto out;
> }
> + ret = 0;
>
> /* we first check '.' and '..' dir */
> dirent.inode = ino;
> @@ -117,8 +113,8 @@ errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
> 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)
> + ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
> + if (ret & BLOCK_ABORT)
> goto out;
>
> dirent.inode = ext2fs_le32_to_cpu(inode.i_block[0]);
> @@ -129,8 +125,8 @@ errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
> 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_INLINE_DATA_CHANGED) {
> + ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
> + if (ret & BLOCK_INLINE_DATA_CHANGED) {
> errcode_t err;
>
> inode.i_block[0] = ext2fs_cpu_to_le32(dirent.inode);
> @@ -138,62 +134,68 @@ errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
> if (err)
> goto out;
> }
> - if (retval & BLOCK_ABORT)
> + if (ret & BLOCK_ABORT)
> goto out;
>
> ctx->buf = (char *)inode.i_block + EXT4_INLINE_DATA_DOTDOT_SIZE;
> ctx->buflen = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DATA_DOTDOT_SIZE;
> #ifdef WORDS_BIGENDIAN
> - retval = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
> - if (retval)
> + ctx->errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
> + if (ctx->errcode) {
> + ret |= BLOCK_ABORT;
> goto out;
> + }
> #endif
> - retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
> - if (retval & BLOCK_INLINE_DATA_CHANGED) {
> - errcode_t err;
> -
> + ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
> + if (ret & BLOCK_INLINE_DATA_CHANGED) {
Was BLOCK_INLINE_DATA_CHANGED ever cleared from retval? As written, I /think/
that writing either dot or dotdot entries will result in the EA being written
too, even if nothing changes there.
Guess I'll have a look at the other patches too.
--D
> #ifdef WORDS_BIGENDIAN
> - err = ext2fs_dirent_swab_out2(fs, ctx->buf, ctx->buflen, 0);
> - if (err)
> + ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf,
> + ctx->buflen, 0);
> + if (ctx->errcode) {
> + ret |= BLOCK_ABORT;
> goto out;
> + }
> #endif
> - err = ext2fs_write_inode(fs, ino, &inode);
> - if (err)
> - goto out;
> + ctx->errcode = ext2fs_write_inode(fs, ino, &inode);
> + if (ctx->errcode)
> + ret |= BLOCK_ABORT;
> }
> - if (retval & BLOCK_ABORT)
> + if (ret & BLOCK_ABORT)
> goto out;
>
> data.fs = fs;
> data.ino = ino;
> - retval = ext2fs_inline_data_ea_get(&data);
> - if (retval)
> + ctx->errcode = ext2fs_inline_data_ea_get(&data);
> + if (ctx->errcode) {
> + ret |= BLOCK_ABORT;
> goto out;
> + }
> if (data.ea_size <= 0)
> goto out;
>
> ctx->buf = data.ea_data;
> ctx->buflen = data.ea_size;
> #ifdef WORDS_BIGENDIAN
> - retval = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
> - if (retval)
> + ctx.errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
> + if (ctx.errcode) {
> + ret |= BLOCK_ABORT;
> goto out;
> + }
> #endif
>
> - retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
> - if (retval & BLOCK_INLINE_DATA_CHANGED) {
> - errcode_t err;
> -
> + ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
> + if (ret & BLOCK_INLINE_DATA_CHANGED) {
> #ifdef WORDS_BIGENDIAN
> - err = ext2fs_dirent_swab_out2(fs, ctx->buf, ctx->buflen, 0);
> - if (err) {
> - retval = err;
> + ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf,
> + ctx->buflen, 0);
> + if (ctx->errcode) {
> + ret |= BLOCK_ABORT;
> goto out1;
> }
> #endif
> - err = ext2fs_inline_data_ea_set(&data);
> - if (err)
> - retval = err;
> + ctx->errcode = ext2fs_inline_data_ea_set(&data);
> + if (ctx->errcode)
> + ret |= BLOCK_ABORT;
> }
>
> out1:
> @@ -201,6 +203,6 @@ out1:
> ctx->buf = 0;
>
> out:
> - retval &= ~(BLOCK_ABORT | BLOCK_INLINE_DATA_CHANGED);
> - return retval & BLOCK_ERROR ? ctx->errcode : retval;
> + ret &= ~(BLOCK_ABORT | BLOCK_INLINE_DATA_CHANGED);
> + return ret;
> }
> --
> 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
On Sat, Dec 07, 2013 at 11:45:02AM +0800, Zheng Liu wrote:
> On Fri, Dec 06, 2013 at 01:38:26PM -0800, Darrick J. Wong wrote:
> > On Fri, Dec 06, 2013 at 05:57:56PM +0800, Zheng Liu wrote:
> > > From: Zheng Liu <[email protected]>
> > >
> > > Now on kernel side we can enable inline data even without ext_attr. So
> > > we need to adjust sanity check on ext2fs_xattrs_read/write to let we
> > > access extended attribute when filesystem is create with '-O ^ext_attr,
> > > inline_data'.
> >
> > Oops, I removed both of those checks in favor of looking for ext_attr or
> > inline_data in ext2fs_xattrs_open(). Oh well, sorry about the
> > miscommunication.
>
> Got it. I am happy to rebase against your latest patch.
Just FYI, you might want to rebase against -next + the EA v5 patch that I sent
out Saturday.
--D
>
> Thanks,
> - Zheng
>
> >
> > --D
> >
> > >
> > > Signed-off-by: Zheng Liu <[email protected]>
> > > ---
> > > lib/ext2fs/ext_attr.c | 28 ++++++++++++++++++++++------
> > > 1 file changed, 22 insertions(+), 6 deletions(-)
> > >
> > > diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
> > > index 09e13b2..e5e0fa9 100644
> > > --- a/lib/ext2fs/ext_attr.c
> > > +++ b/lib/ext2fs/ext_attr.c
> > > @@ -469,9 +469,17 @@ errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle)
> > > unsigned int i;
> > > errcode_t err;
> > >
> > > - if (!EXT2_HAS_COMPAT_FEATURE(handle->fs->super,
> > > - EXT2_FEATURE_COMPAT_EXT_ATTR))
> > > - return 0;
> > > + /*
> > > + * If inline_data is enabled, we don't check ext_attr because
> > > + * inline_data needs to read/write extended attribute even
> > > + * without ext_attr.
> > > + */
> > > + if (!EXT2_HAS_INCOMPAT_FEATURE(handle->fs->super,
> > > + EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
> > > + if (!EXT2_HAS_COMPAT_FEATURE(handle->fs->super,
> > > + EXT2_FEATURE_COMPAT_EXT_ATTR))
> > > + return 0;
> > > + }
> > >
> > > i = EXT2_INODE_SIZE(handle->fs->super);
> > > if (i < sizeof(*inode))
> > > @@ -684,9 +692,17 @@ errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle)
> > > int i;
> > > errcode_t err;
> > >
> > > - if (!EXT2_HAS_COMPAT_FEATURE(handle->fs->super,
> > > - EXT2_FEATURE_COMPAT_EXT_ATTR))
> > > - return 0;
> > > + /*
> > > + * If inline_data is enabled, we don't check ext_attr because
> > > + * inline_data needs to read/write extended attribute even
> > > + * without ext_attr.
> > > + */
> > > + if (!EXT2_HAS_INCOMPAT_FEATURE(handle->fs->super,
> > > + EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
> > > + if (!EXT2_HAS_COMPAT_FEATURE(handle->fs->super,
> > > + EXT2_FEATURE_COMPAT_EXT_ATTR))
> > > + return 0;
> > > + }
> > >
> > > i = EXT2_INODE_SIZE(handle->fs->super);
> > > if (i < sizeof(*inode))
> > > --
> > > 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
On Fri, Dec 06, 2013 at 05:58:06PM +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_inline_data_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/ext2fs.h | 2 +
> lib/ext2fs/ext2fsP.h | 6 ++
> lib/ext2fs/ext_attr.c | 64 +++++++++++++
> lib/ext2fs/fileio.c | 106 ++++++++++++++++++++++
> lib/ext2fs/inline_data.c | 217 +++++++++++++++++++++++++++++++++++----------
> 7 files changed, 362 insertions(+), 49 deletions(-)
>
> diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
> index bc8413d..0908f48 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 f93f0e8..99360da 100644
> --- a/lib/ext2fs/ext2_err.et.in
> +++ b/lib/ext2fs/ext2_err.et.in
> @@ -506,4 +506,7 @@ ec EXT2_ET_NO_INLINE_DATA,
> ec EXT2_ET_INLINE_DATA_NO_BLOCK,
> "No block for an inode with inline data"
>
> +ec EXT2_ET_INLINE_DATA_NO_SPACE,
> + "No free space in inline data"
> +
> end
> diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
> index 322c39d..b10d6dd 100644
> --- a/lib/ext2fs/ext2fs.h
> +++ b/lib/ext2fs/ext2fs.h
> @@ -1171,6 +1171,8 @@ 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);
> +errcode_t ext2fs_xattr_inode_max_size(ext2_filsys fs, ext2_ino_t ino,
> + size_t *size);
>
> /* extent.c */
> extern errcode_t ext2fs_extent_header_verify(void *ptr, int size);
> 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 e5e0fa9..3942f1c 100644
> --- a/lib/ext2fs/ext_attr.c
> +++ b/lib/ext2fs/ext_attr.c
> @@ -842,6 +842,70 @@ errcode_t ext2fs_xattr_get(struct ext2_xattr_handle *h, const char *key,
> return EXT2_ET_EA_KEY_NOT_FOUND;
> }
>
> +errcode_t ext2fs_xattr_inode_max_size(ext2_filsys fs, ext2_ino_t ino,
> + 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(fs->super);
> + if (i < sizeof(*inode))
> + i = sizeof(*inode);
> + err = ext2fs_get_memzero(i, &inode);
> + if (err)
> + return err;
> +
> + err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)inode,
> + EXT2_INODE_SIZE(fs->super));
> + if (err)
> + goto out;
> +
> + /* Does the inode have size for EA? */
> + if (EXT2_INODE_SIZE(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(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(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,
> diff --git a/lib/ext2fs/fileio.c b/lib/ext2fs/fileio.c
> index 02e6263..a9f25db 100644
> --- a/lib/ext2fs/fileio.c
> +++ b/lib/ext2fs/fileio.c
> @@ -224,6 +224,38 @@ 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 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;
> +
> + count = size - file->pos;
> + if (count > wanted)
> + count = wanted;
> + memcpy(buf, file->buf + file->pos, 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 +268,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 +302,66 @@ 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 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) {
> + count = nbytes - file->pos;
> + memcpy(file->buf + file->pos, 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);
Should we simply have ext2_file.inode be a pointer to ext2_inode?
--D
> + 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 +376,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 a25c5bd..1905121 100644
> --- a/lib/ext2fs/inline_data.c
> +++ b/lib/ext2fs/inline_data.c
> @@ -336,14 +336,98 @@ err:
> return retval;
> }
>
> +static errcode_t
> +ext2fs_inline_data_dir_expand(ext2_filsys fs, ext2_ino_t ino,
> + struct ext2_inode *inode, char *buf, size_t size)
> +{
> + errcode_t retval;
> + blk64_t blk;
> + char *blk_buf;
> +
> + retval = ext2fs_get_memzero(fs->blocksize, &blk_buf);
> + if (retval)
> + return retval;
> +
> +#ifdef WORDS_BIGENDIAN
> + retval = ext2fs_dirent_swab_in2(fs, buf, size);
> + if (retval)
> + goto errout;
> +#endif
> +
> + /* Adjust the rec_len */
> + retval = ext2fs_inline_data_convert_dir(fs, ino, blk_buf, buf, size);
> + /* Allocate a new block */
> + retval = ext2fs_new_block2(fs, 0, 0, &blk);
> + if (retval)
> + goto errout;
> + retval = ext2fs_write_dir_block4(fs, blk, blk_buf, 0, ino);
> + if (retval)
> + goto errout;
> +
> + /* Update inode */
> + if (EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT3_FEATURE_INCOMPAT_EXTENTS))
> + inode->i_flags |= EXT4_EXTENTS_FL;
> + inode->i_flags &= ~EXT4_INLINE_DATA_FL;
> + ext2fs_iblk_set(fs, inode, 1);
> + inode->i_size = fs->blocksize;
> + retval = ext2fs_bmap2(fs, ino, inode, 0, BMAP_SET, 0, 0, &blk);
> + if (retval)
> + goto errout;
> + retval = ext2fs_write_inode(fs, ino, inode);
> + if (retval)
> + goto errout;
> + ext2fs_block_alloc_stats(fs, blk, +1);
> +
> +errout:
> + ext2fs_free_mem(&blk_buf);
> + return retval;
> +}
> +
> +static errcode_t
> +ext2fs_inline_data_file_expand(ext2_filsys fs, ext2_ino_t ino,
> + struct ext2_inode *inode, char *buf, size_t size)
> +{
> + ext2_file_t e2_file;
> + errcode_t retval;
> +
> + /* Update inode */
> + if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
> + EXT3_FEATURE_INCOMPAT_EXTENTS)) {
> + int i;
> + struct ext3_extent_header *eh;
> +
> + eh = (struct ext3_extent_header *) &inode->i_block[0];
> + eh->eh_depth = 0;
> + eh->eh_entries = 0;
> + eh->eh_magic = EXT3_EXT_MAGIC;
> + i = (sizeof(inode->i_block) - sizeof(*eh)) /
> + sizeof(struct ext3_extent);
> + eh->eh_max = ext2fs_cpu_to_le16(i);
> + inode->i_flags |= EXT4_EXTENTS_FL;
> + }
> + inode->i_flags &= ~EXT4_INLINE_DATA_FL;
> + ext2fs_iblk_set(fs, inode, 0);
> + inode->i_size = 0;
> + retval = ext2fs_write_inode(fs, ino, inode);
> + if (retval)
> + return retval;
> +
> + /* Write out the block buffer */
> + retval = ext2fs_file_open(fs, ino, EXT2_FILE_WRITE, &e2_file);
> + if (retval)
> + return retval;
> + retval = ext2fs_file_write(e2_file, buf, size, 0);
> + ext2fs_file_close(e2_file);
> + 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;
> + size_t inline_size;
> char *inline_buf = 0;
> - char *blk_buf = 0;
>
> EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
>
> @@ -354,14 +438,13 @@ errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino)
> 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);
> + inline_size = data.ea_size + EXT4_MIN_INLINE_DATA_SIZE;
> + retval = ext2fs_get_mem(inline_size, &inline_buf);
> if (retval)
> goto errout;
>
> @@ -371,56 +454,98 @@ errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino)
> data.ea_data, data.ea_size);
> }
>
> -#ifdef WORDS_BIGENDIAN
> - retval = ext2fs_dirent_swab_in2(fs, inline_buf,
> - data.ea_size + EXT4_MIN_INLINE_DATA_SIZE);
> - if (retval)
> - goto errout;
> -#endif
> -
> 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;
> - retval = ext2fs_write_dir_block4(fs, blk, blk_buf, 0, ino);
> - if (retval)
> - goto errout;
> -
> - /* Update inode */
> - if (EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT3_FEATURE_INCOMPAT_EXTENTS))
> - inode.i_flags |= EXT4_EXTENTS_FL;
> - inode.i_flags &= ~EXT4_INLINE_DATA_FL;
> - ext2fs_iblk_set(fs, &inode, 1);
> - inode.i_size = fs->blocksize;
> - retval = ext2fs_bmap2(fs, ino, &inode, 0, BMAP_SET, 0, 0, &blk);
> - if (retval)
> - goto errout;
> - retval = ext2fs_write_inode(fs, ino, &inode);
> - if (retval)
> - goto errout;
> - ext2fs_block_alloc_stats2(fs, blk, +1);
> + if (LINUX_S_ISDIR(inode.i_mode)) {
> + retval = ext2fs_inline_data_dir_expand(fs, ino, &inode,
> + inline_buf, inline_size);
> + } else {
> + retval = ext2fs_inline_data_file_expand(fs, ino, &inode,
> + inline_buf, inline_size);
> + }
>
> 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;
> }
> +
> +/*
> + * 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;
> + errcode_t retval;
> + size_t max_size;
> +
> + if (!inode) {
> + retval = ext2fs_read_inode(fs, ino, &inode_buf);
> + if (retval)
> + return retval;
> + inode = &inode_buf;
> + }
> +
> + if (size <= EXT4_MIN_INLINE_DATA_SIZE) {
> + memcpy((void *)inode->i_block, buf, size);
> + return ext2fs_write_inode(fs, ino, inode);
> + }
> +
> + retval = ext2fs_xattr_inode_max_size(fs, ino, &max_size);
> + if (retval)
> + return retval;
> +
> + if (size - EXT4_MIN_INLINE_DATA_SIZE > max_size)
> + return EXT2_ET_INLINE_DATA_NO_SPACE;
> +
> + 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
On Mon, Mar 03, 2014 at 05:16:53PM -0500, Theodore Ts'o wrote:
> As I mentioend on today's call, there are some pretty serious issues
> with ext2fs_inline_data_dir_iterate(). In some places, it is
> returning an errcode_t, and in some cases, it was returning the
> BLOCK_ABORT, et. al flags (which would be an int).
>
> Also, all of the callers of ext2fs_inline_data_dir_iterate() pass in
> ext2fs_process_dir_block(), and given that
> ext2fs_inline-data_dir_iterate() is a non-public function, it makes
> the code simpler and more maintable to call ext2fs_process_dir_block()
> directly. I think we would be better having a
> ext2fs_process_dir_buffer() function, instead of complicating the
> ext2fs_process_dir_block() interface, but that's a cleanup that we can
> do in a different commit.
>
> Here are the diffs that I had to apply to make everything be
> consistent. It was a pretty significant change, so I'd appreciate a
> second pair of eyes to sanity check what I changed. This diff is
> versus the state of the tree after applying your PATCH v3 11/30 diff.
>
> - Ted
>
> diff --git a/lib/ext2fs/dir_iterate.c b/lib/ext2fs/dir_iterate.c
> index 6bdfae5..8afccbe 100644
> --- a/lib/ext2fs/dir_iterate.c
> +++ b/lib/ext2fs/dir_iterate.c
> @@ -129,9 +129,7 @@ errcode_t ext2fs_dir_iterate2(ext2_filsys fs,
> 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);
> + (void) ext2fs_inline_data_dir_iterate(fs, dir, &ctx);
retval = 0;
Here we need to clear EXT2_ET_INLINE_DATA_CANT_ITERATE error.
Otherwise, ext2fs_dir_iterate2() will always return this error when we
iterate a dir with inline data.
The patch looks good to me. Thanks for improving this.
Reviewed-by: Zheng Liu <[email protected]>
- Zheng
> }
> if (retval)
> return retval;
> diff --git a/lib/ext2fs/ext2fsP.h b/lib/ext2fs/ext2fsP.h
> index d910f42..b2afd15 100644
> --- a/lib/ext2fs/ext2fsP.h
> +++ b/lib/ext2fs/ext2fsP.h
> @@ -88,15 +88,9 @@ 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);
> +extern int ext2fs_inline_data_dir_iterate(ext2_filsys fs,
> + ext2_ino_t ino,
> + void *priv_data);
>
> /* Generic numeric progress meter */
>
> diff --git a/lib/ext2fs/inline_data.c b/lib/ext2fs/inline_data.c
> index b768b9a..db1bc03 100644
> --- a/lib/ext2fs/inline_data.c
> +++ b/lib/ext2fs/inline_data.c
> @@ -78,36 +78,32 @@ err:
> }
>
>
> -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)
> +int ext2fs_inline_data_dir_iterate(ext2_filsys fs, ext2_ino_t ino,
> + void *priv_data)
> {
> struct dir_context *ctx;
> struct ext2_inode inode;
> struct ext2_dir_entry dirent;
> struct ext2_inline_data data;
> - errcode_t retval = 0;
> + int ret = BLOCK_ABORT;
> e2_blkcnt_t blockcnt = 0;
>
> ctx = (struct dir_context *)priv_data;
>
> - retval = ext2fs_read_inode(fs, ino, &inode);
> - if (retval)
> + ctx->errcode = ext2fs_read_inode(fs, ino, &inode);
> + if (ctx->errcode)
> goto out;
>
> - if (!(inode.i_flags & EXT4_INLINE_DATA_FL))
> - return EXT2_ET_NO_INLINE_DATA;
> + if (!(inode.i_flags & EXT4_INLINE_DATA_FL)) {
> + ctx->errcode = EXT2_ET_NO_INLINE_DATA;
> + goto out;
> + }
>
> if (!LINUX_S_ISDIR(inode.i_mode)) {
> - retval = EXT2_ET_NO_DIRECTORY;
> + ctx->errcode = EXT2_ET_NO_DIRECTORY;
> goto out;
> }
> + ret = 0;
>
> /* we first check '.' and '..' dir */
> dirent.inode = ino;
> @@ -117,8 +113,8 @@ errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
> 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)
> + ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
> + if (ret & BLOCK_ABORT)
> goto out;
>
> dirent.inode = ext2fs_le32_to_cpu(inode.i_block[0]);
> @@ -129,8 +125,8 @@ errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
> 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_INLINE_DATA_CHANGED) {
> + ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
> + if (ret & BLOCK_INLINE_DATA_CHANGED) {
> errcode_t err;
>
> inode.i_block[0] = ext2fs_cpu_to_le32(dirent.inode);
> @@ -138,62 +134,68 @@ errcode_t ext2fs_inline_data_dir_iterate(ext2_filsys fs,
> if (err)
> goto out;
> }
> - if (retval & BLOCK_ABORT)
> + if (ret & BLOCK_ABORT)
> goto out;
>
> ctx->buf = (char *)inode.i_block + EXT4_INLINE_DATA_DOTDOT_SIZE;
> ctx->buflen = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DATA_DOTDOT_SIZE;
> #ifdef WORDS_BIGENDIAN
> - retval = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
> - if (retval)
> + ctx->errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
> + if (ctx->errcode) {
> + ret |= BLOCK_ABORT;
> goto out;
> + }
> #endif
> - retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
> - if (retval & BLOCK_INLINE_DATA_CHANGED) {
> - errcode_t err;
> -
> + ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
> + if (ret & BLOCK_INLINE_DATA_CHANGED) {
> #ifdef WORDS_BIGENDIAN
> - err = ext2fs_dirent_swab_out2(fs, ctx->buf, ctx->buflen, 0);
> - if (err)
> + ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf,
> + ctx->buflen, 0);
> + if (ctx->errcode) {
> + ret |= BLOCK_ABORT;
> goto out;
> + }
> #endif
> - err = ext2fs_write_inode(fs, ino, &inode);
> - if (err)
> - goto out;
> + ctx->errcode = ext2fs_write_inode(fs, ino, &inode);
> + if (ctx->errcode)
> + ret |= BLOCK_ABORT;
> }
> - if (retval & BLOCK_ABORT)
> + if (ret & BLOCK_ABORT)
> goto out;
>
> data.fs = fs;
> data.ino = ino;
> - retval = ext2fs_inline_data_ea_get(&data);
> - if (retval)
> + ctx->errcode = ext2fs_inline_data_ea_get(&data);
> + if (ctx->errcode) {
> + ret |= BLOCK_ABORT;
> goto out;
> + }
> if (data.ea_size <= 0)
> goto out;
>
> ctx->buf = data.ea_data;
> ctx->buflen = data.ea_size;
> #ifdef WORDS_BIGENDIAN
> - retval = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
> - if (retval)
> + ctx.errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
> + if (ctx.errcode) {
> + ret |= BLOCK_ABORT;
> goto out;
> + }
> #endif
>
> - retval |= (*func)(fs, 0, blockcnt++, 0, 0, priv_data);
> - if (retval & BLOCK_INLINE_DATA_CHANGED) {
> - errcode_t err;
> -
> + ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
> + if (ret & BLOCK_INLINE_DATA_CHANGED) {
> #ifdef WORDS_BIGENDIAN
> - err = ext2fs_dirent_swab_out2(fs, ctx->buf, ctx->buflen, 0);
> - if (err) {
> - retval = err;
> + ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf,
> + ctx->buflen, 0);
> + if (ctx->errcode) {
> + ret |= BLOCK_ABORT;
> goto out1;
> }
> #endif
> - err = ext2fs_inline_data_ea_set(&data);
> - if (err)
> - retval = err;
> + ctx->errcode = ext2fs_inline_data_ea_set(&data);
> + if (ctx->errcode)
> + ret |= BLOCK_ABORT;
> }
>
> out1:
> @@ -201,6 +203,6 @@ out1:
> ctx->buf = 0;
>
> out:
> - retval &= ~(BLOCK_ABORT | BLOCK_INLINE_DATA_CHANGED);
> - return retval & BLOCK_ERROR ? ctx->errcode : retval;
> + ret &= ~(BLOCK_ABORT | BLOCK_INLINE_DATA_CHANGED);
> + return ret;
> }
On Mon, Mar 03, 2014 at 05:18:40PM -0500, Theodore Ts'o wrote:
> On Fri, Dec 06, 2013 at 05:58:11PM +0800, Zheng Liu wrote:
> > From: Zheng Liu <[email protected]>
> >
> > Signed-off-by: Theodore Ts'o <[email protected]>
> > Signed-off-by: Zheng Liu <[email protected]>
>
> With my change to patch 11/30, the following change is needed to this
> patch.
>
> - Ted
Thanks for fixing this. The patch looks good to me.
Reviewed-by: Zheng Liu <[email protected]>
- Zheng
>
> diff --git a/lib/ext2fs/dblist_dir.c b/lib/ext2fs/dblist_dir.c
> index 1e36584..2fbb772 100644
> --- a/lib/ext2fs/dblist_dir.c
> +++ b/lib/ext2fs/dblist_dir.c
> @@ -78,9 +78,7 @@ static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry2 *db_info,
> 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);
> + ret = ext2fs_inline_data_dir_iterate(fs, ctx->dir, ctx);
> } else {
> ret = ext2fs_process_dir_block(fs, &db_info->blk,
> db_info->blockcnt, 0, 0,
On Mon, Mar 03, 2014 at 03:31:54PM -0800, Darrick J. Wong wrote:
> > diff --git a/lib/ext2fs/dir_iterate.c b/lib/ext2fs/dir_iterate.c
> > index 6bdfae5..8afccbe 100644
> > --- a/lib/ext2fs/dir_iterate.c
> > +++ b/lib/ext2fs/dir_iterate.c
> > @@ -129,9 +129,7 @@ errcode_t ext2fs_dir_iterate2(ext2_filsys fs,
> > 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);
> > + (void) ext2fs_inline_data_dir_iterate(fs, dir, &ctx);
> > }
> > if (retval)
> > return retval;
>
> Hmm. In order to get into ext2fs_inline_data_dir_iterate(), it has to be the
> case that retval == EXT2_ET_INLINE_DATA_CANT_ITERATE. But then we immediately
> return that error code, which means that anything interesting in ctx.errcode
> will be lost. I /think/ you want to set retval = 0 in that if clause?
Yes, I noticed this morning that it caused a regression test failure
(which is comforting from a code coverage point of view :-). I've
made that change.
>
> Was BLOCK_INLINE_DATA_CHANGED ever cleared from retval? As written, I /think/
> that writing either dot or dotdot entries will result in the EA being written
> too, even if nothing changes there.
Yes, good point. I've added the following change.
diff --git a/lib/ext2fs/inline_data.c b/lib/ext2fs/inline_data.c
index db1bc03..9bfc8ef 100644
--- a/lib/ext2fs/inline_data.c
+++ b/lib/ext2fs/inline_data.c
@@ -133,6 +133,7 @@ int ext2fs_inline_data_dir_iterate(ext2_filsys fs, ext2_ino_t ino,
err = ext2fs_write_inode(fs, ino, &inode);
if (err)
goto out;
+ ret &= ~BLOCK_INLINE_DATA_CHANGED;
}
if (ret & BLOCK_ABORT)
goto out;
@@ -159,6 +160,7 @@ int ext2fs_inline_data_dir_iterate(ext2_filsys fs, ext2_ino_t ino,
ctx->errcode = ext2fs_write_inode(fs, ino, &inode);
if (ctx->errcode)
ret |= BLOCK_ABORT;
+ ret &= ~BLOCK_INLINE_DATA_CHANGED;
}
if (ret & BLOCK_ABORT)
goto out;
> Guess I'll have a look at the other patches too.
I've updated the pu branch with the inline data patches, so if you and
Zheng could take a look at it and sanity check it, I'd really
appreicate it. Thanks!!
- Ted
> I've updated the pu branch with the inline data patches, so if you and
> Zheng could take a look at it and sanity check it, I'd really
> appreicate it. Thanks!!
The inline data patches have now graduated into the "next" branch.
If you could do some sanity testing, that would be great.
Thanks!!
- Ted
errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags)
{
+ return ext2fs_dirent_swab_in(fs, buf, fs->blocksize, flags);
+}
I guess it misses "2" for function name.
Should be:
errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags)
{
+ return ext2fs_dirent_swab_in2(fs, buf, fs->blocksize, flags);
+}
Thanks,
Jon
> On Mon, Mar 3, 2014 at 10:49 AM, Theodore Ts'o <[email protected]> wrote:
>> On Fri, Dec 06, 2013 at 05:57:57PM +0800, Zheng Liu wrote:
>> From: Zheng Liu <[email protected]>
>>
>> Later we will use ext2fs_dirent_swab_in/out to handle big-endian problem
>> for inline data. Now interfaces assume that it handles a block, but it
>> is not true after adding inline data. So this commit defines a new
>> interface for inline data.
>>
>> Signed-off-by: Zheng Liu <[email protected]>
>
> Thanks, applied.
>
> - 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
On Thu, Mar 13, 2014 at 12:32:34PM -0400, jon ernst wrote:
> errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags)
> {
> + return ext2fs_dirent_swab_in(fs, buf, fs->blocksize, flags);
> +}
>
> I guess it misses "2" for function name.
> Should be:
>
> errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags)
> {
> + return ext2fs_dirent_swab_in2(fs, buf, fs->blocksize, flags);
> +}
Nice catch, thanks!!
- Ted
>From f9574ad0ba655b436b3940ff1604df4d11a43115 Mon Sep 17 00:00:00 2001
From: Theodore Ts'o <[email protected]>
Date: Thu, 13 Mar 2014 13:40:37 -0400
Subject: [PATCH] libext2fs: fix build failure on big endian systems
Fix a typo that we didn't notice because all the world's an x86. :-)
Reported-by: jon ernst <[email protected]>
Signed-off-by: "Theodore Ts'o" <[email protected]>
---
lib/ext2fs/swapfs.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c
index e3628b3..f08859b 100644
--- a/lib/ext2fs/swapfs.c
+++ b/lib/ext2fs/swapfs.c
@@ -363,7 +363,7 @@ void ext2fs_swap_mmp(struct mmp_struct *mmp)
errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags)
{
- return ext2fs_dirent_swab_in(fs, buf, fs->blocksize, flags);
+ return ext2fs_dirent_swab_in2(fs, buf, fs->blocksize, flags);
}
errcode_t ext2fs_dirent_swab_in2(ext2_filsys fs, char *buf,
--
1.9.0