Coverage Report

Created: 2023-06-07 06:35

/src/e2fsprogs/lib/ext2fs/ext_attr.c
Line
Count
Source (jump to first uncovered line)
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
0
{
30
0
  struct ext2_inode inode;
31
0
  errcode_t retval;
32
33
0
  retval = ext2fs_read_inode(fs, ino, &inode);
34
0
  if (retval)
35
0
    return retval;
36
0
  *hash = ext2fs_get_ea_inode_hash(&inode);
37
0
  return 0;
38
0
}
39
40
0
#define NAME_HASH_SHIFT 5
41
0
#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
0
{
50
0
  __u32 hash = 0;
51
0
  unsigned char *name = (((unsigned char *) entry) +
52
0
             sizeof(struct ext2_ext_attr_entry));
53
0
  int n;
54
55
0
  for (n = 0; n < entry->e_name_len; n++) {
56
0
    hash = (hash << NAME_HASH_SHIFT) ^
57
0
           (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
58
0
           *name++;
59
0
  }
60
61
  /* The hash needs to be calculated on the data in little-endian. */
62
0
  if (entry->e_value_inum == 0 && entry->e_value_size != 0) {
63
0
    __u32 *value = (__u32 *)data;
64
0
    for (n = (entry->e_value_size + EXT2_EXT_ATTR_ROUND) >>
65
0
       EXT2_EXT_ATTR_PAD_BITS; n; n--) {
66
0
      hash = (hash << VALUE_HASH_SHIFT) ^
67
0
             (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
68
0
             ext2fs_le32_to_cpu(*value++);
69
0
    }
70
0
  }
71
72
0
  return hash;
73
0
}
74
75
__u32 ext2fs_ext_attr_hash_entry_signed(struct ext2_ext_attr_entry *entry,
76
          void *data)
77
0
{
78
0
  __u32 hash = 0;
79
0
  signed char *name = (((signed char *) entry) +
80
0
           sizeof(struct ext2_ext_attr_entry));
81
0
  int n;
82
83
0
  for (n = 0; n < entry->e_name_len; n++) {
84
0
    hash = (hash << NAME_HASH_SHIFT) ^
85
0
           (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
86
0
           *name++;
87
0
  }
88
89
  /* The hash needs to be calculated on the data in little-endian. */
90
0
  if (entry->e_value_inum == 0 && entry->e_value_size != 0) {
91
0
    __u32 *value = (__u32 *)data;
92
0
    for (n = (entry->e_value_size + EXT2_EXT_ATTR_ROUND) >>
93
0
       EXT2_EXT_ATTR_PAD_BITS; n; n--) {
94
0
      hash = (hash << VALUE_HASH_SHIFT) ^
95
0
             (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
96
0
             ext2fs_le32_to_cpu(*value++);
97
0
    }
98
0
  }
99
100
0
  return hash;
101
0
}
102
103
104
/*
105
 * ext2fs_ext_attr_hash_entry3()
106
 *
107
 * Compute the hash of an extended attribute.  This version of the
108
 * function supports hashing entries that reference external inodes
109
 * (ea_inode feature) as well as calculating the old legacy signed
110
 * hash variant.
111
 */
112
errcode_t ext2fs_ext_attr_hash_entry3(ext2_filsys fs,
113
              struct ext2_ext_attr_entry *entry,
114
              void *data, __u32 *hash,
115
              __u32 *signed_hash)
116
0
{
117
0
  *hash = ext2fs_ext_attr_hash_entry(entry, data);
118
0
  if (signed_hash)
119
0
    *signed_hash = ext2fs_ext_attr_hash_entry_signed(entry, data);
120
121
0
  if (entry->e_value_inum) {
122
0
    __u32 ea_inode_hash;
123
0
    errcode_t retval;
124
125
0
    retval = read_ea_inode_hash(fs, entry->e_value_inum,
126
0
              &ea_inode_hash);
127
0
    if (retval)
128
0
      return retval;
129
130
0
    *hash = (*hash << VALUE_HASH_SHIFT) ^
131
0
      (*hash >> (8*sizeof(*hash) - VALUE_HASH_SHIFT)) ^
132
0
      ea_inode_hash;
133
0
    if (signed_hash)
134
0
      *signed_hash = (*signed_hash << VALUE_HASH_SHIFT) ^
135
0
        (*signed_hash >> (8*sizeof(*hash) -
136
0
              VALUE_HASH_SHIFT)) ^
137
0
        ea_inode_hash;
138
0
  }
139
0
  return 0;
140
0
}
141
142
/*
143
 * ext2fs_ext_attr_hash_entry2()
144
 *
145
 * Compute the hash of an extended attribute.
146
 * This version of the function supports hashing entries that reference
147
 * external inodes (ea_inode feature).
148
 */
149
errcode_t ext2fs_ext_attr_hash_entry2(ext2_filsys fs,
150
              struct ext2_ext_attr_entry *entry,
151
              void *data, __u32 *hash)
152
0
{
153
0
  return ext2fs_ext_attr_hash_entry3(fs, entry, data, hash, NULL);
154
0
}
155
156
#undef NAME_HASH_SHIFT
157
#undef VALUE_HASH_SHIFT
158
159
0
#define BLOCK_HASH_SHIFT 16
160
161
/* Mirrors ext4_xattr_rehash() implementation in kernel. */
162
void ext2fs_ext_attr_block_rehash(struct ext2_ext_attr_header *header,
163
          struct ext2_ext_attr_entry *end)
164
0
{
165
0
  struct ext2_ext_attr_entry *here;
166
0
  __u32 hash = 0;
167
168
0
  here = (struct ext2_ext_attr_entry *)(header+1);
169
0
  while (here < end && !EXT2_EXT_IS_LAST_ENTRY(here)) {
170
0
    if (!here->e_hash) {
171
      /* Block is not shared if an entry's hash value == 0 */
172
0
      hash = 0;
173
0
      break;
174
0
    }
175
0
    hash = (hash << BLOCK_HASH_SHIFT) ^
176
0
           (hash >> (8*sizeof(hash) - BLOCK_HASH_SHIFT)) ^
177
0
           here->e_hash;
178
0
    here = EXT2_EXT_ATTR_NEXT(here);
179
0
  }
180
0
  header->h_hash = hash;
181
0
}
182
183
#undef BLOCK_HASH_SHIFT
184
185
__u32 ext2fs_get_ea_inode_hash(struct ext2_inode *inode)
186
0
{
187
0
  return inode->i_atime;
188
0
}
189
190
void ext2fs_set_ea_inode_hash(struct ext2_inode *inode, __u32 hash)
191
0
{
192
0
  inode->i_atime = hash;
193
0
}
194
195
__u64 ext2fs_get_ea_inode_ref(struct ext2_inode *inode)
196
0
{
197
0
  return ((__u64)inode->i_ctime << 32) | inode->osd1.linux1.l_i_version;
198
0
}
199
200
void ext2fs_set_ea_inode_ref(struct ext2_inode *inode, __u64 ref_count)
201
0
{
202
0
  inode->i_ctime = (__u32)(ref_count >> 32);
203
0
  inode->osd1.linux1.l_i_version = (__u32)ref_count;
204
0
}
205
206
static errcode_t check_ext_attr_header(struct ext2_ext_attr_header *header)
207
0
{
208
0
  if ((header->h_magic != EXT2_EXT_ATTR_MAGIC_v1 &&
209
0
       header->h_magic != EXT2_EXT_ATTR_MAGIC) ||
210
0
      header->h_blocks != 1)
211
0
    return EXT2_ET_BAD_EA_HEADER;
212
213
0
  return 0;
214
0
}
215
216
errcode_t ext2fs_read_ext_attr3(ext2_filsys fs, blk64_t block, void *buf,
217
        ext2_ino_t inum)
218
0
{
219
0
  int   csum_failed = 0;
220
0
  errcode_t retval;
221
222
0
  retval = io_channel_read_blk64(fs->io, block, 1, buf);
223
0
  if (retval)
224
0
    return retval;
225
226
0
  if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
227
0
      !ext2fs_ext_attr_block_csum_verify(fs, inum, block, buf))
228
0
    csum_failed = 1;
229
230
#ifdef WORDS_BIGENDIAN
231
  ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1);
232
#endif
233
234
0
  retval = check_ext_attr_header(buf);
235
0
  if (retval == 0 && csum_failed)
236
0
    retval = EXT2_ET_EXT_ATTR_CSUM_INVALID;
237
238
0
  return retval;
239
0
}
240
241
errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block, void *buf)
242
0
{
243
0
  return ext2fs_read_ext_attr3(fs, block, buf, 0);
244
0
}
245
246
errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf)
247
0
{
248
0
  return ext2fs_read_ext_attr2(fs, block, buf);
249
0
}
250
251
errcode_t ext2fs_write_ext_attr3(ext2_filsys fs, blk64_t block, void *inbuf,
252
         ext2_ino_t inum)
