diff options
author | Andreas Dilger <andreas.dilger@intel.com> | 2017-07-04 23:53:59 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2017-07-04 23:53:59 -0400 |
commit | 6a081f6d2a5cff0f5a077065aab39901d54bfb61 (patch) | |
tree | 671b9d6fe2e2498c5cc996648a2b6bdde5e8326f /lib | |
parent | 08ddd208ff89b3b7735e6edc60a7a3717e7daf06 (diff) | |
download | e2fsprogs-6a081f6d2a5cff0f5a077065aab39901d54bfb61.tar.gz |
e2fsck: add support for large xattrs in external inodes
Add support for the INCOMPAT_EA_INODE feature, which stores large
extended attributes into an external inode instead of data blocks.
The inode is referenced by the e_value_inum field (formerly the
unused e_value_block field) from the extent header, and stores the
xattr data starting at byte offset 0 in the inode data block.
The xattr inode stores the referring inode number in its i_mtime,
and the parent i_generation in its own i_generation, so that there
is a solid linkage between the two that e2fsck can verify. The
xattr inode is itself marked with EXT4_EA_INODE_FL as well.
Signed-off-by: Kalpak Shah <kalpak.shah@sun.com>
Signed-off-by: Andreas Dilger <andreas.dilger@intel.com>
Signed-off-by: Tahsin Erdogan <tahsin@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/ext2fs/ext2_ext_attr.h | 2 | ||||
-rw-r--r-- | lib/ext2fs/ext2_fs.h | 3 | ||||
-rw-r--r-- | lib/ext2fs/ext2fs.h | 1 | ||||
-rw-r--r-- | lib/ext2fs/ext_attr.c | 116 | ||||
-rw-r--r-- | lib/ext2fs/swapfs.c | 2 |
5 files changed, 80 insertions, 44 deletions
diff --git a/lib/ext2fs/ext2_ext_attr.h b/lib/ext2fs/ext2_ext_attr.h index bbb0aaa97..f2042ed56 100644 --- a/lib/ext2fs/ext2_ext_attr.h +++ b/lib/ext2fs/ext2_ext_attr.h @@ -29,7 +29,7 @@ struct ext2_ext_attr_entry { __u8 e_name_len; /* length of name */ __u8 e_name_index; /* attribute name index */ __u16 e_value_offs; /* offset in disk block of value */ - __u32 e_value_block; /* disk block attribute is stored on (n/i) */ + __u32 e_value_inum; /* inode in which the value is stored */ __u32 e_value_size; /* size of attribute value */ __u32 e_hash; /* hash value of name and value */ #if 0 diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h index 66b7058c3..3b55000e3 100644 --- a/lib/ext2fs/ext2_fs.h +++ b/lib/ext2fs/ext2_fs.h @@ -922,7 +922,8 @@ EXT4_FEATURE_INCOMPAT_FUNCS(encrypt, 4, ENCRYPT) #define EXT2_FEATURE_COMPAT_SUPP 0 #define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ EXT4_FEATURE_INCOMPAT_MMP| \ - EXT4_FEATURE_INCOMPAT_LARGEDIR) + EXT4_FEATURE_INCOMPAT_LARGEDIR| \ + EXT4_FEATURE_INCOMPAT_EA_INODE) #define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \ diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h index c18ea5f82..f4131a640 100644 --- a/lib/ext2fs/ext2fs.h +++ b/lib/ext2fs/ext2fs.h @@ -598,6 +598,7 @@ typedef struct ext2_icount *ext2_icount_t; EXT3_FEATURE_INCOMPAT_RECOVER|\ EXT3_FEATURE_INCOMPAT_EXTENTS|\ EXT4_FEATURE_INCOMPAT_FLEX_BG|\ + EXT4_FEATURE_INCOMPAT_EA_INODE|\ EXT4_LIB_INCOMPAT_MMP|\ EXT4_FEATURE_INCOMPAT_64BIT|\ EXT4_FEATURE_INCOMPAT_INLINE_DATA|\ diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c index 7a9a2d5a3..14d05c7e5 100644 --- a/lib/ext2fs/ext_attr.c +++ b/lib/ext2fs/ext_attr.c @@ -46,7 +46,7 @@ __u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry, void *data) } /* The hash needs to be calculated on the data in little-endian. */ - if (entry->e_value_block == 0 && entry->e_value_size != 0) { + if (entry->e_value_inum == 0 && entry->e_value_size != 0) { __u32 *value = (__u32 *)data; for (n = (entry->e_value_size + EXT2_EXT_ATTR_ROUND) >> EXT2_EXT_ATTR_PAD_BITS; n; n--) { @@ -626,7 +626,7 @@ static errcode_t write_xattrs_to_buffer(struct ext2_xattr_handle *handle, e->e_name_index = (ret ? idx : 0); e->e_value_offs = end - value_size - (char *)entries_start + value_offset_correction; - e->e_value_block = 0; + e->e_value_inum = 0; e->e_value_size = x->value_len; /* Store name and value */ @@ -824,38 +824,6 @@ static errcode_t read_xattrs_from_buffer(struct ext2_xattr_handle *handle, remain = storage_size; while (remain >= sizeof(struct ext2_ext_attr_entry) && !EXT2_EXT_IS_LAST_ENTRY(entry)) { - __u32 hash; - - /* header eats this space */ - remain -= sizeof(struct ext2_ext_attr_entry); - - /* 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; - - if (entry->e_value_offs + entry->e_value_size > values_size) - return EXT2_ET_EA_BAD_VALUE_OFFSET; - - if (entry->e_value_size > 0 && - value_start + entry->e_value_offs < - (char *)end + sizeof(__u32)) - return EXT2_ET_EA_BAD_VALUE_OFFSET; - - /* 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) { @@ -865,7 +833,13 @@ static errcode_t read_xattrs_from_buffer(struct ext2_xattr_handle *handle, x = handle->attrs + handle->length - 4; } - /* Extract name/value */ + /* header eats this space */ + remain -= sizeof(struct ext2_ext_attr_entry); + + /* attribute len eats this space */ + remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len); + + /* Extract name */ 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, @@ -879,12 +853,72 @@ static errcode_t read_xattrs_from_buffer(struct ext2_xattr_handle *handle, (char *)entry + sizeof(*entry), entry->e_name_len); - err = ext2fs_get_mem(entry->e_value_size, &x->value); - if (err) - return err; + /* Check & copy value */ + if (!ext2fs_has_feature_ea_inode(handle->fs->super) && + entry->e_value_inum != 0) + return EXT2_ET_BAD_EA_BLOCK_NUM; + + if (entry->e_value_inum == 0) { + if (entry->e_value_size > remain) + return EXT2_ET_EA_BAD_VALUE_SIZE; + + if (entry->e_value_offs + entry->e_value_size > values_size) + return EXT2_ET_EA_BAD_VALUE_OFFSET; + + if (entry->e_value_size > 0 && + value_start + entry->e_value_offs < + (char *)end + sizeof(__u32)) + return EXT2_ET_EA_BAD_VALUE_OFFSET; + + remain -= entry->e_value_size; + + err = ext2fs_get_mem(entry->e_value_size, &x->value); + if (err) + return err; + memcpy(x->value, value_start + entry->e_value_offs, + entry->e_value_size); + } else { + ext2_file_t ea_file; + + if (entry->e_value_offs != 0) + return EXT2_ET_EA_BAD_VALUE_OFFSET; + + if (entry->e_value_size > (64 * 1024)) + return EXT2_ET_EA_BAD_VALUE_SIZE; + + err = ext2fs_get_mem(entry->e_value_size, &x->value); + if (err) + return err; + + err = ext2fs_file_open(handle->fs, entry->e_value_inum, + 0, &ea_file); + if (err) + return err; + + if (ext2fs_file_get_size(ea_file) != + entry->e_value_size) + err = EXT2_ET_EA_BAD_VALUE_SIZE; + else + err = ext2fs_file_read(ea_file, x->value, + entry->e_value_size, 0); + ext2fs_file_close(ea_file); + if (err) + return err; + } + x->value_len = entry->e_value_size; - memcpy(x->value, value_start + entry->e_value_offs, - entry->e_value_size); + + /* e_hash may be 0 in older inode's ea */ + if (entry->e_hash != 0) { + __u32 hash; + void *data = (entry->e_value_inum != 0) ? + 0 : value_start + entry->e_value_offs; + + hash = ext2fs_ext_attr_hash_entry(entry, data); + if (entry->e_hash != hash) + return EXT2_ET_BAD_EA_HASH; + } + x++; (*nr_read)++; entry = EXT2_EXT_ATTR_NEXT(entry); @@ -1107,7 +1141,7 @@ errcode_t ext2fs_xattr_inode_max_size(ext2_filsys fs, ext2_ino_t ino, inode->i_extra_isize + sizeof(__u32); entry = (struct ext2_ext_attr_entry *) start; while (!EXT2_EXT_IS_LAST_ENTRY(entry)) { - if (!entry->e_value_block && entry->e_value_size) { + if (!entry->e_value_inum && entry->e_value_size) { unsigned int offs = entry->e_value_offs; if (offs < minoff) minoff = offs; diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c index 2d05ee7b9..23a8570c2 100644 --- a/lib/ext2fs/swapfs.c +++ b/lib/ext2fs/swapfs.c @@ -170,7 +170,7 @@ void ext2fs_swap_ext_attr_entry(struct ext2_ext_attr_entry *to_entry, struct ext2_ext_attr_entry *from_entry) { to_entry->e_value_offs = ext2fs_swab16(from_entry->e_value_offs); - to_entry->e_value_block = ext2fs_swab32(from_entry->e_value_block); + to_entry->e_value_inum = ext2fs_swab32(from_entry->e_value_inum); to_entry->e_value_size = ext2fs_swab32(from_entry->e_value_size); to_entry->e_hash = ext2fs_swab32(from_entry->e_hash); } |