2 * ext_attr.c --- extended attribute blocks
4 * Copyright (C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org>
6 * Copyright (C) 2002 Theodore Ts'o.
9 * This file may be redistributed under the terms of the GNU Library
10 * General Public License, version 2.
24 #include "ext2_ext_attr.h"
28 #define NAME_HASH_SHIFT 5
29 #define VALUE_HASH_SHIFT 16
32 * ext2_xattr_hash_entry()
34 * Compute the hash of an extended attribute.
36 __u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry, void *data)
39 char *name = ((char *) entry) + sizeof(struct ext2_ext_attr_entry);
42 for (n = 0; n < entry->e_name_len; n++) {
43 hash = (hash << NAME_HASH_SHIFT) ^
44 (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
48 /* The hash needs to be calculated on the data in little-endian. */
49 if (entry->e_value_block == 0 && entry->e_value_size != 0) {
50 __u32 *value = (__u32 *)data;
51 for (n = (entry->e_value_size + EXT2_EXT_ATTR_ROUND) >>
52 EXT2_EXT_ATTR_PAD_BITS; n; n--) {
53 hash = (hash << VALUE_HASH_SHIFT) ^
54 (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
55 ext2fs_le32_to_cpu(*value++);
62 #undef NAME_HASH_SHIFT
63 #undef VALUE_HASH_SHIFT
65 #define BLOCK_HASH_SHIFT 16
67 * Re-compute the extended attribute hash value after an entry has changed.
69 static void ext2fs_attr_rehash(struct ext2_ext_attr_header *header,
70 struct ext2_ext_attr_entry *entry)
72 struct ext2_ext_attr_entry *here;
75 entry->e_hash = ext2fs_ext_attr_hash_entry(entry, (char *)header +
78 here = ENTRY(header+1);
79 while (!EXT2_EXT_IS_LAST_ENTRY(here)) {
81 /* Block is not shared if an entry's hash value == 0 */
85 hash = (hash << BLOCK_HASH_SHIFT) ^
86 (hash >> (8*sizeof(hash) - BLOCK_HASH_SHIFT)) ^
88 here = EXT2_EXT_ATTR_NEXT(here);
90 header->h_hash = hash;
93 errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block, void *buf)
97 retval = io_channel_read_blk64(fs->io, block, 1, buf);
100 #ifdef WORDS_BIGENDIAN
101 ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1);
106 errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf)
108 return ext2fs_read_ext_attr2(fs, block, buf);
111 errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block, void *inbuf)
115 #ifdef WORDS_BIGENDIAN
118 retval = ext2fs_get_mem(fs->blocksize, &buf);
122 ext2fs_swap_ext_attr(buf, inbuf, fs->blocksize, 1);
124 write_buf = (char *) inbuf;
126 retval = io_channel_write_blk64(fs->io, block, 1, write_buf);
127 #ifdef WORDS_BIGENDIAN
128 ext2fs_free_mem(&buf);
131 ext2fs_mark_changed(fs);
135 errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf)
137 return ext2fs_write_ext_attr2(fs, block, inbuf);
141 * This function adjusts the reference count of the EA block.
143 errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk,
144 char *block_buf, int adjust,
148 struct ext2_ext_attr_header *header;
151 if ((blk >= ext2fs_blocks_count(fs->super)) ||
152 (blk < fs->super->s_first_data_block))
153 return EXT2_ET_BAD_EA_BLOCK_NUM;
156 retval = ext2fs_get_mem(fs->blocksize, &buf);
162 retval = ext2fs_read_ext_attr2(fs, blk, block_buf);
166 header = BHDR(block_buf);
167 if (header->h_magic != EXT2_EXT_ATTR_MAGIC)
168 return EXT2_ET_EA_BAD_MAGIC;
170 header->h_refcount += adjust;
172 *newcount = header->h_refcount;
174 retval = ext2fs_write_ext_attr2(fs, blk, block_buf);
180 ext2fs_free_mem(&buf);
184 errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
185 char *block_buf, int adjust,
188 return ext2fs_adjust_ea_refcount2(fs, blk, block_buf, adjust, newcount);
191 struct ext2_attr_info {
198 struct ext2_attr_search {
199 struct ext2_ext_attr_entry *first;
202 struct ext2_ext_attr_entry *here;
206 struct ext2_attr_ibody_find {
208 struct ext2_attr_search s;
211 struct ext2_attr_block_find {
212 struct ext2_attr_search s;
216 void ext2fs_attr_shift_entries(struct ext2_ext_attr_entry *entry,
217 int value_offs_shift, char *to,
220 struct ext2_ext_attr_entry *last = entry;
222 /* Adjust the value offsets of the entries */
223 for (; !EXT2_EXT_IS_LAST_ENTRY(last); last = EXT2_EXT_ATTR_NEXT(last)) {
224 if (!last->e_value_block && last->e_value_size) {
225 last->e_value_offs = last->e_value_offs +
229 /* Shift the entries by n bytes and zero freed space in inode */
230 memmove(to, from, n);
232 memset(from, 0, to - from);
236 * This function returns the free space present in the inode or the EA block.
237 * total is number of bytes taken up by the EA entries and is used to shift
238 * the EAs in ext2fs_expand_extra_isize().
240 int ext2fs_attr_free_space(struct ext2_ext_attr_entry *last,
241 int *min_offs, char *base, int *total)
243 for (; !EXT2_EXT_IS_LAST_ENTRY(last); last = EXT2_EXT_ATTR_NEXT(last)) {
244 *total += EXT2_EXT_ATTR_LEN(last->e_name_len);
245 if (!last->e_value_block && last->e_value_size) {
246 int offs = last->e_value_offs;
247 if (offs < *min_offs)
252 return *min_offs - ((char *)last - base) - sizeof(__u32);
255 static errcode_t ext2fs_attr_check_names(struct ext2_ext_attr_entry *entry,
258 while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
259 struct ext2_ext_attr_entry *next = EXT2_EXT_ATTR_NEXT(entry);
260 if ((char *)next >= end)
261 return EXT2_ET_EA_BAD_ENTRIES;
267 /* The unused parameter used to be the blocksize, but with in-inode xattrs
268 * the xattr storage area size depends on where the xattrs are kept. Keep
269 * this parameter for API/ABI compatibility, but it is not needed. */
270 static errcode_t ext2fs_attr_find_entry(struct ext2_ext_attr_entry **pentry,
271 int name_index, const char *name,
272 int unused, int sorted)
274 struct ext2_ext_attr_entry *entry;
279 return EXT2_ET_EA_BAD_NAME;
281 name_len = strlen(name);
283 for (; !EXT2_EXT_IS_LAST_ENTRY(entry);
284 entry = EXT2_EXT_ATTR_NEXT(entry)) {
285 cmp = name_index - entry->e_name_index;
287 cmp = name_len - entry->e_name_len;
289 cmp = memcmp(name, entry->e_name, name_len);
290 if (cmp <= 0 && (sorted || cmp == 0))
295 return cmp ? EXT2_ET_EA_NAME_NOT_FOUND : 0;
298 static errcode_t ext2fs_attr_block_find(ext2_filsys fs,struct ext2_inode *inode,
299 struct ext2_attr_info *i,
300 struct ext2_attr_block_find *bs)
302 struct ext2_ext_attr_header *header;
305 if (inode->i_file_acl) {
306 /* The inode already has an extended attribute block. */
307 error = ext2fs_get_mem(fs->blocksize, &bs->block);
310 error = ext2fs_read_ext_attr(fs, inode->i_file_acl, bs->block);
314 header = BHDR(bs->block);
315 if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
316 error = EXT2_ET_EA_BAD_MAGIC;
320 /* Find the named attribute. */
321 bs->s.base = bs->block;
322 bs->s.first = (struct ext2_ext_attr_entry *)(header + 1);
323 bs->s.end = bs->block + fs->blocksize;
324 bs->s.here = bs->s.first;
325 error = ext2fs_attr_find_entry(&bs->s.here, i->name_index,
326 i->name, fs->blocksize, 1);
327 if (error && error != EXT2_ET_EA_NAME_NOT_FOUND)
329 bs->s.not_found = error;
334 if (error && bs->block)
335 ext2fs_free_mem(&bs->block);
339 static errcode_t ext2fs_attr_ibody_find(ext2_filsys fs,
340 struct ext2_inode_large *inode,
341 struct ext2_attr_info *i,
342 struct ext2_attr_ibody_find *is)
346 if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE)
349 if (inode->i_extra_isize == 0)
352 is->s.first = &IHDR(inode)->h_first_entry[0];
353 is->s.base = (char *)is->s.first;
354 is->s.here = is->s.first;
355 is->s.end = (char *)inode + EXT2_INODE_SIZE(fs->super);
356 if (IHDR(inode)->h_magic == EXT2_EXT_ATTR_MAGIC) {
357 error = ext2fs_attr_check_names(is->s.first, is->s.end);
360 /* Find the named attribute. */
361 error = ext2fs_attr_find_entry(&is->s.here, i->name_index,
363 (char *)is->s.base, 0);
364 if (error && error != EXT2_ET_EA_NAME_NOT_FOUND)
366 is->s.not_found = error;
372 static errcode_t ext2fs_attr_set_entry(ext2_filsys fs, struct ext2_attr_info *i,
373 struct ext2_attr_search *s)
375 struct ext2_ext_attr_entry *last;
376 int free, min_offs = s->end - s->base, name_len = strlen(i->name);
378 /* Compute min_offs and last. */
379 for (last = s->first; !EXT2_EXT_IS_LAST_ENTRY(last);
380 last = EXT2_EXT_ATTR_NEXT(last)) {
381 if (!last->e_value_block && last->e_value_size) {
382 int offs = last->e_value_offs;
388 free = min_offs - ((char *)last - s->base) - sizeof(__u32);
391 if (!s->here->e_value_block && s->here->e_value_size) {
392 int size = s->here->e_value_size;
393 free += EXT2_EXT_ATTR_SIZE(size);
395 free += EXT2_EXT_ATTR_LEN(name_len);
398 if (free < EXT2_EXT_ATTR_LEN(name_len) +
399 EXT2_EXT_ATTR_SIZE(i->value_len))
400 return EXT2_ET_EA_NO_SPACE;
403 if (i->value && s->not_found) {
404 /* Insert the new name. */
405 int size = EXT2_EXT_ATTR_LEN(name_len);
406 int rest = (char *)last - (char *)s->here + sizeof(__u32);
408 memmove((char *)s->here + size, s->here, rest);
409 memset(s->here, 0, size);
410 s->here->e_name_index = i->name_index;
411 s->here->e_name_len = name_len;
412 memcpy(s->here->e_name, i->name, name_len);
414 if (!s->here->e_value_block && s->here->e_value_size) {
415 char *first_val = s->base + min_offs;
416 int offs = s->here->e_value_offs;
417 char *val = s->base + offs;
418 int size = EXT2_EXT_ATTR_SIZE(s->here->e_value_size);
421 size == EXT2_EXT_ATTR_SIZE(i->value_len)) {
422 /* The old and the new value have the same
423 size. Just replace. */
424 s->here->e_value_size = i->value_len;
425 memset(val + size - EXT2_EXT_ATTR_PAD, 0,
426 EXT2_EXT_ATTR_PAD); /* Clear pad bytes */
427 memcpy(val, i->value, i->value_len);
431 /* Remove the old value. */
432 memmove(first_val + size, first_val, val - first_val);
433 memset(first_val, 0, size);
434 s->here->e_value_size = 0;
435 s->here->e_value_offs = 0;
438 /* Adjust all value offsets. */
440 while (!EXT2_EXT_IS_LAST_ENTRY(last)) {
441 int o = last->e_value_offs;
443 if (!last->e_value_block &&
444 last->e_value_size && o < offs)
445 last->e_value_offs = o + size;
446 last = EXT2_EXT_ATTR_NEXT(last);
450 /* Remove the old name. */
451 int size = EXT2_EXT_ATTR_LEN(name_len);
453 last = ENTRY((char *)last - size);
454 memmove((char *)s->here, (char *)s->here + size,
455 (char *)last - (char *)s->here + sizeof(__u32));
456 memset(last, 0, size);
461 /* Insert the new value. */
462 s->here->e_value_size = i->value_len;
464 int size = EXT2_EXT_ATTR_SIZE(i->value_len);
465 char *val = s->base + min_offs - size;
467 s->here->e_value_offs = min_offs - size;
468 memset(val + size - EXT2_EXT_ATTR_PAD, 0,
469 EXT2_EXT_ATTR_PAD); /* Clear the pad bytes. */
470 memcpy(val, i->value, i->value_len);
477 static errcode_t ext2fs_attr_block_set(ext2_filsys fs, struct ext2_inode *inode,
478 struct ext2_attr_info *i,
479 struct ext2_attr_block_find *bs)
481 struct ext2_attr_search *s = &bs->s;
482 char *new_buf = NULL, *old_block = NULL;
487 if (i->value && i->value_len > fs->blocksize)
488 return EXT2_ET_EA_NO_SPACE;
491 if (BHDR(s->base)->h_refcount != 1) {
492 int offset = (char *)s->here - bs->block;
494 /* Decrement the refcount of the shared block */
496 BHDR(s->base)->h_refcount -= 1;
498 error = ext2fs_get_mem(fs->blocksize, &s->base);
502 memcpy(s->base, bs->block, fs->blocksize);
503 s->first = ENTRY(BHDR(s->base)+1);
504 BHDR(s->base)->h_refcount = 1;
505 s->here = ENTRY(s->base + offset);
506 s->end = s->base + fs->blocksize;
509 error = ext2fs_get_mem(fs->blocksize, &s->base);
513 memset(s->base, 0, fs->blocksize);
514 BHDR(s->base)->h_magic = EXT2_EXT_ATTR_MAGIC;
515 BHDR(s->base)->h_blocks = 1;
516 BHDR(s->base)->h_refcount = 1;
517 s->first = ENTRY(BHDR(s->base)+1);
518 s->here = ENTRY(BHDR(s->base)+1);
519 s->end = s->base + fs->blocksize;
522 error = ext2fs_attr_set_entry(fs, i, s);
526 if (!EXT2_EXT_IS_LAST_ENTRY(s->first))
527 ext2fs_attr_rehash(BHDR(s->base), s->here);
529 if (!EXT2_EXT_IS_LAST_ENTRY(s->first)) {
530 if (bs->block && bs->block == s->base) {
531 /* We are modifying this block in-place */
533 blk = inode->i_file_acl;
534 error = ext2fs_write_ext_attr(fs, blk, s->base);
538 /* We need to allocate a new block */
539 error = ext2fs_new_block(fs, 0, 0, &blk);
542 ext2fs_block_alloc_stats(fs, blk, 1);
543 error = ext2fs_write_ext_attr(fs, blk, s->base);
548 BHDR(s->base)->h_refcount -= 1;
549 error = ext2fs_write_ext_attr(fs,
558 /* Update the i_blocks if we added a new EA block */
559 if (!inode->i_file_acl && new_buf)
560 inode->i_blocks += fs->blocksize / 512;
561 /* Update the inode. */
562 inode->i_file_acl = new_buf ? blk : 0;
566 ext2fs_free_mem(&s->base);
570 static errcode_t ext2fs_attr_ibody_set(ext2_filsys fs,
571 struct ext2_inode_large *inode,
572 struct ext2_attr_info *i,
573 struct ext2_attr_ibody_find *is)
575 struct ext2_attr_search *s = &is->s;
578 if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE)
579 return EXT2_ET_EA_NO_SPACE;
581 error = ext2fs_attr_set_entry(fs, i, s);
585 if (!EXT2_EXT_IS_LAST_ENTRY(s->first))
586 IHDR(inode)->h_magic = EXT2_EXT_ATTR_MAGIC;
588 IHDR(inode)->h_magic = 0;
590 return ext2fs_write_inode_full(fs, is->ino, (struct ext2_inode *)inode,
591 EXT2_INODE_SIZE(fs->super));
597 } ext2_attr_index_prefix[] = {
598 [EXT2_ATTR_INDEX_USER] = { EXT2_ATTR_INDEX_USER_PREFIX,
599 sizeof(EXT2_ATTR_INDEX_USER_PREFIX) },
600 [EXT2_ATTR_INDEX_POSIX_ACL_ACCESS] = {
601 EXT2_ATTR_INDEX_POSIX_ACL_ACCESS_PREFIX,
602 sizeof(EXT2_ATTR_INDEX_POSIX_ACL_ACCESS_PREFIX) },
603 [EXT2_ATTR_INDEX_POSIX_ACL_DEFAULT] = {
604 EXT2_ATTR_INDEX_POSIX_ACL_DEFAULT_PREFIX,
605 sizeof(EXT2_ATTR_INDEX_POSIX_ACL_DEFAULT_PREFIX) },
606 [EXT2_ATTR_INDEX_TRUSTED] = { EXT2_ATTR_INDEX_TRUSTED_PREFIX,
607 sizeof(EXT2_ATTR_INDEX_TRUSTED_PREFIX) },
608 [EXT2_ATTR_INDEX_LUSTRE] = { EXT2_ATTR_INDEX_LUSTRE_PREFIX,
609 sizeof(EXT2_ATTR_INDEX_LUSTRE_PREFIX) },
610 [EXT2_ATTR_INDEX_SECURITY] = { EXT2_ATTR_INDEX_SECURITY_PREFIX,
611 sizeof(EXT2_ATTR_INDEX_SECURITY_PREFIX)},
615 errcode_t ext2fs_attr_set(ext2_filsys fs, ext2_ino_t ino,
616 struct ext2_inode *inode,
617 int name_index, const char *name, const char *value,
618 int value_len, int flags)
620 struct ext2_inode_large *inode_large = NULL;
621 struct ext2_attr_info i = {
622 .name_index = name_index,
625 .value_len = value_len,
627 struct ext2_attr_ibody_find is = {
629 .s = { .not_found = -ENODATA, },
631 struct ext2_attr_block_find bs = {
632 .s = { .not_found = -ENODATA, },
637 return EXT2_ET_EA_BAD_NAME;
638 if (strlen(name) > 255)
639 return EXT2_ET_EA_NAME_TOO_BIG;
641 /* If the prefix is still present, skip it */
642 if (strncmp(name, ext2_attr_index_prefix[name_index].str,
643 ext2_attr_index_prefix[name_index].len) == 0)
644 i.name += ext2_attr_index_prefix[name_index].len;
646 if (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE) {
647 inode_large = (struct ext2_inode_large *)inode;
649 error = ext2fs_attr_ibody_find(fs, inode_large, &i, &is);
653 if (is.s.not_found) {
654 error = ext2fs_attr_block_find(fs, inode, &i, &bs);
659 if (is.s.not_found && bs.s.not_found) {
660 error = EXT2_ET_EA_NAME_NOT_FOUND;
661 if (flags & XATTR_REPLACE)
667 error = EXT2_ET_EA_NAME_EXISTS;
668 if (flags & XATTR_CREATE)
673 if (!is.s.not_found &&
674 (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE))
675 error = ext2fs_attr_ibody_set(fs, inode_large, &i, &is);
676 else if (!bs.s.not_found)
677 error = ext2fs_attr_block_set(fs, inode, &i, &bs);
679 if (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE)
680 error = ext2fs_attr_ibody_set(fs, inode_large, &i, &is);
681 if (!error && !bs.s.not_found) {
683 error = ext2fs_attr_block_set(fs, inode, &i, &bs);
684 } else if (error == EXT2_ET_EA_NO_SPACE) {
685 error = ext2fs_attr_block_set(fs, inode, &i, &bs);
688 if (!is.s.not_found) {
690 if (EXT2_INODE_SIZE(fs->super) >
691 EXT2_GOOD_OLD_INODE_SIZE)
692 error = ext2fs_attr_ibody_set(fs,
693 inode_large, &i, &is);
702 static errcode_t ext2fs_attr_check_block(ext2_filsys fs, char *buffer)
704 if (BHDR(buffer)->h_magic != (EXT2_EXT_ATTR_MAGIC) ||
705 BHDR(buffer)->h_blocks != 1)
706 return EXT2_ET_EA_BAD_MAGIC;
708 return ext2fs_attr_check_names((struct ext2_ext_attr_entry *)
710 buffer + fs->blocksize);
713 static errcode_t ext2fs_attr_block_get(ext2_filsys fs, struct ext2_inode *inode,
714 int name_index, const char *name,
715 void *buffer, size_t buffer_size,
718 struct ext2_ext_attr_header *header = NULL;
719 struct ext2_ext_attr_entry *entry;
720 char *block_buf = NULL;
723 error = EXT2_ET_EA_NAME_NOT_FOUND;
724 if (!inode->i_file_acl)
727 error = ext2fs_get_mem(fs->blocksize, &block_buf);
730 error = ext2fs_read_ext_attr(fs, inode->i_file_acl, block_buf);
734 error = ext2fs_attr_check_block(fs, block_buf);
738 header = BHDR(block_buf);
739 entry = (struct ext2_ext_attr_entry *)(header + 1);
740 error = ext2fs_attr_find_entry(&entry, name_index, name,
745 *easize = entry->e_value_size;
747 if (entry->e_value_size > buffer_size) {
748 error = EXT2_ET_EA_TOO_BIG;
751 memcpy(buffer, block_buf + entry->e_value_offs,
752 entry->e_value_size);
758 ext2fs_free_mem(&block_buf);
762 static errcode_t ext2fs_attr_check_ibody(ext2_filsys fs,
763 struct ext2_inode_large *inode)
765 const int inode_size = EXT2_INODE_SIZE(fs->super);
767 if (inode_size == EXT2_GOOD_OLD_INODE_SIZE)
768 return EXT2_ET_EA_NAME_NOT_FOUND;
770 if (IHDR(inode)->h_magic != EXT2_EXT_ATTR_MAGIC)
771 return EXT2_ET_EA_BAD_MAGIC;
773 return ext2fs_attr_check_names(&IHDR(inode)->h_first_entry[0],
774 (char *)inode + inode_size);
778 static errcode_t ext2fs_attr_ibody_get(ext2_filsys fs,
779 struct ext2_inode_large *inode,
780 int name_index, const char *name,
781 void *buffer, size_t buffer_size,
784 struct ext2_ext_attr_entry *entry;
787 error = ext2fs_attr_check_ibody(fs, inode);
791 entry = &IHDR(inode)->h_first_entry[0];
793 error = ext2fs_attr_find_entry(&entry, name_index, name,
794 (char *)inode + EXT2_INODE_SIZE(fs->super) -
799 *easize = entry->e_value_size;
801 if (entry->e_value_size > buffer_size) {
802 error = EXT2_ET_EA_TOO_BIG;
805 memcpy(buffer, (char *)&IHDR(inode)->h_first_entry[0] +
806 entry->e_value_offs, entry->e_value_size);
814 errcode_t ext2fs_attr_get(ext2_filsys fs, struct ext2_inode *inode,
815 int name_index, const char *name, char *buffer,
816 size_t buffer_size, int *easize)
820 error = ext2fs_attr_ibody_get(fs, (struct ext2_inode_large *)inode,
821 name_index, name, buffer, buffer_size,
823 if (error == EXT2_ET_EA_NAME_NOT_FOUND || error == EXT2_ET_EA_BAD_MAGIC)
824 error = ext2fs_attr_block_get(fs, inode, name_index, name,
825 buffer, buffer_size, easize);
830 int ext2fs_attr_get_next_attr(struct ext2_ext_attr_entry *entry, int name_index,
831 char *buffer, int buffer_size, int start)
833 const int prefix_len = ext2_attr_index_prefix[name_index].len;
836 if (!start && !EXT2_EXT_IS_LAST_ENTRY(entry))
837 entry = EXT2_EXT_ATTR_NEXT(entry);
839 for (; !EXT2_EXT_IS_LAST_ENTRY(entry);
840 entry = EXT2_EXT_ATTR_NEXT(entry)) {
843 if (name_index == entry->e_name_index)
846 if (EXT2_EXT_IS_LAST_ENTRY(entry))
849 total_len = prefix_len + entry->e_name_len + 1;
850 if (buffer && total_len <= buffer_size) {
851 memcpy(buffer, ext2_attr_index_prefix[name_index].str,
853 memcpy(buffer + prefix_len, entry->e_name, entry->e_name_len);
854 buffer[prefix_len + entry->e_name_len] = '\0';
860 errcode_t ext2fs_expand_extra_isize(ext2_filsys fs, ext2_ino_t ino,
861 struct ext2_inode_large *inode,
862 int new_extra_isize, int *ret,
865 struct ext2_inode *inode_buf = NULL;
866 struct ext2_ext_attr_header *header = NULL;
867 struct ext2_ext_attr_entry *entry = NULL, *last = NULL;
868 struct ext2_attr_ibody_find is = {
870 .s = { .not_found = EXT2_ET_EA_NO_SPACE, },
872 struct ext2_attr_block_find bs = {
873 .s = { .not_found = EXT2_ET_EA_NO_SPACE, },
875 char *start, *end, *block_buf = NULL, *buffer =NULL, *b_entry_name=NULL;
876 int total_ino = 0, total_blk, free, offs, tried_min_extra_isize = 0;
877 int s_min_extra_isize = fs->super->s_min_extra_isize;
881 *needed_size = new_extra_isize;
882 error = ext2fs_get_mem(fs->blocksize, &block_buf);
887 error = ext2fs_get_mem(EXT2_INODE_SIZE(fs->super), &inode_buf);
891 error = ext2fs_read_inode_full(fs, ino, inode_buf,
892 EXT2_INODE_SIZE(fs->super));
896 inode = (struct ext2_inode_large *)inode_buf;
900 if (inode->i_extra_isize >= new_extra_isize)
903 start = (char *)inode + EXT2_GOOD_OLD_INODE_SIZE + inode->i_extra_isize;
904 /* No extended attributes present */
905 if (IHDR(inode)->h_magic != EXT2_EXT_ATTR_MAGIC) {
907 EXT2_INODE_SIZE(fs->super) - EXT2_GOOD_OLD_INODE_SIZE -
908 inode->i_extra_isize);
909 inode->i_extra_isize = new_extra_isize;
915 start += sizeof(__u32);
916 end = (char *)inode + EXT2_INODE_SIZE(fs->super);
917 last = entry = (struct ext2_ext_attr_entry *)start;
919 /* Consider space takenup by magic number */
920 total_ino = sizeof(__u32);
921 free = ext2fs_attr_free_space(last, &offs, start, &total_ino);
923 /* Enough free space available in the inode for expansion */
924 if (free >= new_extra_isize) {
925 ext2fs_attr_shift_entries(entry,
926 inode->i_extra_isize - new_extra_isize,
928 EXT2_GOOD_OLD_INODE_SIZE +
930 start - sizeof(__u32), total_ino);
931 inode->i_extra_isize = new_extra_isize;
937 if (inode->i_file_acl) {
938 error = ext2fs_read_ext_attr(fs, inode->i_file_acl, block_buf);
942 header = BHDR(block_buf);
943 if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
944 error = EXT2_ET_EA_BAD_MAGIC;
947 end = block_buf + fs->blocksize;
948 last = entry = (struct ext2_ext_attr_entry *)(header+1);
949 start = (char *)entry;
951 free = ext2fs_attr_free_space(last, &offs, start, &total_blk);
952 if (free < new_extra_isize) {
953 if (!tried_min_extra_isize && s_min_extra_isize) {
954 tried_min_extra_isize++;
955 new_extra_isize = s_min_extra_isize;
959 *ret = EXT2_EXPAND_EISIZE_NOSPC;
960 error = EXT2_ET_EA_NO_SPACE;
964 if (ret && *ret == EXT2_EXPAND_EISIZE_UNSAFE) {
965 *ret = EXT2_EXPAND_EISIZE_NEW_BLOCK;
969 free = fs->blocksize;
972 while (new_extra_isize > 0) {
973 int offs, size, entry_size;
974 struct ext2_ext_attr_entry *small_entry = NULL;
975 struct ext2_attr_info i = {
979 unsigned int total_size, shift_bytes, temp = ~0U, extra_isize=0;
981 start = (char *)inode + EXT2_GOOD_OLD_INODE_SIZE +
982 inode->i_extra_isize + sizeof(__u32);
983 end = (char *)inode + EXT2_INODE_SIZE(fs->super);
984 last = (struct ext2_ext_attr_entry *)start;
986 /* Find the entry best suited to be pushed into EA block */
988 for (; !EXT2_EXT_IS_LAST_ENTRY(last);
989 last = EXT2_EXT_ATTR_NEXT(last)) {
990 total_size = EXT2_EXT_ATTR_SIZE(last->e_value_size) +
991 EXT2_EXT_ATTR_LEN(last->e_name_len);
992 if (total_size <= free && total_size < temp) {
993 if (total_size < new_extra_isize) {
1002 if (entry == NULL) {
1004 entry = small_entry;
1006 if (!tried_min_extra_isize &&
1007 s_min_extra_isize) {
1008 tried_min_extra_isize++;
1009 new_extra_isize = s_min_extra_isize;
1013 *ret = EXT2_EXPAND_EISIZE_NOSPC;
1014 error = EXT2_ET_EA_NO_SPACE;
1018 offs = entry->e_value_offs;
1019 size = entry->e_value_size;
1020 entry_size = EXT2_EXT_ATTR_LEN(entry->e_name_len);
1021 i.name_index = entry->e_name_index;
1022 error = ext2fs_get_mem(size, &buffer);
1025 error = ext2fs_get_mem(entry->e_name_len + 1, &b_entry_name);
1028 /* Save the entry name and the entry value */
1029 memcpy((char *)buffer, (char *)start + offs,
1030 EXT2_EXT_ATTR_SIZE(size));
1031 memcpy((char *)b_entry_name, (char *)entry->e_name,
1033 b_entry_name[entry->e_name_len] = '\0';
1034 i.name = b_entry_name;
1036 error = ext2fs_attr_ibody_find(fs, inode, &i, &is);
1040 error = ext2fs_attr_set_entry(fs, &i, &is.s);
1044 entry = (struct ext2_ext_attr_entry *)start;
1045 if (entry_size + EXT2_EXT_ATTR_SIZE(size) >= new_extra_isize)
1046 shift_bytes = new_extra_isize;
1048 shift_bytes = entry_size + EXT2_EXT_ATTR_SIZE(size);
1049 ext2fs_attr_shift_entries(entry,
1050 inode->i_extra_isize - shift_bytes,
1051 (char *)inode +EXT2_GOOD_OLD_INODE_SIZE+
1052 extra_isize + shift_bytes,
1053 start - sizeof(__u32),
1054 total_ino - entry_size);
1056 extra_isize += shift_bytes;
1057 new_extra_isize -= shift_bytes;
1059 *needed_size = new_extra_isize;
1060 inode->i_extra_isize = extra_isize;
1062 i.name = b_entry_name;
1065 error = ext2fs_attr_block_find(fs, (struct ext2_inode *)inode,
1070 /* Add entry which was removed from the inode into the block */
1071 error = ext2fs_attr_block_set(fs, (struct ext2_inode *)inode,
1078 error = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)inode,
1079 EXT2_INODE_SIZE(fs->super));
1082 ext2fs_free_mem(&inode_buf);
1084 ext2fs_free_mem(&block_buf);
1086 ext2fs_free_mem(&buffer);
1088 ext2fs_free_mem(&b_entry_name);