253
0
{
254
0
  errcode_t retval;
255
0
  char    *write_buf;
256
257
#ifdef WORDS_BIGENDIAN
258
  retval = ext2fs_get_mem(fs->blocksize, &write_buf);
259
  if (retval)
260
    return retval;
261
  ext2fs_swap_ext_attr(write_buf, inbuf, fs->blocksize, 1);
262
#else
263
0
  write_buf = (char *) inbuf;
264
0
#endif
265
266
0
  retval = ext2fs_ext_attr_block_csum_set(fs, inum, block,
267
0
      (struct ext2_ext_attr_header *)write_buf);
268
0
  if (retval)
269
0
    return retval;
270
271
0
  retval = io_channel_write_blk64(fs->io, block, 1, write_buf);
272
#ifdef WORDS_BIGENDIAN
273
  ext2fs_free_mem(&write_buf);
274
#endif
275
0
  if (!retval)
276
0
    ext2fs_mark_changed(fs);
277
0
  return retval;
278
0
}
279
280
errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block, void *inbuf)
281
0
{
282
0
  return ext2fs_write_ext_attr3(fs, block, inbuf, 0);
283
0
}
284
285
errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf)
286
0
{
287
0
  return ext2fs_write_ext_attr2(fs, block, inbuf);
288
0
}
289
290
/*
291
 * This function adjusts the reference count of the EA block.
292
 */
293
errcode_t ext2fs_adjust_ea_refcount3(ext2_filsys fs, blk64_t blk,
294
            char *block_buf, int adjust,
295
            __u32 *newcount, ext2_ino_t inum)
296
0
{
297
0
  errcode_t retval;
298
0
  struct ext2_ext_attr_header *header;
299
0
  char  *buf = 0;
300
301
0
  if ((blk >= ext2fs_blocks_count(fs->super)) ||
302
0
      (blk < fs->super->s_first_data_block))
303
0
    return EXT2_ET_BAD_EA_BLOCK_NUM;
304
305
0
  if (!block_buf) {
306
0
    retval = ext2fs_get_mem(fs->blocksize, &buf);
307
0
    if (retval)
308
0
      return retval;
309
0
    block_buf = buf;
310
0
  }
311
312
0
  retval = ext2fs_read_ext_attr3(fs, blk, block_buf, inum);
313
0
  if (retval)
314
0
    goto errout;
315
316
0
  header = (struct ext2_ext_attr_header *) block_buf;
317
0
  header->h_refcount += adjust;
318
0
  if (newcount)
319
0
    *newcount = header->h_refcount;
320
321
0
  retval = ext2fs_write_ext_attr3(fs, blk, block_buf, inum);
322
0
  if (retval)
323
0
    goto errout;
324
325
0
errout:
326
0
  if (buf)
327
0
    ext2fs_free_mem(&buf);
328
0
  return retval;
329
0
}
330
331
errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk,
332
            char *block_buf, int adjust,
333
            __u32 *newcount)
334
0
{
335
0
  return ext2fs_adjust_ea_refcount3(fs, blk, block_buf, adjust,
336
0
            newcount, 0);
337
0
}
338
339
errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
340
          char *block_buf, int adjust,
341
          __u32 *newcount)
342
0
{
343
0
  return ext2fs_adjust_ea_refcount2(fs, blk, block_buf, adjust,
344
0
            newcount);
345
0
}
346
347
/* Manipulate the contents of extended attribute regions */
348
struct ext2_xattr {
349
  int name_index;
350
  char *name;
351
  char *short_name;
352
  void *value;
353
  unsigned int value_len;
354
  ext2_ino_t ea_ino;
355
};
356
357
struct ext2_xattr_handle {
358
  errcode_t magic;
359
  ext2_filsys fs;
360
  struct ext2_xattr *attrs;
361
  int capacity;
362
  int count;
363
  int ibody_count;
364
  ext2_ino_t ino;
365
  unsigned int flags;
366
};
367
368
static errcode_t ext2fs_xattrs_expand(struct ext2_xattr_handle *h,
369
              unsigned int expandby)
370
0
{
371
0
  struct ext2_xattr *new_attrs;
372
0
  errcode_t err;
373
374
0
  err = ext2fs_get_arrayzero(h->capacity + expandby,
375
0
           sizeof(struct ext2_xattr), &new_attrs);
376
0
  if (err)
377
0
    return err;
378
379
0
  memcpy(new_attrs, h->attrs, h->capacity * sizeof(struct ext2_xattr));
380
0
  ext2fs_free_mem(&h->attrs);
381
0
  h->capacity += expandby;
382
0
  h->attrs = new_attrs;
383
384
0
  return 0;
385
0
}
386
387
struct ea_name_index {
388
  int index;
389
  const char *name;
390
};
391
392
/* Keep these names sorted in order of decreasing specificity. */
393
static struct ea_name_index ea_names[] = {
394
  {10, "gnu."},
395
  {3, "system.posix_acl_default"},
396
  {2, "system.posix_acl_access"},
397
  {8, "system.richacl"},
398
  {6, "security."},
399
  {4, "trusted."},
400
  {7, "system."},
401
  {1, "user."},
402
  {0, NULL},
403
};
404
405
static const char *find_ea_prefix(int index)
406
0
{
407
0
  struct ea_name_index *e;
408
409
0
  for (e = ea_names; e->name; e++)
410
0
    if (e->index == index)
411
0
      return e->name;
412
413
0
  return NULL;
414
0
}
415
416
static int find_ea_index(const char *fullname, const char **name, int *index)
417
0
{
418
0
  struct ea_name_index *e;
419
420
0
  for (e = ea_names; e->name; e++) {
421
0
    if (strncmp(fullname, e->name, strlen(e->name)) == 0) {
422
0
      *name = fullname + strlen(e->name);
423
0
      *index = e->index;
424
0
      return 1;
425
0
    }
426
0
  }
427
0
  return 0;
428
0
}
429
430
errcode_t ext2fs_free_ext_attr(ext2_filsys fs, ext2_ino_t ino,
431
             struct ext2_inode_large *inode)
432
0
{
433
0
  struct ext2_ext_attr_header *header;
434
0
  void *block_buf = NULL;
435
0
  blk64_t blk;
436
0
  errcode_t err;
437
0
  struct ext2_inode_large i;
438
439
  /* Read inode? */
440
0
  if (inode == NULL) {
441
0
    err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&i,
442
0
               sizeof(struct ext2_inode_large));
443
0
    if (err)
444
0
      return err;
445
0
    inode = &i;
446
0
  }
447
448
  /* Do we already have an EA block? */
449
0
  blk = ext2fs_file_acl_block(fs, (struct ext2_inode *)inode);
450
0
  if (blk == 0)
451
0
    return 0;
452
453
  /* Find block, zero it, write back */
454
0
  if ((blk < fs->super->s_first_data_block) ||
455
0
      (blk >= ext2fs_blocks_count(fs->super))) {
456
0
    err = EXT2_ET_BAD_EA_BLOCK_NUM;
457
0
    goto out;
458
0
  }
459
460
0
  err = ext2fs_get_mem(fs->blocksize, &block_buf);
461
0
  if (err)
462
0
    goto out;
463
464
0
  err = ext2fs_read_ext_attr3(fs, blk, block_buf, ino);
465
0
  if (err)
466
0
    goto out2;
467
468
  /* We only know how to deal with v2 EA blocks */
469
0
  header = (struct ext2_ext_attr_header *) block_buf;
470
0
  if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
471
0
    err = EXT2_ET_BAD_EA_HEADER;
472
0
    goto out2;
473
0
  }
474
475
0
  header->h_refcount--;
476
0
  err = ext2fs_write_ext_attr3(fs, blk, block_buf, ino);
477
0
  if (err)
478
0
    goto out2;
479
480
  /* Erase link to block */
481
0
  ext2fs_file_acl_block_set(fs, (struct ext2_inode *)inode, 0);
482
0
  if (header->h_refcount == 0)
483
0
    ext2fs_block_alloc_stats2(fs, blk, -1);
484
0
  err = ext2fs_iblk_sub_blocks(fs, (struct ext2_inode *)inode, 1);
485
0
  if (err)
486
0
    goto out2;
487
488
  /* Write inode? */
489
0
  if (inode == &i) {
490
0
    err = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&i,
491
0
                sizeof(struct ext2_inode_large));
492
0
    if (err)
493
0
      goto out2;
494
0
  }
495
496
0
out2:
497
0
  ext2fs_free_mem(&block_buf);
498
0
out:
499
0
  return err;
500
0
}
501
502
static errcode_t prep_ea_block_for_write(ext2_filsys fs, ext2_ino_t ino,
503
           struct ext2_inode_large *inode)
