Whamcloud - gitweb
libext2fs: eliminate empty element holes in ext2_xattr_handle->attrs
[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
22 #include "ext2_fs.h"
23 #include "ext2_ext_attr.h"
24 #include "ext4_acl.h"
25
26 #include "ext2fs.h"
27
28 static errcode_t read_ea_inode_hash(ext2_filsys fs, ext2_ino_t ino, __u32 *hash)
29 {
30         struct ext2_inode inode;
31         errcode_t retval;
32
33         retval = ext2fs_read_inode(fs, ino, &inode);
34         if (retval)
35                 return retval;
36         *hash = ext2fs_get_ea_inode_hash(&inode);
37         return 0;
38 }
39
40 #define NAME_HASH_SHIFT 5
41 #define VALUE_HASH_SHIFT 16
42
43 /*
44  * ext2_xattr_hash_entry()
45  *
46  * Compute the hash of an extended attribute.
47  */
48 __u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry, void *data)
49 {
50         __u32 hash = 0;
51         char *name = ((char *) entry) + sizeof(struct ext2_ext_attr_entry);
52         int n;
53
54         for (n = 0; n < entry->e_name_len; n++) {
55                 hash = (hash << NAME_HASH_SHIFT) ^
56                        (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
57                        *name++;
58         }
59
60         /* The hash needs to be calculated on the data in little-endian. */
61         if (entry->e_value_inum == 0 && entry->e_value_size != 0) {
62                 __u32 *value = (__u32 *)data;
63                 for (n = (entry->e_value_size + EXT2_EXT_ATTR_ROUND) >>
64                          EXT2_EXT_ATTR_PAD_BITS; n; n--) {
65                         hash = (hash << VALUE_HASH_SHIFT) ^
66                                (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
67                                ext2fs_le32_to_cpu(*value++);
68                 }
69         }
70
71         return hash;
72 }
73
74 /*
75  * ext2fs_ext_attr_hash_entry2()
76  *
77  * Compute the hash of an extended attribute.
78  * This version of the function supports hashing entries that reference
79  * external inodes (ea_inode feature).
80  */
81 errcode_t ext2fs_ext_attr_hash_entry2(ext2_filsys fs,
82                                       struct ext2_ext_attr_entry *entry,
83                                       void *data, __u32 *hash)
84 {
85         *hash = ext2fs_ext_attr_hash_entry(entry, data);
86
87         if (entry->e_value_inum) {
88                 __u32 ea_inode_hash;
89                 errcode_t retval;
90
91                 retval = read_ea_inode_hash(fs, entry->e_value_inum,
92                                             &ea_inode_hash);
93                 if (retval)
94                         return retval;
95
96                 *hash = (*hash << VALUE_HASH_SHIFT) ^
97                         (*hash >> (8*sizeof(*hash) - VALUE_HASH_SHIFT)) ^
98                         ea_inode_hash;
99         }
100         return 0;
101 }
102
103 #undef NAME_HASH_SHIFT
104 #undef VALUE_HASH_SHIFT
105
106 #define BLOCK_HASH_SHIFT 16
107
108 /* Mirrors ext4_xattr_rehash() implementation in kernel. */
109 void ext2fs_ext_attr_block_rehash(struct ext2_ext_attr_header *header,
110                                   struct ext2_ext_attr_entry *end)
111 {
112         struct ext2_ext_attr_entry *here;
113         __u32 hash = 0;
114
115         here = (struct ext2_ext_attr_entry *)(header+1);
116         while (here < end && !EXT2_EXT_IS_LAST_ENTRY(here)) {
117                 if (!here->e_hash) {
118                         /* Block is not shared if an entry's hash value == 0 */
119                         hash = 0;
120                         break;
121                 }
122                 hash = (hash << BLOCK_HASH_SHIFT) ^
123                        (hash >> (8*sizeof(hash) - BLOCK_HASH_SHIFT)) ^
124                        here->e_hash;
125                 here = EXT2_EXT_ATTR_NEXT(here);
126         }
127         header->h_hash = hash;
128 }
129
130 #undef BLOCK_HASH_SHIFT
131
132 __u32 ext2fs_get_ea_inode_hash(struct ext2_inode *inode)
133 {
134         return inode->i_atime;
135 }
136
137 void ext2fs_set_ea_inode_hash(struct ext2_inode *inode, __u32 hash)
138 {
139         inode->i_atime = hash;
140 }
141
142 static errcode_t check_ext_attr_header(struct ext2_ext_attr_header *header)
143 {
144         if ((header->h_magic != EXT2_EXT_ATTR_MAGIC_v1 &&
145              header->h_magic != EXT2_EXT_ATTR_MAGIC) ||
146             header->h_blocks != 1)
147                 return EXT2_ET_BAD_EA_HEADER;
148
149         return 0;
150 }
151
152 errcode_t ext2fs_read_ext_attr3(ext2_filsys fs, blk64_t block, void *buf,
153                                 ext2_ino_t inum)
154 {
155         int             csum_failed = 0;
156         errcode_t       retval;
157
158         retval = io_channel_read_blk64(fs->io, block, 1, buf);
159         if (retval)
160                 return retval;
161
162         if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
163             !ext2fs_ext_attr_block_csum_verify(fs, inum, block, buf))
164                 csum_failed = 1;
165
166 #ifdef WORDS_BIGENDIAN
167         ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1);
168 #endif
169
170         retval = check_ext_attr_header(buf);
171         if (retval == 0 && csum_failed)
172                 retval = EXT2_ET_EXT_ATTR_CSUM_INVALID;
173
174         return retval;
175 }
176
177 errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block, void *buf)
178 {
179         return ext2fs_read_ext_attr3(fs, block, buf, 0);
180 }
181
182 errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf)
183 {
184         return ext2fs_read_ext_attr2(fs, block, buf);
185 }
186
187 errcode_t ext2fs_write_ext_attr3(ext2_filsys fs, blk64_t block, void *inbuf,
188                                  ext2_ino_t inum)
189 {
190         errcode_t       retval;
191         char            *write_buf;
192
193 #ifdef WORDS_BIGENDIAN
194         retval = ext2fs_get_mem(fs->blocksize, &write_buf);
195         if (retval)
196                 return retval;
197         ext2fs_swap_ext_attr(write_buf, inbuf, fs->blocksize, 1);
198 #else
199         write_buf = (char *) inbuf;
200 #endif
201
202         retval = ext2fs_ext_attr_block_csum_set(fs, inum, block,
203                         (struct ext2_ext_attr_header *)write_buf);
204         if (retval)
205                 return retval;
206
207         retval = io_channel_write_blk64(fs->io, block, 1, write_buf);
208 #ifdef WORDS_BIGENDIAN
209         ext2fs_free_mem(&write_buf);
210 #endif
211         if (!retval)
212                 ext2fs_mark_changed(fs);
213         return retval;
214 }
215
216 errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block, void *inbuf)
217 {
218         return ext2fs_write_ext_attr3(fs, block, inbuf, 0);
219 }
220
221 errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf)
222 {
223         return ext2fs_write_ext_attr2(fs, block, inbuf);
224 }
225
226 /*
227  * This function adjusts the reference count of the EA block.
228  */
229 errcode_t ext2fs_adjust_ea_refcount3(ext2_filsys fs, blk64_t blk,
230                                     char *block_buf, int adjust,
231                                     __u32 *newcount, ext2_ino_t inum)
232 {
233         errcode_t       retval;
234         struct ext2_ext_attr_header *header;
235         char    *buf = 0;
236
237         if ((blk >= ext2fs_blocks_count(fs->super)) ||
238             (blk < fs->super->s_first_data_block))
239                 return EXT2_ET_BAD_EA_BLOCK_NUM;
240
241         if (!block_buf) {
242                 retval = ext2fs_get_mem(fs->blocksize, &buf);
243                 if (retval)
244                         return retval;
245                 block_buf = buf;
246         }
247
248         retval = ext2fs_read_ext_attr3(fs, blk, block_buf, inum);
249         if (retval)
250                 goto errout;
251
252         header = (struct ext2_ext_attr_header *) block_buf;
253         header->h_refcount += adjust;
254         if (newcount)
255                 *newcount = header->h_refcount;
256
257         retval = ext2fs_write_ext_attr3(fs, blk, block_buf, inum);
258         if (retval)
259                 goto errout;
260
261 errout:
262         if (buf)
263                 ext2fs_free_mem(&buf);
264         return retval;
265 }
266
267 errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk,
268                                     char *block_buf, int adjust,
269                                     __u32 *newcount)
270 {
271         return ext2fs_adjust_ea_refcount3(fs, blk, block_buf, adjust,
272                                           newcount, 0);
273 }
274
275 errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
276                                         char *block_buf, int adjust,
277                                         __u32 *newcount)
278 {
279         return ext2fs_adjust_ea_refcount2(fs, blk, block_buf, adjust,
280                                           newcount);
281 }
282
283 /* Manipulate the contents of extended attribute regions */
284 struct ext2_xattr {
285         char *name;
286         void *value;
287         size_t value_len;
288 };
289
290 struct ext2_xattr_handle {
291         errcode_t magic;
292         ext2_filsys fs;
293         struct ext2_xattr *attrs;
294         size_t capacity, count;
295         ext2_ino_t ino;
296         unsigned int flags;
297         int dirty;
298 };
299
300 static errcode_t ext2fs_xattrs_expand(struct ext2_xattr_handle *h,
301                                       unsigned int expandby)
302 {
303         struct ext2_xattr *new_attrs;
304         errcode_t err;
305
306         err = ext2fs_get_arrayzero(h->capacity + expandby,
307                                    sizeof(struct ext2_xattr), &new_attrs);
308         if (err)
309                 return err;
310
311         memcpy(new_attrs, h->attrs, h->capacity * sizeof(struct ext2_xattr));
312         ext2fs_free_mem(&h->attrs);
313         h->capacity += expandby;
314         h->attrs = new_attrs;
315
316         return 0;
317 }
318
319 struct ea_name_index {
320         int index;
321         const char *name;
322 };
323
324 /* Keep these names sorted in order of decreasing specificity. */
325 static struct ea_name_index ea_names[] = {
326         {3, "system.posix_acl_default"},
327         {2, "system.posix_acl_access"},
328         {8, "system.richacl"},
329         {6, "security."},
330         {4, "trusted."},
331         {7, "system."},
332         {1, "user."},
333         {0, NULL},
334 };
335
336 static int find_ea_index(char *fullname, char **name, int *index);
337
338 /* Pull inlinedata to the front. */
339 static int attr_compare(const void *a, const void *b)
340 {
341         const struct ext2_xattr *xa = a, *xb = b;
342         char *xa_suffix, *xb_suffix;
343         int xa_idx, xb_idx;
344         int cmp;
345
346         if (!strcmp(xa->name, "system.data"))
347                 return -1;
348         else if (!strcmp(xb->name, "system.data"))
349                 return +1;
350
351         /*
352          * Duplicate the kernel's sorting algorithm because xattr blocks
353          * require sorted keys.
354          */
355         xa_suffix = xa->name;
356         xb_suffix = xb->name;
357         xa_idx = xb_idx = 0;
358         find_ea_index(xa->name, &xa_suffix, &xa_idx);
359         find_ea_index(xb->name, &xb_suffix, &xb_idx);
360         cmp = xa_idx - xb_idx;
361         if (cmp)
362                 return cmp;
363         cmp = strlen(xa_suffix) - strlen(xb_suffix);
364         if (cmp)
365                 return cmp;
366         cmp = strcmp(xa_suffix, xb_suffix);
367         return cmp;
368 }
369
370 static const char *find_ea_prefix(int index)
371 {
372         struct ea_name_index *e;
373
374         for (e = ea_names; e->name; e++)
375                 if (e->index == index)
376                         return e->name;
377
378         return NULL;
379 }
380
381 static int find_ea_index(char *fullname, char **name, int *index)
382 {
383         struct ea_name_index *e;
384
385         for (e = ea_names; e->name; e++) {
386                 if (strncmp(fullname, e->name, strlen(e->name)) == 0) {
387                         *name = (char *)fullname + strlen(e->name);
388                         *index = e->index;
389                         return 1;
390                 }
391         }
392         return 0;
393 }
394
395 errcode_t ext2fs_free_ext_attr(ext2_filsys fs, ext2_ino_t ino,
396                                struct ext2_inode_large *inode)
397 {
398         struct ext2_ext_attr_header *header;
399         void *block_buf = NULL;
400         blk64_t blk;
401         errcode_t err;
402         struct ext2_inode_large i;
403
404         /* Read inode? */
405         if (inode == NULL) {
406                 err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&i,
407                                              sizeof(struct ext2_inode_large));
408                 if (err)
409                         return err;
410                 inode = &i;
411         }
412
413         /* Do we already have an EA block? */
414         blk = ext2fs_file_acl_block(fs, (struct ext2_inode *)inode);
415         if (blk == 0)
416                 return 0;
417
418         /* Find block, zero it, write back */
419         if ((blk < fs->super->s_first_data_block) ||
420             (blk >= ext2fs_blocks_count(fs->super))) {
421                 err = EXT2_ET_BAD_EA_BLOCK_NUM;
422                 goto out;
423         }
424
425         err = ext2fs_get_mem(fs->blocksize, &block_buf);
426         if (err)
427                 goto out;
428
429         err = ext2fs_read_ext_attr3(fs, blk, block_buf, ino);
430         if (err)
431                 goto out2;
432
433         /* We only know how to deal with v2 EA blocks */
434         header = (struct ext2_ext_attr_header *) block_buf;
435         if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
436                 err = EXT2_ET_BAD_EA_HEADER;
437                 goto out2;
438         }
439
440         header->h_refcount--;
441         err = ext2fs_write_ext_attr3(fs, blk, block_buf, ino);
442         if (err)
443                 goto out2;
444
445         /* Erase link to block */
446         ext2fs_file_acl_block_set(fs, (struct ext2_inode *)inode, 0);
447         if (header->h_refcount == 0)
448                 ext2fs_block_alloc_stats2(fs, blk, -1);
449         err = ext2fs_iblk_sub_blocks(fs, (struct ext2_inode *)inode, 1);
450         if (err)
451                 goto out2;
452
453         /* Write inode? */
454         if (inode == &i) {
455                 err = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&i,
456                                               sizeof(struct ext2_inode_large));
457                 if (err)
458                         goto out2;
459         }
460
461 out2:
462         ext2fs_free_mem(&block_buf);
463 out:
464         return err;
465 }
466
467 static errcode_t prep_ea_block_for_write(ext2_filsys fs, ext2_ino_t ino,
468                                          struct ext2_inode_large *inode)
469 {
470         struct ext2_ext_attr_header *header;
471         void *block_buf = NULL;
472         blk64_t blk, goal;
473         errcode_t err;
474
475         /* Do we already have an EA block? */
476         blk = ext2fs_file_acl_block(fs, (struct ext2_inode *)inode);
477         if (blk != 0) {
478                 if ((blk < fs->super->s_first_data_block) ||
479                     (blk >= ext2fs_blocks_count(fs->super))) {
480                         err = EXT2_ET_BAD_EA_BLOCK_NUM;
481                         goto out;
482                 }
483
484                 err = ext2fs_get_mem(fs->blocksize, &block_buf);
485                 if (err)
486                         goto out;
487
488                 err = ext2fs_read_ext_attr3(fs, blk, block_buf, ino);
489                 if (err)
490                         goto out2;
491
492                 /* We only know how to deal with v2 EA blocks */
493                 header = (struct ext2_ext_attr_header *) block_buf;
494                 if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
495                         err = EXT2_ET_BAD_EA_HEADER;
496                         goto out2;
497                 }
498
499                 /* Single-user block.  We're done here. */
500                 if (header->h_refcount == 1)
501                         goto out2;
502
503                 /* We need to CoW the block. */
504                 header->h_refcount--;
505                 err = ext2fs_write_ext_attr3(fs, blk, block_buf, ino);
506                 if (err)
507                         goto out2;
508         } else {
509                 /* No block, we must increment i_blocks */
510                 err = ext2fs_iblk_add_blocks(fs, (struct ext2_inode *)inode,
511                                              1);
512                 if (err)
513                         goto out;
514         }
515
516         /* Allocate a block */
517         goal = ext2fs_find_inode_goal(fs, ino, (struct ext2_inode *)inode, 0);
518         err = ext2fs_alloc_block2(fs, goal, NULL, &blk);
519         if (err)
520                 goto out2;
521         ext2fs_file_acl_block_set(fs, (struct ext2_inode *)inode, blk);
522 out2:
523         if (block_buf)
524                 ext2fs_free_mem(&block_buf);
525 out:
526         return err;
527 }
528
529
530 static inline int
531 posix_acl_xattr_count(size_t size)
532 {
533         if (size < sizeof(posix_acl_xattr_header))
534                 return -1;
535         size -= sizeof(posix_acl_xattr_header);
536         if (size % sizeof(posix_acl_xattr_entry))
537                 return -1;
538         return size / sizeof(posix_acl_xattr_entry);
539 }
540
541 /*
542  * The lgetxattr function returns data formatted in the POSIX extended
543  * attribute format.  The on-disk format uses a more compact encoding.
544  * See the ext4_acl_to_disk in fs/ext4/acl.c.
545  */
546 static errcode_t convert_posix_acl_to_disk_buffer(const void *value, size_t size,
547                                                   void *out_buf, size_t *size_out)
548 {
549         posix_acl_xattr_header *header = (posix_acl_xattr_header*) value;
550         posix_acl_xattr_entry *entry = (posix_acl_xattr_entry *)(header+1), *end;
551         ext4_acl_header *ext_acl;
552         size_t s;
553         void *e;
554
555         int count;
556
557         if (!value)
558                 return EINVAL;
559         if (size < sizeof(posix_acl_xattr_header))
560                 return ENOMEM;
561         if (header->a_version != ext2fs_cpu_to_le32(POSIX_ACL_XATTR_VERSION))
562                 return EINVAL;
563
564         count = posix_acl_xattr_count(size);
565         ext_acl = out_buf;
566         ext_acl->a_version = ext2fs_cpu_to_le32(EXT4_ACL_VERSION);
567
568         if (count <= 0)
569                 return EINVAL;
570
571         e = (char *) out_buf + sizeof(ext4_acl_header);
572         s = sizeof(ext4_acl_header);
573         for (end = entry + count; entry != end;entry++) {
574                 ext4_acl_entry *disk_entry = (ext4_acl_entry*) e;
575                 disk_entry->e_tag = ext2fs_cpu_to_le16(entry->e_tag);
576                 disk_entry->e_perm = ext2fs_cpu_to_le16(entry->e_perm);
577
578                 switch(entry->e_tag) {
579                         case ACL_USER_OBJ:
580                         case ACL_GROUP_OBJ:
581                         case ACL_MASK:
582                         case ACL_OTHER:
583                                 e += sizeof(ext4_acl_entry_short);
584                                 s += sizeof(ext4_acl_entry_short);
585                                 break;
586                         case ACL_USER:
587                         case ACL_GROUP:
588                                 disk_entry->e_id =  ext2fs_cpu_to_le32(entry->e_id);
589                                 e += sizeof(ext4_acl_entry);
590                                 s += sizeof(ext4_acl_entry);
591                                 break;
592                 }
593         }
594         *size_out = s;
595         return 0;
596 }
597
598 static errcode_t convert_disk_buffer_to_posix_acl(const void *value, size_t size,
599                                                   void **out_buf, size_t *size_out)
600 {
601         posix_acl_xattr_header *header;
602         posix_acl_xattr_entry *entry;
603         ext4_acl_header *ext_acl = (ext4_acl_header *) value;
604         errcode_t err;
605         const char *cp;
606         char *out;
607
608         if ((!value) ||
609             (size < sizeof(ext4_acl_header)) ||
610             (ext_acl->a_version != ext2fs_cpu_to_le32(EXT4_ACL_VERSION)))
611                 return EINVAL;
612
613         err = ext2fs_get_mem(size * 2, &out);
614         if (err)
615                 return err;
616
617         header = (posix_acl_xattr_header *) out;
618         header->a_version = ext2fs_cpu_to_le32(POSIX_ACL_XATTR_VERSION);
619         entry = (posix_acl_xattr_entry *) (out + sizeof(posix_acl_xattr_header));
620
621         cp = value + sizeof(ext4_acl_header);
622         size -= sizeof(ext4_acl_header);
623
624         while (size > 0) {
625                 const ext4_acl_entry *disk_entry = (const ext4_acl_entry *) cp;
626
627                 entry->e_tag = ext2fs_le16_to_cpu(disk_entry->e_tag);
628                 entry->e_perm = ext2fs_le16_to_cpu(disk_entry->e_perm);
629
630                 switch(entry->e_tag) {
631                         case ACL_USER_OBJ:
632                         case ACL_GROUP_OBJ:
633                         case ACL_MASK:
634                         case ACL_OTHER:
635                                 entry->e_id = 0;
636                                 cp += sizeof(ext4_acl_entry_short);
637                                 size -= sizeof(ext4_acl_entry_short);
638                                 break;
639                         case ACL_USER:
640                         case ACL_GROUP:
641                                 entry->e_id = ext2fs_le32_to_cpu(disk_entry->e_id);
642                                 cp += sizeof(ext4_acl_entry);
643                                 size -= sizeof(ext4_acl_entry);
644                                 break;
645                 default:
646                         ext2fs_free_mem(&out);
647                         return EINVAL;
648                         break;
649                 }
650                 entry++;
651         }
652         *out_buf = out;
653         *size_out = ((char *) entry - out);
654         return 0;
655 }
656
657
658 static errcode_t write_xattrs_to_buffer(struct ext2_xattr_handle *handle,
659                                         struct ext2_xattr **pos,
660                                         void *entries_start,
661                                         unsigned int storage_size,
662                                         unsigned int value_offset_correction,
663                                         int write_hash)
664 {
665         struct ext2_xattr *x = *pos;
666         struct ext2_ext_attr_entry *e = entries_start;
667         char *end = (char *) entries_start + storage_size;
668         char *shortname;
669         unsigned int entry_size, value_size;
670         int idx, ret;
671
672         memset(entries_start, 0, storage_size);
673         /* For all remaining x...  */
674         for (; x < handle->attrs + handle->count; x++) {
675                 /* Calculate index and shortname position */
676                 shortname = x->name;
677                 ret = find_ea_index(x->name, &shortname, &idx);
678
679                 /* Calculate entry and value size */
680                 entry_size = (sizeof(*e) + strlen(shortname) +
681                               EXT2_EXT_ATTR_PAD - 1) &
682                              ~(EXT2_EXT_ATTR_PAD - 1);
683                 value_size = ((x->value_len + EXT2_EXT_ATTR_PAD - 1) /
684                               EXT2_EXT_ATTR_PAD) * EXT2_EXT_ATTR_PAD;
685
686                 /*
687                  * Would entry collide with value?
688                  * Note that we must leave sufficient room for a (u32)0 to
689                  * mark the end of the entries.
690                  */
691                 if ((char *)e + entry_size + sizeof(__u32) > end - value_size)
692                         break;
693
694                 /* Fill out e appropriately */
695                 e->e_name_len = strlen(shortname);
696                 e->e_name_index = (ret ? idx : 0);
697                 e->e_value_offs = end - value_size - (char *)entries_start +
698                                 value_offset_correction;
699                 e->e_value_inum = 0;
700                 e->e_value_size = x->value_len;
701
702                 /* Store name and value */
703                 end -= value_size;
704                 memcpy((char *)e + sizeof(*e), shortname, e->e_name_len);
705                 memcpy(end, x->value, e->e_value_size);
706
707                 if (write_hash)
708                         e->e_hash = ext2fs_ext_attr_hash_entry(e, end);
709                 else
710                         e->e_hash = 0;
711
712                 e = EXT2_EXT_ATTR_NEXT(e);
713                 *(__u32 *)e = 0;
714         }
715         *pos = x;
716
717         return 0;
718 }
719
720 errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle)
721 {
722         struct ext2_xattr *x;
723         struct ext2_inode_large *inode;
724         char *start, *block_buf = NULL;
725         struct ext2_ext_attr_header *header;
726         __u32 ea_inode_magic;
727         blk64_t blk;
728         unsigned int storage_size;
729         unsigned int i;
730         errcode_t err;
731
732         EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE);
733         i = EXT2_INODE_SIZE(handle->fs->super);
734         if (i < sizeof(*inode))
735                 i = sizeof(*inode);
736         err = ext2fs_get_memzero(i, &inode);
737         if (err)
738                 return err;
739
740         err = ext2fs_read_inode_full(handle->fs, handle->ino,
741                                      (struct ext2_inode *)inode,
742                                      EXT2_INODE_SIZE(handle->fs->super));
743         if (err)
744                 goto out;
745
746         /* If extra_isize isn't set, we need to set it now */
747         if (inode->i_extra_isize == 0 &&
748             EXT2_INODE_SIZE(handle->fs->super) > EXT2_GOOD_OLD_INODE_SIZE) {
749                 char *p = (char *)inode;
750                 size_t extra = handle->fs->super->s_want_extra_isize;
751
752                 if (extra == 0)
753                         extra = sizeof(__u32);
754                 memset(p + EXT2_GOOD_OLD_INODE_SIZE, 0, extra);
755                 inode->i_extra_isize = extra;
756         }
757         if (inode->i_extra_isize & 3) {
758                 err = EXT2_ET_INODE_CORRUPTED;
759                 goto out;
760         }
761
762         /* Force the inlinedata attr to the front. */
763         x = handle->attrs;
764         qsort(x, handle->count, sizeof(struct ext2_xattr), attr_compare);
765
766         /* Does the inode have space for EA? */
767         if (inode->i_extra_isize < sizeof(inode->i_extra_isize) ||
768             EXT2_INODE_SIZE(handle->fs->super) <= EXT2_GOOD_OLD_INODE_SIZE +
769                                                   inode->i_extra_isize +
770                                                   sizeof(__u32))
771                 goto write_ea_block;
772
773         /* Write the inode EA */
774         ea_inode_magic = EXT2_EXT_ATTR_MAGIC;
775         memcpy(((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
776                inode->i_extra_isize, &ea_inode_magic, sizeof(__u32));
777         storage_size = EXT2_INODE_SIZE(handle->fs->super) -
778                 EXT2_GOOD_OLD_INODE_SIZE - inode->i_extra_isize -
779                 sizeof(__u32);
780         start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
781                 inode->i_extra_isize + sizeof(__u32);
782
783         err = write_xattrs_to_buffer(handle, &x, start, storage_size, 0, 0);
784         if (err)
785                 goto out;
786
787 write_ea_block:
788         /* Are we done? */
789         if (x >= handle->attrs + handle->count)
790                 goto skip_ea_block;
791
792         /* Write the EA block */
793         err = ext2fs_get_memzero(handle->fs->blocksize, &block_buf);
794         if (err)
795                 goto out;
796
797         storage_size = handle->fs->blocksize -
798                 sizeof(struct ext2_ext_attr_header);
799         start = block_buf + sizeof(struct ext2_ext_attr_header);
800
801         err = write_xattrs_to_buffer(handle, &x, start, storage_size,
802                                      start - block_buf, 1);
803         if (err)
804                 goto out2;
805
806         if (x < handle->attrs + handle->count) {
807                 err = EXT2_ET_EA_NO_SPACE;
808                 goto out2;
809         }
810
811         /* Write a header on the EA block */
812         header = (struct ext2_ext_attr_header *) block_buf;
813         header->h_magic = EXT2_EXT_ATTR_MAGIC;
814         header->h_refcount = 1;
815         header->h_blocks = 1;
816
817         /* Get a new block for writing */
818         err = prep_ea_block_for_write(handle->fs, handle->ino, inode);
819         if (err)
820                 goto out2;
821
822         /* Finally, write the new EA block */
823         blk = ext2fs_file_acl_block(handle->fs,
824                                     (struct ext2_inode *)inode);
825         err = ext2fs_write_ext_attr3(handle->fs, blk, block_buf,
826                                      handle->ino);
827         if (err)
828                 goto out2;
829
830 skip_ea_block:
831         blk = ext2fs_file_acl_block(handle->fs, (struct ext2_inode *)inode);
832         if (!block_buf && blk) {
833                 /* xattrs shrunk, free the block */
834                 err = ext2fs_free_ext_attr(handle->fs, handle->ino, inode);
835                 if (err)
836                         goto out;
837         }
838
839         /* Write the inode */
840         err = ext2fs_write_inode_full(handle->fs, handle->ino,
841                                       (struct ext2_inode *)inode,
842                                       EXT2_INODE_SIZE(handle->fs->super));
843         if (err)
844                 goto out2;
845
846 out2:
847         ext2fs_free_mem(&block_buf);
848 out:
849         ext2fs_free_mem(&inode);
850         handle->dirty = 0;
851         return err;
852 }
853
854 static errcode_t read_xattrs_from_buffer(struct ext2_xattr_handle *handle,
855                                          struct ext2_inode_large *inode,
856                                          struct ext2_ext_attr_entry *entries,
857                                          unsigned int storage_size,
858                                          char *value_start)
859 {
860         struct ext2_xattr *x;
861         struct ext2_ext_attr_entry *entry, *end;
862         const char *prefix;
863         unsigned int remain, prefix_len;
864         errcode_t err;
865         unsigned int values_size = storage_size +
866                         ((char *)entries - value_start);
867
868         /* find the end */
869         end = entries;
870         remain = storage_size;
871         while (remain >= sizeof(struct ext2_ext_attr_entry) &&
872                !EXT2_EXT_IS_LAST_ENTRY(end)) {
873
874                 /* header eats this space */
875                 remain -= sizeof(struct ext2_ext_attr_entry);
876
877                 /* is attribute name valid? */
878                 if (EXT2_EXT_ATTR_SIZE(end->e_name_len) > remain)
879                         return EXT2_ET_EA_BAD_NAME_LEN;
880
881                 /* attribute len eats this space */
882                 remain -= EXT2_EXT_ATTR_SIZE(end->e_name_len);
883                 end = EXT2_EXT_ATTR_NEXT(end);
884         }
885
886         entry = entries;
887         remain = storage_size;
888         while (remain >= sizeof(struct ext2_ext_attr_entry) &&
889                !EXT2_EXT_IS_LAST_ENTRY(entry)) {
890
891                 /* Allocate space for more attrs? */
892                 if (handle->count == handle->capacity) {
893                         err = ext2fs_xattrs_expand(handle, 4);
894                         if (err)
895                                 return err;
896                 }
897
898                 x = handle->attrs + handle->count;
899
900                 /* header eats this space */
901                 remain -= sizeof(struct ext2_ext_attr_entry);
902
903                 /* attribute len eats this space */
904                 remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
905
906                 /* Extract name */
907                 prefix = find_ea_prefix(entry->e_name_index);
908                 prefix_len = (prefix ? strlen(prefix) : 0);
909                 err = ext2fs_get_memzero(entry->e_name_len + prefix_len + 1,
910                                          &x->name);
911                 if (err)
912                         return err;
913                 if (prefix)
914                         memcpy(x->name, prefix, prefix_len);
915                 if (entry->e_name_len)
916                         memcpy(x->name + prefix_len,
917                                (char *)entry + sizeof(*entry),
918                                entry->e_name_len);
919
920                 /* Check & copy value */
921                 if (!ext2fs_has_feature_ea_inode(handle->fs->super) &&
922                     entry->e_value_inum != 0)
923                         return EXT2_ET_BAD_EA_BLOCK_NUM;
924
925                 if (entry->e_value_inum == 0) {
926                         if (entry->e_value_size > remain)
927                                 return EXT2_ET_EA_BAD_VALUE_SIZE;
928
929                         if (entry->e_value_offs + entry->e_value_size > values_size)
930                                 return EXT2_ET_EA_BAD_VALUE_OFFSET;
931
932                         if (entry->e_value_size > 0 &&
933                             value_start + entry->e_value_offs <
934                             (char *)end + sizeof(__u32))
935                                 return EXT2_ET_EA_BAD_VALUE_OFFSET;
936
937                         remain -= entry->e_value_size;
938
939                         err = ext2fs_get_mem(entry->e_value_size, &x->value);
940                         if (err)
941                                 return err;
942                         memcpy(x->value, value_start + entry->e_value_offs,
943                                entry->e_value_size);
944                 } else {
945                         ext2_file_t ea_file;
946
947                         if (entry->e_value_offs != 0)
948                                 return EXT2_ET_EA_BAD_VALUE_OFFSET;
949
950                         if (entry->e_value_size > (64 * 1024))
951                                 return EXT2_ET_EA_BAD_VALUE_SIZE;
952
953                         err = ext2fs_get_mem(entry->e_value_size, &x->value);
954                         if (err)
955                                 return err;
956
957                         err = ext2fs_file_open(handle->fs, entry->e_value_inum,
958                                                0, &ea_file);
959                         if (err)
960                                 return err;
961
962                         if (ext2fs_file_get_size(ea_file) !=
963                             entry->e_value_size)
964                                 err = EXT2_ET_EA_BAD_VALUE_SIZE;
965                         else
966                                 err = ext2fs_file_read(ea_file, x->value,
967                                                        entry->e_value_size, 0);
968                         ext2fs_file_close(ea_file);
969                         if (err)
970                                 return err;
971                 }
972
973                 x->value_len = entry->e_value_size;
974
975                 /* e_hash may be 0 in older inode's ea */
976                 if (entry->e_hash != 0) {
977                         __u32 hash;
978                         void *data = (entry->e_value_inum != 0) ?
979                                         0 : value_start + entry->e_value_offs;
980
981                         err = ext2fs_ext_attr_hash_entry2(handle->fs, entry,
982                                                           data, &hash);
983                         if (err)
984                                 return err;
985                         if (entry->e_hash != hash) {
986                                 struct ext2_inode child;
987
988                                 /* Check whether this is an old Lustre-style
989                                  * ea_inode reference.
990                                  */
991                                 err = ext2fs_read_inode(handle->fs,
992                                                         entry->e_value_inum,
993                                                         &child);
994                                 if (err)
995                                         return err;
996                                 if (child.i_mtime != handle->ino ||
997                                     child.i_generation != inode->i_generation)
998                                         return EXT2_ET_BAD_EA_HASH;
999                         }
1000                 }
1001
1002                 handle->count++;
1003                 entry = EXT2_EXT_ATTR_NEXT(entry);
1004         }
1005
1006         return 0;
1007 }
1008
1009 static void xattrs_free_keys(struct ext2_xattr_handle *h)
1010 {
1011         struct ext2_xattr *a = h->attrs;
1012         size_t i;
1013
1014         for (i = 0; i < h->capacity; i++) {
1015                 if (a[i].name)
1016                         ext2fs_free_mem(&a[i].name);
1017                 if (a[i].value)
1018                         ext2fs_free_mem(&a[i].value);
1019         }
1020         h->count = 0;
1021 }
1022
1023 errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle)
1024 {
1025         struct ext2_inode_large *inode;
1026         struct ext2_ext_attr_header *header;
1027         __u32 ea_inode_magic;
1028         unsigned int storage_size;
1029         char *start, *block_buf = NULL;
1030         blk64_t blk;
1031         size_t i;
1032         errcode_t err;
1033
1034         EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE);
1035         i = EXT2_INODE_SIZE(handle->fs->super);
1036         if (i < sizeof(*inode))
1037                 i = sizeof(*inode);
1038         err = ext2fs_get_memzero(i, &inode);
1039         if (err)
1040                 return err;
1041
1042         err = ext2fs_read_inode_full(handle->fs, handle->ino,
1043                                      (struct ext2_inode *)inode,
1044                                      EXT2_INODE_SIZE(handle->fs->super));
1045         if (err)
1046                 goto out;
1047
1048         xattrs_free_keys(handle);
1049
1050         /* Does the inode have space for EA? */
1051         if (inode->i_extra_isize < sizeof(inode->i_extra_isize) ||
1052             EXT2_INODE_SIZE(handle->fs->super) <= EXT2_GOOD_OLD_INODE_SIZE +
1053                                                   inode->i_extra_isize +
1054                                                   sizeof(__u32))
1055                 goto read_ea_block;
1056         if (inode->i_extra_isize & 3) {
1057                 err = EXT2_ET_INODE_CORRUPTED;
1058                 goto out;
1059         }
1060
1061         /* Look for EA in the inode */
1062         memcpy(&ea_inode_magic, ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
1063                inode->i_extra_isize, sizeof(__u32));
1064         if (ea_inode_magic == EXT2_EXT_ATTR_MAGIC) {
1065                 storage_size = EXT2_INODE_SIZE(handle->fs->super) -
1066                         EXT2_GOOD_OLD_INODE_SIZE - inode->i_extra_isize -
1067                         sizeof(__u32);
1068                 start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
1069                         inode->i_extra_isize + sizeof(__u32);
1070
1071                 err = read_xattrs_from_buffer(handle, inode,
1072                                         (struct ext2_ext_attr_entry *) start,
1073                                         storage_size, start);
1074                 if (err)
1075                         goto out;
1076         }
1077
1078 read_ea_block:
1079         /* Look for EA in a separate EA block */
1080         blk = ext2fs_file_acl_block(handle->fs, (struct ext2_inode *)inode);
1081         if (blk != 0) {
1082                 if ((blk < handle->fs->super->s_first_data_block) ||
1083                     (blk >= ext2fs_blocks_count(handle->fs->super))) {
1084                         err = EXT2_ET_BAD_EA_BLOCK_NUM;
1085                         goto out;
1086                 }
1087
1088                 err = ext2fs_get_mem(handle->fs->blocksize, &block_buf);
1089                 if (err)
1090                         goto out;
1091
1092                 err = ext2fs_read_ext_attr3(handle->fs, blk, block_buf,
1093                                             handle->ino);
1094                 if (err)
1095                         goto out3;
1096
1097                 /* We only know how to deal with v2 EA blocks */
1098                 header = (struct ext2_ext_attr_header *) block_buf;
1099                 if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
1100                         err = EXT2_ET_BAD_EA_HEADER;
1101                         goto out3;
1102                 }
1103
1104                 /* Read EAs */
1105                 storage_size = handle->fs->blocksize -
1106                         sizeof(struct ext2_ext_attr_header);
1107                 start = block_buf + sizeof(struct ext2_ext_attr_header);
1108                 err = read_xattrs_from_buffer(handle, inode,
1109                                         (struct ext2_ext_attr_entry *) start,
1110                                         storage_size, block_buf);
1111                 if (err)
1112                         goto out3;
1113
1114                 ext2fs_free_mem(&block_buf);
1115         }
1116
1117         ext2fs_free_mem(&block_buf);
1118         ext2fs_free_mem(&inode);
1119         return 0;
1120
1121 out3:
1122         ext2fs_free_mem(&block_buf);
1123 out:
1124         ext2fs_free_mem(&inode);
1125         return err;
1126 }
1127
1128 errcode_t ext2fs_xattrs_iterate(struct ext2_xattr_handle *h,
1129                                 int (*func)(char *name, char *value,
1130                                             size_t value_len, void *data),
1131                                 void *data)
1132 {
1133         struct ext2_xattr *x;
1134         int ret;
1135
1136         EXT2_CHECK_MAGIC(h, EXT2_ET_MAGIC_EA_HANDLE);
1137         for (x = h->attrs; x < h->attrs + h->count; x++) {
1138                 ret = func(x->name, x->value, x->value_len, data);
1139                 if (ret & XATTR_CHANGED)
1140                         h->dirty = 1;
1141                 if (ret & XATTR_ABORT)
1142                         return 0;
1143         }
1144
1145         return 0;
1146 }
1147
1148 errcode_t ext2fs_xattr_get(struct ext2_xattr_handle *h, const char *key,
1149                            void **value, size_t *value_len)
1150 {
1151         struct ext2_xattr *x;
1152         char *val;
1153         errcode_t err;
1154
1155         EXT2_CHECK_MAGIC(h, EXT2_ET_MAGIC_EA_HANDLE);
1156         for (x = h->attrs; x < h->attrs + h->count; x++) {
1157                 if (strcmp(x->name, key))
1158                         continue;
1159
1160                 if (!(h->flags & XATTR_HANDLE_FLAG_RAW) &&
1161                     ((strcmp(key, "system.posix_acl_default") == 0) ||
1162                      (strcmp(key, "system.posix_acl_access") == 0))) {
1163                         err = convert_disk_buffer_to_posix_acl(x->value, x->value_len,
1164                                                                value, value_len);
1165                         return err;
1166                 } else {
1167                         err = ext2fs_get_mem(x->value_len, &val);
1168                         if (err)
1169                                 return err;
1170                         memcpy(val, x->value, x->value_len);
1171                         *value = val;
1172                         *value_len = x->value_len;
1173                         return 0;
1174                 }
1175         }
1176
1177         return EXT2_ET_EA_KEY_NOT_FOUND;
1178 }
1179
1180 errcode_t ext2fs_xattr_inode_max_size(ext2_filsys fs, ext2_ino_t ino,
1181                                       size_t *size)
1182 {
1183         struct ext2_ext_attr_entry *entry;
1184         struct ext2_inode_large *inode;
1185         __u32 ea_inode_magic;
1186         unsigned int minoff;
1187         char *start;
1188         size_t i;
1189         errcode_t err;
1190
1191         i = EXT2_INODE_SIZE(fs->super);
1192         if (i < sizeof(*inode))
1193                 i = sizeof(*inode);
1194         err = ext2fs_get_memzero(i, &inode);
1195         if (err)
1196                 return err;
1197
1198         err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)inode,
1199                                      EXT2_INODE_SIZE(fs->super));
1200         if (err)
1201                 goto out;
1202
1203         /* Does the inode have size for EA? */
1204         if (EXT2_INODE_SIZE(fs->super) <= EXT2_GOOD_OLD_INODE_SIZE +
1205                                                   inode->i_extra_isize +
1206                                                   sizeof(__u32)) {
1207                 err = EXT2_ET_INLINE_DATA_NO_SPACE;
1208                 goto out;
1209         }
1210
1211         minoff = EXT2_INODE_SIZE(fs->super) - sizeof(*inode) - sizeof(__u32);
1212         memcpy(&ea_inode_magic, ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
1213                inode->i_extra_isize, sizeof(__u32));
1214         if (ea_inode_magic == EXT2_EXT_ATTR_MAGIC) {
1215                 /* has xattrs.  calculate the size */
1216                 start= ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
1217                         inode->i_extra_isize + sizeof(__u32);
1218                 entry = (struct ext2_ext_attr_entry *) start;
1219                 while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
1220                         if (!entry->e_value_inum && entry->e_value_size) {
1221                                 unsigned int offs = entry->e_value_offs;
1222                                 if (offs < minoff)
1223                                         minoff = offs;
1224                         }
1225                         entry = EXT2_EXT_ATTR_NEXT(entry);
1226                 }
1227                 *size = minoff - ((char *)entry - (char *)start) - sizeof(__u32);
1228         } else {
1229                 /* no xattr.  return a maximum size */
1230                 *size = EXT2_EXT_ATTR_SIZE(minoff -
1231                                            EXT2_EXT_ATTR_LEN(strlen("data")) -
1232                                            EXT2_EXT_ATTR_ROUND - sizeof(__u32));
1233         }
1234
1235 out:
1236         ext2fs_free_mem(&inode);
1237         return err;
1238 }
1239
1240 errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *handle,
1241                            const char *key,
1242                            const void *value,
1243                            size_t value_len)
1244 {
1245         struct ext2_xattr *x;
1246         char *new_value;
1247         errcode_t err;
1248
1249         EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE);
1250
1251         err = ext2fs_get_mem(value_len, &new_value);
1252         if (err)
1253                 return err;
1254         if (!(handle->flags & XATTR_HANDLE_FLAG_RAW) &&
1255             ((strcmp(key, "system.posix_acl_default") == 0) ||
1256              (strcmp(key, "system.posix_acl_access") == 0))) {
1257                 err = convert_posix_acl_to_disk_buffer(value, value_len,
1258                                                        new_value, &value_len);
1259                 if (err)
1260                         goto errout;
1261         } else
1262                 memcpy(new_value, value, value_len);
1263
1264         for (x = handle->attrs; x < handle->attrs + handle->count; x++) {
1265                 /* Replace xattr */
1266                 if (strcmp(x->name, key) == 0) {
1267                         ext2fs_free_mem(&x->value);
1268                         x->value = new_value;
1269                         x->value_len = value_len;
1270                         handle->dirty = 1;
1271                         return 0;
1272                 }
1273         }
1274
1275         if (handle->count == handle->capacity) {
1276                 /* Expand array, append slot */
1277                 err = ext2fs_xattrs_expand(handle, 4);
1278                 if (err)
1279                         goto errout;
1280
1281                 x = handle->attrs + handle->capacity - 4;
1282         }
1283
1284         err = ext2fs_get_mem(strlen(key) + 1, &x->name);
1285         if (err)
1286                 goto errout;
1287         strcpy(x->name, key);
1288
1289         err = ext2fs_get_mem(value_len, &x->value);
1290         if (err)
1291                 goto errout;
1292         memcpy(x->value, value, value_len);
1293         x->value_len = value_len;
1294         handle->dirty = 1;
1295         handle->count++;
1296         return 0;
1297 errout:
1298         ext2fs_free_mem(&new_value);
1299         return err;
1300 }
1301
1302 errcode_t ext2fs_xattr_remove(struct ext2_xattr_handle *handle,
1303                               const char *key)
1304 {
1305         struct ext2_xattr *x;
1306         struct ext2_xattr *end = handle->attrs + handle->count;
1307
1308         EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE);
1309         for (x = handle->attrs; x < end; x++) {
1310                 if (strcmp(x->name, key) == 0) {
1311                         ext2fs_free_mem(&x->name);
1312                         ext2fs_free_mem(&x->value);
1313                         memmove(x, x + 1, (char *)end - (char *)(x + 1));
1314                         memset(end, 0, sizeof(*end));
1315                         handle->dirty = 1;
1316                         handle->count--;
1317                         return 0;
1318                 }
1319         }
1320
1321         /* no key found, success! */
1322         return 0;
1323 }
1324
1325 errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
1326                              struct ext2_xattr_handle **handle)
1327 {
1328         struct ext2_xattr_handle *h;
1329         errcode_t err;
1330
1331         if (!ext2fs_has_feature_xattr(fs->super) &&
1332             !ext2fs_has_feature_inline_data(fs->super))
1333                 return EXT2_ET_MISSING_EA_FEATURE;
1334
1335         err = ext2fs_get_memzero(sizeof(*h), &h);
1336         if (err)
1337                 return err;
1338
1339         h->magic = EXT2_ET_MAGIC_EA_HANDLE;
1340         h->capacity = 4;
1341         err = ext2fs_get_arrayzero(h->capacity, sizeof(struct ext2_xattr),
1342                                    &h->attrs);
1343         if (err) {
1344                 ext2fs_free_mem(&h);
1345                 return err;
1346         }
1347         h->count = 0;
1348         h->ino = ino;
1349         h->fs = fs;
1350         *handle = h;
1351         return 0;
1352 }
1353
1354 errcode_t ext2fs_xattrs_close(struct ext2_xattr_handle **handle)
1355 {
1356         struct ext2_xattr_handle *h = *handle;
1357         errcode_t err;
1358
1359         EXT2_CHECK_MAGIC(h, EXT2_ET_MAGIC_EA_HANDLE);
1360         if (h->dirty) {
1361                 err = ext2fs_xattrs_write(h);
1362                 if (err)
1363                         return err;
1364         }
1365
1366         xattrs_free_keys(h);
1367         ext2fs_free_mem(&h->attrs);
1368         ext2fs_free_mem(handle);
1369         return 0;
1370 }
1371
1372 errcode_t ext2fs_xattrs_count(struct ext2_xattr_handle *handle, size_t *count)
1373 {
1374         EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE);
1375         *count = handle->count;
1376         return 0;
1377 }
1378
1379 errcode_t ext2fs_xattrs_flags(struct ext2_xattr_handle *handle,
1380                               unsigned int *new_flags, unsigned int *old_flags)
1381 {
1382         EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE);
1383         if (old_flags)
1384                 *old_flags = handle->flags;
1385         if (new_flags)
1386                 handle->flags = *new_flags;
1387         return 0;
1388 }