e2fsprogs-expand-extra-isize.patch
PATCH 6/15 e2fsprogs-expand-extra-isize.patch
This patch adds a "-E expand_extra_isize" feature which makes sure that _every_ used inode has i_extra_isize >= s_min_extra_isize if s_min_extra_isize is set. Else it makes sure that i_extra_isize of every inode is equal to sizeof(ext2_inode_large) - 128.
This is useful for the case where nanosecond timestamps or 64-bit inode version fields are required for all inodes in the filesystem.
This patch adds a "-E expand_extra_isize" feature which makes sure that _every_ used inode has i_extra_isize >= s_min_extra_isize if s_min_extra_isize is set. Else it makes sure that i_extra_isize of every inode is equal to sizeof(ext2_inode_large) - 128.
This is useful for the case where nanosecond timestamps or 64-bit inode version fields are required for all inodes in the filesystem.
lib/ext2fs/ext_attr.c +913 -3
@@ -17,6 +17,7 @@#endif #include <string.h> #include <time.h> +#include <errno.h> #include "ext2_fs.h" #include "ext2_ext_attr.h"
@@ -60,11 +61,39 @@ __u32 ext2fs_ext_attr_hash_entry(struct#undef NAME_HASH_SHIFT#undef VALUE_HASH_SHIFT +#define BLOCK_HASH_SHIFT 16 +/* +* Re-compute the extended attribute hash value after an entry has changed. +*/ +static void ext2fs_attr_rehash (struct ext2_ext_attr_header *header, +struct ext2_ext_attr_entry *entry) +{ +struct ext2_ext_attr_entry *here; +__u32 hash =0 ; + + entry->e_hash =ext2fs_ext_attr_hash_entry (entry, (char *) header + + entry->e_value_offs); + + here =ENTRY (header+1 ); +while (!EXT2_EXT_IS_LAST_ENTRY (here)) { +if (!here->e_hash) { +/* Block is not shared if an entry's hash value == 0 */ + hash =0 ; +break ; + } + hash = (hash <<BLOCK_HASH_SHIFT ) ^ + (hash >> (8 *sizeof (hash) -BLOCK_HASH_SHIFT )) ^ + here->e_hash; + here =EXT2_EXT_ATTR_NEXT (here); + } + header->h_hash = hash; +} +errcode_t ext2fs_read_ext_attr (ext2_filsys fs,blk_t block,void *buf) {errcode_t retval; - retval =io_channel_read_blk (fs->io, block,1 , buf); + retval =io_channel_read_blk (fs->io, block,1 , buf);if (retval)return retval;#ifdef WORDS_BIGENDIAN
@@ -88,7 +117,7 @@ errcode_t ext2fs_write_ext_attr(ext2_fil#else write_buf = (char *) inbuf;#endif - retval =io_channel_write_blk (fs->io, block,1 , write_buf); + retval =io_channel_write_blk (fs->io, block,1 , write_buf);if (buf)ext2fs_free_mem (&buf);if (!retval)
@@ -122,7 +151,10 @@ errcode_t ext2fs_adjust_ea_refcount(ext2if (retval) goto errout; - header = (struct ext2_ext_attr_header *) block_buf; + header =BHDR (block_buf); +if (header->h_magic !=EXT2_EXT_ATTR_MAGIC ) +return EXT2_ET_EA_BAD_MAGIC ; + header->h_refcount += adjust;if (newcount) *newcount = header->h_refcount;
@@ -136,3 +168,881 @@ errout:ext2fs_free_mem (&buf);return retval; } + +struct ext2_attr_info { +int name_index; +const char *name; +const char *value; +int value_len; +}; + +struct ext2_attr_search { +struct ext2_ext_attr_entry *first; +char *base; +char *end; +struct ext2_ext_attr_entry *here; +int not_found; +}; + +struct ext2_attr_ibody_find { +ext2_ino_t ino; +struct ext2_attr_search s; +}; + +struct ext2_attr_block_find { +struct ext2_attr_search s; +char *block; +}; + +void ext2fs_attr_shift_entries (struct ext2_ext_attr_entry *entry, +int value_offs_shift,char *to, +char *from,int n) +{ +struct ext2_ext_attr_entry *last = entry; + +/* Adjust the value offsets of the entries */ +for (; !EXT2_EXT_IS_LAST_ENTRY (last); last =EXT2_EXT_ATTR_NEXT (last)) { +if (!last->e_value_block && last->e_value_size) { + last->e_value_offs = last->e_value_offs + + value_offs_shift; + } + } +/* Shift the entries by n bytes */ +memmove (to, from, n); +} + +/* +* This function returns the free space present in the inode or the EA block. +* total is number of bytes taken up by the EA entries and is used to shift the +* EAs in ext2fs_expand_extra_isize(). +*/ +int ext2fs_attr_free_space (struct ext2_ext_attr_entry *last, +int *min_offs,char *base,int *total) +{ +for (; !EXT2_EXT_IS_LAST_ENTRY (last); last =EXT2_EXT_ATTR_NEXT (last)) { + *total +=EXT2_EXT_ATTR_LEN (last->e_name_len); +if (!last->e_value_block && last->e_value_size) { +int offs = last->e_value_offs; +if (offs < *min_offs) + *min_offs = offs; + } + } + +return (*min_offs - ((char *)last - base) -sizeof (__u32)); +} + +static errcode_t ext2fs_attr_check_names (struct ext2_ext_attr_entry *entry, +char *end) +{ +while (!EXT2_EXT_IS_LAST_ENTRY (entry)) { +struct ext2_ext_attr_entry *next =EXT2_EXT_ATTR_NEXT (entry); +if ((char *)next >= end) +return EXT2_ET_EA_BAD_ENTRIES ; + entry = next; + } +return 0 ; +} + +static errcode_t ext2fs_attr_find_entry (struct ext2_ext_attr_entry **pentry, +int name_index,const char *name, +int size,int sorted) +{ +struct ext2_ext_attr_entry *entry; +int name_len; +int cmp =1 ; + +if (name ==NULL ) +return EXT2_ET_EA_BAD_NAME ; + + name_len =strlen (name); + entry = *pentry; +for (; !EXT2_EXT_IS_LAST_ENTRY (entry); + entry =EXT2_EXT_ATTR_NEXT (entry)) { + cmp = name_index - entry->e_name_index; +if (!cmp) + cmp = name_len - entry->e_name_len; +if (!cmp) + cmp =memcmp (name, entry->e_name, name_len); +if (cmp <=0 && (sorted || cmp ==0 )) +break ; + } + *pentry = entry; + +return cmp ?EXT2_ET_EA_NAME_NOT_FOUND :0 ; +} + +static errcode_t ext2fs_attr_block_find (ext2_filsys fs,struct ext2_inode *inode, +struct ext2_attr_info *i, +struct ext2_attr_block_find *bs) +{ +struct ext2_ext_attr_header *header; +errcode_t error; + +if (inode->i_file_acl) { +/* The inode already has an extended attribute block. */ + error =ext2fs_get_mem (fs->blocksize, &bs->block); +if (error) +return error; + error =ext2fs_read_ext_attr (fs, inode->i_file_acl, bs->block); +if (error) + goto cleanup; + + header =BHDR (bs->block); +if (header->h_magic !=EXT2_EXT_ATTR_MAGIC ) { + error =EXT2_ET_EA_BAD_MAGIC ; + goto cleanup; + } + +/* Find the named attribute. */ + bs->s.base = bs->block; + bs->s.first = (struct ext2_ext_attr_entry *)(header +1 ); + bs->s.end = bs->block + fs->blocksize; + bs->s.here = bs->s.first; + error =ext2fs_attr_find_entry (&bs->s.here, i->name_index, + i->name, fs->blocksize,1 ); +if (error && error !=EXT2_ET_EA_NAME_NOT_FOUND ) + goto cleanup; + bs->s.not_found = error; + } + error =0 ; + +cleanup: +if (error && bs->block) +ext2fs_free_mem (&bs->block); +return error; +} + +static errcode_t ext2fs_attr_ibody_find (ext2_filsys fs, +struct ext2_inode_large *inode, +struct ext2_attr_info *i, +struct ext2_attr_ibody_find *is) +{ +__u32 *eamagic; +char *start; +errcode_t error; + +if (EXT2_INODE_SIZE (fs->super) ==EXT2_GOOD_OLD_INODE_SIZE ) +return 0 ; + +if (inode->i_extra_isize ==0 ) +return 0 ; + eamagic =IHDR (inode); + + start = (char *) inode +EXT2_GOOD_OLD_INODE_SIZE + + inode->i_extra_isize +sizeof (__u32); + is->s.first = (struct ext2_ext_attr_entry *) start; + is->s.base = start; + is->s.here = is->s.first; + is->s.end = (char *) inode +EXT2_INODE_SIZE (fs->super); +if (*eamagic ==EXT2_EXT_ATTR_MAGIC ) { + error =ext2fs_attr_check_names ((struct ext2_ext_attr_entry *) + start, is->s.end); +if (error) +return error; +/* Find the named attribute. */ + error =ext2fs_attr_find_entry (&is->s.here, i->name_index, + i->name, is->s.end - + (char *)is->s.base,0 ); +if (error && error !=EXT2_ET_EA_NAME_NOT_FOUND ) +return error; + is->s.not_found = error; + } + +return 0 ; +} + +static errcode_t ext2fs_attr_set_entry (ext2_filsys fs,struct ext2_attr_info *i, +struct ext2_attr_search *s) +{ +struct ext2_ext_attr_entry *last; +int free, min_offs = s->end - s->base, name_len =strlen (i->name); + +/* Compute min_offs and last. */ +for (last = s->first; !EXT2_EXT_IS_LAST_ENTRY (last); + last =EXT2_EXT_ATTR_NEXT (last)) { +if (!last->e_value_block && last->e_value_size) { +int offs = last->e_value_offs; + +if (offs < min_offs) + min_offs = offs; + } + } + free = min_offs - ((char *)last - s->base) -sizeof (__u32); + +if (!s->not_found) { +if (!s->here->e_value_block && s->here->e_value_size) { +int size = s->here->e_value_size; + free +=EXT2_EXT_ATTR_SIZE (size); + } + free +=EXT2_EXT_ATTR_LEN (name_len); + } +if (i->value) { +if (free <EXT2_EXT_ATTR_LEN (name_len) + +EXT2_EXT_ATTR_SIZE (i->value_len)) +return EXT2_ET_EA_NO_SPACE ; + } + +if (i->value && s->not_found) { +/* Insert the new name. */ +int size =EXT2_EXT_ATTR_LEN (name_len); +int rest = (char *)last - (char *)s->here +sizeof (__u32); + +memmove ((char *)s->here + size, s->here, rest); +memset (s->here,0 , size); + s->here->e_name_index = i->name_index; + s->here->e_name_len = name_len; +memcpy (s->here->e_name, i->name, name_len); + }else { +if (!s->here->e_value_block && s->here->e_value_size) { +char *first_val = s->base + min_offs; +int offs = s->here->e_value_offs; +char *val = s->base + offs; +int size =EXT2_EXT_ATTR_SIZE (s->here->e_value_size); + +if (i->value && + size ==EXT2_EXT_ATTR_SIZE (i->value_len)) { +/* The old and the new value have the same +size. Just replace. */ + s->here->e_value_size = i->value_len; +memset (val + size -EXT2_EXT_ATTR_PAD ,0 , +EXT2_EXT_ATTR_PAD );/* Clear pad bytes */ +memcpy (val, i->value, i->value_len); +return 0 ; + } + +/* Remove the old value. */ +memmove (first_val + size, first_val, val - first_val); +memset (first_val,0 , size); + s->here->e_value_size =0 ; + s->here->e_value_offs =0 ; + min_offs += size; + +/* Adjust all value offsets. */ + last = s->first; +while (!EXT2_EXT_IS_LAST_ENTRY (last)) { +int o = last->e_value_offs; + +if (!last->e_value_block && + last->e_value_size && o < offs) + last->e_value_offs = o + size; + last =EXT2_EXT_ATTR_NEXT (last); + } + } +if (!i->value) { +/* Remove the old name. */ +int size =EXT2_EXT_ATTR_LEN (name_len); + + last =ENTRY ((char *)last - size); +memmove ((char *)s->here, (char *)s->here + size, + (char *)last - (char *)s->here +sizeof (__u32)); +memset (last,0 , size); + } + } + +if (i->value) { +/* Insert the new value. */ + s->here->e_value_size = i->value_len; +if (i->value_len) { +int size =EXT2_EXT_ATTR_SIZE (i->value_len); +char *val = s->base + min_offs - size; + + s->here->e_value_offs = min_offs - size; +memset (val + size -EXT2_EXT_ATTR_PAD ,0 , +EXT2_EXT_ATTR_PAD );/* Clear the pad bytes. */ +memcpy (val, i->value, i->value_len); + } + } + +return 0 ; +} + +static errcode_t ext2fs_attr_block_set (ext2_filsys fs,struct ext2_inode *inode, +struct ext2_attr_info *i, +struct ext2_attr_block_find *bs) +{ +struct ext2_attr_search *s = &bs->s; +char *new_buf =NULL , *old_block =NULL ; +blk_t blk; +int clear_flag =0 ; +errcode_t error; + +if (i->value && i->value_len > fs->blocksize) +return EXT2_ET_EA_NO_SPACE ; + +if (s->base) { +if (BHDR (s->base)->h_refcount !=1 ) { +int offset = (char *)s->here - bs->block; + +/* Decrement the refcount of the shared block */ + old_block = s->base; +BHDR (s->base)->h_refcount -=1 ; + + error =ext2fs_get_mem (fs->blocksize, &s->base); +if (error) + goto cleanup; + clear_flag =1 ; +memcpy (s->base, bs->block, fs->blocksize); + s->first =ENTRY (BHDR (s->base)+1 ); +BHDR (s->base)->h_refcount =1 ; + s->here =ENTRY (s->base + offset); + s->end = s->base + fs->blocksize; + } + }else { + error =ext2fs_get_mem (fs->blocksize, &s->base); +if (error) + goto cleanup; + clear_flag =1 ; +memset (s->base,0 , fs->blocksize); +BHDR (s->base)->h_magic =EXT2_EXT_ATTR_MAGIC ; +BHDR (s->base)->h_blocks =1 ; +BHDR (s->base)->h_refcount =1 ; + s->first =ENTRY (BHDR (s->base)+1 ); + s->here =ENTRY (BHDR (s->base)+1 ); + s->end = s->base + fs->blocksize; + } + + error =ext2fs_attr_set_entry (fs, i, s); +if (error) + goto cleanup; + +if (!EXT2_EXT_IS_LAST_ENTRY (s->first)) +ext2fs_attr_rehash (BHDR (s->base), s->here); + +if (!EXT2_EXT_IS_LAST_ENTRY (s->first)) { +if (bs->block && bs->block == s->base) { +/* We are modifying this block in-place */ + new_buf = bs->block; + blk = inode->i_file_acl; + error =ext2fs_write_ext_attr (fs, blk, s->base); +if (error) + goto cleanup; + }else { +/* We need to allocate a new block */ + error =ext2fs_new_block (fs,0 ,0 , &blk); +if (error) + goto cleanup; +ext2fs_block_alloc_stats (fs, blk,+1 ); + error =ext2fs_write_ext_attr (fs, blk, s->base); +if (error) + goto cleanup; + new_buf = s->base; +if (old_block) { +BHDR (s->base)->h_refcount -=1 ; + error =ext2fs_write_ext_attr (fs, + inode->i_file_acl, + s->base); +if (error) + goto cleanup; + } + } + } + +/* Update the i_blocks if we added a new EA block */ +if (!inode->i_file_acl && new_buf) + inode->i_blocks += fs->blocksize /512 ; +/* Update the inode. */ + inode->i_file_acl = new_buf ? blk :0 ; + +cleanup: +if (clear_flag) +ext2fs_free_mem (&s->base); +return 0 ; +} + +static errcode_t ext2fs_attr_ibody_set (ext2_filsys fs, +struct ext2_inode_large *inode, +struct ext2_attr_info *i, +struct ext2_attr_ibody_find *is) +{ +__u32 *eamagic; +struct ext2_attr_search *s = &is->s; +errcode_t error; + +if (EXT2_INODE_SIZE (fs->super) ==EXT2_GOOD_OLD_INODE_SIZE ) +return EXT2_ET_EA_NO_SPACE ; + + error =ext2fs_attr_set_entry (fs, i, s); +if (error) +return error; + + eamagic =IHDR (inode); +if (!EXT2_EXT_IS_LAST_ENTRY (s->first)) + *eamagic =EXT2_EXT_ATTR_MAGIC ; +else + *eamagic =0 ; + +return ext2fs_write_inode_full (fs, is->ino, (struct ext2_inode *)inode, +EXT2_INODE_SIZE (fs->super)); +} + + +errcode_t ext2fs_attr_set (ext2_filsys fs,ext2_ino_t ino, +struct ext2_inode *inode, +int name_index,const char *name,const char *value, +int value_len,int flags) +{ +struct ext2_inode_large *inode_large =NULL ; +struct ext2_attr_info i = { + .name_index = name_index, + .name = name, + .value = value, + .value_len = value_len, + }; +struct ext2_attr_ibody_find is = { + .ino = ino, + .s = { .not_found = -ENODATA , }, + }; +struct ext2_attr_block_find bs = { + .s = { .not_found = -ENODATA , }, + }; +errcode_t error; + +if (!name) +return EXT2_ET_EA_BAD_NAME ; +if (strlen (name) >255 ) +return EXT2_ET_EA_NAME_TOO_BIG ; + +if (EXT2_INODE_SIZE (fs->super) >EXT2_GOOD_OLD_INODE_SIZE ) { + inode_large = (struct ext2_inode_large *)inode; + + error =ext2fs_attr_ibody_find (fs, inode_large, &i, &is); +if (error) + goto cleanup; + } +if (is.s.not_found) { + error =ext2fs_attr_block_find (fs, inode, &i, &bs); +if (error) + goto cleanup; + } + +if (is.s.not_found && bs.s.not_found) { + error =EXT2_ET_EA_NAME_NOT_FOUND ; +if (flags &XATTR_REPLACE ) + goto cleanup; + error =0 ; +if (!value) + goto cleanup; + }else { + error =EXT2_ET_EA_NAME_EXISTS ; +if (flags &XATTR_CREATE ) + goto cleanup; + } + +if (!value) { +if (!is.s.not_found && + (EXT2_INODE_SIZE (fs->super) >EXT2_GOOD_OLD_INODE_SIZE )) + error =ext2fs_attr_ibody_set (fs, inode_large, &i, &is); +else if (!bs.s.not_found) + error =ext2fs_attr_block_set (fs, inode, &i, &bs); + }else { +if (EXT2_INODE_SIZE (fs->super) >EXT2_GOOD_OLD_INODE_SIZE ) + error =ext2fs_attr_ibody_set (fs, inode_large, &i, &is); +if (!error && !bs.s.not_found) { + i.value =NULL ; + error =ext2fs_attr_block_set (fs, inode, &i, &bs); + }else if (error ==EXT2_ET_EA_NO_SPACE ) { + error =ext2fs_attr_block_set (fs, inode, &i, &bs); +if (error) + goto cleanup; +if (!is.s.not_found) { + i.value =NULL ; +if (EXT2_INODE_SIZE (fs->super) > +EXT2_GOOD_OLD_INODE_SIZE ) + error =ext2fs_attr_ibody_set (fs, + inode_large, &i, &is); + } + } + } + +cleanup: +return error; +} + +static errcode_t ext2fs_attr_check_block (ext2_filsys fs,char *buffer) +{ +if (BHDR (buffer)->h_magic != (EXT2_EXT_ATTR_MAGIC ) || +BHDR (buffer)->h_blocks !=1 ) +return EXT2_ET_EA_BAD_MAGIC ; + +return ext2fs_attr_check_names ((struct ext2_ext_attr_entry *) + (BHDR (buffer) +1 ), + buffer + fs->blocksize); +} + +static errcode_t ext2fs_attr_block_get (ext2_filsys fs,struct ext2_inode *inode, +int name_index,const char *name, +void *buffer,size_t buffer_size, +int *easize) +{ +struct ext2_ext_attr_header *header =NULL ; +struct ext2_ext_attr_entry *entry; +char *block_buf =NULL ; +errcode_t error; + + error =EXT2_ET_EA_NAME_NOT_FOUND ; +if (!inode->i_file_acl) + goto cleanup; + + error =ext2fs_get_mem (fs->blocksize, &block_buf); +if (error) +return error; + error =ext2fs_read_ext_attr (fs, inode->i_file_acl, block_buf); +if (error) + goto cleanup; + + error =ext2fs_attr_check_block (fs, block_buf); +if (error) + goto cleanup; + + header =BHDR (block_buf); + entry = (struct ext2_ext_attr_entry *)(header+1 ); + error =ext2fs_attr_find_entry (&entry, name_index, name, + fs->blocksize,1 ); +if (error) + goto cleanup; +if (easize) + *easize = entry->e_value_size; +if (buffer) { + error =EXT2_ET_EA_TOO_BIG ; +if (entry->e_value_size > buffer_size) + goto cleanup; +memcpy (buffer, block_buf + entry->e_value_offs, + entry->e_value_size); + } + +cleanup: +if (block_buf) +ext2fs_free_mem (&block_buf); +return error; +} + +static errcode_t ext2fs_attr_ibody_get (ext2_filsys fs, +struct ext2_inode_large *inode, +int name_index,const char *name, +void *buffer,size_t buffer_size, +int *easize) +{ +struct ext2_ext_attr_entry *entry; +int error; +char *end, *start; +__u32 *eamagic; + +if (EXT2_INODE_SIZE (fs->super) ==EXT2_GOOD_OLD_INODE_SIZE ) +return EXT2_ET_EA_NAME_NOT_FOUND ; + + eamagic =IHDR (inode); + error =ext2fs_attr_check_block (fs, buffer); +if (error) +return error; + + start = (char *)inode +EXT2_GOOD_OLD_INODE_SIZE + + inode->i_extra_isize +sizeof (__u32); + entry = (struct ext2_ext_attr_entry *)start; + end = (char *)inode +EXT2_INODE_SIZE (fs->super); + error =ext2fs_attr_check_names (entry, end); +if (error) + goto cleanup; + error =ext2fs_attr_find_entry (&entry, name_index, name, + end - (char *)entry,0 ); +if (error) + goto cleanup; +if (easize) + *easize = entry->e_value_size; +if (buffer) { + error =EXT2_ET_EA_TOO_BIG ; +if (entry->e_value_size > buffer_size) + goto cleanup; +memcpy (buffer, start + entry->e_value_offs,entry->e_value_size); + } + +cleanup: +return error; +} + + +errcode_t ext2fs_attr_get (ext2_filsys fs,struct ext2_inode *inode, +int name_index,const char *name,char *buffer, +size_t buffer_size,int *easize) +{ +errcode_t error; + + error =ext2fs_attr_ibody_get (fs, (struct ext2_inode_large *)inode, + name_index, name, buffer, buffer_size, + easize); +if (error ==EXT2_ET_EA_NAME_NOT_FOUND ) + error =ext2fs_attr_block_get (fs, inode, name_index, name, + buffer, buffer_size, easize); + +return error; +} + +char *ext2_attr_index_prefix[] = { + [EXT2_ATTR_INDEX_USER ] =EXT2_ATTR_INDEX_USER_PREFIX , + [EXT2_ATTR_INDEX_POSIX_ACL_ACCESS ] =EXT2_ATTR_INDEX_POSIX_ACL_ACCESS_PREFIX , + [EXT2_ATTR_INDEX_POSIX_ACL_DEFAULT ] =EXT2_ATTR_INDEX_POSIX_ACL_DEFAULT_PREFIX , + [EXT2_ATTR_INDEX_TRUSTED ] =EXT2_ATTR_INDEX_TRUSTED_PREFIX , + [EXT2_ATTR_INDEX_LUSTRE ] =EXT2_ATTR_INDEX_LUSTRE_PREFIX , + [EXT2_ATTR_INDEX_SECURITY ] =EXT2_ATTR_INDEX_SECURITY_PREFIX , +NULL +}; + +int ext2fs_attr_get_next_attr (struct ext2_ext_attr_entry *entry,int name_index, +char *buffer,int buffer_size,int start) +{ +const int prefix_len =strlen (ext2_attr_index_prefix[name_index]); +int total_len; + +if (!start && !EXT2_EXT_IS_LAST_ENTRY (entry)) + entry =EXT2_EXT_ATTR_NEXT (entry); + +for (; !EXT2_EXT_IS_LAST_ENTRY (entry); + entry =EXT2_EXT_ATTR_NEXT (entry)) { +if (!name_index) +break ; +if (name_index == entry->e_name_index) +break ; + } +if (EXT2_EXT_IS_LAST_ENTRY (entry)) +return 0 ; + + total_len = prefix_len + entry->e_name_len +1 ; +if (buffer && total_len <= buffer_size) { +memcpy (buffer, ext2_attr_index_prefix[name_index], prefix_len); +memcpy (buffer + prefix_len, entry->e_name, entry->e_name_len); + buffer[prefix_len + entry->e_name_len] ='\0' ; + } + +return total_len; +} + +errcode_t ext2fs_expand_extra_isize (ext2_filsys fs,ext2_ino_t ino, +struct ext2_inode_large *inode, +int new_extra_isize,int *ret, +int *needed_size) +{ +struct ext2_inode *inode_buf =NULL ; +__u32 *eamagic =NULL ; +struct ext2_ext_attr_header *header =NULL ; +struct ext2_ext_attr_entry *entry =NULL , *last =NULL ; +struct ext2_attr_ibody_find is = { + .ino = ino, + .s = { .not_found =EXT2_ET_EA_NO_SPACE , }, + }; +struct ext2_attr_block_find bs = { + .s = { .not_found =EXT2_ET_EA_NO_SPACE , }, + }; +char *start, *end, *block_buf =NULL , *buffer =NULL , *b_entry_name=NULL ; +int total_ino =0 , total_blk, free, offs, tried_min_extra_isize =0 ; +int s_min_extra_isize = fs->super->s_min_extra_isize; +errcode_t error =0 ; + +if (needed_size) + *needed_size = new_extra_isize; + error =ext2fs_get_mem (fs->blocksize, &block_buf); +if (error) +return error; + +if (inode ==NULL ) { + error =ext2fs_get_mem (EXT2_INODE_SIZE (fs->super), &inode_buf); +if (error) + goto cleanup; + + error =ext2fs_read_inode_full (fs, ino, inode_buf, +EXT2_INODE_SIZE (fs->super)); +if (error) + goto cleanup; + + inode = (struct ext2_inode_large *)inode_buf; + } + +retry: +if (inode->i_extra_isize >= new_extra_isize) + goto cleanup; + + eamagic =IHDR (inode); +/* No extended attributes present */ +if (*eamagic !=EXT2_EXT_ATTR_MAGIC ) { +memset ((char *)inode +EXT2_GOOD_OLD_INODE_SIZE + + inode->i_extra_isize,0 , +EXT2_INODE_SIZE (fs->super) -EXT2_GOOD_OLD_INODE_SIZE - + inode->i_extra_isize); + inode->i_extra_isize = new_extra_isize; +if (needed_size) + *needed_size =0 ; + goto write_inode; + } + + start = (char *) inode +EXT2_GOOD_OLD_INODE_SIZE + + inode->i_extra_isize +sizeof (__u32); + end = (char *) inode +EXT2_INODE_SIZE (fs->super); + last = entry = (struct ext2_ext_attr_entry *) start; + offs = end - start; +/* Consider space takenup by magic number */ + total_ino =sizeof (__u32); + free =ext2fs_attr_free_space (last, &offs, start, &total_ino); + +/* Enough free space available in the inode for expansion */ +if (free >= new_extra_isize) { +ext2fs_attr_shift_entries (entry, inode->i_extra_isize - + new_extra_isize, (char *)inode + +EXT2_GOOD_OLD_INODE_SIZE + new_extra_isize, + (char *)start -sizeof (__u32), total_ino); + inode->i_extra_isize = new_extra_isize; +if (needed_size) + *needed_size =0 ; + goto write_inode; + } + +if (inode->i_file_acl) { + error =ext2fs_read_ext_attr (fs, inode->i_file_acl, block_buf); +if (error) + goto cleanup; + + header =BHDR (block_buf); +if (header->h_magic !=EXT2_EXT_ATTR_MAGIC ) { + error =EXT2_ET_EA_BAD_MAGIC ; + goto cleanup; + } + end = block_buf + fs->blocksize; + last = entry = (struct ext2_ext_attr_entry *)(header+1 ); + start = (char *) entry; + offs = end - start; + free =ext2fs_attr_free_space (last, &offs, start, &total_blk); +if (free < new_extra_isize) { +if (!tried_min_extra_isize && s_min_extra_isize) { + tried_min_extra_isize++; + new_extra_isize = s_min_extra_isize; + goto retry; + } +if (ret) + *ret =EXT2_EXPAND_EISIZE_NOSPC ; + error =EXT2_ET_EA_NO_SPACE ; + goto cleanup; + } + }else { +if (ret && *ret ==EXT2_EXPAND_EISIZE_UNSAFE ) { + *ret =EXT2_EXPAND_EISIZE_NEW_BLOCK ; + error =0 ; + goto cleanup; + } + free = fs->blocksize; + } + +while (new_extra_isize >0 ) { +int offs, size, entry_size; +struct ext2_ext_attr_entry *small_entry =NULL ; +struct ext2_attr_info i = { + .value =NULL , + .value_len =0 , + }; +unsigned int total_size, shift_bytes, temp = ~0U , extra_isize=0 ; + + start = (char *) inode +EXT2_GOOD_OLD_INODE_SIZE + + inode->i_extra_isize +sizeof (__u32); + end = (char *) inode +EXT2_INODE_SIZE (fs->super); + last = (struct ext2_ext_attr_entry *) start; + +/* Find the entry best suited to be pushed into EA block */ + entry =NULL ; +for (; !EXT2_EXT_IS_LAST_ENTRY (last); + last =EXT2_EXT_ATTR_NEXT (last)) { + total_size =EXT2_EXT_ATTR_SIZE (last->e_value_size) + +EXT2_EXT_ATTR_LEN (last->e_name_len); +if (total_size <= free && total_size < temp) { +if (total_size < new_extra_isize) { + small_entry = last; + }else { + entry = last; + temp = total_size; + } + } + } + +if (entry ==NULL ) { +if (small_entry) { + entry = small_entry; + }else { +if (!tried_min_extra_isize && + s_min_extra_isize) { + tried_min_extra_isize++; + new_extra_isize = s_min_extra_isize; + goto retry; + } +if (ret) + *ret =EXT2_EXPAND_EISIZE_NOSPC ; + error =EXT2_ET_EA_NO_SPACE ; + goto cleanup; + } + } + offs = entry->e_value_offs; + size = entry->e_value_size; + entry_size =EXT2_EXT_ATTR_LEN (entry->e_name_len); + i.name_index = entry->e_name_index; + error =ext2fs_get_mem (size, &buffer); +if (error) + goto cleanup; + error =ext2fs_get_mem (entry->e_name_len +1 , &b_entry_name); +if (error) + goto cleanup; +/* Save the entry name and the entry value */ +memcpy ((char *)buffer, (char *) start + offs, +EXT2_EXT_ATTR_SIZE (size)); +memcpy ((char *)b_entry_name, (char *)entry->e_name, + entry->e_name_len); + b_entry_name[entry->e_name_len] ='\0' ; + i.name = b_entry_name; + + error =ext2fs_attr_ibody_find (fs, inode, &i, &is); +if (error) + goto cleanup; + + error =ext2fs_attr_set_entry (fs, &i, &is.s); +if (error) + goto cleanup; + + entry = (struct ext2_ext_attr_entry *) start; +if (entry_size +EXT2_EXT_ATTR_SIZE (size) >= new_extra_isize) + shift_bytes = new_extra_isize; +else + shift_bytes = entry_size +EXT2_EXT_ATTR_SIZE (size); +ext2fs_attr_shift_entries (entry, inode->i_extra_isize - + shift_bytes, (char *)inode + +EXT2_GOOD_OLD_INODE_SIZE + extra_isize + shift_bytes, + (char *)start -sizeof (__u32), total_ino - entry_size); + + extra_isize += shift_bytes; + new_extra_isize -= shift_bytes; +if (needed_size) + *needed_size = new_extra_isize; + inode->i_extra_isize = extra_isize; + + i.name = b_entry_name; + i.value = buffer; + i.value_len = size; + error =ext2fs_attr_block_find (fs, (struct ext2_inode *) inode, + &i, &bs); +if (error) + goto cleanup; + +/* Add entry which was removed from the inode into the block */ + error =ext2fs_attr_block_set (fs, (struct ext2_inode *) inode, + &i, &bs); +if (error) + goto cleanup; + } + +write_inode: + error =ext2fs_write_inode_full (fs, ino, (struct ext2_inode *) inode, +EXT2_INODE_SIZE (fs->super)); +cleanup: +if (inode_buf) +ext2fs_free_mem (&inode_buf); +if (block_buf) +ext2fs_free_mem (&block_buf); +if (buffer) +ext2fs_free_mem (&buffer); +if (b_entry_name) +ext2fs_free_mem (&b_entry_name); + +return error; +}
e2fsck/unix.c +55 -0
73 lines not on this page · raw message
lib/ext2fs/ext2_ext_attr.h +30 -1
@@ -15,6 +15,9 @@/* Maximum number of references to one attribute block */ #define EXT2_EXT_ATTR_REFCOUNT_MAX 1024 +#define XATTR_CREATE 0x1/* set value, fail if attr already exists */ +#define XATTR_REPLACE 0x2/* set value, fail if attr does not exist */ +struct ext2_ext_attr_header {__u32 h_magic;/* magic number for identification */ __u32 h_refcount;/* reference count */
@@ -30,11 +33,37 @@ struct ext2_ext_attr_entry {__u32 e_value_block;/* disk block attribute is stored on (n/i) */ __u32 e_value_size;/* size of attribute value */ __u32 e_hash;/* hash value of name and value */ -#if 0 +#if 1 char e_name[0 ];/* attribute name */ #endif }; +#define BHDR (block) ((struct ext2_ext_attr_header *) block) +#define IHDR (inode) \ + ((__u32 *) ((char *)inode + \ + EXT2_GOOD_OLD_INODE_SIZE + \ + (inode)->i_extra_isize)) +#define ENTRY (ptr) ((struct ext2_ext_attr_entry *)(ptr)) + +/* Name indexes */ +#define EXT2_ATTR_INDEX_USER 1 +#define EXT2_ATTR_INDEX_POSIX_ACL_ACCESS 2 +#define EXT2_ATTR_INDEX_POSIX_ACL_DEFAULT 3 +#define EXT2_ATTR_INDEX_TRUSTED 4 +#define EXT2_ATTR_INDEX_LUSTRE 5 +#define EXT2_ATTR_INDEX_SECURITY 6 +#define EXT2_ATTR_INDEX_MAX 7 + +#define EXT2_ATTR_INDEX_USER_PREFIX "user." +#define EXT2_ATTR_INDEX_POSIX_ACL_ACCESS_PREFIX "system.posix_acl_access" +#define EXT2_ATTR_INDEX_POSIX_ACL_DEFAULT_PREFIX "system.posix_acl_default" +#define EXT2_ATTR_INDEX_TRUSTED_PREFIX "trusted." +#define EXT2_ATTR_INDEX_LUSTRE_PREFIX "lustre." +#define EXT2_ATTR_INDEX_SECURITY_PREFIX "security." + +#define EXT2_ATTR_PREFIX (index) (index ## _PREFIX) +#define EXT2_ATTR_PREFIX_LEN (index) (index ## _PRE_LEN) +#define EXT2_EXT_ATTR_PAD_BITS 2#define EXT2_EXT_ATTR_PAD ((unsigned) 1<<EXT2_EXT_ATTR_PAD_BITS)#define EXT2_EXT_ATTR_ROUND (EXT2_EXT_ATTR_PAD-1)
e2fsck/e2fsck.h +10 -0
@@ -175,6 +175,7 @@ struct resource_track {#define E2F_FLAG_RESTARTED 0x0200/* E2fsck has been restarted */ #define E2F_FLAG_RESIZE_INODE 0x0400/* Request to recreate resize inode */ #define E2F_FLAG_GOT_DEVSIZE 0x0800/* Device size has been fetched */ +#define E2F_FLAG_EXPAND_EISIZE 0x2000/* Expand the inodes (i_extra_isize) */ /* * Defines for indicating the e2fsck pass number
@@ -354,6 +355,15 @@ struct e2fsck_struct {profile_t profile;int blocks_per_page; +/* Expand large inodes to atleast these many bytes */ +int want_extra_isize; +/* minimum i_extra_isize found in used inodes. Should not be lesser +* than s_min_extra_isize. +*/ +__u32 min_extra_isize; +int fs_unexpanded_inodes; +ext2fs_inode_bitmap expand_eisize_map; +/* * For the use of callers of the e2fsck functions; not used by * e2fsck functions themselves.
lib/ext2fs/ext2_fs.h +7 -0
@@ -421,6 +421,12 @@ struct ext2_inode_large {#define i_size_high i_dir_acl +#define EXT2_FITS_IN_INODE (inode, field) \ + ((offsetof(struct ext2_inode_large, field) + \ + sizeof((inode)->field)) <= \ + (EXT2_GOOD_OLD_INODE_SIZE + \ + (inode)->i_extra_isize)) \ +#if defined(__KERNEL__) || defined(__linux__)#define i_reserved1 osd1.linux1.l_i_reserved1#define i_frag osd2.linux2.l_i_frag
@@ -650,6 +656,7 @@ struct ext2_super_block {#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \ + EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE| \ EXT2_FEATURE_RO_COMPAT_BTREE_DIR)/*
e2fsck/pass1.c +193 -7
251 lines not on this page · raw message
lib/ext2fs/ext2fs.h +16 -0
@@ -491,6 +491,12 @@ typedef struct ext2_icount *ext2_icount_#define EXT2_CHECK_MAGIC (struct, code) \ if ((struct)->magic != (code)) return (code) +/* +* Flags for returning status of ext2fs_expand_extra_isize() +*/ +#define EXT2_EXPAND_EISIZE_UNSAFE 0x0001 +#define EXT2_EXPAND_EISIZE_NEW_BLOCK 0x0002 +#define EXT2_EXPAND_EISIZE_NOSPC 0x0004/* * For ext2 compression support
@@ -828,6 +834,16 @@ extern errcode_t ext2fs_expand_dir(ext2_/* ext_attr.c */ extern __u32 ext2fs_ext_attr_hash_entry (struct ext2_ext_attr_entry *entry,void *data); +int ext2fs_attr_get_next_attr (struct ext2_ext_attr_entry *entry,int name_index, +char *buffer,int buffer_size,int start); +errcode_t ext2fs_attr_set (ext2_filsys fs,ext2_ino_t ino, +struct ext2_inode *inode, +int name_index,const char *name,const char *value, +int value_len,int flags); +extern errcode_t ext2fs_expand_extra_isize (ext2_filsys fs,ext2_ino_t ino, +struct ext2_inode_large *inode, +int new_extra_isize,int *ret, +int *needed_size);extern errcode_t ext2fs_read_ext_attr (ext2_filsys fs,blk_t block,void *buf);extern errcode_t ext2fs_write_ext_attr (ext2_filsys fs,blk_t block,void *buf);
e2fsck/problem.h +32 -0
@@ -215,6 +215,16 @@ struct problem_context {/* Journal transaction found corrupt */ #define PR_0_JNL_TXN_CORRUPT 0x00003A +/* Invalid s_min_extra_isize */ +#define PR_0_MIN_EXTRA_ISIZE_INVALID 0x00003A + +/* Invalid s_want_extra_isize */ +#define PR_0_WANT_EXTRA_ISIZE_INVALID 0x00003B + +/* Clear EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE flag */ +#define PR_0_CLEAR_EXTRA_ISIZE 0x00003C + +/* * Pass 1 errors */
@@ -516,6 +526,25 @@ struct problem_context {/* eh_depth for in-inode header is bad */ #define PR_1_EXTENT_EH_DEPTH_BAD 0x010063 +/* Warning for user that all inodes need to be expanded atleast by +* s_min_extra_isize +*/ +#define PR_1_EXPAND_EISIZE_WARNING 0x010068 + +/* Expand the inode */ +#define PR_1_EXPAND_EISIZE 0x010069 + +/* Delete an EA so that EXTRA_ISIZE may be enabled */ +#define PR_1_EISIZE_DELETE_EA 0x01006A + +/* An EA needs to be deleted by e2fsck is being run with -p or -y */ +#define PR_1_EA_BLK_NOSPC 0x01006B + +/* Disable EXTRA_ISIZE feature as inode cannot be expanded +* without deletion of an EA +*/ +#define PR_1_CLEAR_EXTRA_ISIZE 0x01006C +/* * Pass 1b errors */
@@ -983,6 +1012,9 @@ struct problem_context {/* Inode in use but group is marked INODE_UNINIT */ #define PR_5_INODE_UNINIT 0x050019 +/* Expand the inodes which need a new EA block */ +#define PR_5_EXPAND_EISIZE 0x05001a +/* * Post-Pass 5 errors */
e2fsck/problem.c +50 -0
68 lines not on this page · raw message
e2fsck/e2fsck.c +1 -0
@@ -153,6 +153,7 @@ errcode_t e2fsck_reset_context(e2fsck_t ctx->fs_tind_count =0 ; ctx->fs_fragmented =0 ; ctx->large_files =0 ; + ctx->fs_unexpanded_inodes =0 ;for (i=0 ; i <MAX_EXTENT_DEPTH_COUNT ; i++) ctx->extent_depth_count[i] =0 ;
e2fsck/pass5.c +36 -0
@@ -64,6 +64,42 @@ void e2fsck_pass5(e2fsck_t ctx)ext2fs_free_block_bitmap (ctx->block_found_map); ctx->block_found_map =0 ; +if (ctx->flags &E2F_FLAG_EXPAND_EISIZE ) { +int min_extra_isize; + +if (!ctx->expand_eisize_map) + goto set_min_extra_isize; + +for (pctx.ino =1 ; pctx.ino < ctx->fs->super->s_inodes_count; + pctx.ino++) { +if (ext2fs_test_inode_bitmap (ctx->expand_eisize_map, + pctx.ino)) { +fix_problem (ctx,PR_5_EXPAND_EISIZE , &pctx); +ext2fs_expand_extra_isize (ctx->fs, pctx.ino,0 , + ctx->want_extra_isize, +NULL ,NULL ); + } + } +ext2fs_free_inode_bitmap (ctx->expand_eisize_map); + +set_min_extra_isize: +if (ctx->fs->super->s_min_extra_isize) + min_extra_isize = ctx->fs->super->s_min_extra_isize; +else + min_extra_isize = ctx->want_extra_isize; +if (ctx->min_extra_isize >= min_extra_isize && + !ctx->fs_unexpanded_inodes) { + ctx->fs->super->s_min_extra_isize =ctx->min_extra_isize; + ctx->fs->super->s_feature_ro_compat |= +EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE ; + }else { + ctx->fs->super->s_min_extra_isize =0 ; + ctx->fs->super->s_feature_ro_compat &= + ~EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE ; + } +ext2fs_mark_super_dirty (ctx->fs); + } +#ifdef RESOURCE_TRACK if (ctx->options &E2F_OPT_TIME2 ) {e2fsck_clear_progbar (ctx);
lib/ext2fs/ext2_err.et.in +24 -0
@@ -416,4 +416,28 @@ ec EXT2_ET_EXTENT_INVALID_LENGTH, ec EXT2_ET_IO_CHANNEL_NO_SUPPORT_64, "I/O Channel does not support 64-bit block numbers" +ec EXT2_ET_EA_BAD_MAGIC, + "Extended attribute block has bad magic value" + +ec EXT2_ET_EA_BAD_ENTRIES, + "Extended attribute block has bad entries" + +ec EXT2_ET_EA_NO_SPACE, + "No free space for extended attribute" + +ec EXT2_ET_EA_TOO_BIG, + "Extended attribute too big for buffer" + +ec EXT2_ET_EA_NAME_TOO_BIG, + "Extended attribute name too big for header" + +ec EXT2_ET_EA_BAD_NAME, + "Extended attribute name is bad" + +ec EXT2_ET_EA_NAME_NOT_FOUND, + "Extended attribute name not found" + +ec EXT2_ET_EA_NAME_EXISTS, + "Extended attribute name already exists" + end