504
0
{
505
0
  struct ext2_ext_attr_header *header;
506
0
  void *block_buf = NULL;
507
0
  blk64_t blk, goal;
508
0
  errcode_t err;
509
510
  /* Do we already have an EA block? */
511
0
  blk = ext2fs_file_acl_block(fs, (struct ext2_inode *)inode);
512
0
  if (blk != 0) {
513
0
    if ((blk < fs->super->s_first_data_block) ||
514
0
        (blk >= ext2fs_blocks_count(fs->super))) {
515
0
      err = EXT2_ET_BAD_EA_BLOCK_NUM;
516
0
      goto out;
517
0
    }
518
519
0
    err = ext2fs_get_mem(fs->blocksize, &block_buf);
520
0
    if (err)
521
0
      goto out;
522
523
0
    err = ext2fs_read_ext_attr3(fs, blk, block_buf, ino);
524
0
    if (err)
525
0
      goto out2;
526
527
    /* We only know how to deal with v2 EA blocks */
528
0
    header = (struct ext2_ext_attr_header *) block_buf;
529
0
    if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
530
0
      err = EXT2_ET_BAD_EA_HEADER;
531
0
      goto out2;
532
0
    }
533
534
    /* Single-user block.  We're done here. */
535
0
    if (header->h_refcount == 1)
536
0
      goto out2;
537
538
    /* We need to CoW the block. */
539
0
    header->h_refcount--;
540
0
    err = ext2fs_write_ext_attr3(fs, blk, block_buf, ino);
541
0
    if (err)
542
0
      goto out2;
543
0
  } else {
544
    /* No block, we must increment i_blocks */
545
0
    err = ext2fs_iblk_add_blocks(fs, (struct ext2_inode *)inode,
546
0
               1);
547
0
    if (err)
548
0
      goto out;
549
0
  }
550
551
  /* Allocate a block */
552
0
  goal = ext2fs_find_inode_goal(fs, ino, (struct ext2_inode *)inode, 0);
553
0
  err = ext2fs_alloc_block2(fs, goal, NULL, &blk);
554
0
  if (err)
555
0
    goto out2;
556
0
  ext2fs_file_acl_block_set(fs, (struct ext2_inode *)inode, blk);
557
0
out2:
558
0
  if (block_buf)
559
0
    ext2fs_free_mem(&block_buf);
560
0
out:
561
0
  return err;
562
0
}
563
564
565
static inline int
566
posix_acl_xattr_count(size_t size)
567
0
{
568
0
        if (size < sizeof(posix_acl_xattr_header))
569
0
                return -1;
570
0
        size -= sizeof(posix_acl_xattr_header);
571
0
        if (size % sizeof(posix_acl_xattr_entry))
572
0
                return -1;
573
0
        return size / sizeof(posix_acl_xattr_entry);
574
0
}
575
576
/*
577
 * The lgetxattr function returns data formatted in the POSIX extended
578
 * attribute format.  The on-disk format uses a more compact encoding.
579
 * See the ext4_acl_to_disk in fs/ext4/acl.c.
580
 */
581
static errcode_t convert_posix_acl_to_disk_buffer(const void *value, size_t size,
582
              void *out_buf, size_t *size_out)
583
0
{
584
0
  const posix_acl_xattr_header *header =
585
0
    (const posix_acl_xattr_header*) value;
586
0
  const posix_acl_xattr_entry *end, *entry =
587
0
    (const posix_acl_xattr_entry *)(header+1);
588
0
  ext4_acl_header *ext_acl;
589
0
  size_t s;
590
0
  char *e;
591
592
0
  int count;
593
594
0
  if (!value)
595
0
    return EINVAL;
596
0
  if (size < sizeof(posix_acl_xattr_header))
597
0
    return ENOMEM;
598
0
  if (header->a_version != ext2fs_cpu_to_le32(POSIX_ACL_XATTR_VERSION))
599
0
    return EINVAL;
600
601
0
  count = posix_acl_xattr_count(size);
602
0
  ext_acl = out_buf;
603
0
  ext_acl->a_version = ext2fs_cpu_to_le32(EXT4_ACL_VERSION);
604
605
0
  if (count <= 0)
606
0
    return EINVAL;
607
608
0
  e = (char *) out_buf + sizeof(ext4_acl_header);
609
0
  s = sizeof(ext4_acl_header);
610
0
  for (end = entry + count; entry != end;entry++) {
611
0
    ext4_acl_entry *disk_entry = (ext4_acl_entry*) e;
612
0
    disk_entry->e_tag = entry->e_tag;
613
0
    disk_entry->e_perm = entry->e_perm;
614
615
0
    switch(ext2fs_le16_to_cpu(entry->e_tag)) {
616
0
      case ACL_USER_OBJ:
617
0
      case ACL_GROUP_OBJ:
618
0
      case ACL_MASK:
619
0
      case ACL_OTHER:
620
0
        e += sizeof(ext4_acl_entry_short);
621
0
        s += sizeof(ext4_acl_entry_short);
622
0
        break;
623
0
      case ACL_USER:
624
0
      case ACL_GROUP:
625
0
        disk_entry->e_id = entry->e_id;
626
0
        e += sizeof(ext4_acl_entry);
627
0
        s += sizeof(ext4_acl_entry);
628
0
        break;
629
0
      default:
630
0
        return EINVAL;
631
0
    }
632
0
  }
633
0
  *size_out = s;
634
0
  return 0;
635
0
}
636
637
static errcode_t convert_disk_buffer_to_posix_acl(const void *value, size_t size,
638
              void **out_buf, size_t *size_out)
639
0
{
640
0
  posix_acl_xattr_header *header;
641
0
  posix_acl_xattr_entry *entry;
642
0
  const ext4_acl_header *ext_acl = (const ext4_acl_header *) value;
643
0
  errcode_t err;
644
0
  const char *cp;
645
0
  char *out;
646
647
0
  if ((!value) ||
648
0
      (size < sizeof(ext4_acl_header)) ||
649
0
      (ext_acl->a_version != ext2fs_cpu_to_le32(EXT4_ACL_VERSION)))
650
0
    return EINVAL;
651
652
0
  err = ext2fs_get_mem(size * 2, &out);
653
0
  if (err)
654
0
    return err;
655
656
0
  header = (posix_acl_xattr_header *) out;
657
0
  header->a_version = ext2fs_cpu_to_le32(POSIX_ACL_XATTR_VERSION);
658
0
  entry = (posix_acl_xattr_entry *) (out + sizeof(posix_acl_xattr_header));
659
660
0
  cp = (const char *) value + sizeof(ext4_acl_header);
661
0
  size -= sizeof(ext4_acl_header);
662
663
0
  while (size > 0) {
664
0
    const ext4_acl_entry *disk_entry = (const ext4_acl_entry *) cp;
665
666
0
    entry->e_tag = disk_entry->e_tag;
667
0
    entry->e_perm = disk_entry->e_perm;
668
669
0
    switch(ext2fs_le16_to_cpu(entry->e_tag)) {
670
0
      case ACL_USER_OBJ:
671
0
      case ACL_GROUP_OBJ:
672
0
      case ACL_MASK:
673
0
      case ACL_OTHER:
674
0
        entry->e_id = 0;
675
0
        cp += sizeof(ext4_acl_entry_short);
676
0
        size -= sizeof(ext4_acl_entry_short);
677
0
        break;
678
0
      case ACL_USER:
679
0
      case ACL_GROUP:
680
0
        entry->e_id = disk_entry->e_id;
681
0
        cp += sizeof(ext4_acl_entry);
682
0
        size -= sizeof(ext4_acl_entry);
683
0
        break;
684
0
      default:
685
0
        ext2fs_free_mem(&out);
686
0
        return EINVAL;
687
0
    }
688
0
    entry++;
689
0
  }
690
0
  *out_buf = out;
691
0
  *size_out = ((char *) entry - out);
692
0
  return 0;
693
0
}
694
695
static errcode_t
696
write_xattrs_to_buffer(ext2_filsys fs, struct ext2_xattr *attrs, int count,
697
           void *entries_start, unsigned int storage_size,
698
           unsigned int value_offset_correction, int write_hash)
699
0
{
700
0
  struct ext2_xattr *x;
701
0
  struct ext2_ext_attr_entry *e = entries_start;
702
0
  char *end = (char *) entries_start + storage_size;
703
0
  unsigned int value_size;
704
0
  errcode_t err;
705
706
0
  memset(entries_start, 0, storage_size);
707
0
  for (x = attrs; x < attrs + count; x++) {
708
0
    value_size = ((x->value_len + EXT2_EXT_ATTR_PAD - 1) /
709
0
            EXT2_EXT_ATTR_PAD) * EXT2_EXT_ATTR_PAD;
710
711
    /* Fill out e appropriately */
712
0
    e->e_name_len = strlen(x->short_name);
713
0
    e->e_name_index = x->name_index;
714
715
0
    e->e_value_size = x->value_len;
716
0
    e->e_value_inum = x->ea_ino;
717
718
    /* Store name */
719
0
    memcpy((char *)e + sizeof(*e), x->short_name, e->e_name_len);
720
0
    if (x->ea_ino) {
721
0
      e->e_value_offs = 0;
722
0
    } else {
723
0
      end -= value_size;
724
0
      e->e_value_offs = end - (char *) entries_start +
725
0
            value_offset_correction;
726
0
      memcpy(end, x->value, e->e_value_size);
727
0
    }
728
729
0
    if (write_hash || x->ea_ino) {
730
0
      err = ext2fs_ext_attr_hash_entry2(fs, e,
731
0
                x->ea_ino ? 0 : end,
732
0
                &e->e_hash);
733
0
      if (err)
734
0
        return err;
735
0
    } else
736
0
      e->e_hash = 0;
737
738
0
    e = EXT2_EXT_ATTR_NEXT(e);
739
0
    *(__u32 *)e = 0;
740
0
  }
741
0
  return 0;
742
0
}
743
744
errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle)
745
0
{
746
0
  ext2_filsys fs = handle->fs;
747
0
  const unsigned int inode_size = EXT2_INODE_SIZE(fs->super);
748
0
  struct ext2_inode_large *inode;
749
0
  char *start, *block_buf = NULL;
750
0
  struct ext2_ext_attr_header *header;
751
0
  __u32 ea_inode_magic;
752
0
  blk64_t blk;
753
0
  unsigned int storage_size;
754
0
  unsigned int i;
755
0
  errcode_t err;
756
757
0
  EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE);
