Whamcloud - gitweb
91a7f1636de4d9ffcaea45c2f6533fa9b97b9e67
[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 static errcode_t ext2fs_attr_find_entry(struct ext2_ext_attr_entry **pentry,
268                                         int name_index, const char *name,
269                                         int size, int sorted)
270 {
271         struct ext2_ext_attr_entry *entry;
272         int name_len;
273         int cmp = 1;
274
275         if (name == NULL)
276                 return EXT2_ET_EA_BAD_NAME;
277
278         name_len = strlen(name);
279         entry = *pentry;
280         for (; !EXT2_EXT_IS_LAST_ENTRY(entry);
281                 entry = EXT2_EXT_ATTR_NEXT(entry)) {
282                 cmp = name_index - entry->e_name_index;
283                 if (!cmp)
284                         cmp = name_len - entry->e_name_len;
285                 if (!cmp)
286                         cmp = memcmp(name, entry->e_name, name_len);
287                 if (cmp <= 0 && (sorted || cmp == 0))
288                         break;
289         }
290         *pentry = entry;
291
292         return cmp ? EXT2_ET_EA_NAME_NOT_FOUND : 0;
293 }
294
295 static errcode_t ext2fs_attr_block_find(ext2_filsys fs,struct ext2_inode *inode,
296                                         struct ext2_attr_info *i,
297                                         struct ext2_attr_block_find *bs)
298 {
299         struct ext2_ext_attr_header *header;
300         errcode_t error;
301
302         if (inode->i_file_acl) {
303                 /* The inode already has an extended attribute block. */
304                 error = ext2fs_get_mem(fs->blocksize, &bs->block);
305                 if (error)
306                         return error;
307                 error = ext2fs_read_ext_attr(fs, inode->i_file_acl, bs->block);
308                 if (error)
309                         goto cleanup;
310
311                 header = BHDR(bs->block);
312                 if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
313                         error = EXT2_ET_EA_BAD_MAGIC;
314                         goto cleanup;
315                 }
316
317                 /* Find the named attribute. */
318                 bs->s.base = bs->block;
319                 bs->s.first = (struct ext2_ext_attr_entry *)(header + 1);
320                 bs->s.end = bs->block + fs->blocksize;
321                 bs->s.here = bs->s.first;
322                 error = ext2fs_attr_find_entry(&bs->s.here, i->name_index,
323                                                i->name, fs->blocksize, 1);
324                 if (error && error != EXT2_ET_EA_NAME_NOT_FOUND)
325                         goto cleanup;
326                 bs->s.not_found = error;
327         }
328         error = 0;
329
330 cleanup:
331         if (error && bs->block)
332                 ext2fs_free_mem(&bs->block);
333         return error;
334 }
335
336 static errcode_t ext2fs_attr_ibody_find(ext2_filsys fs,
337                                         struct ext2_inode_large *inode,
338                                         struct ext2_attr_info *i,
339                                         struct ext2_attr_ibody_find *is)
340 {
341         __u32 *eamagic;
342         char *start;
343         errcode_t error;
344
345         if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE)
346                 return 0;
347
348         if (inode->i_extra_isize == 0)
349                 return 0;
350         eamagic = IHDR(inode);
351
352         start = (char *)inode + EXT2_GOOD_OLD_INODE_SIZE +
353                         inode->i_extra_isize + sizeof(__u32);
354         is->s.first = (struct ext2_ext_attr_entry *)start;
355         is->s.base = start;
356         is->s.here = is->s.first;
357         is->s.end = (char *)inode + EXT2_INODE_SIZE(fs->super);
358         if (*eamagic == EXT2_EXT_ATTR_MAGIC) {
359                 error = ext2fs_attr_check_names((struct ext2_ext_attr_entry *)
360                                                 start, is->s.end);
361                 if (error)
362                         return error;
363                 /* Find the named attribute. */
364                 error = ext2fs_attr_find_entry(&is->s.here, i->name_index,
365                                                i->name, is->s.end -
366                                                (char *)is->s.base, 0);
367                 if (error && error != EXT2_ET_EA_NAME_NOT_FOUND)
368                         return error;
369                 is->s.not_found = error;
370         }
371
372         return 0;
373 }
374
375 static errcode_t ext2fs_attr_set_entry(ext2_filsys fs, struct ext2_attr_info *i,
376                                        struct ext2_attr_search *s)
377 {
378         struct ext2_ext_attr_entry *last;
379         int free, min_offs = s->end - s->base, name_len = strlen(i->name);
380
381         /* Compute min_offs and last. */
382         for (last = s->first; !EXT2_EXT_IS_LAST_ENTRY(last);
383              last = EXT2_EXT_ATTR_NEXT(last)) {
384                 if (!last->e_value_block && last->e_value_size) {
385                         int offs = last->e_value_offs;
386
387                         if (offs < min_offs)
388                                 min_offs = offs;
389                 }
390         }
391         free = min_offs - ((char *)last - s->base) - sizeof(__u32);
392
393         if (!s->not_found) {
394                 if (!s->here->e_value_block && s->here->e_value_size) {
395                         int size = s->here->e_value_size;
396                         free += EXT2_EXT_ATTR_SIZE(size);
397                 }
398                 free += EXT2_EXT_ATTR_LEN(name_len);
399         }
400         if (i->value) {
401                 if (free < EXT2_EXT_ATTR_LEN(name_len) +
402                            EXT2_EXT_ATTR_SIZE(i->value_len))
403                         return EXT2_ET_EA_NO_SPACE;
404         }
405
406         if (i->value && s->not_found) {
407                 /* Insert the new name. */
408                 int size = EXT2_EXT_ATTR_LEN(name_len);
409                 int rest = (char *)last - (char *)s->here + sizeof(__u32);
410
411                 memmove((char *)s->here + size, s->here, rest);
412                 memset(s->here, 0, size);
413                 s->here->e_name_index = i->name_index;
414                 s->here->e_name_len = name_len;
415                 memcpy(s->here->e_name, i->name, name_len);
416         } else {
417                 if (!s->here->e_value_block && s->here->e_value_size) {
418                         char *first_val = s->base + min_offs;
419                         int offs = s->here->e_value_offs;
420                         char *val = s->base + offs;
421                         int size = EXT2_EXT_ATTR_SIZE(s->here->e_value_size);
422
423                         if (i->value &&
424                             size == EXT2_EXT_ATTR_SIZE(i->value_len)) {
425                                 /* The old and the new value have the same
426                                    size. Just replace. */
427                                 s->here->e_value_size = i->value_len;
428                                 memset(val + size - EXT2_EXT_ATTR_PAD, 0,
429                                        EXT2_EXT_ATTR_PAD); /* Clear pad bytes */
430                                 memcpy(val, i->value, i->value_len);
431                                 return 0;
432                         }
433
434                         /* Remove the old value. */
435                         memmove(first_val + size, first_val, val - first_val);
436                         memset(first_val, 0, size);
437                         s->here->e_value_size = 0;
438                         s->here->e_value_offs = 0;
439                         min_offs += size;
440
441                         /* Adjust all value offsets. */
442                         last = s->first;
443                         while (!EXT2_EXT_IS_LAST_ENTRY(last)) {
444                                 int o = last->e_value_offs;
445
446                                 if (!last->e_value_block &&
447                                     last->e_value_size && o < offs)
448                                         last->e_value_offs = o + size;
449                                 last = EXT2_EXT_ATTR_NEXT(last);
450                         }
451                 }
452                 if (!i->value) {
453                         /* Remove the old name. */
454                         int size = EXT2_EXT_ATTR_LEN(name_len);
455
456                         last = ENTRY((char *)last - size);
457                         memmove((char *)s->here, (char *)s->here + size,
458                                 (char *)last - (char *)s->here + sizeof(__u32));
459                         memset(last, 0, size);
460                 }
461         }
462
463         if (i->value) {
464                 /* Insert the new value. */
465                 s->here->e_value_size = i->value_len;
466                 if (i->value_len) {
467                         int size = EXT2_EXT_ATTR_SIZE(i->value_len);
468                         char *val = s->base + min_offs - size;
469
470                         s->here->e_value_offs = min_offs - size;
471                         memset(val + size - EXT2_EXT_ATTR_PAD, 0,
472                                EXT2_EXT_ATTR_PAD); /* Clear the pad bytes. */
473                         memcpy(val, i->value, i->value_len);
474                 }
475         }
476
477         return 0;
478 }
479
480 static errcode_t ext2fs_attr_block_set(ext2_filsys fs, struct ext2_inode *inode,
481                                        struct ext2_attr_info *i,
482                                        struct ext2_attr_block_find *bs)
483 {
484         struct ext2_attr_search *s = &bs->s;
485         char *new_buf = NULL, *old_block = NULL;
486         blk_t blk;
487         int clear_flag = 0;
488         errcode_t error;
489
490         if (i->value && i->value_len > fs->blocksize)
491                 return EXT2_ET_EA_NO_SPACE;
492
493         if (s->base) {
494                 if (BHDR(s->base)->h_refcount != 1) {
495                         int offset = (char *)s->here - bs->block;
496
497                         /* Decrement the refcount of the shared block */
498                         old_block = s->base;
499                         BHDR(s->base)->h_refcount -= 1;
500
501                         error = ext2fs_get_mem(fs->blocksize, &s->base);
502                         if (error)
503                                 goto cleanup;
504                         clear_flag = 1;
505                         memcpy(s->base, bs->block, fs->blocksize);
506                         s->first = ENTRY(BHDR(s->base)+1);
507                         BHDR(s->base)->h_refcount = 1;
508                         s->here = ENTRY(s->base + offset);
509                         s->end = s->base + fs->blocksize;
510                 }
511         } else {
512                 error = ext2fs_get_mem(fs->blocksize, &s->base);
513                 if (error)
514                         goto cleanup;
515                 clear_flag = 1;
516                 memset(s->base, 0, fs->blocksize);
517                 BHDR(s->base)->h_magic = EXT2_EXT_ATTR_MAGIC;
518                 BHDR(s->base)->h_blocks = 1;
519                 BHDR(s->base)->h_refcount = 1;
520                 s->first = ENTRY(BHDR(s->base)+1);
521                 s->here = ENTRY(BHDR(s->base)+1);
522                 s->end = s->base + fs->blocksize;
523         }
524
525         error = ext2fs_attr_set_entry(fs, i, s);
526         if (error)
527                 goto cleanup;
528
529         if (!EXT2_EXT_IS_LAST_ENTRY(s->first))
530                 ext2fs_attr_rehash(BHDR(s->base), s->here);
531
532         if (!EXT2_EXT_IS_LAST_ENTRY(s->first)) {
533                 if (bs->block && bs->block == s->base) {
534                         /* We are modifying this block in-place */
535                         new_buf = bs->block;
536                         blk = inode->i_file_acl;
537                         error = ext2fs_write_ext_attr(fs, blk, s->base);
538                         if (error)
539                                 goto cleanup;
540                 } else {
541                         /* We need to allocate a new block */
542                         error = ext2fs_new_block(fs, 0, 0, &blk);
543                         if (error)
544                                 goto cleanup;
545                         ext2fs_block_alloc_stats(fs, blk, 1);
546                         error = ext2fs_write_ext_attr(fs, blk, s->base);
547                         if (error)
548                                 goto cleanup;
549                         new_buf = s->base;
550                         if (old_block) {
551                                 BHDR(s->base)->h_refcount -= 1;
552                                 error = ext2fs_write_ext_attr(fs,
553                                                               inode->i_file_acl,
554                                                               s->base);
555                                 if (error)
556                                         goto cleanup;
557                         }
558                 }
559         }
560
561         /* Update the i_blocks if we added a new EA block */
562         if (!inode->i_file_acl && new_buf)
563                 inode->i_blocks += fs->blocksize / 512;
564         /* Update the inode. */
565         inode->i_file_acl = new_buf ? blk : 0;
566
567 cleanup:
568         if (clear_flag)
569                 ext2fs_free_mem(&s->base);
570         return 0;
571 }
572
573 static errcode_t ext2fs_attr_ibody_set(ext2_filsys fs,
574                                        struct ext2_inode_large *inode,
575                                        struct ext2_attr_info *i,
576                                        struct ext2_attr_ibody_find *is)
577 {
578         __u32 *eamagic;
579         struct ext2_attr_search *s = &is->s;
580         errcode_t error;
581
582         if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE)
583                 return EXT2_ET_EA_NO_SPACE;
584
585         error = ext2fs_attr_set_entry(fs, i, s);
586         if (error)
587                 return error;
588
589         eamagic = IHDR(inode);
590         if (!EXT2_EXT_IS_LAST_ENTRY(s->first))
591                 *eamagic = EXT2_EXT_ATTR_MAGIC;
592         else
593                 *eamagic = 0;
594
595         return ext2fs_write_inode_full(fs, is->ino, (struct ext2_inode *)inode,
596                                        EXT2_INODE_SIZE(fs->super));
597 }
598
599 static struct {
600         char str[28];
601         int len;
602 } ext2_attr_index_prefix[] = {
603         [EXT2_ATTR_INDEX_USER] = { EXT2_ATTR_INDEX_USER_PREFIX,
604                                    sizeof(EXT2_ATTR_INDEX_USER_PREFIX) },
605         [EXT2_ATTR_INDEX_POSIX_ACL_ACCESS] = {
606                 EXT2_ATTR_INDEX_POSIX_ACL_ACCESS_PREFIX,
607                 sizeof(EXT2_ATTR_INDEX_POSIX_ACL_ACCESS_PREFIX) },
608         [EXT2_ATTR_INDEX_POSIX_ACL_DEFAULT] = {
609                 EXT2_ATTR_INDEX_POSIX_ACL_DEFAULT_PREFIX,
610                 sizeof(EXT2_ATTR_INDEX_POSIX_ACL_DEFAULT_PREFIX) },
611         [EXT2_ATTR_INDEX_TRUSTED] = { EXT2_ATTR_INDEX_TRUSTED_PREFIX,
612                                       sizeof(EXT2_ATTR_INDEX_TRUSTED_PREFIX) },
613         [EXT2_ATTR_INDEX_LUSTRE] = { EXT2_ATTR_INDEX_LUSTRE_PREFIX,
614                                      sizeof(EXT2_ATTR_INDEX_LUSTRE_PREFIX) },
615         [EXT2_ATTR_INDEX_SECURITY] = { EXT2_ATTR_INDEX_SECURITY_PREFIX,
616                                        sizeof(EXT2_ATTR_INDEX_SECURITY_PREFIX)},
617         { "", 0 }
618 };
619
620 errcode_t ext2fs_attr_set(ext2_filsys fs, ext2_ino_t ino,
621                           struct ext2_inode *inode,
622                           int name_index, const char *name, const char *value,
623                           int value_len, int flags)
624 {
625         struct ext2_inode_large *inode_large = NULL;
626         struct ext2_attr_info i = {
627                 .name_index = name_index,
628                 .name = name,
629                 .value = value,
630                 .value_len = value_len,
631         };
632         struct ext2_attr_ibody_find is = {
633                 .ino = ino,
634                 .s = { .not_found = -ENODATA, },
635         };
636         struct ext2_attr_block_find bs = {
637                 .s = { .not_found = -ENODATA, },
638         };
639         errcode_t error;
640
641         if (!name)
642                 return EXT2_ET_EA_BAD_NAME;
643         if (strlen(name) > 255)
644                 return EXT2_ET_EA_NAME_TOO_BIG;
645
646         /* If the prefix is still present, skip it */
647         if (strncmp(name, ext2_attr_index_prefix[name_index].str,
648                     ext2_attr_index_prefix[name_index].len) == 0)
649                 i.name += ext2_attr_index_prefix[name_index].len;
650
651         if (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE) {
652                 inode_large = (struct ext2_inode_large *)inode;
653
654                 error = ext2fs_attr_ibody_find(fs, inode_large, &i, &is);
655                 if (error)
656                         goto cleanup;
657         }
658         if (is.s.not_found) {
659                 error = ext2fs_attr_block_find(fs, inode, &i, &bs);
660                 if (error)
661                         goto cleanup;
662         }
663
664         if (is.s.not_found && bs.s.not_found) {
665                 error = EXT2_ET_EA_NAME_NOT_FOUND;
666                 if (flags & XATTR_REPLACE)
667                         goto cleanup;
668                 error = 0;
669                 if (!value)
670                         goto cleanup;
671         } else {
672                 error = EXT2_ET_EA_NAME_EXISTS;
673                 if (flags & XATTR_CREATE)
674                         goto cleanup;
675         }
676
677         if (!value) {
678                 if (!is.s.not_found &&
679                     (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE))
680                         error = ext2fs_attr_ibody_set(fs, inode_large, &i, &is);
681                 else if (!bs.s.not_found)
682                         error = ext2fs_attr_block_set(fs, inode, &i, &bs);
683         } else {
684                 if (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE)
685                         error = ext2fs_attr_ibody_set(fs, inode_large, &i, &is);
686                 if (!error && !bs.s.not_found) {
687                         i.value = NULL;
688                         error = ext2fs_attr_block_set(fs, inode, &i, &bs);
689                 } else if (error == EXT2_ET_EA_NO_SPACE) {
690                         error = ext2fs_attr_block_set(fs, inode, &i, &bs);
691                         if (error)
692                                 goto cleanup;
693                         if (!is.s.not_found) {
694                                 i.value = NULL;
695                                 if (EXT2_INODE_SIZE(fs->super) >
696                                     EXT2_GOOD_OLD_INODE_SIZE)
697                                         error = ext2fs_attr_ibody_set(fs,
698                                                         inode_large, &i, &is);
699                         }
700                 }
701         }
702
703 cleanup:
704         return error;
705 }
706
707 static errcode_t ext2fs_attr_check_block(ext2_filsys fs, char *buffer)
708 {
709         if (BHDR(buffer)->h_magic != (EXT2_EXT_ATTR_MAGIC) ||
710             BHDR(buffer)->h_blocks != 1)
711                 return EXT2_ET_EA_BAD_MAGIC;
712
713         return ext2fs_attr_check_names((struct ext2_ext_attr_entry *)
714                                        (BHDR(buffer) + 1),
715                                        buffer + fs->blocksize);
716 }
717
718 static errcode_t ext2fs_attr_block_get(ext2_filsys fs, struct ext2_inode *inode,
719                                        int name_index, const char *name,
720                                        void *buffer, size_t buffer_size,
721                                        int *easize)
722 {
723         struct ext2_ext_attr_header *header = NULL;
724         struct ext2_ext_attr_entry *entry;
725         char *block_buf = NULL;
726         errcode_t error;
727
728         error = EXT2_ET_EA_NAME_NOT_FOUND;
729         if (!inode->i_file_acl)
730                 goto cleanup;
731
732         error = ext2fs_get_mem(fs->blocksize, &block_buf);
733         if (error)
734                 return error;
735         error = ext2fs_read_ext_attr(fs, inode->i_file_acl, block_buf);
736         if (error)
737                 goto cleanup;
738
739         error = ext2fs_attr_check_block(fs, block_buf);
740         if (error)
741                 goto cleanup;
742
743         header = BHDR(block_buf);
744         entry = (struct ext2_ext_attr_entry *)(header + 1);
745         error = ext2fs_attr_find_entry(&entry, name_index, name,
746                                        fs->blocksize, 1);
747         if (error)
748                 goto cleanup;
749         if (easize)
750                 *easize = entry->e_value_size;
751         if (buffer) {
752                 if (entry->e_value_size > buffer_size) {
753                         error = EXT2_ET_EA_TOO_BIG;
754                         goto cleanup;
755                 }
756                 memcpy(buffer, block_buf + entry->e_value_offs,
757                        entry->e_value_size);
758         }
759
760 cleanup:
761         if (block_buf)
762                 ext2fs_free_mem(&block_buf);
763         return error;
764 }
765
766 static errcode_t ext2fs_attr_ibody_get(ext2_filsys fs,
767                                        struct ext2_inode_large *inode,
768                                        int name_index, const char *name,
769                                        void *buffer, size_t buffer_size,
770                                        int *easize)
771 {
772         struct ext2_ext_attr_entry *entry;
773         int error;
774         char *end, *start;
775         __u32 *eamagic;
776
777         if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE)
778                 return EXT2_ET_EA_NAME_NOT_FOUND;
779
780         eamagic = IHDR(inode);
781         error = ext2fs_attr_check_block(fs, buffer);
782         if (error)
783                 return error;
784
785         start = (char *)inode + EXT2_GOOD_OLD_INODE_SIZE +
786                         inode->i_extra_isize + sizeof(__u32);
787         entry = (struct ext2_ext_attr_entry *)start;
788         end = (char *)inode + EXT2_INODE_SIZE(fs->super);
789         error = ext2fs_attr_check_names(entry, end);
790         if (error)
791                 goto cleanup;
792         error = ext2fs_attr_find_entry(&entry, name_index, name,
793                                        end - (char *)entry, 0);
794         if (error)
795                 goto cleanup;
796         if (easize)
797                 *easize = entry->e_value_size;
798         if (buffer) {
799                 if (entry->e_value_size > buffer_size) {
800                         error = EXT2_ET_EA_TOO_BIG;
801                         goto cleanup;
802                 }
803                 memcpy(buffer, start + entry->e_value_offs,entry->e_value_size);
804         }
805
806 cleanup:
807         return error;
808 }
809
810
811 errcode_t ext2fs_attr_get(ext2_filsys fs, struct ext2_inode *inode,
812                           int name_index, const char *name, char *buffer,
813                           size_t buffer_size, int *easize)
814 {
815         errcode_t error;
816
817         error = ext2fs_attr_ibody_get(fs, (struct ext2_inode_large *)inode,
818                                       name_index, name, buffer, buffer_size,
819                                       easize);
820         if (error == EXT2_ET_EA_NAME_NOT_FOUND)
821                 error = ext2fs_attr_block_get(fs, inode, name_index, name,
822                                               buffer, buffer_size, easize);
823
824         return error;
825 }
826
827 int ext2fs_attr_get_next_attr(struct ext2_ext_attr_entry *entry, int name_index,
828                               char *buffer, int buffer_size, int start)
829 {
830         const int prefix_len = ext2_attr_index_prefix[name_index].len;
831         int total_len;
832
833         if (!start && !EXT2_EXT_IS_LAST_ENTRY(entry))
834                 entry = EXT2_EXT_ATTR_NEXT(entry);
835
836         for (; !EXT2_EXT_IS_LAST_ENTRY(entry);
837              entry = EXT2_EXT_ATTR_NEXT(entry)) {
838                 if (!name_index)
839                         break;
840                 if (name_index == entry->e_name_index)
841                         break;
842         }
843         if (EXT2_EXT_IS_LAST_ENTRY(entry))
844                 return 0;
845
846         total_len = prefix_len + entry->e_name_len + 1;
847         if (buffer && total_len <= buffer_size) {
848                 memcpy(buffer, ext2_attr_index_prefix[name_index].str,
849                        prefix_len);
850                 memcpy(buffer + prefix_len, entry->e_name, entry->e_name_len);
851                 buffer[prefix_len + entry->e_name_len] = '\0';
852         }
853
854         return total_len;
855 }
856
857 errcode_t ext2fs_expand_extra_isize(ext2_filsys fs, ext2_ino_t ino,
858                                     struct ext2_inode_large *inode,
859                                     int new_extra_isize, int *ret,
860                                     int *needed_size)
861 {
862         struct ext2_inode *inode_buf = NULL;
863         __u32 *eamagic = NULL;
864         struct ext2_ext_attr_header *header = NULL;
865         struct ext2_ext_attr_entry *entry = NULL, *last = NULL;
866         struct ext2_attr_ibody_find is = {
867                 .ino = ino,
868                 .s = { .not_found = EXT2_ET_EA_NO_SPACE, },
869         };
870         struct ext2_attr_block_find bs = {
871                 .s = { .not_found = EXT2_ET_EA_NO_SPACE, },
872         };
873         char *start, *end, *block_buf = NULL, *buffer =NULL, *b_entry_name=NULL;
874         int total_ino = 0, total_blk, free, offs, tried_min_extra_isize = 0;
875         int s_min_extra_isize = fs->super->s_min_extra_isize;
876         errcode_t error = 0;
877
878         if (needed_size)
879                 *needed_size = new_extra_isize;
880         error = ext2fs_get_mem(fs->blocksize, &block_buf);
881         if (error)
882                 return error;
883
884         if (inode == NULL) {
885                 error = ext2fs_get_mem(EXT2_INODE_SIZE(fs->super), &inode_buf);
886                 if (error)
887                         goto cleanup;
888
889                 error = ext2fs_read_inode_full(fs, ino, inode_buf,
890                                                EXT2_INODE_SIZE(fs->super));
891                 if (error)
892                         goto cleanup;
893
894                 inode = (struct ext2_inode_large *)inode_buf;
895         }
896
897 retry:
898         if (inode->i_extra_isize >= new_extra_isize)
899                 goto cleanup;
900
901         eamagic = IHDR(inode);
902         start = (char *)inode + EXT2_GOOD_OLD_INODE_SIZE + inode->i_extra_isize;
903         /* No extended attributes present */
904         if (*eamagic != EXT2_EXT_ATTR_MAGIC) {
905                 memset(start, 0,
906                        EXT2_INODE_SIZE(fs->super) - EXT2_GOOD_OLD_INODE_SIZE -
907                        inode->i_extra_isize);
908                 inode->i_extra_isize = new_extra_isize;
909                 if (needed_size)
910                         *needed_size = 0;
911                 goto write_inode;
912         }
913
914         start += sizeof(__u32);
915         end = (char *)inode + EXT2_INODE_SIZE(fs->super);
916         last = entry = (struct ext2_ext_attr_entry *)start;
917         offs = end - start;
918         /* Consider space takenup by magic number */
919         total_ino = sizeof(__u32);
920         free = ext2fs_attr_free_space(last, &offs, start, &total_ino);
921
922         /* Enough free space available in the inode for expansion */
923         if (free >= new_extra_isize) {
924                 ext2fs_attr_shift_entries(entry,
925                                         inode->i_extra_isize - new_extra_isize,
926                                         (char *)inode +
927                                         EXT2_GOOD_OLD_INODE_SIZE +
928                                         new_extra_isize,
929                                         start - sizeof(__u32), total_ino);
930                 inode->i_extra_isize = new_extra_isize;
931                 if (needed_size)
932                         *needed_size = 0;
933                 goto write_inode;
934         }
935
936         if (inode->i_file_acl) {
937                 error = ext2fs_read_ext_attr(fs, inode->i_file_acl, block_buf);
938                 if (error)
939                         goto cleanup;
940
941                 header = BHDR(block_buf);
942                 if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
943                         error = EXT2_ET_EA_BAD_MAGIC;
944                         goto cleanup;
945                 }
946                 end = block_buf + fs->blocksize;
947                 last = entry = (struct ext2_ext_attr_entry *)(header+1);
948                 start = (char *)entry;
949                 offs = end - start;
950                 free = ext2fs_attr_free_space(last, &offs, start, &total_blk);
951                 if (free < new_extra_isize) {
952                         if (!tried_min_extra_isize && s_min_extra_isize) {
953                                 tried_min_extra_isize++;
954                                 new_extra_isize = s_min_extra_isize;
955                                 goto retry;
956                         }
957                         if (ret)
958                                 *ret = EXT2_EXPAND_EISIZE_NOSPC;
959                         error = EXT2_ET_EA_NO_SPACE;
960                         goto cleanup;
961                 }
962         } else {
963                 if (ret && *ret == EXT2_EXPAND_EISIZE_UNSAFE) {
964                         *ret = EXT2_EXPAND_EISIZE_NEW_BLOCK;
965                         error = 0;
966                         goto cleanup;
967                 }
968                 free = fs->blocksize;
969         }
970
971         while (new_extra_isize > 0) {
972                 int offs, size, entry_size;
973                 struct ext2_ext_attr_entry *small_entry = NULL;
974                 struct ext2_attr_info i = {
975                         .value = NULL,
976                         .value_len = 0,
977                 };
978                 unsigned int total_size, shift_bytes, temp = ~0U, extra_isize=0;
979
980                 start = (char *)inode + EXT2_GOOD_OLD_INODE_SIZE +
981                                 inode->i_extra_isize + sizeof(__u32);
982                 end = (char *)inode + EXT2_INODE_SIZE(fs->super);
983                 last = (struct ext2_ext_attr_entry *)start;
984
985                 /* Find the entry best suited to be pushed into EA block */
986                 entry = NULL;
987                 for (; !EXT2_EXT_IS_LAST_ENTRY(last);
988                         last = EXT2_EXT_ATTR_NEXT(last)) {
989                         total_size = EXT2_EXT_ATTR_SIZE(last->e_value_size) +
990                                         EXT2_EXT_ATTR_LEN(last->e_name_len);
991                         if (total_size <= free && total_size < temp) {
992                                 if (total_size < new_extra_isize) {
993                                         small_entry = last;
994                                 } else {
995                                         entry = last;
996                                         temp = total_size;
997                                 }
998                         }
999                 }
1000
1001                 if (entry == NULL) {
1002                         if (small_entry) {
1003                                 entry = small_entry;
1004                         } else {
1005                                 if (!tried_min_extra_isize &&
1006                                     s_min_extra_isize) {
1007                                         tried_min_extra_isize++;
1008                                         new_extra_isize = s_min_extra_isize;
1009                                         goto retry;
1010                                 }
1011                                 if (ret)
1012                                         *ret = EXT2_EXPAND_EISIZE_NOSPC;
1013                                 error = EXT2_ET_EA_NO_SPACE;
1014                                 goto cleanup;
1015                         }
1016                 }
1017                 offs = entry->e_value_offs;
1018                 size = entry->e_value_size;
1019                 entry_size = EXT2_EXT_ATTR_LEN(entry->e_name_len);
1020                 i.name_index = entry->e_name_index;
1021                 error = ext2fs_get_mem(size, &buffer);
1022                 if (error)
1023                         goto cleanup;
1024                 error = ext2fs_get_mem(entry->e_name_len + 1, &b_entry_name);
1025                 if (error)
1026                         goto cleanup;
1027                 /* Save the entry name and the entry value */
1028                 memcpy((char *)buffer, (char *)start + offs,
1029                        EXT2_EXT_ATTR_SIZE(size));
1030                 memcpy((char *)b_entry_name, (char *)entry->e_name,
1031                        entry->e_name_len);
1032                 b_entry_name[entry->e_name_len] = '\0';
1033                 i.name = b_entry_name;
1034
1035                 error = ext2fs_attr_ibody_find(fs, inode, &i, &is);
1036                 if (error)
1037                         goto cleanup;
1038
1039                 error = ext2fs_attr_set_entry(fs, &i, &is.s);
1040                 if (error)
1041                         goto cleanup;
1042
1043                 entry = (struct ext2_ext_attr_entry *)start;
1044                 if (entry_size + EXT2_EXT_ATTR_SIZE(size) >= new_extra_isize)
1045                         shift_bytes = new_extra_isize;
1046                 else
1047                         shift_bytes = entry_size + EXT2_EXT_ATTR_SIZE(size);
1048                 ext2fs_attr_shift_entries(entry,
1049                                         inode->i_extra_isize - shift_bytes,
1050                                         (char *)inode +EXT2_GOOD_OLD_INODE_SIZE+
1051                                         extra_isize + shift_bytes,
1052                                         start - sizeof(__u32),
1053                                         total_ino - entry_size);
1054
1055                 extra_isize += shift_bytes;
1056                 new_extra_isize -= shift_bytes;
1057                 if (needed_size)
1058                         *needed_size = new_extra_isize;
1059                 inode->i_extra_isize = extra_isize;
1060
1061                 i.name = b_entry_name;
1062                 i.value = buffer;
1063                 i.value_len = size;
1064                 error = ext2fs_attr_block_find(fs, (struct ext2_inode *)inode,
1065                                                &i, &bs);
1066                 if (error)
1067                         goto cleanup;
1068
1069                 /* Add entry which was removed from the inode into the block */
1070                 error = ext2fs_attr_block_set(fs, (struct ext2_inode *)inode,
1071                                               &i, &bs);
1072                 if (error)
1073                         goto cleanup;
1074         }
1075
1076 write_inode:
1077         error = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)inode,
1078                                         EXT2_INODE_SIZE(fs->super));
1079 cleanup:
1080         if (inode_buf)
1081                 ext2fs_free_mem(&inode_buf);
1082         if (block_buf)
1083                 ext2fs_free_mem(&block_buf);
1084         if (buffer)
1085                 ext2fs_free_mem(&buffer);
1086         if (b_entry_name)
1087                 ext2fs_free_mem(&b_entry_name);
1088
1089         return error;
1090 }