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