758
0
  i = inode_size;
759
0
  if (i < sizeof(*inode))
760
0
    i = sizeof(*inode);
761
0
  err = ext2fs_get_memzero(i, &inode);
762
0
  if (err)
763
0
    return err;
764
765
0
  err = ext2fs_read_inode_full(fs, handle->ino, EXT2_INODE(inode),
766
0
             inode_size);
767
0
  if (err)
768
0
    goto out;
769
770
  /* If extra_isize isn't set, we need to set it now */
771
0
  if (inode->i_extra_isize == 0 &&
772
0
      inode_size > EXT2_GOOD_OLD_INODE_SIZE) {
773
0
    char *p = (char *)inode;
774
0
    size_t extra = fs->super->s_want_extra_isize;
775
776
0
    if (extra == 0)
777
0
      extra = sizeof(__u32);
778
0
    memset(p + EXT2_GOOD_OLD_INODE_SIZE, 0, extra);
779
0
    inode->i_extra_isize = extra;
780
0
  }
781
0
  if (inode->i_extra_isize & 3) {
782
0
    err = EXT2_ET_INODE_CORRUPTED;
783
0
    goto out;
784
0
  }
785
786
  /* Does the inode have space for EA? */
787
0
  if (inode->i_extra_isize < sizeof(inode->i_extra_isize) ||
788
0
      inode_size <= EXT2_GOOD_OLD_INODE_SIZE + inode->i_extra_isize +
789
0
                sizeof(__u32))
790
0
    goto write_ea_block;
791
792
  /* Write the inode EA */
793
0
  ea_inode_magic = EXT2_EXT_ATTR_MAGIC;
