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_inum == 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 {
213 struct ext2_attr_search s;
217 void ext2fs_attr_shift_entries(struct ext2_ext_attr_entry *entry,
218 int value_offs_shift, char *to,
221 struct ext2_ext_attr_entry *last = entry;
223 /* Adjust the value offsets of the entries */
224 for (; !EXT2_EXT_IS_LAST_ENTRY(last); last = EXT2_EXT_ATTR_NEXT(last)) {
225 if (last->e_value_inum == 0 && last->e_value_size) {
226 last->e_value_offs = last->e_value_offs +
230 /* Shift the entries by n bytes and zero freed space in inode */
231 memmove(to, from, n);
233 memset(from, 0, to - from);
237 * This function returns the free space present in the inode or the EA block.
238 * total is number of bytes taken up by the EA entries and is used to shift
239 * the EAs in ext2fs_expand_extra_isize().
241 int ext2fs_attr_free_space(struct ext2_ext_attr_entry *last,
242 int *min_offs, char *base, int *total)
244 for (; !EXT2_EXT_IS_LAST_ENTRY(last); last = EXT2_EXT_ATTR_NEXT(last)) {
245 *total += EXT2_EXT_ATTR_LEN(last->e_name_len);
246 if (last->e_value_inum == 0 && last->e_value_size) {
247 int offs = last->e_value_offs;
248 if (offs < *min_offs)
253 return *min_offs - ((char *)last - base) - sizeof(__u32);
256 static errcode_t ext2fs_attr_check_names(struct ext2_ext_attr_entry *entry,
259 while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
260 struct ext2_ext_attr_entry *next = EXT2_EXT_ATTR_NEXT(entry);
261 if ((char *)next >= end)
262 return EXT2_ET_EA_BAD_ENTRIES;
268 /* The unused parameter used to be the blocksize, but with in-inode xattrs
269 * the xattr storage area size depends on where the xattrs are kept. Keep
270 * this parameter for API/ABI compatibility, but it is not needed. */
271 static errcode_t ext2fs_attr_find_entry(struct ext2_ext_attr_entry **pentry,
272 int name_index, const char *name,
273 int unused, int sorted)
275 struct ext2_ext_attr_entry *entry;
280 return EXT2_ET_EA_BAD_NAME;
282 name_len = strlen(name);
284 for (; !EXT2_EXT_IS_LAST_ENTRY(entry);
285 entry = EXT2_EXT_ATTR_NEXT(entry)) {
286 cmp = name_index - entry->e_name_index;
288 cmp = name_len - entry->e_name_len;
290 cmp = memcmp(name, entry->e_name, name_len);
291 if (cmp <= 0 && (sorted || cmp == 0))
296 return cmp ? EXT2_ET_EA_NAME_NOT_FOUND : 0;
299 static errcode_t ext2fs_attr_block_find(ext2_filsys fs,struct ext2_inode *inode,
300 struct ext2_attr_info *i,
301 struct ext2_attr_block_find *bs)
303 struct ext2_ext_attr_header *header;
306 if (inode->i_file_acl) {
307 /* The inode already has an extended attribute block. */
308 error = ext2fs_get_mem(fs->blocksize, &bs->block);
311 error = ext2fs_read_ext_attr(fs, inode->i_file_acl, bs->block);
315 header = BHDR(bs->block);
316 if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
317 error = EXT2_ET_EA_BAD_MAGIC;
321 /* Find the named attribute. */
322 bs->s.base = bs->block;
323 bs->s.first = (struct ext2_ext_attr_entry *)(header + 1);
324 bs->s.end = bs->block + fs->blocksize;
325 bs->s.here = bs->s.first;
326 error = ext2fs_attr_find_entry(&bs->s.here, i->name_index,
327 i->name, fs->blocksize, 1);
328 if (error && error != EXT2_ET_EA_NAME_NOT_FOUND)
330 bs->s.not_found = error;
335 if (error && bs->block)
336 ext2fs_free_mem(&bs->block);
340 static errcode_t ext2fs_attr_ibody_find(ext2_filsys fs,
341 struct ext2_inode_large *inode,
342 struct ext2_attr_info *i,
343 struct ext2_attr_ibody_find *is)
347 if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE)
350 if (inode->i_extra_isize == 0)
353 is->s.first = &IHDR(inode)->h_first_entry[0];
354 is->s.base = (char *)is->s.first;
355 is->s.here = is->s.first;
356 is->s.end = (char *)inode + EXT2_INODE_SIZE(fs->super);
357 if (IHDR(inode)->h_magic == EXT2_EXT_ATTR_MAGIC) {
358 error = ext2fs_attr_check_names(is->s.first, is->s.end);
361 /* Find the named attribute. */
362 error = ext2fs_attr_find_entry(&is->s.here, i->name_index,
364 (char *)is->s.base, 0);
365 if (error && error != EXT2_ET_EA_NAME_NOT_FOUND)
367 is->s.not_found = error;
373 static errcode_t ext2fs_attr_set_entry(ext2_filsys fs, struct ext2_attr_info *i,
374 struct ext2_attr_search *s)
376 struct ext2_ext_attr_entry *last;
377 int free, min_offs = s->end - s->base, name_len = strlen(i->name);
379 /* Compute min_offs and last. */
380 for (last = s->first; !EXT2_EXT_IS_LAST_ENTRY(last);
381 last = EXT2_EXT_ATTR_NEXT(last)) {
382 if (last->e_value_inum == 0 && last->e_value_size) {
383 int offs = last->e_value_offs;
389 free = min_offs - ((char *)last - s->base) - sizeof(__u32);
392 if (s->here->e_value_inum == 0 && s->here->e_value_size) {
393 int size = s->here->e_value_size;
394 free += EXT2_EXT_ATTR_SIZE(size);
396 free += EXT2_EXT_ATTR_LEN(name_len);
399 if (free < EXT2_EXT_ATTR_LEN(name_len) +
400 EXT2_EXT_ATTR_SIZE(i->value_len))
401 return EXT2_ET_EA_NO_SPACE;
404 if (i->value && s->not_found) {
405 /* Insert the new name. */
406 int size = EXT2_EXT_ATTR_LEN(name_len);
407 int rest = (char *)last - (char *)s->here + sizeof(__u32);
409 memmove((char *)s->here + size, s->here, rest);
410 memset(s->here, 0, size);
411 s->here->e_name_index = i->name_index;
412 s->here->e_name_len = name_len;
413 memcpy(s->here->e_name, i->name, name_len);
415 if (s->here->e_value_inum == 0 && s->here->e_value_size) {
416 char *first_val = s->base + min_offs;
417 int offs = s->here->e_value_offs;
418 char *val = s->base + offs;
419 int size = EXT2_EXT_ATTR_SIZE(s->here->e_value_size);
422 size == EXT2_EXT_ATTR_SIZE(i->value_len)) {
423 /* The old and the new value have the same
424 size. Just replace. */
425 s->here->e_value_size = i->value_len;
426 memset(val + size - EXT2_EXT_ATTR_PAD, 0,
427 EXT2_EXT_ATTR_PAD); /* Clear pad bytes */
428 memcpy(val, i->value, i->value_len);
432 /* Remove the old value. */
433 memmove(first_val + size, first_val, val - first_val);
434 memset(first_val, 0, size);
435 s->here->e_value_size = 0;
436 s->here->e_value_offs = 0;
439 /* Adjust all value offsets. */
441 while (!EXT2_EXT_IS_LAST_ENTRY(last)) {
442 int o = last->e_value_offs;
444 if (last->e_value_inum == 0 &&
445 last->e_value_size && o < offs)
446 last->e_value_offs = o + size;
447 last = EXT2_EXT_ATTR_NEXT(last);
451 /* Remove the old name. */
452 int size = EXT2_EXT_ATTR_LEN(name_len);
454 last = ENTRY((char *)last - size);
455 memmove((char *)s->here, (char *)s->here + size,
456 (char *)last - (char *)s->here + sizeof(__u32));
457 memset(last, 0, size);
462 /* Insert the new value. */
463 s->here->e_value_size = i->value_len;
465 int size = EXT2_EXT_ATTR_SIZE(i->value_len);
466 char *val = s->base + min_offs - size;
468 s->here->e_value_offs = min_offs - size;
469 memset(val + size - EXT2_EXT_ATTR_PAD, 0,
470 EXT2_EXT_ATTR_PAD); /* Clear the pad bytes. */
471 memcpy(val, i->value, i->value_len);
478 static errcode_t ext2fs_attr_block_set(ext2_filsys fs, struct ext2_inode *inode,
479 struct ext2_attr_info *i,
480 struct ext2_attr_block_find *bs)
482 struct ext2_attr_search *s = &bs->s;
483 char *new_buf = NULL, *old_block = NULL;
488 if (i->value && i->value_len > fs->blocksize)
489 return EXT2_ET_EA_NO_SPACE;
492 if (BHDR(s->base)->h_refcount != 1) {
493 int offset = (char *)s->here - bs->block;
495 /* Decrement the refcount of the shared block */
497 BHDR(s->base)->h_refcount -= 1;
499 error = ext2fs_get_mem(fs->blocksize, &s->base);
503 memcpy(s->base, bs->block, fs->blocksize);
504 s->first = ENTRY(BHDR(s->base)+1);
505 BHDR(s->base)->h_refcount = 1;
506 s->here = ENTRY(s->base + offset);
507 s->end = s->base + fs->blocksize;
510 error = ext2fs_get_mem(fs->blocksize, &s->base);
514 memset(s->base, 0, fs->blocksize);
515 BHDR(s->base)->h_magic = EXT2_EXT_ATTR_MAGIC;
516 BHDR(s->base)->h_blocks = 1;
517 BHDR(s->base)->h_refcount = 1;
518 s->first = ENTRY(BHDR(s->base)+1);
519 s->here = ENTRY(BHDR(s->base)+1);
520 s->end = s->base + fs->blocksize;
523 error = ext2fs_attr_set_entry(fs, i, s);
527 if (!EXT2_EXT_IS_LAST_ENTRY(s->first))
528 ext2fs_attr_rehash(BHDR(s->base), s->here);
530 if (!EXT2_EXT_IS_LAST_ENTRY(s->first)) {
531 if (bs->block && bs->block == s->base) {
532 /* We are modifying this block in-place */
534 blk = inode->i_file_acl;
535 error = ext2fs_write_ext_attr(fs, blk, s->base);
539 /* We need to allocate a new block */
540 error = ext2fs_new_block(fs, 0, 0, &blk);
543 ext2fs_block_alloc_stats(fs, blk, 1);
544 error = ext2fs_write_ext_attr(fs, blk, s->base);
549 BHDR(s->base)->h_refcount -= 1;
550 error = ext2fs_write_ext_attr(fs,
559 /* Update the i_blocks if we added a new EA block */
560 if (!inode->i_file_acl && new_buf)
561 inode->i_blocks += fs->blocksize / 512;
563 /* Drop the previous xattr block. */
566 ext2fs_read_block_bitmap(fs);
567 ext2fs_block_alloc_stats(fs, inode->i_file_acl, -1);
568 inode->i_blocks -= fs->blocksize / 512;
571 /* Update the inode. */
572 inode->i_file_acl = new_buf ? blk : 0;
574 ext2fs_write_inode(fs, bs->ino, inode);
578 ext2fs_free_mem(&s->base);
582 static errcode_t ext2fs_attr_ibody_set(ext2_filsys fs,
583 struct ext2_inode_large *inode,
584 struct ext2_attr_info *i,
585 struct ext2_attr_ibody_find *is)
587 struct ext2_attr_search *s = &is->s;
590 if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE)
591 return EXT2_ET_EA_NO_SPACE;
593 error = ext2fs_attr_set_entry(fs, i, s);
597 if (!EXT2_EXT_IS_LAST_ENTRY(s->first))
598 IHDR(inode)->h_magic = EXT2_EXT_ATTR_MAGIC;
600 IHDR(inode)->h_magic = 0;
602 return ext2fs_write_inode_full(fs, is->ino, (struct ext2_inode *)inode,
603 EXT2_INODE_SIZE(fs->super));
609 } ext2_attr_index_prefix[] = {
610 [EXT2_ATTR_INDEX_USER] = { EXT2_ATTR_INDEX_USER_PREFIX,
611 sizeof(EXT2_ATTR_INDEX_USER_PREFIX) },
612 [EXT2_ATTR_INDEX_POSIX_ACL_ACCESS] = {
613 EXT2_ATTR_INDEX_POSIX_ACL_ACCESS_PREFIX,
614 sizeof(EXT2_ATTR_INDEX_POSIX_ACL_ACCESS_PREFIX) },
615 [EXT2_ATTR_INDEX_POSIX_ACL_DEFAULT] = {
616 EXT2_ATTR_INDEX_POSIX_ACL_DEFAULT_PREFIX,
617 sizeof(EXT2_ATTR_INDEX_POSIX_ACL_DEFAULT_PREFIX) },
618 [EXT2_ATTR_INDEX_TRUSTED] = { EXT2_ATTR_INDEX_TRUSTED_PREFIX,
619 sizeof(EXT2_ATTR_INDEX_TRUSTED_PREFIX) },
620 [EXT2_ATTR_INDEX_LUSTRE] = { EXT2_ATTR_INDEX_LUSTRE_PREFIX,
621 sizeof(EXT2_ATTR_INDEX_LUSTRE_PREFIX) },
622 [EXT2_ATTR_INDEX_SECURITY] = { EXT2_ATTR_INDEX_SECURITY_PREFIX,
623 sizeof(EXT2_ATTR_INDEX_SECURITY_PREFIX)},
627 errcode_t ext2fs_attr_set(ext2_filsys fs, ext2_ino_t ino,
628 struct ext2_inode *inode,
629 int name_index, const char *name, const char *value,
630 int value_len, int flags)
632 struct ext2_inode_large *inode_large = NULL;
633 struct ext2_attr_info i = {
634 .name_index = name_index,
637 .value_len = value_len,
639 struct ext2_attr_ibody_find is = {
641 .s = { .not_found = -ENODATA, },
643 struct ext2_attr_block_find bs = {
644 .s = { .not_found = -ENODATA, },
649 return EXT2_ET_EA_BAD_NAME;
650 if (strlen(name) > 255)
651 return EXT2_ET_EA_NAME_TOO_BIG;
653 /* If the prefix is still present, skip it */
654 if (strncmp(name, ext2_attr_index_prefix[name_index].str,
655 ext2_attr_index_prefix[name_index].len) == 0)
656 i.name += ext2_attr_index_prefix[name_index].len;
658 if (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE) {
659 inode_large = (struct ext2_inode_large *)inode;
661 error = ext2fs_attr_ibody_find(fs, inode_large, &i, &is);
665 if (is.s.not_found) {
666 error = ext2fs_attr_block_find(fs, inode, &i, &bs);
671 if (is.s.not_found && bs.s.not_found) {
672 error = EXT2_ET_EA_NAME_NOT_FOUND;
673 if (flags & XATTR_REPLACE)
679 error = EXT2_ET_EA_NAME_EXISTS;
680 if (flags & XATTR_CREATE)
685 if (!is.s.not_found &&
686 (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE))
687 error = ext2fs_attr_ibody_set(fs, inode_large, &i, &is);
688 else if (!bs.s.not_found)
689 error = ext2fs_attr_block_set(fs, inode, &i, &bs);
691 if (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE)
692 error = ext2fs_attr_ibody_set(fs, inode_large, &i, &is);
693 if (!error && !bs.s.not_found) {
695 error = ext2fs_attr_block_set(fs, inode, &i, &bs);
696 } else if (error == EXT2_ET_EA_NO_SPACE) {
697 error = ext2fs_attr_block_set(fs, inode, &i, &bs);
700 if (!is.s.not_found) {
702 if (EXT2_INODE_SIZE(fs->super) >
703 EXT2_GOOD_OLD_INODE_SIZE)
704 error = ext2fs_attr_ibody_set(fs,
705 inode_large, &i, &is);
714 static errcode_t ext2fs_attr_check_block(ext2_filsys fs, char *buffer)
716 if (BHDR(buffer)->h_magic != (EXT2_EXT_ATTR_MAGIC) ||
717 BHDR(buffer)->h_blocks != 1)
718 return EXT2_ET_EA_BAD_MAGIC;
720 return ext2fs_attr_check_names((struct ext2_ext_attr_entry *)
722 buffer + fs->blocksize);
725 static errcode_t ext2fs_attr_block_get(ext2_filsys fs, struct ext2_inode *inode,
726 int name_index, const char *name,
727 void *buffer, size_t buffer_size,
730 struct ext2_ext_attr_header *header = NULL;
731 struct ext2_ext_attr_entry *entry;
732 char *block_buf = NULL;
735 error = EXT2_ET_EA_NAME_NOT_FOUND;
736 if (!inode->i_file_acl)
739 error = ext2fs_get_mem(fs->blocksize, &block_buf);
742 error = ext2fs_read_ext_attr(fs, inode->i_file_acl, block_buf);
746 error = ext2fs_attr_check_block(fs, block_buf);
750 header = BHDR(block_buf);
751 entry = (struct ext2_ext_attr_entry *)(header + 1);
752 error = ext2fs_attr_find_entry(&entry, name_index, name,
757 *easize = entry->e_value_size;
759 if (entry->e_value_size > buffer_size) {
760 error = EXT2_ET_EA_TOO_BIG;
763 memcpy(buffer, block_buf + entry->e_value_offs,
764 entry->e_value_size);
770 ext2fs_free_mem(&block_buf);
774 static errcode_t ext2fs_attr_check_ibody(ext2_filsys fs,
775 struct ext2_inode_large *inode)
777 const int inode_size = EXT2_INODE_SIZE(fs->super);
779 if (inode_size == EXT2_GOOD_OLD_INODE_SIZE)
780 return EXT2_ET_EA_NAME_NOT_FOUND;
782 if (IHDR(inode)->h_magic != EXT2_EXT_ATTR_MAGIC)
783 return EXT2_ET_EA_BAD_MAGIC;
785 return ext2fs_attr_check_names(&IHDR(inode)->h_first_entry[0],
786 (char *)inode + inode_size);
790 static errcode_t ext2fs_attr_ibody_get(ext2_filsys fs,
791 struct ext2_inode_large *inode,
792 int name_index, const char *name,
793 void *buffer, size_t buffer_size,
796 struct ext2_ext_attr_entry *entry;
799 error = ext2fs_attr_check_ibody(fs, inode);
803 entry = &IHDR(inode)->h_first_entry[0];
805 error = ext2fs_attr_find_entry(&entry, name_index, name,
806 (char *)inode + EXT2_INODE_SIZE(fs->super) -
811 *easize = entry->e_value_size;
813 if (entry->e_value_size > buffer_size) {
814 error = EXT2_ET_EA_TOO_BIG;
817 memcpy(buffer, (char *)&IHDR(inode)->h_first_entry[0] +
818 entry->e_value_offs, entry->e_value_size);
826 errcode_t ext2fs_attr_get(ext2_filsys fs, struct ext2_inode *inode,
827 int name_index, const char *name, char *buffer,
828 size_t buffer_size, int *easize)
832 error = ext2fs_attr_ibody_get(fs, (struct ext2_inode_large *)inode,
833 name_index, name, buffer, buffer_size,
835 if (error == EXT2_ET_EA_NAME_NOT_FOUND || error == EXT2_ET_EA_BAD_MAGIC)
836 error = ext2fs_attr_block_get(fs, inode, name_index, name,
837 buffer, buffer_size, easize);
842 int ext2fs_attr_get_next_attr(struct ext2_ext_attr_entry *entry, int name_index,
843 char *buffer, int buffer_size, int start)
845 const int prefix_len = ext2_attr_index_prefix[name_index].len;
848 if (!start && !EXT2_EXT_IS_LAST_ENTRY(entry))
849 entry = EXT2_EXT_ATTR_NEXT(entry);
851 for (; !EXT2_EXT_IS_LAST_ENTRY(entry);
852 entry = EXT2_EXT_ATTR_NEXT(entry)) {
855 if (name_index == entry->e_name_index)
858 if (EXT2_EXT_IS_LAST_ENTRY(entry))
861 total_len = prefix_len + entry->e_name_len + 1;
862 if (buffer && total_len <= buffer_size) {
863 memcpy(buffer, ext2_attr_index_prefix[name_index].str,
865 memcpy(buffer + prefix_len, entry->e_name, entry->e_name_len);
866 buffer[prefix_len + entry->e_name_len] = '\0';
872 errcode_t ext2fs_expand_extra_isize(ext2_filsys fs, ext2_ino_t ino,
873 struct ext2_inode_large *inode,
874 int new_extra_isize, int *ret,
877 struct ext2_inode *inode_buf = NULL;
878 struct ext2_ext_attr_header *header = NULL;
879 struct ext2_ext_attr_entry *entry = NULL, *last = NULL;
880 struct ext2_attr_ibody_find is = {
882 .s = { .not_found = EXT2_ET_EA_NO_SPACE, },
884 struct ext2_attr_block_find bs = {
886 .s = { .not_found = EXT2_ET_EA_NO_SPACE, },
888 char *start, *end, *block_buf = NULL, *buffer =NULL, *b_entry_name=NULL;
889 int total_ino = 0, total_blk, free, offs, tried_min_extra_isize = 0;
890 int s_min_extra_isize = fs->super->s_min_extra_isize;
894 *needed_size = new_extra_isize;
895 error = ext2fs_get_mem(fs->blocksize, &block_buf);
900 error = ext2fs_get_mem(EXT2_INODE_SIZE(fs->super), &inode_buf);
904 error = ext2fs_read_inode_full(fs, ino, inode_buf,
905 EXT2_INODE_SIZE(fs->super));
909 inode = (struct ext2_inode_large *)inode_buf;
913 if (inode->i_extra_isize >= new_extra_isize)
916 start = (char *)inode + EXT2_GOOD_OLD_INODE_SIZE + inode->i_extra_isize;
917 /* No extended attributes present */
918 if (IHDR(inode)->h_magic != EXT2_EXT_ATTR_MAGIC) {
920 EXT2_INODE_SIZE(fs->super) - EXT2_GOOD_OLD_INODE_SIZE -
921 inode->i_extra_isize);
922 inode->i_extra_isize = new_extra_isize;
928 start += sizeof(__u32);
929 end = (char *)inode + EXT2_INODE_SIZE(fs->super);
930 last = entry = (struct ext2_ext_attr_entry *)start;
932 /* Consider space takenup by magic number */
933 total_ino = sizeof(__u32);
934 free = ext2fs_attr_free_space(last, &offs, start, &total_ino);
936 /* Enough free space available in the inode for expansion */
937 if (free >= new_extra_isize) {
938 ext2fs_attr_shift_entries(entry,
939 inode->i_extra_isize - new_extra_isize,
941 EXT2_GOOD_OLD_INODE_SIZE +
943 start - sizeof(__u32), total_ino);
944 inode->i_extra_isize = new_extra_isize;
950 if (inode->i_file_acl) {
951 error = ext2fs_read_ext_attr(fs, inode->i_file_acl, block_buf);
955 header = BHDR(block_buf);
956 if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
957 error = EXT2_ET_EA_BAD_MAGIC;
960 end = block_buf + fs->blocksize;
961 last = entry = (struct ext2_ext_attr_entry *)(header+1);
962 start = (char *)entry;
964 free = ext2fs_attr_free_space(last, &offs, start, &total_blk);
965 if (free < new_extra_isize) {
966 if (!tried_min_extra_isize && s_min_extra_isize) {
967 tried_min_extra_isize++;
968 new_extra_isize = s_min_extra_isize;
972 *ret = EXT2_EXPAND_EISIZE_NOSPC;
973 error = EXT2_ET_EA_NO_SPACE;
977 if (ret && *ret == EXT2_EXPAND_EISIZE_UNSAFE) {
978 *ret = EXT2_EXPAND_EISIZE_NEW_BLOCK;
982 free = fs->blocksize;
985 while (new_extra_isize > 0) {
986 int offs, size, entry_size;
987 struct ext2_ext_attr_entry *small_entry = NULL;
988 struct ext2_attr_info i = {
992 unsigned int total_size, shift_bytes, temp = ~0U, extra_isize=0;
994 start = (char *)inode + EXT2_GOOD_OLD_INODE_SIZE +
995 inode->i_extra_isize + sizeof(__u32);
996 end = (char *)inode + EXT2_INODE_SIZE(fs->super);
997 last = (struct ext2_ext_attr_entry *)start;
999 /* Find the entry best suited to be pushed into EA block */
1001 for (; !EXT2_EXT_IS_LAST_ENTRY(last);
1002 last = EXT2_EXT_ATTR_NEXT(last)) {
1003 total_size = EXT2_EXT_ATTR_SIZE(last->e_value_size) +
1004 EXT2_EXT_ATTR_LEN(last->e_name_len);
1005 if (total_size <= free && total_size < temp) {
1006 if (total_size < new_extra_isize) {
1015 if (entry == NULL) {
1017 entry = small_entry;
1019 if (!tried_min_extra_isize &&
1020 s_min_extra_isize) {
1021 tried_min_extra_isize++;
1022 new_extra_isize = s_min_extra_isize;
1026 *ret = EXT2_EXPAND_EISIZE_NOSPC;
1027 error = EXT2_ET_EA_NO_SPACE;
1031 offs = entry->e_value_offs;
1032 size = entry->e_value_size;
1033 entry_size = EXT2_EXT_ATTR_LEN(entry->e_name_len);
1034 i.name_index = entry->e_name_index;
1035 error = ext2fs_get_mem(size, &buffer);
1038 error = ext2fs_get_mem(entry->e_name_len + 1, &b_entry_name);
1041 /* Save the entry name and the entry value */
1042 memcpy((char *)buffer, (char *)start + offs,
1043 EXT2_EXT_ATTR_SIZE(size));
1044 memcpy((char *)b_entry_name, (char *)entry->e_name,
1046 b_entry_name[entry->e_name_len] = '\0';
1047 i.name = b_entry_name;
1049 error = ext2fs_attr_ibody_find(fs, inode, &i, &is);
1053 error = ext2fs_attr_set_entry(fs, &i, &is.s);
1057 entry = (struct ext2_ext_attr_entry *)start;
1058 if (entry_size + EXT2_EXT_ATTR_SIZE(size) >= new_extra_isize)
1059 shift_bytes = new_extra_isize;
1061 shift_bytes = entry_size + EXT2_EXT_ATTR_SIZE(size);
1062 ext2fs_attr_shift_entries(entry,
1063 inode->i_extra_isize - shift_bytes,
1064 (char *)inode +EXT2_GOOD_OLD_INODE_SIZE+
1065 extra_isize + shift_bytes,
1066 start - sizeof(__u32),
1067 total_ino - entry_size);
1069 extra_isize += shift_bytes;
1070 new_extra_isize -= shift_bytes;
1072 *needed_size = new_extra_isize;
1073 inode->i_extra_isize = extra_isize;
1075 i.name = b_entry_name;
1078 error = ext2fs_attr_block_find(fs, (struct ext2_inode *)inode,
1083 /* Add entry which was removed from the inode into the block */
1084 error = ext2fs_attr_block_set(fs, (struct ext2_inode *)inode,
1091 error = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)inode,
1092 EXT2_INODE_SIZE(fs->super));
1095 ext2fs_free_mem(&inode_buf);
1097 ext2fs_free_mem(&block_buf);
1099 ext2fs_free_mem(&buffer);
1101 ext2fs_free_mem(&b_entry_name);