Whamcloud - gitweb
e2fsck: clean up xattr checking code, add test
[tools/e2fsprogs.git] / lib / ext2fs / ext_attr.c
1 /*
2  * ext_attr.c --- extended attribute blocks
3  *
4  * Copyright (C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org>
5  *
6  * Copyright (C) 2002 Theodore Ts'o.
7  *
8  * %Begin-Header%
9  * This file may be redistributed under the terms of the GNU Library
10  * General Public License, version 2.
11  * %End-Header%
12  */
13
14 #include "config.h"
15 #include <stdio.h>
16 #if HAVE_UNISTD_H
17 #include <unistd.h>
18 #endif
19 #include <string.h>
20 #include <time.h>
21 #include <errno.h>
22
23 #include "ext2_fs.h"
24 #include "ext2_ext_attr.h"
25
26 #include "ext2fs.h"
27
28 #define NAME_HASH_SHIFT 5
29 #define VALUE_HASH_SHIFT 16
30
31 /*
32  * ext2_xattr_hash_entry()
33  *
34  * Compute the hash of an extended attribute.
35  */
36 __u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry, void *data)
37 {
38         __u32 hash = 0;
39         char *name = ((char *) entry) + sizeof(struct ext2_ext_attr_entry);
40         int n;
41
42         for (n = 0; n < entry->e_name_len; n++) {
43                 hash = (hash << NAME_HASH_SHIFT) ^
44                        (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
45                        *name++;
46         }
47
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++);
56                 }
57         }
58
59         return hash;
60 }
61
62 #undef NAME_HASH_SHIFT
63 #undef VALUE_HASH_SHIFT
64
65 #define BLOCK_HASH_SHIFT 16
66 /*
67  * Re-compute the extended attribute hash value after an entry has changed.
68  */
69 static void ext2fs_attr_rehash(struct ext2_ext_attr_header *header,
70                                 struct ext2_ext_attr_entry *entry)
71 {
72         struct ext2_ext_attr_entry *here;
73         __u32 hash = 0;
74
75         entry->e_hash = ext2fs_ext_attr_hash_entry(entry, (char *)header +
76                                                    entry->e_value_offs);
77
78         here = ENTRY(header+1);
79         while (!EXT2_EXT_IS_LAST_ENTRY(here)) {
80                 if (!here->e_hash) {
81                         /* Block is not shared if an entry's hash value == 0 */
82                         hash = 0;
83                         break;
84                 }
85                 hash = (hash << BLOCK_HASH_SHIFT) ^
86                        (hash >> (8*sizeof(hash) - BLOCK_HASH_SHIFT)) ^
87                        here->e_hash;
88                 here = EXT2_EXT_ATTR_NEXT(here);
89         }
90         header->h_hash = hash;
91 }
92
93 errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block, void *buf)
94 {
95         errcode_t       retval;
96
97         retval = io_channel_read_blk64(fs->io, block, 1, buf);
98         if (retval)
99                 return retval;
100 #ifdef WORDS_BIGENDIAN
101         ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1);
102 #endif
103         return 0;
104 }
105
106 errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf)
107 {
108         return ext2fs_read_ext_attr2(fs, block, buf);
109 }
110
111 errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block, void *inbuf)
112 {
113         errcode_t       retval;
114         char            *write_buf;
115 #ifdef WORDS_BIGENDIAN
116         char            *buf = NULL;
117
118         retval = ext2fs_get_mem(fs->blocksize, &buf);
119         if (retval)
120                 return retval;
121         write_buf = buf;
122         ext2fs_swap_ext_attr(buf, inbuf, fs->blocksize, 1);
123 #else
124         write_buf = (char *) inbuf;
125 #endif
126         retval = io_channel_write_blk64(fs->io, block, 1, write_buf);
127 #ifdef WORDS_BIGENDIAN
128         ext2fs_free_mem(&buf);
129 #endif
130         if (!retval)
131                 ext2fs_mark_changed(fs);
132         return retval;
133 }
134
135 errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf)
136 {
137         return ext2fs_write_ext_attr2(fs, block, inbuf);
138 }
139
140 /*
141  * This function adjusts the reference count of the EA block.
142  */
143 errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk,
144                                     char *block_buf, int adjust,
145                                     __u32 *newcount)
146 {
147         errcode_t       retval;
148         struct ext2_ext_attr_header *header;
149         char    *buf = 0;
150
151         if ((blk >= ext2fs_blocks_count(fs->super)) ||
152             (blk < fs->super->s_first_data_block))
153                 return EXT2_ET_BAD_EA_BLOCK_NUM;
154
155         if (!block_buf) {
156                 retval = ext2fs_get_mem(fs->blocksize, &buf);
157                 if (retval)
158                         return retval;
159                 block_buf = buf;
160         }
161
162         retval = ext2fs_read_ext_attr2(fs, blk, block_buf);
163         if (retval)
164                 goto errout;
165
166         header = BHDR(block_buf);
167         if (header->h_magic != EXT2_EXT_ATTR_MAGIC)
168                 return EXT2_ET_EA_BAD_MAGIC;
169
170         header->h_refcount += adjust;
171         if (newcount)
172                 *newcount = header->h_refcount;
173
174         retval = ext2fs_write_ext_attr2(fs, blk, block_buf);
175         if (retval)
176                 goto errout;
177
178 errout:
179         if (buf)
180                 ext2fs_free_mem(&buf);
181         return retval;
182 }
183
184 errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
185                                         char *block_buf, int adjust,
186                                         __u32 *newcount)
187 {
188         return ext2fs_adjust_ea_refcount2(fs, blk, block_buf, adjust, newcount);
189 }
190
191 struct ext2_attr_info {
192         int name_index;
193         const char *name;
194         const char *value;
195         int value_len;
196 };
197
198 struct ext2_attr_search {
199         struct ext2_ext_attr_entry *first;
200         char *base;
201         char *end;
202         struct ext2_ext_attr_entry *here;
203         int not_found;
204 };
205
206 struct ext2_attr_ibody_find {
207         ext2_ino_t ino;
208         struct ext2_attr_search s;
209 };
210
211 struct ext2_attr_block_find {
212         struct ext2_attr_search s;
213         char *block;
214 };
215
216 void ext2fs_attr_shift_entries(struct ext2_ext_attr_entry *entry,
217                                int value_offs_shift, char *to,
218                                char *from, int n)
219 {
220         struct ext2_ext_attr_entry *last = entry;
221
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 +
226                                                         value_offs_shift;
227                 }
228         }
229         /* Shift the entries by n bytes and zero freed space in inode */
230         memmove(to, from, n);
231         if (to > from)
232                 memset(from, 0, to - from);
233 }
234
235 /*
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().
239  */
240 int ext2fs_attr_free_space(struct ext2_ext_attr_entry *last,
241                            int *min_offs, char *base, int *total)
242 {
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)
248                                 *min_offs = offs;
249                 }
250         }
251
252         return *min_offs - ((char *)last - base) - sizeof(__u32);
253 }
254
255 static errcode_t ext2fs_attr_check_names(struct ext2_ext_attr_entry *entry,
256                                          char *end)
257 {
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;
262                 entry = next;
263         }
264         return 0;
265 }
266
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)
273 {
274         struct ext2_ext_attr_entry *entry;
275         int name_len;
276         int cmp = 1;
277
278         if (name == NULL)
279                 return EXT2_ET_EA_BAD_NAME;
280
281         name_len = strlen(name);
282         entry = *pentry;
283         for (; !EXT2_EXT_IS_LAST_ENTRY(entry);
284                 entry = EXT2_EXT_ATTR_NEXT(entry)) {
285                 cmp = name_index - entry->e_name_index;
286                 if (!cmp)
287                         cmp = name_len - entry->e_name_len;
288                 if (!cmp)
289                         cmp = memcmp(name, entry->e_name, name_len);
290                 if (cmp <= 0 && (sorted || cmp == 0))
291                         break;
292         }
293         *pentry = entry;
294
295         return cmp ? EXT2_ET_EA_NAME_NOT_FOUND : 0;
296 }
297
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)
301 {
302         struct ext2_ext_attr_header *header;
303         errcode_t error;
304
305         if (inode->i_file_acl) {
306                 /* The inode already has an extended attribute block. */
307                 error = ext2fs_get_mem(fs->blocksize, &bs->block);
308                 if (error)
309                         return error;
310                 error = ext2fs_read_ext_attr(fs, inode->i_file_acl, bs->block);
311                 if (error)
312                         goto cleanup;
313
314                 header = BHDR(bs->block);
315                 if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
316                         error = EXT2_ET_EA_BAD_MAGIC;
317                         goto cleanup;
318                 }
319
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)
328                         goto cleanup;
329                 bs->s.not_found = error;
330         }
331         error = 0;
332
333 cleanup:
334         if (error && bs->block)
335                 ext2fs_free_mem(&bs->block);
336         return error;
337 }
338
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)
343 {
344         errcode_t error;
345
346         if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE)
347                 return 0;
348
349         if (inode->i_extra_isize == 0)
350                 return 0;
351
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);
358                 if (error)
359                         return error;
360                 /* Find the named attribute. */
361                 error = ext2fs_attr_find_entry(&is->s.here, i->name_index,
362                                                i->name, is->s.end -
363                                                (char *)is->s.base, 0);
364                 if (error && error != EXT2_ET_EA_NAME_NOT_FOUND)
365                         return error;
366                 is->s.not_found = error;
367         }
368
369         return 0;
370 }
371
372 static errcode_t ext2fs_attr_set_entry(ext2_filsys fs, struct ext2_attr_info *i,
373                                        struct ext2_attr_search *s)
374 {
375         struct ext2_ext_attr_entry *last;
376         int free, min_offs = s->end - s->base, name_len = strlen(i->name);
377
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;
383
384                         if (offs < min_offs)
385                                 min_offs = offs;
386                 }
387         }
388         free = min_offs - ((char *)last - s->base) - sizeof(__u32);
389
390         if (!s->not_found) {
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);
394                 }
395                 free += EXT2_EXT_ATTR_LEN(name_len);
396         }
397         if (i->value) {
398                 if (free < EXT2_EXT_ATTR_LEN(name_len) +
399                            EXT2_EXT_ATTR_SIZE(i->value_len))
400                         return EXT2_ET_EA_NO_SPACE;
401         }
402
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);
407
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);
413         } else {
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);
419
420                         if (i->value &&
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);
428                                 return 0;
429                         }
430
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;
436                         min_offs += size;
437
438                         /* Adjust all value offsets. */
439                         last = s->first;
440                         while (!EXT2_EXT_IS_LAST_ENTRY(last)) {
441                                 int o = last->e_value_offs;
442
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);
447                         }
448                 }
449                 if (!i->value) {
450                         /* Remove the old name. */
451                         int size = EXT2_EXT_ATTR_LEN(name_len);
452
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);
457                 }
458         }
459
460         if (i->value) {
461                 /* Insert the new value. */
462                 s->here->e_value_size = i->value_len;
463                 if (i->value_len) {
464                         int size = EXT2_EXT_ATTR_SIZE(i->value_len);
465                         char *val = s->base + min_offs - size;
466
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);
471                 }
472         }
473
474         return 0;
475 }
476
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)
480 {
481         struct ext2_attr_search *s = &bs->s;
482         char *new_buf = NULL, *old_block = NULL;
483         blk_t blk;
484         int clear_flag = 0;
485         errcode_t error;
486
487         if (i->value && i->value_len > fs->blocksize)
488                 return EXT2_ET_EA_NO_SPACE;
489
490         if (s->base) {
491                 if (BHDR(s->base)->h_refcount != 1) {
492                         int offset = (char *)s->here - bs->block;
493
494                         /* Decrement the refcount of the shared block */
495                         old_block = s->base;
496                         BHDR(s->base)->h_refcount -= 1;
497
498                         error = ext2fs_get_mem(fs->blocksize, &s->base);
499                         if (error)
500                                 goto cleanup;
501                         clear_flag = 1;
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;
507                 }
508         } else {
509                 error = ext2fs_get_mem(fs->blocksize, &s->base);
510                 if (error)
511                         goto cleanup;
512                 clear_flag = 1;
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;
520         }
521
522         error = ext2fs_attr_set_entry(fs, i, s);
523         if (error)
524                 goto cleanup;
525
526         if (!EXT2_EXT_IS_LAST_ENTRY(s->first))
527                 ext2fs_attr_rehash(BHDR(s->base), s->here);
528
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 */
532                         new_buf = bs->block;
533                         blk = inode->i_file_acl;
534                         error = ext2fs_write_ext_attr(fs, blk, s->base);
535                         if (error)
536                                 goto cleanup;
537                 } else {
538                         /* We need to allocate a new block */
539                         error = ext2fs_new_block(fs, 0, 0, &blk);
540                         if (error)
541                                 goto cleanup;
542                         ext2fs_block_alloc_stats(fs, blk, 1);
543                         error = ext2fs_write_ext_attr(fs, blk, s->base);
544                         if (error)
545                                 goto cleanup;
546                         new_buf = s->base;
547                         if (old_block) {
548                                 BHDR(s->base)->h_refcount -= 1;
549                                 error = ext2fs_write_ext_attr(fs,
550                                                               inode->i_file_acl,
551                                                               s->base);
552                                 if (error)
553                                         goto cleanup;
554                         }
555                 }
556         }
557
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;
563
564 cleanup:
565         if (clear_flag)
566                 ext2fs_free_mem(&s->base);
567         return 0;
568 }
569
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)
574 {
575         struct ext2_attr_search *s = &is->s;
576         errcode_t error;
577
578         if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE)
579                 return EXT2_ET_EA_NO_SPACE;
580
581         error = ext2fs_attr_set_entry(fs, i, s);
582         if (error)
583                 return error;
584
585         if (!EXT2_EXT_IS_LAST_ENTRY(s->first))
586                 IHDR(inode)->h_magic = EXT2_EXT_ATTR_MAGIC;
587         else
588                 IHDR(inode)->h_magic = 0;
589
590         return ext2fs_write_inode_full(fs, is->ino, (struct ext2_inode *)inode,
591                                        EXT2_INODE_SIZE(fs->super));
592 }
593
594 static struct {
595         char str[28];
596         int len;
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)},
612         { "", 0 }
613 };
614
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)
619 {
620         struct ext2_inode_large *inode_large = NULL;
621         struct ext2_attr_info i = {
622                 .name_index = name_index,
623                 .name = name,
624                 .value = value,
625                 .value_len = value_len,
626         };
627         struct ext2_attr_ibody_find is = {
628                 .ino = ino,
629                 .s = { .not_found = -ENODATA, },
630         };
631         struct ext2_attr_block_find bs = {
632                 .s = { .not_found = -ENODATA, },
633         };
634         errcode_t error;
635
636         if (!name)
637                 return EXT2_ET_EA_BAD_NAME;
638         if (strlen(name) > 255)
639                 return EXT2_ET_EA_NAME_TOO_BIG;
640
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;
645
646         if (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE) {
647                 inode_large = (struct ext2_inode_large *)inode;
648
649                 error = ext2fs_attr_ibody_find(fs, inode_large, &i, &is);
650                 if (error)
651                         goto cleanup;
652         }
653         if (is.s.not_found) {
654                 error = ext2fs_attr_block_find(fs, inode, &i, &bs);
655                 if (error)
656                         goto cleanup;
657         }
658
659         if (is.s.not_found && bs.s.not_found) {
660                 error = EXT2_ET_EA_NAME_NOT_FOUND;
661                 if (flags & XATTR_REPLACE)
662                         goto cleanup;
663                 error = 0;
664                 if (!value)
665                         goto cleanup;
666         } else {
667                 error = EXT2_ET_EA_NAME_EXISTS;
668                 if (flags & XATTR_CREATE)
669                         goto cleanup;
670         }
671
672         if (!value) {
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);
678         } else {
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) {
682                         i.value = NULL;
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);
686                         if (error)
687                                 goto cleanup;
688                         if (!is.s.not_found) {
689                                 i.value = NULL;
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);
694                         }
695                 }
696         }
697
698 cleanup:
699         return error;
700 }
701
702 static errcode_t ext2fs_attr_check_block(ext2_filsys fs, char *buffer)
703 {
704         if (BHDR(buffer)->h_magic != (EXT2_EXT_ATTR_MAGIC) ||
705             BHDR(buffer)->h_blocks != 1)
706                 return EXT2_ET_EA_BAD_MAGIC;
707
708         return ext2fs_attr_check_names((struct ext2_ext_attr_entry *)
709                                        (BHDR(buffer) + 1),
710                                        buffer + fs->blocksize);
711 }
712
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,
716                                        int *easize)
717 {
718         struct ext2_ext_attr_header *header = NULL;
719         struct ext2_ext_attr_entry *entry;
720         char *block_buf = NULL;
721         errcode_t error;
722
723         error = EXT2_ET_EA_NAME_NOT_FOUND;
724         if (!inode->i_file_acl)
725                 goto cleanup;
726
727         error = ext2fs_get_mem(fs->blocksize, &block_buf);
728         if (error)
729                 return error;
730         error = ext2fs_read_ext_attr(fs, inode->i_file_acl, block_buf);
731         if (error)
732                 goto cleanup;
733
734         error = ext2fs_attr_check_block(fs, block_buf);
735         if (error)
736                 goto cleanup;
737
738         header = BHDR(block_buf);
739         entry = (struct ext2_ext_attr_entry *)(header + 1);
740         error = ext2fs_attr_find_entry(&entry, name_index, name,
741                                        fs->blocksize, 1);
742         if (error)
743                 goto cleanup;
744         if (easize)
745                 *easize = entry->e_value_size;
746         if (buffer) {
747                 if (entry->e_value_size > buffer_size) {
748                         error = EXT2_ET_EA_TOO_BIG;
749                         goto cleanup;
750                 }
751                 memcpy(buffer, block_buf + entry->e_value_offs,
752                        entry->e_value_size);
753                 error = 0;
754         }
755
756 cleanup:
757         if (block_buf)
758                 ext2fs_free_mem(&block_buf);
759         return error;
760 }
761
762 static errcode_t ext2fs_attr_check_ibody(ext2_filsys fs,
763                                          struct ext2_inode_large *inode)
764 {
765         const int inode_size = EXT2_INODE_SIZE(fs->super);
766
767         if (inode_size == EXT2_GOOD_OLD_INODE_SIZE)
768                 return EXT2_ET_EA_NAME_NOT_FOUND;
769
770         if (IHDR(inode)->h_magic != EXT2_EXT_ATTR_MAGIC)
771                 return EXT2_ET_EA_BAD_MAGIC;
772
773         return ext2fs_attr_check_names(&IHDR(inode)->h_first_entry[0],
774                                        (char *)inode + inode_size);
775 }
776
777
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,
782                                        int *easize)
783 {
784         struct ext2_ext_attr_entry *entry;
785         int error;
786
787         error = ext2fs_attr_check_ibody(fs, inode);
788         if (error)
789                 return error;
790
791         entry = &IHDR(inode)->h_first_entry[0];
792
793         error = ext2fs_attr_find_entry(&entry, name_index, name,
794                                 (char *)inode + EXT2_INODE_SIZE(fs->super) -
795                                 (char *)entry, 0);
796         if (error)
797                 goto cleanup;
798         if (easize)
799                 *easize = entry->e_value_size;
800         if (buffer) {
801                 if (entry->e_value_size > buffer_size) {
802                         error = EXT2_ET_EA_TOO_BIG;
803                         goto cleanup;
804                 }
805                 memcpy(buffer, (char *)&IHDR(inode)->h_first_entry[0] +
806                                entry->e_value_offs, entry->e_value_size);
807         }
808
809 cleanup:
810         return error;
811 }
812
813
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)
817 {
818         errcode_t error;
819
820         error = ext2fs_attr_ibody_get(fs, (struct ext2_inode_large *)inode,
821                                       name_index, name, buffer, buffer_size,
822                                       easize);
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);
826
827         return error;
828 }
829
830 int ext2fs_attr_get_next_attr(struct ext2_ext_attr_entry *entry, int name_index,
831                               char *buffer, int buffer_size, int start)
832 {
833         const int prefix_len = ext2_attr_index_prefix[name_index].len;
834         int total_len;
835
836         if (!start && !EXT2_EXT_IS_LAST_ENTRY(entry))
837                 entry = EXT2_EXT_ATTR_NEXT(entry);
838
839         for (; !EXT2_EXT_IS_LAST_ENTRY(entry);
840              entry = EXT2_EXT_ATTR_NEXT(entry)) {
841                 if (!name_index)
842                         break;
843                 if (name_index == entry->e_name_index)
844                         break;
845         }
846         if (EXT2_EXT_IS_LAST_ENTRY(entry))
847                 return 0;
848
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,
852                        prefix_len);
853                 memcpy(buffer + prefix_len, entry->e_name, entry->e_name_len);
854                 buffer[prefix_len + entry->e_name_len] = '\0';
855         }
856
857         return total_len;
858 }
859
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,
863                                     int *needed_size)
864 {
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 = {
869                 .ino = ino,
870                 .s = { .not_found = EXT2_ET_EA_NO_SPACE, },
871         };
872         struct ext2_attr_block_find bs = {
873                 .s = { .not_found = EXT2_ET_EA_NO_SPACE, },
874         };
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;
878         errcode_t error = 0;
879
880         if (needed_size)
881                 *needed_size = new_extra_isize;
882         error = ext2fs_get_mem(fs->blocksize, &block_buf);
883         if (error)
884                 return error;
885
886         if (inode == NULL) {
887                 error = ext2fs_get_mem(EXT2_INODE_SIZE(fs->super), &inode_buf);
888                 if (error)
889                         goto cleanup;
890
891                 error = ext2fs_read_inode_full(fs, ino, inode_buf,
892                                                EXT2_INODE_SIZE(fs->super));
893                 if (error)
894                         goto cleanup;
895
896                 inode = (struct ext2_inode_large *)inode_buf;
897         }
898
899 retry:
900         if (inode->i_extra_isize >= new_extra_isize)
901                 goto cleanup;
902
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) {
906                 memset(start, 0,
907                        EXT2_INODE_SIZE(fs->super) - EXT2_GOOD_OLD_INODE_SIZE -
908                        inode->i_extra_isize);
909                 inode->i_extra_isize = new_extra_isize;
910                 if (needed_size)
911                         *needed_size = 0;
912                 goto write_inode;
913         }
914
915         start += sizeof(__u32);
916         end = (char *)inode + EXT2_INODE_SIZE(fs->super);
917         last = entry = (struct ext2_ext_attr_entry *)start;
918         offs = end - start;
919         /* Consider space takenup by magic number */
920         total_ino = sizeof(__u32);
921         free = ext2fs_attr_free_space(last, &offs, start, &total_ino);
922
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,
927                                         (char *)inode +
928                                         EXT2_GOOD_OLD_INODE_SIZE +
929                                         new_extra_isize,
930                                         start - sizeof(__u32), total_ino);
931                 inode->i_extra_isize = new_extra_isize;
932                 if (needed_size)
933                         *needed_size = 0;
934                 goto write_inode;
935         }
936
937         if (inode->i_file_acl) {
938                 error = ext2fs_read_ext_attr(fs, inode->i_file_acl, block_buf);
939                 if (error)
940                         goto cleanup;
941
942                 header = BHDR(block_buf);
943                 if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
944                         error = EXT2_ET_EA_BAD_MAGIC;
945                         goto cleanup;
946                 }
947                 end = block_buf + fs->blocksize;
948                 last = entry = (struct ext2_ext_attr_entry *)(header+1);
949                 start = (char *)entry;
950                 offs = end - start;
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;
956                                 goto retry;
957                         }
958                         if (ret)
959                                 *ret = EXT2_EXPAND_EISIZE_NOSPC;
960                         error = EXT2_ET_EA_NO_SPACE;
961                         goto cleanup;
962                 }
963         } else {
964                 if (ret && *ret == EXT2_EXPAND_EISIZE_UNSAFE) {
965                         *ret = EXT2_EXPAND_EISIZE_NEW_BLOCK;
966                         error = 0;
967                         goto cleanup;
968                 }
969                 free = fs->blocksize;
970         }
971
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 = {
976                         .value = NULL,
977                         .value_len = 0,
978                 };
979                 unsigned int total_size, shift_bytes, temp = ~0U, extra_isize=0;
980
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;
985
986                 /* Find the entry best suited to be pushed into EA block */
987                 entry = NULL;
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) {
994                                         small_entry = last;
995                                 } else {
996                                         entry = last;
997                                         temp = total_size;
998                                 }
999                         }
1000                 }
1001
1002                 if (entry == NULL) {
1003                         if (small_entry) {
1004                                 entry = small_entry;
1005                         } else {
1006                                 if (!tried_min_extra_isize &&
1007                                     s_min_extra_isize) {
1008                                         tried_min_extra_isize++;
1009                                         new_extra_isize = s_min_extra_isize;
1010                                         goto retry;
1011                                 }
1012                                 if (ret)
1013                                         *ret = EXT2_EXPAND_EISIZE_NOSPC;
1014                                 error = EXT2_ET_EA_NO_SPACE;
1015                                 goto cleanup;
1016                         }
1017                 }
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);
1023                 if (error)
1024                         goto cleanup;
1025                 error = ext2fs_get_mem(entry->e_name_len + 1, &b_entry_name);
1026                 if (error)
1027                         goto cleanup;
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,
1032                        entry->e_name_len);
1033                 b_entry_name[entry->e_name_len] = '\0';
1034                 i.name = b_entry_name;
1035
1036                 error = ext2fs_attr_ibody_find(fs, inode, &i, &is);
1037                 if (error)
1038                         goto cleanup;
1039
1040                 error = ext2fs_attr_set_entry(fs, &i, &is.s);
1041                 if (error)
1042                         goto cleanup;
1043
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;
1047                 else
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);
1055
1056                 extra_isize += shift_bytes;
1057                 new_extra_isize -= shift_bytes;
1058                 if (needed_size)
1059                         *needed_size = new_extra_isize;
1060                 inode->i_extra_isize = extra_isize;
1061
1062                 i.name = b_entry_name;
1063                 i.value = buffer;
1064                 i.value_len = size;
1065                 error = ext2fs_attr_block_find(fs, (struct ext2_inode *)inode,
1066                                                &i, &bs);
1067                 if (error)
1068                         goto cleanup;
1069
1070                 /* Add entry which was removed from the inode into the block */
1071                 error = ext2fs_attr_block_set(fs, (struct ext2_inode *)inode,
1072                                               &i, &bs);
1073                 if (error)
1074                         goto cleanup;
1075         }
1076
1077 write_inode:
1078         error = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)inode,
1079                                         EXT2_INODE_SIZE(fs->super));
1080 cleanup:
1081         if (inode_buf)
1082                 ext2fs_free_mem(&inode_buf);
1083         if (block_buf)
1084                 ext2fs_free_mem(&block_buf);
1085         if (buffer)
1086                 ext2fs_free_mem(&buffer);
1087         if (b_entry_name)
1088                 ext2fs_free_mem(&b_entry_name);
1089
1090         return error;
1091 }