794
0
  memcpy(((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
795
0
         inode->i_extra_isize, &ea_inode_magic, sizeof(__u32));
796
0
  storage_size = inode_size - EXT2_GOOD_OLD_INODE_SIZE -
797
0
        inode->i_extra_isize - sizeof(__u32);
798
0
  start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
799
0
        inode->i_extra_isize + sizeof(__u32);
800
801
0
  err = write_xattrs_to_buffer(fs, handle->attrs, handle->ibody_count,
802
0
             start, storage_size, 0, 0);
803
0
  if (err)
804
0
    goto out;
805
0
write_ea_block:
806
  /* Are we done? */
807
0
  if (handle->ibody_count == handle->count &&
808
0
      !ext2fs_file_acl_block(fs, EXT2_INODE(inode)))
809
0
    goto skip_ea_block;
810
811
  /* Write the EA block */
812
0
  err = ext2fs_get_memzero(fs->blocksize, &block_buf);
813
0
  if (err)
814
0
    goto out;
815
816
0
  storage_size = fs->blocksize - sizeof(struct ext2_ext_attr_header);
817
0
  start = block_buf + sizeof(struct ext2_ext_attr_header);
818
819
0
  err = write_xattrs_to_buffer(fs, handle->attrs + handle->ibody_count,
820
0
             handle->count - handle->ibody_count, start,
821
0
             storage_size, start - block_buf, 1);
822
0
  if (err)
823
0
    goto out2;
824
825
  /* Write a header on the EA block */
826
0
  header = (struct ext2_ext_attr_header *) block_buf;
827
0
  header->h_magic = EXT2_EXT_ATTR_MAGIC;
828
0
  header->h_refcount = 1;
829
0
  header->h_blocks = 1;
830
831
  /* Get a new block for writing */
832
0
  err = prep_ea_block_for_write(fs, handle->ino, inode);
833
0
  if (err)
834
0
    goto out2;
835
836
  /* Finally, write the new EA block */
837
0
  blk = ext2fs_file_acl_block(fs, EXT2_INODE(inode));
838
0
  err = ext2fs_write_ext_attr3(fs, blk, block_buf, handle->ino);
839
0
  if (err)
840
0
    goto out2;
841
842
0
skip_ea_block:
843
0
  blk = ext2fs_file_acl_block(fs, (struct ext2_inode *)inode);
844
0
  if (!block_buf && blk) {
845
    /* xattrs shrunk, free the block */
846
0
    err = ext2fs_free_ext_attr(fs, handle->ino, inode);
847
0
    if (err)
848
0
      goto out;
849
0
  }
850
851
  /* Write the inode */
852
0
  err = ext2fs_write_inode_full(fs, handle->ino, EXT2_INODE(inode),
853
0
              inode_size);
854
0
  if (err)
855
0
    goto out2;
856
857
0
out2:
858
0
  ext2fs_free_mem(&block_buf);
859
0
out:
860
0
  ext2fs_free_mem(&inode);
861
0
  return err;
862
0
}
863
864
static errcode_t read_xattrs_from_buffer(struct ext2_xattr_handle *handle,
865
           struct ext2_inode_large *inode,
866
           struct ext2_ext_attr_entry *entries,
867
           unsigned int storage_size,
868
           char *value_start)
869
0
{
870
0
  struct ext2_xattr *x;
871
0
  struct ext2_ext_attr_entry *entry, *end;
872
0
  const char *prefix;
873
0
  unsigned int remain, prefix_len;
874
0
  errcode_t err;
875
0
  unsigned int values_size = storage_size +
876
0
      ((char *)entries - value_start);
877
878
  /* find the end */
879
0
  end = entries;
880
0
  remain = storage_size;
881
0
  while (remain >= sizeof(struct ext2_ext_attr_entry) &&
882
0
         !EXT2_EXT_IS_LAST_ENTRY(end)) {
883
884
    /* header eats this space */
885
0
    remain -= sizeof(struct ext2_ext_attr_entry);
886
887
    /* is attribute name valid? */
888
0
    if (EXT2_EXT_ATTR_SIZE(end->e_name_len) > remain)
889
0
      return EXT2_ET_EA_BAD_NAME_LEN;
890
891
    /* attribute len eats this space */
892
0
    remain -= EXT2_EXT_ATTR_SIZE(end->e_name_len);
893
0
    end = EXT2_EXT_ATTR_NEXT(end);
894
0
  }
895
896
0
  entry = entries;
897
0
  remain = storage_size;
898
0
  while (remain >= sizeof(struct ext2_ext_attr_entry) &&
899
0
         !EXT2_EXT_IS_LAST_ENTRY(entry)) {
900
901
    /* Allocate space for more attrs? */
902
0
    if (handle->count == handle->capacity) {
903
0
      err = ext2fs_xattrs_expand(handle, 4);
904
0
      if (err)
905
0
        return err;
906
0
    }
907
908
0
    x = handle->attrs + handle->count;
909
910
    /* header eats this space */
911
0
    remain -= sizeof(struct ext2_ext_attr_entry);
912
913
    /* attribute len eats this space */
914
0
    remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
915
916
    /* Extract name */
917
0
    prefix = find_ea_prefix(entry->e_name_index);
918
0
    prefix_len = (prefix ? strlen(prefix) : 0);
919
0
    err = ext2fs_get_memzero(entry->e_name_len + prefix_len + 1,
920
0
           &x->name);
921
0
    if (err)
922
0
      return err;
923
0
    if (prefix)
924
0
      memcpy(x->name, prefix, prefix_len);
925
0
    if (entry->e_name_len)
926
0
      memcpy(x->name + prefix_len,
927
0
             (char *)entry + sizeof(*entry),
928
0
             entry->e_name_len);
929
0
    x->short_name = x->name + prefix_len;
930
0
    x->name_index = entry->e_name_index;
931
932
    /* Check & copy value */
933
0
    if (!ext2fs_has_feature_ea_inode(handle->fs->super) &&
934
0
        entry->e_value_inum != 0)
935
0
      return EXT2_ET_BAD_EA_BLOCK_NUM;
936
937
0
    if (entry->e_value_inum == 0) {
938
0
      if (entry->e_value_size > remain)
939
0
        return EXT2_ET_EA_BAD_VALUE_SIZE;
940
941
0
      if (entry->e_value_offs + entry->e_value_size > values_size)
942
0
        return EXT2_ET_EA_BAD_VALUE_OFFSET;
943
944
0
      if (entry->e_value_size > 0 &&
945
0
          value_start + entry->e_value_offs <
946
0
          (char *)end + sizeof(__u32))
947
0
        return EXT2_ET_EA_BAD_VALUE_OFFSET;
948
949
0
      remain -= entry->e_value_size;
950
951
0
      err = ext2fs_get_mem(entry->e_value_size, &x->value);
952
0
      if (err)
953
0
        return err;
954
0
      memcpy(x->value, value_start + entry->e_value_offs,
955
0
             entry->e_value_size);
956
0
    } else {
957
0
      struct ext2_inode *ea_inode;
958
0
      ext2_file_t ea_file;
959
960
0
      if (entry->e_value_offs != 0)
961
0
        return EXT2_ET_EA_BAD_VALUE_OFFSET;
962
963
0
      if (entry->e_value_size > (64 * 1024))
964
0
        return EXT2_ET_EA_BAD_VALUE_SIZE;
965
966
0
      err = ext2fs_get_mem(entry->e_value_size, &x->value);
967
0
      if (err)
968
0
        return err;
969
970
0
      err = ext2fs_file_open(handle->fs, entry->e_value_inum,
971
0
                 0, &ea_file);
972
0
      if (err)
973
0
        return err;
974
975
0
      ea_inode = ext2fs_file_get_inode(ea_file);
976
0
      if ((ea_inode->i_flags & EXT4_INLINE_DATA_FL) ||
977
0
          !(ea_inode->i_flags & EXT4_EA_INODE_FL) ||
978
0
          ea_inode->i_links_count == 0)
979
0
        err = EXT2_ET_EA_INODE_CORRUPTED;
980
0
      else if ((__u64) ext2fs_file_get_size(ea_file) !=
981
0
         entry->e_value_size)
982
0
        err = EXT2_ET_EA_BAD_VALUE_SIZE;
983
0
      else
984
0
        err = ext2fs_file_read(ea_file, x->value,
985
0
                   entry->e_value_size, 0);
986
0
      ext2fs_file_close(ea_file);
987
0
      if (err)
988
0
        return err;
989
0
    }
990
991
0
    x->ea_ino = entry->e_value_inum;
992
0
    x->value_len = entry->e_value_size;
993
994
    /* e_hash may be 0 in older inode's ea */
995
0
    if (entry->e_hash != 0) {
996
0
      __u32 hash, signed_hash;
997
998
0
      void *data = (entry->e_value_inum != 0) ?
999
0
          0 : value_start + entry->e_value_offs;
1000
1001
0
      err = ext2fs_ext_attr_hash_entry3(handle->fs, entry,
1002
0
                data, &hash,
1003
0
                &signed_hash);
1004
0
      if (err)
1005
0
        return err;
1006
0
      if ((entry->e_hash != hash) &&
1007
0
          (entry->e_hash != signed_hash)) {
1008
0
        struct ext2_inode child;
1009
1010
        /* Check whether this is an old Lustre-style
1011
         * ea_inode reference.
1012
         */
1013
0
        err = ext2fs_read_inode(handle->fs,
1014
0
              entry->e_value_inum,
1015
0
              &child);
1016
0
        if (err)
1017
0
          return err;
1018
0
        if (child.i_mtime != handle->ino ||
1019
0
            child.i_generation != inode->i_generation)
1020
0
          return EXT2_ET_BAD_EA_HASH;
1021
0
      }
1022
0
    }
1023
1024
0
    handle->count++;
1025
0
    entry = EXT2_EXT_ATTR_NEXT(entry);
1026
0
  }
1027
1028
0
  return 0;
1029
0
}
1030
1031
static void xattrs_free_keys(struct ext2_xattr_handle *h)
1032
0
{
1033
0
  struct ext2_xattr *a = h->attrs;
1034
0
  int i;
1035
1036
0
  for (i = 0; i < h->capacity; i++) {
1037
0
    if (a[i].name)
1038
0
      ext2fs_free_mem(&a[i].name);
1039
0
    if (a[i].value)
1040
0
      ext2fs_free_mem(&a[i].value);
1041
0
  }
1042
0
  h->count = 0;
1043
0
  h->ibody_count = 0;
1044
0
}
1045
1046
/* fetch xattrs from an already-loaded inode */
1047
errcode_t ext2fs_xattrs_read_inode(struct ext2_xattr_handle *handle,
1048
           struct ext2_inode_large *inode)
1049
0
{
1050
0
  struct ext2_ext_attr_header *header;
1051
0
  __u32 ea_inode_magic;
1052
0
  unsigned int storage_size;
1053
0
  char *start, *block_buf = NULL;
1054
0
  blk64_t blk;
1055
0
  errcode_t err = 0;
1056
1057
0
  EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE);
1058
1059
0
  xattrs_free_keys(handle);
1060
1061
  /* Does the inode have space for EA? */
1062
0
  if (inode->i_extra_isize < sizeof(inode->i_extra_isize) ||
1063
0
      EXT2_INODE_SIZE(handle->fs->super) <= EXT2_GOOD_OLD_INODE_SIZE +
1064
0
              inode->i_extra_isize +
1065
0
              sizeof(__u32))
1066
0
    goto read_ea_block;
1067
0
  if (inode->i_extra_isize & 3) {
1068
0
    err = EXT2_ET_INODE_CORRUPTED;
1069
0
    goto out;
1070
0
  }
1071
1072
  /* Look for EA in the inode */
1073
0
  memcpy(&ea_inode_magic, ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
1074
0
         inode->i_extra_isize, sizeof(__u32));
1075
0
  if (ea_inode_magic == EXT2_EXT_ATTR_MAGIC) {
1076
0
    storage_size = EXT2_INODE_SIZE(handle->fs->super) -
1077
0
      EXT2_GOOD_OLD_INODE_SIZE - inode->i_extra_isize -
1078
0
      sizeof(__u32);
1079
0
    start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
1080
0
      inode->i_extra_isize + sizeof(__u32);
1081
1082
0
    err = read_xattrs_from_buffer(handle, inode,
1083
0
          (struct ext2_ext_attr_entry *) start,
1084
0
          storage_size, start);
1085
0
    if (err)
1086
0
      goto out;
1087
1088
0
    handle->ibody_count = handle->count;
1089
0
  }
1090
1091
0
read_ea_block:
1092
  /* Look for EA in a separate EA block */
1093
0
  blk = ext2fs_file_acl_block(handle->fs, EXT2_INODE(inode));
1094
0
  if (blk != 0) {
1095
0
    if ((blk < handle->fs->super->s_first_data_block) ||
1096
0
        (blk >= ext2fs_blocks_count(handle->fs->super))) {
1097
0
      err = EXT2_ET_BAD_EA_BLOCK_NUM;
1098
0
      goto out;
1099
0
    }
1100
1101
0
    err = ext2fs_get_mem(handle->fs->blocksize, &block_buf);
1102
0
    if (err)
1103
0
      goto out;
1104
1105
0
    err = ext2fs_read_ext_attr3(handle->fs, blk, block_buf,
1106
0
              handle->ino);
1107
0
    if (err)
1108
0
      goto out3;
1109
1110
    /* We only know how to deal with v2 EA blocks */
1111
0
    header = (struct ext2_ext_attr_header *) block_buf;
1112
0
    if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
1113
0
      err = EXT2_ET_BAD_EA_HEADER;
1114
0
      goto out3;
1115
0
    }
1116
1117
    /* Read EAs */
1118
0
    storage_size = handle->fs->blocksize -
1119
0
      sizeof(struct ext2_ext_attr_header);
1120
0
    start = block_buf + sizeof(struct ext2_ext_attr_header);
1121
0
    err = read_xattrs_from_buffer(handle, inode,
1122
0
          (struct ext2_ext_attr_entry *) start,
1123
0
          storage_size, block_buf);
1124
0
  }
1125
1126
0
out3:
1127
0
  if (block_buf)
1128
0
    ext2fs_free_mem(&block_buf);
1129
0
out:
1130
0
  return err;
1131
0
}
1132
1133
errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle)
1134
0
{
1135
0
  struct ext2_inode_large *inode;
1136
0
  size_t inode_size = EXT2_INODE_SIZE(handle->fs->super);
1137
0
  errcode_t err;
1138
1139
0
  EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE);
1140
1141
0
  if (inode_size < sizeof(*inode))
1142
0
    inode_size = sizeof(*inode);
1143
0
  err = ext2fs_get_memzero(inode_size, &inode);
1144
0
  if (err)
1145
0
    return err;
1146
1147
0
  err = ext2fs_read_inode_full(handle->fs, handle->ino, EXT2_INODE(inode),
1148
0
             EXT2_INODE_SIZE(handle->fs->super));
1149
0
  if (err)
1150
0
    goto out;
1151
1152
0
  err = ext2fs_xattrs_read_inode(handle, inode);
1153
1154
0
out:
1155
0
  ext2fs_free_mem(&inode);
1156
1157
0
  return err;
