On Fri, Aug 02, 2013 at 05:49:30PM +0800, Zheng Liu wrote: > From: Zheng Liu <wenqing.lz@xxxxxxxxxx> > > We need to define some functions to operate extended attribute in order > to support inline data. > > Signed-off-by: Zheng Liu <wenqing.lz@xxxxxxxxxx> > Signed-off-by: Theodore Ts'o <tytso@xxxxxxx> > --- > lib/ext2fs/ext2_err.et.in | 3 + > lib/ext2fs/ext2_ext_attr.h | 31 ++++++++ > lib/ext2fs/ext2fs.h | 9 +++ > lib/ext2fs/ext_attr.c | 186 ++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 229 insertions(+) > > diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in > index d20c6b7..7e6d6e5 100644 > --- a/lib/ext2fs/ext2_err.et.in > +++ b/lib/ext2fs/ext2_err.et.in > @@ -476,4 +476,7 @@ ec EXT2_ET_MMP_CSUM_INVALID, > ec EXT2_ET_FILE_EXISTS, > "Ext2 file already exists" > > +ec EXT2_ET_EXT_ATTR_CURRUPTED, > + "Extended attribute currupted" "corrupted". (Or maybe shorten that to "corrupt"?) --D > + > end > diff --git a/lib/ext2fs/ext2_ext_attr.h b/lib/ext2fs/ext2_ext_attr.h > index bbb0aaa..99ad849 100644 > --- a/lib/ext2fs/ext2_ext_attr.h > +++ b/lib/ext2fs/ext2_ext_attr.h > @@ -15,6 +15,10 @@ > /* Maximum number of references to one attribute block */ > #define EXT2_EXT_ATTR_REFCOUNT_MAX 1024 > > +/* Name indexes */ > +#define EXT4_EXT_ATTR_INDEX_SYSTEM 7 > +#define EXT4_EXT_ATTR_SYSTEM_DATA "data" > + > struct ext2_ext_attr_header { > __u32 h_magic; /* magic number for identification */ > __u32 h_refcount; /* reference count */ > @@ -25,6 +29,10 @@ struct ext2_ext_attr_header { > __u32 h_reserved[3]; /* zero right now */ > }; > > +struct ext2_ext_attr_ibody_header { > + __u32 h_magic; > +}; > + > struct ext2_ext_attr_entry { > __u8 e_name_len; /* length of name */ > __u8 e_name_index; /* attribute name index */ > @@ -57,6 +65,29 @@ struct ext2_ext_attr_entry { > #define EXT2_XATTR_SIZE(size) \ > (((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND) > > +#define IHDR(inode) \ > + ((struct ext2_ext_attr_ibody_header *) \ > + ((char *)inode + \ > + EXT2_GOOD_OLD_INODE_SIZE + \ > + inode->i_extra_isize)) > +#define IFIRST(hdr) ((struct ext2_ext_attr_entry *)((hdr)+1)) > +#define EXT2_ZERO_EXT_ATTR_VALUE ((void *)-1) > + > +struct ext2_ext_attr_info { > + int name_index; > + const char *name; > + const void *value; > + size_t value_len; > +}; > + > +struct ext2_ext_attr_search { > + struct ext2_ext_attr_entry *first; > + void *base; > + void *end; > + struct ext2_ext_attr_entry *here; > + int not_found; > +}; > + > #ifdef __KERNEL__ > # ifdef CONFIG_EXT2_FS_EXT_ATTR > extern int ext2_get_ext_attr(struct inode *, const char *, char *, size_t, int); > diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h > index 3346c00..8c30197 100644 > --- a/lib/ext2fs/ext2fs.h > +++ b/lib/ext2fs/ext2fs.h > @@ -1136,6 +1136,15 @@ extern errcode_t ext2fs_adjust_ea_refcount3(ext2_filsys fs, blk64_t blk, > char *block_buf, > int adjust, __u32 *newcount, > ext2_ino_t inum); > +extern errcode_t ext2fs_ext_attr_ibody_find(ext2_filsys fs, > + struct ext2_inode_large *inode, > + struct ext2_ext_attr_info *i, > + struct ext2_ext_attr_search *s); > +extern errcode_t ext2fs_ext_attr_find_entry(struct ext2_ext_attr_entry **pentry, > + int name_index, const char *name, > + size_t size, int sorted); > +extern errcode_t ext2fs_ext_attr_set_entry(struct ext2_ext_attr_info *i, > + struct ext2_ext_attr_search *s); > > /* 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..6d55340 100644 > --- a/lib/ext2fs/ext_attr.c > +++ b/lib/ext2fs/ext_attr.c > @@ -186,3 +186,189 @@ errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk, > return ext2fs_adjust_ea_refcount2(fs, blk, block_buf, adjust, > newcount); > } > + > +static errcode_t > +ext2fs_ext_attr_check_names(struct ext2_ext_attr_entry *entry, void *end) > +{ > + while (!EXT2_EXT_IS_LAST_ENTRY(entry)) { > + struct ext2_ext_attr_entry *next = EXT2_EXT_ATTR_NEXT(entry); > + if ((void *)next >= end) > + return EXT2_ET_EXT_ATTR_CURRUPTED; > + entry = next; > + } > + return 0; > +} > + > +static inline errcode_t > +ext2fs_ext_attr_check_entry(struct ext2_ext_attr_entry *entry, size_t size) > +{ > + size_t value_size = entry->e_value_size; > + > + if (entry->e_value_block != 0 || value_size > size || > + entry->e_value_offs + value_size > size) > + return EXT2_ET_EXT_ATTR_CURRUPTED; > + return 0; > +} > + > +errcode_t ext2fs_ext_attr_find_entry(struct ext2_ext_attr_entry **pentry, > + int name_index, const char *name, > + size_t size, int sorted) > +{ > + struct ext2_ext_attr_entry *entry; > + size_t name_len; > + int cmp = 1; > + > + if (name == NULL) > + return EXT2_ET_INVALID_ARGUMENT; > + name_len = strlen(name); > + for (entry = *pentry; !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, EXT2_EXT_ATTR_NAME(entry), > + name_len); > + if (cmp <= 0 && (sorted || cmp == 0)) > + break; > + } > + *pentry = entry; > + if (!cmp && ext2fs_ext_attr_check_entry(entry, size)) > + return EXT2_ET_EXT_ATTR_CURRUPTED; > + return cmp ? ENODATA : 0; > +} > + > +errcode_t ext2fs_ext_attr_ibody_find(ext2_filsys fs, > + struct ext2_inode_large *inode, > + struct ext2_ext_attr_info *i, > + struct ext2_ext_attr_search *s) > +{ > + struct ext2_ext_attr_ibody_header *header; > + errcode_t error; > + > + if (inode->i_extra_isize == 0) > + return 0; > + header = IHDR(inode); > + s->base = s->first = IFIRST(header); > + s->here = s->first; > + s->end = (char *)inode + EXT2_INODE_SIZE(fs->super); > + > + error = ext2fs_ext_attr_check_names(IFIRST(header), s->end); > + if (error) > + return error; > + /* Find the named attribute. */ > + error = ext2fs_ext_attr_find_entry(&s->here, i->name_index, > + i->name, (char *)s->end - > + (char *)s->base, 0); > + if (error && error != ENODATA) > + return error; > + s->not_found = error; > + return 0; > +} > + > +errcode_t ext2fs_ext_attr_set_entry(struct ext2_ext_attr_info *i, > + struct ext2_ext_attr_search *s) > +{ > + struct ext2_ext_attr_entry *last; > + size_t freesize, min_offs = (char *)s->end - (char *)s->base; > + size_t name_len = strlen(i->name); > + > + /* Compute min_offs and last. */ > + last = s->first; > + for (; !EXT2_EXT_IS_LAST_ENTRY(last); last = EXT2_EXT_ATTR_NEXT(last)) { > + if (!last->e_value_block && last->e_value_size) { > + size_t offs = last->e_value_offs; > + if (offs < min_offs) > + min_offs = offs; > + } > + } > + freesize = min_offs - ((char *)last - (char *)s->base) - sizeof(__u32); > + if (!s->not_found) { > + if (!s->here->e_value_block && s->here->e_value_size) { > + size_t size = s->here->e_value_size; > + freesize += EXT2_EXT_ATTR_SIZE(size); > + } > + freesize += EXT2_EXT_ATTR_LEN(name_len); > + } > + if (i->value) { > + if (freesize < EXT2_EXT_ATTR_SIZE(i->value_len) || > + freesize < EXT2_EXT_ATTR_LEN(name_len) + > + EXT2_EXT_ATTR_SIZE(i->value_len)) > + return ENOSPC; > + } > + > + if (i->value && s->not_found) { > + /* Insert the new name. */ > + size_t size = EXT2_EXT_ATTR_LEN(name_len); > + size_t 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(EXT2_EXT_ATTR_NAME(s->here), i->name, name_len); > + } else { > + if (!s->here->e_value_block && s->here->e_value_size) { > + char *first_val = (char *) s->base + min_offs; > + size_t offs = s->here->e_value_offs; > + char *val = (char *)s->base + offs; > + size_t 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; > + if (i->value == EXT2_ZERO_EXT_ATTR_VALUE) { > + memset(val, 0, size); > + } else { > + memset(val + size - EXT2_EXT_ATTR_PAD, 0, > + EXT2_EXT_ATTR_PAD); > + 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)) { > + size_t 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. */ > + size_t size = EXT2_EXT_ATTR_LEN(name_len); > + last = (struct ext2_ext_attr_entry *)last - size; > + memmove(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) { > + size_t size = EXT2_EXT_ATTR_SIZE(i->value_len); > + char *val = (char *)s->base + min_offs - size; > + s->here->e_value_offs = min_offs - size; > + if (i->value == EXT2_ZERO_EXT_ATTR_VALUE) { > + memset(val, 0, size); > + } else { > + memset(val + size - EXT2_EXT_ATTR_PAD, 0, > + EXT2_EXT_ATTR_PAD); > + memcpy(val, i->value, i->value_len); > + } > + } > + } > + return 0; > +} > -- > 1.7.9.7 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-ext4" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html