Whamcloud - gitweb
e2fsck: handle preallocation for large PAGE_SIZE
[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                 error = EXT2_ET_EA_TOO_BIG;
753                 if (entry->e_value_size > buffer_size)
754                         goto cleanup;
755                 memcpy(buffer, block_buf + entry->e_value_offs,
756                        entry->e_value_size);
757         }
758
759 cleanup:
760         if (block_buf)
761                 ext2fs_free_mem(&block_buf);
762         return error;
763 }
764
765 static errcode_t ext2fs_attr_ibody_get(ext2_filsys fs,
766                                        struct ext2_inode_large *inode,
767                                        int name_index, const char *name,
768                                        void *buffer, size_t buffer_size,
769                                        int *easize)
770 {
771         struct ext2_ext_attr_entry *entry;
772         int error;
773         char *end, *start;
774         __u32 *eamagic;
775
776         if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE)
777                 return EXT2_ET_EA_NAME_NOT_FOUND;
778
779         eamagic = IHDR(inode);
780         error = ext2fs_attr_check_block(fs, buffer);
781         if (error)
782                 return error;
783
784         start = (char *)inode + EXT2_GOOD_OLD_INODE_SIZE +
785                         inode->i_extra_isize + sizeof(__u32);
786         entry = (struct ext2_ext_attr_entry *)start;
787         end = (char *)inode + EXT2_INODE_SIZE(fs->super);
788         error = ext2fs_attr_check_names(entry, end);
789         if (error)
790                 goto cleanup;
791         error = ext2fs_attr_find_entry(&entry, name_index, name,
792                                        end - (char *)entry, 0);
793         if (error)
794                 goto cleanup;
795         if (easize)
796                 *easize = entry->e_value_size;
797         if (buffer) {
798                 error = EXT2_ET_EA_TOO_BIG;
799                 if (entry->e_value_size > buffer_size)
800                         goto cleanup;
801                 memcpy(buffer, start + entry->e_value_offs,entry->e_value_size);
802         }
803
804 cleanup:
805         return error;
806 }
807
808
809 errcode_t ext2fs_attr_get(ext2_filsys fs, struct ext2_inode *inode,
810                           int name_index, const char *name, char *buffer,
811                           size_t buffer_size, int *easize)
812 {
813         errcode_t error;
814
815         error = ext2fs_attr_ibody_get(fs, (struct ext2_inode_large *)inode,
816                                       name_index, name, buffer, buffer_size,
817                                       easize);
818         if (error == EXT2_ET_EA_NAME_NOT_FOUND)
819                 error = ext2fs_attr_block_get(fs, inode, name_index, name,
820                                               buffer, buffer_size, easize);
821
822         return error;
823 }
824
825 int ext2fs_attr_get_next_attr(struct ext2_ext_attr_entry *entry, int name_index,
826                               char *buffer, int buffer_size, int start)
827 {
828         const int prefix_len = ext2_attr_index_prefix[name_index].len;
829         int total_len;
830
831         if (!start && !EXT2_EXT_IS_LAST_ENTRY(entry))
832                 entry = EXT2_EXT_ATTR_NEXT(entry);
833
834         for (; !EXT2_EXT_IS_LAST_ENTRY(entry);
835              entry = EXT2_EXT_ATTR_NEXT(entry)) {
836                 if (!name_index)
837                         break;
838                 if (name_index == entry->e_name_index)
839                         break;
840         }
841         if (EXT2_EXT_IS_LAST_ENTRY(entry))
842                 return 0;
843
844         total_len = prefix_len + entry->e_name_len + 1;
845         if (buffer && total_len <= buffer_size) {
846                 memcpy(buffer, ext2_attr_index_prefix[name_index].str,
847                        prefix_len);
848                 memcpy(buffer + prefix_len, entry->e_name, entry->e_name_len);
849                 buffer[prefix_len + entry->e_name_len] = '\0';
850         }
851
852         return total_len;
853 }
854
855 errcode_t ext2fs_expand_extra_isize(ext2_filsys fs, ext2_ino_t ino,
856                                     struct ext2_inode_large *inode,
857                                     int new_extra_isize, int *ret,
858                                     int *needed_size)
859 {
860         struct ext2_inode *inode_buf = NULL;
861         __u32 *eamagic = NULL;
862         struct ext2_ext_attr_header *header = NULL;
863         struct ext2_ext_attr_entry *entry = NULL, *last = NULL;
864         struct ext2_attr_ibody_find is = {
865                 .ino = ino,
866                 .s = { .not_found = EXT2_ET_EA_NO_SPACE, },
867         };
868         struct ext2_attr_block_find bs = {
869                 .s = { .not_found = EXT2_ET_EA_NO_SPACE, },
870         };
871         char *start, *end, *block_buf = NULL, *buffer =NULL, *b_entry_name=NULL;
872         int total_ino = 0, total_blk, free, offs, tried_min_extra_isize = 0;
873         int s_min_extra_isize = fs->super->s_min_extra_isize;
874         errcode_t error = 0;
875
876         if (needed_size)
877                 *needed_size = new_extra_isize;
878         error = ext2fs_get_mem(fs->blocksize, &block_buf);
879         if (error)
880                 return error;
881
882         if (inode == NULL) {
883                 error = ext2fs_get_mem(EXT2_INODE_SIZE(fs->super), &inode_buf);
884                 if (error)
885                         goto cleanup;
886
887                 error = ext2fs_read_inode_full(fs, ino, inode_buf,
888                                                EXT2_INODE_SIZE(fs->super));
889                 if (error)
890                         goto cleanup;
891
892                 inode = (struct ext2_inode_large *)inode_buf;
893         }
894
895 retry:
896         if (inode->i_extra_isize >= new_extra_isize)
897                 goto cleanup;
898
899         eamagic = IHDR(inode);
900         start = (char *)inode + EXT2_GOOD_OLD_INODE_SIZE + inode->i_extra_isize;
901         /* No extended attributes present */
902         if (*eamagic != EXT2_EXT_ATTR_MAGIC) {
903                 memset(start, 0,
904                        EXT2_INODE_SIZE(fs->super) - EXT2_GOOD_OLD_INODE_SIZE -
905                        inode->i_extra_isize);
906                 inode->i_extra_isize = new_extra_isize;
907                 if (needed_size)
908                         *needed_size = 0;
909                 goto write_inode;
910         }
911
912         start += sizeof(__u32);
913         end = (char *)inode + EXT2_INODE_SIZE(fs->super);
914         last = entry = (struct ext2_ext_attr_entry *)start;
915         offs = end - start;
916         /* Consider space takenup by magic number */
917         total_ino = sizeof(__u32);
918         free = ext2fs_attr_free_space(last, &offs, start, &total_ino);
919
920         /* Enough free space available in the inode for expansion */
921         if (free >= new_extra_isize) {
922                 ext2fs_attr_shift_entries(entry,
923                                         inode->i_extra_isize - new_extra_isize,
924                                         (char *)inode +
925                                         EXT2_GOOD_OLD_INODE_SIZE +
926                                         new_extra_isize,
927                                         start - sizeof(__u32), total_ino);
928                 inode->i_extra_isize = new_extra_isize;
929                 if (needed_size)
930                         *needed_size = 0;
931                 goto write_inode;
932         }
933
934         if (inode->i_file_acl) {
935                 error = ext2fs_read_ext_attr(fs, inode->i_file_acl, block_buf);
936                 if (error)
937                         goto cleanup;
938
939                 header = BHDR(block_buf);
940                 if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
941                         error = EXT2_ET_EA_BAD_MAGIC;
942                         goto cleanup;
943                 }
944                 end = block_buf + fs->blocksize;
945                 last = entry = (struct ext2_ext_attr_entry *)(header+1);
946                 start = (char *)entry;
947                 offs = end - start;
948                 free = ext2fs_attr_free_space(last, &offs, start, &total_blk);
949                 if (free < new_extra_isize) {
950                         if (!tried_min_extra_isize && s_min_extra_isize) {
951                                 tried_min_extra_isize++;
952                                 new_extra_isize = s_min_extra_isize;
953                                 goto retry;
954                         }
955                         if (ret)
956                                 *ret = EXT2_EXPAND_EISIZE_NOSPC;
957                         error = EXT2_ET_EA_NO_SPACE;
958                         goto cleanup;
959                 }
960         } else {
961                 if (ret && *ret == EXT2_EXPAND_EISIZE_UNSAFE) {
962                         *ret = EXT2_EXPAND_EISIZE_NEW_BLOCK;
963                         error = 0;
964                         goto cleanup;
965                 }
966                 free = fs->blocksize;
967         }
968
969         while (new_extra_isize > 0) {
970                 int offs, size, entry_size;
971                 struct ext2_ext_attr_entry *small_entry = NULL;
972                 struct ext2_attr_info i = {
973                         .value = NULL,
974                         .value_len = 0,
975                 };
976                 unsigned int total_size, shift_bytes, temp = ~0U, extra_isize=0;
977
978                 start = (char *)inode + EXT2_GOOD_OLD_INODE_SIZE +
979                                 inode->i_extra_isize + sizeof(__u32);
980                 end = (char *)inode + EXT2_INODE_SIZE(fs->super);
981                 last = (struct ext2_ext_attr_entry *)start;
982
983                 /* Find the entry best suited to be pushed into EA block */
984                 entry = NULL;
985                 for (; !EXT2_EXT_IS_LAST_ENTRY(last);
986                         last = EXT2_EXT_ATTR_NEXT(last)) {
987                         total_size = EXT2_EXT_ATTR_SIZE(last->e_value_size) +
988                                         EXT2_EXT_ATTR_LEN(last->e_name_len);
989                         if (total_size <= free && total_size < temp) {
990                                 if (total_size < new_extra_isize) {
991                                         small_entry = last;
992                                 } else {
993                                         entry = last;
994                                         temp = total_size;
995                                 }
996                         }
997                 }
998
999                 if (entry == NULL) {
1000                         if (small_entry) {
1001                                 entry = small_entry;
1002                         } else {
1003                                 if (!tried_min_extra_isize &&
1004                                     s_min_extra_isize) {
1005                                         tried_min_extra_isize++;
1006                                         new_extra_isize = s_min_extra_isize;
1007                                         goto retry;
1008                                 }
1009                                 if (ret)
1010                                         *ret = EXT2_EXPAND_EISIZE_NOSPC;
1011                                 error = EXT2_ET_EA_NO_SPACE;
1012                                 goto cleanup;
1013                         }
1014                 }
1015                 offs = entry->e_value_offs;
1016                 size = entry->e_value_size;
1017                 entry_size = EXT2_EXT_ATTR_LEN(entry->e_name_len);
1018                 i.name_index = entry->e_name_index;
1019                 error = ext2fs_get_mem(size, &buffer);
1020                 if (error)
1021                         goto cleanup;
1022                 error = ext2fs_get_mem(entry->e_name_len + 1, &b_entry_name);
1023                 if (error)
1024                         goto cleanup;
1025                 /* Save the entry name and the entry value */
1026                 memcpy((char *)buffer, (char *)start + offs,
1027                        EXT2_EXT_ATTR_SIZE(size));
1028                 memcpy((char *)b_entry_name, (char *)entry->e_name,
1029                        entry->e_name_len);
1030                 b_entry_name[entry->e_name_len] = '\0';
1031                 i.name = b_entry_name;
1032
1033                 error = ext2fs_attr_ibody_find(fs, inode, &i, &is);
1034                 if (error)
1035                         goto cleanup;
1036
1037                 error = ext2fs_attr_set_entry(fs, &i, &is.s);
1038                 if (error)
1039                         goto cleanup;
1040
1041                 entry = (struct ext2_ext_attr_entry *)start;
1042                 if (entry_size + EXT2_EXT_ATTR_SIZE(size) >= new_extra_isize)
1043                         shift_bytes = new_extra_isize;
1044                 else
1045                         shift_bytes = entry_size + EXT2_EXT_ATTR_SIZE(size);
1046                 ext2fs_attr_shift_entries(entry,
1047                                         inode->i_extra_isize - shift_bytes,
1048                                         (char *)inode +EXT2_GOOD_OLD_INODE_SIZE+
1049                                         extra_isize + shift_bytes,
1050                                         start - sizeof(__u32),
1051                                         total_ino - entry_size);
1052
1053                 extra_isize += shift_bytes;
1054                 new_extra_isize -= shift_bytes;
1055                 if (needed_size)
1056                         *needed_size = new_extra_isize;
1057                 inode->i_extra_isize = extra_isize;
1058
1059                 i.name = b_entry_name;
1060                 i.value = buffer;
1061                 i.value_len = size;
1062                 error = ext2fs_attr_block_find(fs, (struct ext2_inode *)inode,
1063                                                &i, &bs);
1064                 if (error)
1065                         goto cleanup;
1066
1067                 /* Add entry which was removed from the inode into the block */
1068                 error = ext2fs_attr_block_set(fs, (struct ext2_inode *)inode,
1069                                               &i, &bs);
1070                 if (error)
1071                         goto cleanup;
1072         }
1073
1074 write_inode:
1075         error = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)inode,
1076                                         EXT2_INODE_SIZE(fs->super));
1077 cleanup:
1078         if (inode_buf)
1079                 ext2fs_free_mem(&inode_buf);
1080         if (block_buf)
1081                 ext2fs_free_mem(&block_buf);
1082         if (buffer)
1083                 ext2fs_free_mem(&buffer);
1084         if (b_entry_name)
1085                 ext2fs_free_mem(&b_entry_name);
1086
1087         return error;
1088 }