1158
0
}
1159
1160
errcode_t ext2fs_xattrs_iterate(struct ext2_xattr_handle *h,
1161
        int (*func)(char *name, char *value,
1162
              size_t value_len, void *data),
1163
        void *data)
1164
0
{
1165
0
  struct ext2_xattr *x;
1166
0
  int dirty = 0;
1167
0
  int ret;
1168
1169
0
  EXT2_CHECK_MAGIC(h, EXT2_ET_MAGIC_EA_HANDLE);
1170
0
  for (x = h->attrs; x < h->attrs + h->count; x++) {
1171
0
    ret = func(x->name, x->value, x->value_len, data);
1172
0
    if (ret & XATTR_CHANGED)
1173
0
      dirty = 1;
1174
0
    if (ret & XATTR_ABORT)
1175
0
      break;
1176
0
  }
1177
1178
0
  if (dirty)
1179
0
    return ext2fs_xattrs_write(h);
1180
0
  return 0;
1181
0
}
1182
1183
errcode_t ext2fs_xattr_get(struct ext2_xattr_handle *h, const char *key,
1184
         void **value, size_t *value_len)
1185
0
{
1186
0
  struct ext2_xattr *x;
1187
0
  char *val;
1188
0
  errcode_t err;
1189
1190
0
  EXT2_CHECK_MAGIC(h, EXT2_ET_MAGIC_EA_HANDLE);
1191
0
  for (x = h->attrs; x < h->attrs + h->count; x++) {
1192
0
    if (strcmp(x->name, key))
1193
0
      continue;
1194
1195
0
    if (!(h->flags & XATTR_HANDLE_FLAG_RAW) &&
1196
0
        ((strcmp(key, "system.posix_acl_default") == 0) ||
1197
0
         (strcmp(key, "system.posix_acl_access") == 0))) {
1198
0
      err = convert_disk_buffer_to_posix_acl(x->value, x->value_len,
1199
0
                     value, value_len);
1200
0
      return err;
1201
0
    } else {
1202
0
      err = ext2fs_get_mem(x->value_len, &val);
1203
0
      if (err)
1204
0
        return err;
1205
0
      memcpy(val, x->value, x->value_len);
1206
0
      *value = val;
1207
0
      *value_len = x->value_len;
1208
0
      return 0;
1209
0
    }
1210
0
  }
1211
1212
0
  return EXT2_ET_EA_KEY_NOT_FOUND;
1213
0
}
1214
1215
errcode_t ext2fs_xattr_inode_max_size(ext2_filsys fs, ext2_ino_t ino,
1216
              size_t *size)
1217
0
{
1218
0
  struct ext2_ext_attr_entry *entry;
1219
0
  struct ext2_inode_large *inode;
1220
0
  __u32 ea_inode_magic;
1221
0
  unsigned int minoff;
1222
0
  char *start;
1223
0
  size_t i;
1224
0
  errcode_t err;
1225
1226
0
  i = EXT2_INODE_SIZE(fs->super);
1227
0
  if (i < sizeof(*inode))
1228
0
    i = sizeof(*inode);
1229
0
  err = ext2fs_get_memzero(i, &inode);
1230
0
  if (err)
1231
0
    return err;
1232
1233
0
  err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)inode,
1234
0
             EXT2_INODE_SIZE(fs->super));
1235
0
  if (err)
1236
0
    goto out;
1237
1238
  /* Does the inode have size for EA? */
1239
0
  if (EXT2_INODE_SIZE(fs->super) <= EXT2_GOOD_OLD_INODE_SIZE +
1240
0
              inode->i_extra_isize +
1241
0
              sizeof(__u32)) {
1242
0
    err = EXT2_ET_INLINE_DATA_NO_SPACE;
1243
0
    goto out;
1244
0
  }
1245
1246
0
  minoff = EXT2_INODE_SIZE(fs->super) - sizeof(*inode) - sizeof(__u32);
1247
0
  memcpy(&ea_inode_magic, ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
1248
0
         inode->i_extra_isize, sizeof(__u32));
1249
0
  if (ea_inode_magic == EXT2_EXT_ATTR_MAGIC) {
1250
    /* has xattrs.  calculate the size */
1251
0
    start= ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
1252
0
      inode->i_extra_isize + sizeof(__u32);
1253
0
    entry = (struct ext2_ext_attr_entry *) start;
1254
0
    while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
1255
0
      if (!entry->e_value_inum && entry->e_value_size) {
1256
0
        unsigned int offs = entry->e_value_offs;
1257
0
        if (offs < minoff)
1258
0
          minoff = offs;
1259
0
      }
1260
0
      entry = EXT2_EXT_ATTR_NEXT(entry);
1261
0
    }
1262
0
    *size = minoff - ((char *)entry - (char *)start) - sizeof(__u32);
1263
0
  } else {
1264
    /* no xattr.  return a maximum size */
1265
0
    *size = EXT2_EXT_ATTR_SIZE(minoff -
1266
0
             EXT2_EXT_ATTR_LEN(strlen("data")) -
1267
0
             EXT2_EXT_ATTR_ROUND - sizeof(__u32));
1268
0
  }
1269
1270
0
out:
1271
0
  ext2fs_free_mem(&inode);
1272
0
  return err;
1273
0
}
1274
1275
static errcode_t xattr_create_ea_inode(ext2_filsys fs, const void *value,
1276
               size_t value_len, ext2_ino_t *ea_ino)
1277
0
{
1278
0
  struct ext2_inode inode;
1279
0
  ext2_ino_t ino;
1280
0
  ext2_file_t file;
1281
0
  __u32 hash;
1282
0
  errcode_t ret;
1283
1284
0
  ret = ext2fs_new_inode(fs, 0, 0, 0, &ino);
1285
0
  if (ret)
1286
0
    return ret;
1287
1288
0
  memset(&inode, 0, sizeof(inode));
1289
0
  inode.i_flags |= EXT4_EA_INODE_FL;
1290
0
  if (ext2fs_has_feature_extents(fs->super))
1291
0
    inode.i_flags |= EXT4_EXTENTS_FL;
1292
0
  inode.i_size = 0;
1293
0
  inode.i_mode = LINUX_S_IFREG | 0600;
1294
0
  inode.i_links_count = 1;
1295
0
  ret = ext2fs_write_new_inode(fs, ino, &inode);
1296
0
  if (ret)
1297
0
    return ret;
1298
  /*
1299
   * ref_count and hash utilize inode's i_*time fields.
1300
   * ext2fs_write_new_inode() call above initializes these fields with
1301
   * current time. That's why ref count and hash updates are done
1302
   * separately below.
1303
   */
1304
0
  ext2fs_set_ea_inode_ref(&inode, 1);
1305
0
  hash = ext2fs_crc32c_le(fs->csum_seed, value, value_len);
1306
0
  ext2fs_set_ea_inode_hash(&inode, hash);
1307
1308
0
  ret = ext2fs_write_inode(fs, ino, &inode);
1309
0
  if (ret)
1310
0
    return ret;
1311
1312
0
  ret = ext2fs_file_open(fs, ino, EXT2_FILE_WRITE, &file);
1313
0
  if (ret)
1314
0
    return ret;
1315
0
  ret = ext2fs_file_write(file, value, value_len, NULL);
1316
0
  ext2fs_file_close(file);
1317
0
  if (ret)
1318
0
    return ret;
1319
1320
0
  ext2fs_inode_alloc_stats2(fs, ino, 1 /* inuse */, 0 /* isdir */);
1321
1322
0
  *ea_ino = ino;
1323
0
  return 0;
1324
0
}
1325
1326
static errcode_t xattr_inode_dec_ref(ext2_filsys fs, ext2_ino_t ino)
1327
0
{
1328
0
  struct ext2_inode_large inode;
1329
0
  __u64 ref_count;
1330
0
  errcode_t ret;
1331
1332
0
  ret = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&inode,
1333
0
             sizeof(inode));
1334
0
  if (ret)
1335
0
    goto out;
1336
1337
0
  ref_count = ext2fs_get_ea_inode_ref(EXT2_INODE(&inode));
1338
0
  ref_count--;
1339
0
  ext2fs_set_ea_inode_ref(EXT2_INODE(&inode), ref_count);
1340
1341
0
  if (ref_count)
1342
0
    goto write_out;
1343
1344
0
  inode.i_links_count = 0;
1345
0
  inode.i_dtime = fs->now ? fs->now : time(0);
1346
1347
0
  ret = ext2fs_free_ext_attr(fs, ino, &inode);
1348
0
  if (ret)
1349
0
    goto write_out;
1350
1351
0
  if (ext2fs_inode_has_valid_blocks2(fs, (struct ext2_inode *)&inode)) {
1352
0
    ret = ext2fs_punch(fs, ino, (struct ext2_inode *)&inode, NULL,
1353
0
           0, ~0ULL);
1354
0
    if (ret)
1355
0
      goto out;
1356
0
  }
1357
1358
0
  ext2fs_inode_alloc_stats2(fs, ino, -1 /* inuse */, 0 /* is_dir */);
1359
1360
0
write_out:
1361
0
  ret = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&inode,
