aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorAndreas Dilger <andreas.dilger@intel.com>2017-07-04 23:53:59 -0400
committerTheodore Ts'o <tytso@mit.edu>2017-07-04 23:53:59 -0400
commit6a081f6d2a5cff0f5a077065aab39901d54bfb61 (patch)
tree671b9d6fe2e2498c5cc996648a2b6bdde5e8326f /lib
parent08ddd208ff89b3b7735e6edc60a7a3717e7daf06 (diff)
downloade2fsprogs-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.h2
-rw-r--r--lib/ext2fs/ext2_fs.h3
-rw-r--r--lib/ext2fs/ext2fs.h1
-rw-r--r--lib/ext2fs/ext_attr.c116
-rw-r--r--lib/ext2fs/swapfs.c2
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);
}