1362
0
              sizeof(inode));
1363
0
out:
1364
0
  return ret;
1365
0
}
1366
1367
static errcode_t xattr_update_entry(ext2_filsys fs, struct ext2_xattr *x,
1368
            const char *name, const char *short_name,
1369
            int index, const void *value,
1370
            size_t value_len, int in_inode)
1371
0
{
1372
0
  ext2_ino_t ea_ino = 0;
1373
0
  void *new_value = NULL;
1374
0
  char *new_name = NULL;
1375
0
  int name_len;
1376
0
  errcode_t ret;
1377
1378
0
  if (!x->name) {
1379
0
    name_len = strlen(name);
1380
0
    ret = ext2fs_get_mem(name_len + 1, &new_name);
1381
0
    if (ret)
1382
0
      goto fail;
1383
0
    memcpy(new_name, name, name_len + 1);
1384
0
  }
1385
1386
0
  ret = ext2fs_get_mem(value_len, &new_value);
1387
0
  if (ret)
1388
0
    goto fail;
1389
0
  memcpy(new_value, value, value_len);
1390
1391
0
  if (in_inode) {
1392
0
    ret = xattr_create_ea_inode(fs, value, value_len, &ea_ino);
1393
0
    if (ret)
1394
0
      goto fail;
1395
0
  }
1396
1397
0
  if (x->ea_ino) {
1398
0
    ret = xattr_inode_dec_ref(fs, x->ea_ino);
1399
0
    if (ret)
1400
0
      goto fail;
1401
0
  }
1402
1403
0
  if (!x->name) {
1404
0
    x->name = new_name;
1405
0
    x->short_name = new_name + (short_name  - name);
1406
0
  }
1407
0
  x->name_index = index;
1408
1409
0
  if (x->value)
1410
0
    ext2fs_free_mem(&x->value);
1411
0
  x->value = new_value;
1412
0
  x->value_len = value_len;
1413
0
  x->ea_ino = ea_ino;
1414
0
  return 0;
1415
0
fail:
1416
0
  if (new_name)
1417
0
    ext2fs_free_mem(&new_name);
1418
0
  if (new_value)
1419
0
    ext2fs_free_mem(&new_value);
1420
0
  if (ea_ino)
1421
0
    xattr_inode_dec_ref(fs, ea_ino);
1422
0
  return ret;
1423
0
}
1424
1425
static int xattr_find_position(struct ext2_xattr *attrs, int count,
1426
             const char *shortname, int name_idx)
1427
0
{
1428
0
  struct ext2_xattr *x;
1429
0
  int i;
1430
0
  int shortname_len, x_shortname_len;
1431
1432
0
  shortname_len = strlen(shortname);
1433
1434
0
  for (i = 0, x = attrs; i < count; i++, x++) {
1435
0
    if (name_idx < x->name_index)
1436
0
      break;
1437
0
    if (name_idx > x->name_index)
1438
0
      continue;
1439
1440
0
    x_shortname_len = strlen(x->short_name);
1441
0
    if (shortname_len < x_shortname_len)
1442
0
      break;
1443
0
    if (shortname_len > x_shortname_len)
1444
0
      continue;
1445
1446
0
    if (memcmp(shortname, x->short_name, shortname_len) <= 0)
1447
0
      break;
1448
0
  }
1449
0
  return i;
1450
0
}
1451
1452
static errcode_t xattr_array_update(struct ext2_xattr_handle *h,
1453
            const char *name,
1454
            const void *value, size_t value_len,
1455
            int ibody_free, int block_free,
1456
            int old_idx, int in_inode)
1457
0
{
1458
0
  struct ext2_xattr tmp;
1459
0
  int add_to_ibody;
1460
0
  int needed;
1461
0
  int name_len, name_idx = 0;
1462
0
  const char *shortname = name;
1463
0
  int new_idx;
1464
0
  int ret;
1465
1466
0
  find_ea_index(name, &shortname, &name_idx);
1467
0
  name_len = strlen(shortname);
1468
1469
0
  needed = EXT2_EXT_ATTR_LEN(name_len);
1470
0
  if (!in_inode)
1471
0
    needed += EXT2_EXT_ATTR_SIZE(value_len);
1472
1473
0
  if (old_idx >= 0 && old_idx < h->ibody_count) {
1474
0
    ibody_free += EXT2_EXT_ATTR_LEN(name_len);
1475
0
    if (!h->attrs[old_idx].ea_ino)
1476
0
      ibody_free += EXT2_EXT_ATTR_SIZE(
1477
0
            h->attrs[old_idx].value_len);
1478
0
  }
1479
1480
0
  if (needed <= ibody_free) {
1481
0
    if (old_idx < 0) {
1482
0
      new_idx = h->ibody_count;
1483
0
      add_to_ibody = 1;
1484
0
      goto add_new;
1485
0
    }
1486
1487
    /* Update the existing entry. */
1488
0
    ret = xattr_update_entry(h->fs, &h->attrs[old_idx], name,
1489
0
           shortname, name_idx, value,
1490
0
           value_len, in_inode);
1491
0
    if (ret)
1492
0
      return ret;
1493
0
    if (h->ibody_count <= old_idx) {
1494
      /* Move entry from block to the end of ibody. */
1495
0
      tmp = h->attrs[old_idx];
1496
0
      memmove(h->attrs + h->ibody_count + 1,
1497
0
        h->attrs + h->ibody_count,
1498
0
        (old_idx - h->ibody_count) * sizeof(*h->attrs));
1499
0
      h->attrs[h->ibody_count] = tmp;
1500
0
      h->ibody_count++;
1501
0
    }
1502
0
    return 0;
1503
0
  }
1504
1505
0
  if (h->ibody_count <= old_idx) {
1506
0
    block_free += EXT2_EXT_ATTR_LEN(name_len);
1507
0
    if (!h->attrs[old_idx].ea_ino)
1508
0
      block_free +=
1509
0
        EXT2_EXT_ATTR_SIZE(h->attrs[old_idx].value_len);
1510
0
  }
1511
1512
0
  if (needed > block_free)
1513
0
    return EXT2_ET_EA_NO_SPACE;
1514
1515
0
  if (old_idx >= 0) {
1516
    /* Update the existing entry. */
1517
0
    ret = xattr_update_entry(h->fs, &h->attrs[old_idx], name,
1518
0
           shortname, name_idx, value,
1519
0
           value_len, in_inode);
1520
0
    if (ret)
1521
0
      return ret;
1522
0
    if (old_idx < h->ibody_count) {
1523
      /*
1524
       * Move entry from ibody to the block. Note that
1525
       * entries in the block are sorted.
1526
       */
1527
0
      new_idx = xattr_find_position(h->attrs + h->ibody_count,
1528
0
                  h->count - h->ibody_count,
1529
0
                  shortname, name_idx);
1530
0
      new_idx += h->ibody_count - 1;
1531
0
      tmp = h->attrs[old_idx];
1532
0
      memmove(h->attrs + old_idx, h->attrs + old_idx + 1,
1533
0
        (new_idx - old_idx) * sizeof(*h->attrs));
1534
0
      h->attrs[new_idx] = tmp;
1535
0
      h->ibody_count--;
1536
0
    }
1537
0
    return 0;
1538
0
  }
1539
1540
0
  new_idx = xattr_find_position(h->attrs + h->ibody_count,
1541
0
              h->count - h->ibody_count,
1542
0
              shortname, name_idx);
1543
0
  new_idx += h->ibody_count;
1544
0
  add_to_ibody = 0;
1545
1546
0
add_new:
1547
0
  if (h->count == h->capacity) {
1548
0
    ret = ext2fs_xattrs_expand(h, 4);
1549
0
    if (ret)
1550
0
      return ret;
1551
0
  }
1552
1553
0
  ret = xattr_update_entry(h->fs, &h->attrs[h->count], name, shortname,
1554
0
         name_idx, value, value_len, in_inode);
1555
0
  if (ret)
1556
0
    return ret;
1557
1558
0
  tmp = h->attrs[h->count];
1559
0
  memmove(h->attrs + new_idx + 1, h->attrs + new_idx,
1560
0
    (h->count - new_idx)*sizeof(*h->attrs));
1561
0
  h->attrs[new_idx] = tmp;
1562
0
  if (add_to_ibody)
1563
0
    h->ibody_count++;
1564
0
  h->count++;
1565
0
  return 0;
1566
0
}
1567
1568
static int space_used(struct ext2_xattr *attrs, int count)
1569
0
{
1570
0
  int total = 0;
1571
0
  struct ext2_xattr *x;
1572
0
  int i, len;
1573
1574
0
  for (i = 0, x = attrs; i < count; i++, x++) {
1575
0
    len = strlen(x->short_name);
1576
0
    total += EXT2_EXT_ATTR_LEN(len);
1577
0
    if (!x->ea_ino)
1578
0
      total += EXT2_EXT_ATTR_SIZE(x->value_len);
1579
0
  }
1580
0
  return total;
1581
0
}
1582
1583
/*
1584
 * The minimum size of EA value when you start storing it in an external inode
1585
 * size of block - size of header - size of 1 entry - 4 null bytes
1586
 */
1587
#define EXT4_XATTR_MIN_LARGE_EA_SIZE(b) \
1588
0
  ((b) - EXT2_EXT_ATTR_LEN(3) - sizeof(struct ext2_ext_attr_header) - 4)
1589
1590
errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *h,
1591
         const char *name,
1592
         const void *value,
1593
         size_t value_len)
1594
0
{
1595
0
  ext2_filsys fs = h->fs;
1596
0
  const int inode_size = EXT2_INODE_SIZE(fs->super);
1597
0
  struct ext2_inode_large *inode = NULL;
1598
0
  struct ext2_xattr *x;
1599
0
  char *new_value;
1600
0
  int ibody_free, block_free;
1601
0
  int in_inode = 0;
1602
0
  int old_idx = -1;
1603
0
  int extra_isize;
1604
0
  errcode_t ret;
1605
1606
0
  EXT2_CHECK_MAGIC(h, EXT2_ET_MAGIC_EA_HANDLE);
1607
1608
0
  ret = ext2fs_get_mem(value_len, &new_value);
1609
0
  if (ret)
1610
0
    return ret;
1611
0
  if (!(h->flags & XATTR_HANDLE_FLAG_RAW) &&
1612
0
      ((strcmp(name, "system.posix_acl_default") == 0) ||
1613
0
       (strcmp(name, "system.posix_acl_access") == 0))) {
1614
0
    ret = convert_posix_acl_to_disk_buffer(value, value_len,
1615
0
                   new_value, &value_len);
1616
0
    if (ret)
1617
0
      goto out;
1618
0
  } else if (value_len)
1619
0
    memcpy(new_value, value, value_len);
1620
1621
  /* Imitate kernel behavior by skipping update if value is the same. */
1622
0
  for (x = h->attrs; x < h->attrs + h->count; x++) {
1623
0
    if (!strcmp(x->name, name)) {
1624
0
      if (!x->ea_ino && x->value_len == value_len &&
1625
0
          (!value_len ||
1626
0
           !memcmp(x->value, new_value, value_len))) {
1627
0
        ret = 0;
1628
0
        goto out;
1629
0
      }
1630
0
      old_idx = x - h->attrs;
1631
0
      break;
1632
0
    }
1633
0
  }
1634
1635
0
  ret = ext2fs_get_memzero(inode_size, &inode);
1636
0
  if (ret)
1637
0
    goto out;
1638
0
  ret = ext2fs_read_inode_full(fs, h->ino,
1639
0
             (struct ext2_inode *)inode,
1640
0
             inode_size);
1641
0
  if (ret)
1642
0
    goto out;
1643
0
  if (inode_size > EXT2_GOOD_OLD_INODE_SIZE) {
1644
0
    extra_isize = inode->i_extra_isize;
1645
0
    if (extra_isize == 0) {
1646
0
      extra_isize = fs->super->s_want_extra_isize;
1647
0
      if (extra_isize == 0)
1648
0
        extra_isize = sizeof(__u32);
1649
0
    }
1650
0
    ibody_free = inode_size - EXT2_GOOD_OLD_INODE_SIZE;
1651
0
    ibody_free -= extra_isize;
1652
    /* Extended attribute magic and final null entry. */
1653
0
    ibody_free -= sizeof(__u32) * 2;
1654
0
    ibody_free -= space_used(h->attrs, h->ibody_count);
1655
0
  } else
1656
0
    ibody_free = 0;
1657
1658
  /* Inline data can only go to ibody. */
1659
0
  if (strcmp(name, "system.data") == 0) {
1660
0
    if (h->ibody_count <= old_idx) {
1661
0
      ret = EXT2_ET_FILESYSTEM_CORRUPTED;
1662
0
      goto out;
1663
0
    }
1664
0
    ret = xattr_array_update(h, name, new_value, value_len,
1665
0
           ibody_free,
1666
0
           0 /* block_free */, old_idx,
1667
0
           0 /* in_inode */);
1668
0
    if (ret)
1669
0
      goto out;
1670
0
    goto write_out;
1671
0
  }
1672
1673
0
  block_free = fs->blocksize;
1674
0
  block_free -= sizeof(struct ext2_ext_attr_header);
1675
  /* Final null entry. */
1676
0
  block_free -= sizeof(__u32);
1677
0
  block_free -= space_used(h->attrs + h->ibody_count,
1678
0
         h->count - h->ibody_count);
1679
1680
0
  if (ext2fs_has_feature_ea_inode(fs->super) &&
1681
0
      value_len > EXT4_XATTR_MIN_LARGE_EA_SIZE(fs->blocksize))
1682
0
    in_inode = 1;
1683
1684
0
  ret = xattr_array_update(h, name, new_value, value_len, ibody_free,
1685
0
         block_free, old_idx, in_inode);
1686
0
  if (ret == EXT2_ET_EA_NO_SPACE && !in_inode &&
1687
0
      ext2fs_has_feature_ea_inode(fs->super))
1688
0
    ret = xattr_array_update(h, name, new_value, value_len,
1689
0
      ibody_free, block_free, old_idx, 1 /* in_inode */);
1690
0
  if (ret)
1691
0
    goto out;
1692
1693
0
write_out:
1694
0
  ret = ext2fs_xattrs_write(h);
1695
0
out:
1696
0
  if (inode)
1697
0
    ext2fs_free_mem(&inode);
1698
0
  ext2fs_free_mem(&new_value);
1699
0
  return ret;
1700
0
}
1701
1702
errcode_t ext2fs_xattr_remove(struct ext2_xattr_handle *handle,
1703
            const char *key)
1704
0
{
1705
0
  struct ext2_xattr *x;
1706
0
  struct ext2_xattr *end = handle->attrs + handle->count;
1707
1708
0
  EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE);
1709
0
  for (x = handle->attrs; x < end; x++) {
1710
0
    if (strcmp(x->name, key) == 0) {
1711
0
      ext2fs_free_mem(&x->name);
1712
0
      ext2fs_free_mem(&x->value);
1713
0
      if (x->ea_ino)
1714
0
        xattr_inode_dec_ref(handle->fs, x->ea_ino);
1715
0
      memmove(x, x + 1, (end - x - 1)*sizeof(*x));
1716
0
      memset(end - 1, 0, sizeof(*end));
1717
0
      if (x < handle->attrs + handle->ibody_count)
1718
0
        handle->ibody_count--;
1719
0
      handle->count--;
1720
0
      return ext2fs_xattrs_write(handle);
1721
0
    }
1722
0
  }
1723
1724
  /* no key found, success! */
1725
0
  return 0;
1726
0
}
1727
1728
errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
1729
           struct ext2_xattr_handle **handle)
1730
0
{
1731
0
  struct ext2_xattr_handle *h;
1732
0
  errcode_t err;
1733
1734
0
  if (!ext2fs_has_feature_xattr(fs->super) &&
1735
0
      !ext2fs_has_feature_inline_data(fs->super))
1736
0
    return EXT2_ET_MISSING_EA_FEATURE;
1737
1738
0
  err = ext2fs_get_memzero(sizeof(*h), &h);
1739
0
  if (err)
1740
0
    return err;
1741
1742
0
  h->magic = EXT2_ET_MAGIC_EA_HANDLE;
1743
0
  h->capacity = 4;
1744
0
  err = ext2fs_get_arrayzero(h->capacity, sizeof(struct ext2_xattr),
1745
0
           &h->attrs);
1746
0
  if (err) {
1747
0
    ext2fs_free_mem(&h);
1748
0
    return err;
1749
0
  }
1750
0
  h->count = 0;
1751
0
  h->ino = ino;
1752
0
  h->fs = fs;
1753
0
  *handle = h;
1754
0
  return 0;
1755
0
}
1756
1757
errcode_t ext2fs_xattrs_close(struct ext2_xattr_handle **handle)
1758
0
{
1759
0
  struct ext2_xattr_handle *h = *handle;
1760
1761
0
  EXT2_CHECK_MAGIC(h, EXT2_ET_MAGIC_EA_HANDLE);
1762
0
  xattrs_free_keys(h);
1763
0
  ext2fs_free_mem(&h->attrs);
1764
0
  ext2fs_free_mem(handle);
1765
0
  return 0;
1766
0
}
1767
1768
errcode_t ext2fs_xattrs_count(struct ext2_xattr_handle *handle, size_t *count)
1769
0
{
1770
0
  EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE);
1771
0
  *count = handle->count;
1772
0
  return 0;
1773
0
}
1774
1775
errcode_t ext2fs_xattrs_flags(struct ext2_xattr_handle *handle,
1776
            unsigned int *new_flags, unsigned int *old_flags)
1777
0
{
1778
0
  EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE);
1779
0
  if (old_flags)
1780
0
    *old_flags = handle->flags;
1781
0
  if (new_flags)
1782
0
    handle->flags = *new_flags;
1783
0
  return 0;
1784
0
}