hc
2024-01-31 f9004dbfff8a3fbbd7e2a88c8a4327c7f2f8e5b2
kernel/fs/ext2/xattr.c
....@@ -135,6 +135,53 @@
135135 return handler;
136136 }
137137
138
+static bool
139
+ext2_xattr_header_valid(struct ext2_xattr_header *header)
140
+{
141
+ if (header->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||
142
+ header->h_blocks != cpu_to_le32(1))
143
+ return false;
144
+
145
+ return true;
146
+}
147
+
148
+static bool
149
+ext2_xattr_entry_valid(struct ext2_xattr_entry *entry,
150
+ char *end, size_t end_offs)
151
+{
152
+ struct ext2_xattr_entry *next;
153
+ size_t size;
154
+
155
+ next = EXT2_XATTR_NEXT(entry);
156
+ if ((char *)next >= end)
157
+ return false;
158
+
159
+ if (entry->e_value_block != 0)
160
+ return false;
161
+
162
+ size = le32_to_cpu(entry->e_value_size);
163
+ if (size > end_offs ||
164
+ le16_to_cpu(entry->e_value_offs) + size > end_offs)
165
+ return false;
166
+
167
+ return true;
168
+}
169
+
170
+static int
171
+ext2_xattr_cmp_entry(int name_index, size_t name_len, const char *name,
172
+ struct ext2_xattr_entry *entry)
173
+{
174
+ int cmp;
175
+
176
+ cmp = name_index - entry->e_name_index;
177
+ if (!cmp)
178
+ cmp = name_len - entry->e_name_len;
179
+ if (!cmp)
180
+ cmp = memcmp(name, entry->e_name, name_len);
181
+
182
+ return cmp;
183
+}
184
+
138185 /*
139186 * ext2_xattr_get()
140187 *
....@@ -153,7 +200,7 @@
153200 struct ext2_xattr_entry *entry;
154201 size_t name_len, size;
155202 char *end;
156
- int error;
203
+ int error, not_found;
157204 struct mb_cache *ea_block_cache = EA_BLOCK_CACHE(inode);
158205
159206 ea_idebug(inode, "name=%d.%s, buffer=%p, buffer_size=%ld",
....@@ -177,9 +224,9 @@
177224 ea_bdebug(bh, "b_count=%d, refcount=%d",
178225 atomic_read(&(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount));
179226 end = bh->b_data + bh->b_size;
180
- if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||
181
- HDR(bh)->h_blocks != cpu_to_le32(1)) {
182
-bad_block: ext2_error(inode->i_sb, "ext2_xattr_get",
227
+ if (!ext2_xattr_header_valid(HDR(bh))) {
228
+bad_block:
229
+ ext2_error(inode->i_sb, "ext2_xattr_get",
183230 "inode %ld: bad block %d", inode->i_ino,
184231 EXT2_I(inode)->i_file_acl);
185232 error = -EIO;
....@@ -189,29 +236,25 @@
189236 /* find named attribute */
190237 entry = FIRST_ENTRY(bh);
191238 while (!IS_LAST_ENTRY(entry)) {
192
- struct ext2_xattr_entry *next =
193
- EXT2_XATTR_NEXT(entry);
194
- if ((char *)next >= end)
239
+ if (!ext2_xattr_entry_valid(entry, end,
240
+ inode->i_sb->s_blocksize))
195241 goto bad_block;
196
- if (name_index == entry->e_name_index &&
197
- name_len == entry->e_name_len &&
198
- memcmp(name, entry->e_name, name_len) == 0)
242
+
243
+ not_found = ext2_xattr_cmp_entry(name_index, name_len, name,
244
+ entry);
245
+ if (!not_found)
199246 goto found;
200
- entry = next;
247
+ if (not_found < 0)
248
+ break;
249
+
250
+ entry = EXT2_XATTR_NEXT(entry);
201251 }
202252 if (ext2_xattr_cache_insert(ea_block_cache, bh))
203253 ea_idebug(inode, "cache insert failed");
204254 error = -ENODATA;
205255 goto cleanup;
206256 found:
207
- /* check the buffer size */
208
- if (entry->e_value_block != 0)
209
- goto bad_block;
210257 size = le32_to_cpu(entry->e_value_size);
211
- if (size > inode->i_sb->s_blocksize ||
212
- le16_to_cpu(entry->e_value_offs) + size > inode->i_sb->s_blocksize)
213
- goto bad_block;
214
-
215258 if (ext2_xattr_cache_insert(ea_block_cache, bh))
216259 ea_idebug(inode, "cache insert failed");
217260 if (buffer) {
....@@ -267,9 +310,9 @@
267310 ea_bdebug(bh, "b_count=%d, refcount=%d",
268311 atomic_read(&(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount));
269312 end = bh->b_data + bh->b_size;
270
- if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||
271
- HDR(bh)->h_blocks != cpu_to_le32(1)) {
272
-bad_block: ext2_error(inode->i_sb, "ext2_xattr_list",
313
+ if (!ext2_xattr_header_valid(HDR(bh))) {
314
+bad_block:
315
+ ext2_error(inode->i_sb, "ext2_xattr_list",
273316 "inode %ld: bad block %d", inode->i_ino,
274317 EXT2_I(inode)->i_file_acl);
275318 error = -EIO;
....@@ -279,11 +322,10 @@
279322 /* check the on-disk data structure */
280323 entry = FIRST_ENTRY(bh);
281324 while (!IS_LAST_ENTRY(entry)) {
282
- struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(entry);
283
-
284
- if ((char *)next >= end)
325
+ if (!ext2_xattr_entry_valid(entry, end,
326
+ inode->i_sb->s_blocksize))
285327 goto bad_block;
286
- entry = next;
328
+ entry = EXT2_XATTR_NEXT(entry);
287329 }
288330 if (ext2_xattr_cache_insert(ea_block_cache, bh))
289331 ea_idebug(inode, "cache insert failed");
....@@ -343,6 +385,7 @@
343385 return;
344386
345387 spin_lock(&EXT2_SB(sb)->s_lock);
388
+ ext2_update_dynamic_rev(sb);
346389 EXT2_SET_COMPAT_FEATURE(sb, EXT2_FEATURE_COMPAT_EXT_ATTR);
347390 spin_unlock(&EXT2_SB(sb)->s_lock);
348391 mark_buffer_dirty(EXT2_SB(sb)->s_sbh);
....@@ -367,7 +410,7 @@
367410 struct super_block *sb = inode->i_sb;
368411 struct buffer_head *bh = NULL;
369412 struct ext2_xattr_header *header = NULL;
370
- struct ext2_xattr_entry *here, *last;
413
+ struct ext2_xattr_entry *here = NULL, *last = NULL;
371414 size_t name_len, free, min_offs = sb->s_blocksize;
372415 int not_found = 1, error;
373416 char *end;
....@@ -394,6 +437,9 @@
394437 name_len = strlen(name);
395438 if (name_len > 255 || value_len > sb->s_blocksize)
396439 return -ERANGE;
440
+ error = dquot_initialize(inode);
441
+ if (error)
442
+ return error;
397443 down_write(&EXT2_I(inode)->xattr_sem);
398444 if (EXT2_I(inode)->i_file_acl) {
399445 /* The inode already has an extended attribute block. */
....@@ -406,47 +452,39 @@
406452 le32_to_cpu(HDR(bh)->h_refcount));
407453 header = HDR(bh);
408454 end = bh->b_data + bh->b_size;
409
- if (header->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||
410
- header->h_blocks != cpu_to_le32(1)) {
411
-bad_block: ext2_error(sb, "ext2_xattr_set",
455
+ if (!ext2_xattr_header_valid(header)) {
456
+bad_block:
457
+ ext2_error(sb, "ext2_xattr_set",
412458 "inode %ld: bad block %d", inode->i_ino,
413459 EXT2_I(inode)->i_file_acl);
414460 error = -EIO;
415461 goto cleanup;
416462 }
417
- /* Find the named attribute. */
418
- here = FIRST_ENTRY(bh);
419
- while (!IS_LAST_ENTRY(here)) {
420
- struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(here);
421
- if ((char *)next >= end)
422
- goto bad_block;
423
- if (!here->e_value_block && here->e_value_size) {
424
- size_t offs = le16_to_cpu(here->e_value_offs);
425
- if (offs < min_offs)
426
- min_offs = offs;
427
- }
428
- not_found = name_index - here->e_name_index;
429
- if (!not_found)
430
- not_found = name_len - here->e_name_len;
431
- if (!not_found)
432
- not_found = memcmp(name, here->e_name,name_len);
433
- if (not_found <= 0)
434
- break;
435
- here = next;
436
- }
437
- last = here;
438
- /* We still need to compute min_offs and last. */
463
+ /*
464
+ * Find the named attribute. If not found, 'here' will point
465
+ * to entry where the new attribute should be inserted to
466
+ * maintain sorting.
467
+ */
468
+ last = FIRST_ENTRY(bh);
439469 while (!IS_LAST_ENTRY(last)) {
440
- struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(last);
441
- if ((char *)next >= end)
470
+ if (!ext2_xattr_entry_valid(last, end, sb->s_blocksize))
442471 goto bad_block;
443
- if (!last->e_value_block && last->e_value_size) {
472
+ if (last->e_value_size) {
444473 size_t offs = le16_to_cpu(last->e_value_offs);
445474 if (offs < min_offs)
446475 min_offs = offs;
447476 }
448
- last = next;
477
+ if (not_found > 0) {
478
+ not_found = ext2_xattr_cmp_entry(name_index,
479
+ name_len,
480
+ name, last);
481
+ if (not_found <= 0)
482
+ here = last;
483
+ }
484
+ last = EXT2_XATTR_NEXT(last);
449485 }
486
+ if (not_found > 0)
487
+ here = last;
450488
451489 /* Check whether we have enough space left. */
452490 free = min_offs - ((char*)last - (char*)header) - sizeof(__u32);
....@@ -454,7 +492,6 @@
454492 /* We will use a new extended attribute block. */
455493 free = sb->s_blocksize -
456494 sizeof(struct ext2_xattr_header) - sizeof(__u32);
457
- here = last = NULL; /* avoid gcc uninitialized warning. */
458495 }
459496
460497 if (not_found) {
....@@ -470,14 +507,7 @@
470507 error = -EEXIST;
471508 if (flags & XATTR_CREATE)
472509 goto cleanup;
473
- if (!here->e_value_block && here->e_value_size) {
474
- size_t size = le32_to_cpu(here->e_value_size);
475
-
476
- if (le16_to_cpu(here->e_value_offs) + size >
477
- sb->s_blocksize || size > sb->s_blocksize)
478
- goto bad_block;
479
- free += EXT2_XATTR_SIZE(size);
480
- }
510
+ free += EXT2_XATTR_SIZE(le32_to_cpu(here->e_value_size));
481511 free += EXT2_XATTR_LEN(name_len);
482512 }
483513 error = -ENOSPC;
....@@ -506,11 +536,10 @@
506536
507537 unlock_buffer(bh);
508538 ea_bdebug(bh, "cloning");
509
- header = kmalloc(bh->b_size, GFP_KERNEL);
539
+ header = kmemdup(HDR(bh), bh->b_size, GFP_KERNEL);
510540 error = -ENOMEM;
511541 if (header == NULL)
512542 goto cleanup;
513
- memcpy(header, HDR(bh), bh->b_size);
514543 header->h_refcount = cpu_to_le32(1);
515544
516545 offset = (char *)here - bh->b_data;
....@@ -542,7 +571,7 @@
542571 here->e_name_len = name_len;
543572 memcpy(here->e_name, name, name_len);
544573 } else {
545
- if (!here->e_value_block && here->e_value_size) {
574
+ if (here->e_value_size) {
546575 char *first_val = (char *)header + min_offs;
547576 size_t offs = le16_to_cpu(here->e_value_offs);
548577 char *val = (char *)header + offs;
....@@ -562,18 +591,19 @@
562591 /* Remove the old value. */
563592 memmove(first_val + size, first_val, val - first_val);
564593 memset(first_val, 0, size);
565
- here->e_value_offs = 0;
566594 min_offs += size;
567595
568596 /* Adjust all value offsets. */
569597 last = ENTRY(header+1);
570598 while (!IS_LAST_ENTRY(last)) {
571599 size_t o = le16_to_cpu(last->e_value_offs);
572
- if (!last->e_value_block && o < offs)
600
+ if (o < offs)
573601 last->e_value_offs =
574602 cpu_to_le16(o + size);
575603 last = EXT2_XATTR_NEXT(last);
576604 }
605
+
606
+ here->e_value_offs = 0;
577607 }
578608 if (value == NULL) {
579609 /* Remove the old name. */
....@@ -664,10 +694,10 @@
664694 /* We need to allocate a new block */
665695 ext2_fsblk_t goal = ext2_group_first_block_no(sb,
666696 EXT2_I(inode)->i_block_group);
667
- int block = ext2_new_block(inode, goal, &error);
697
+ ext2_fsblk_t block = ext2_new_block(inode, goal, &error);
668698 if (error)
669699 goto cleanup;
670
- ea_idebug(inode, "creating block %d", block);
700
+ ea_idebug(inode, "creating block %lu", block);
671701
672702 new_bh = sb_getblk(sb, block);
673703 if (unlikely(!new_bh)) {
....@@ -765,11 +795,19 @@
765795 struct buffer_head *bh = NULL;
766796 struct ext2_sb_info *sbi = EXT2_SB(inode->i_sb);
767797
768
- down_write(&EXT2_I(inode)->xattr_sem);
798
+ /*
799
+ * We are the only ones holding inode reference. The xattr_sem should
800
+ * better be unlocked! We could as well just not acquire xattr_sem at
801
+ * all but this makes the code more futureproof. OTOH we need trylock
802
+ * here to avoid false-positive warning from lockdep about reclaim
803
+ * circular dependency.
804
+ */
805
+ if (WARN_ON_ONCE(!down_write_trylock(&EXT2_I(inode)->xattr_sem)))
806
+ return;
769807 if (!EXT2_I(inode)->i_file_acl)
770808 goto cleanup;
771809
772
- if (!ext2_data_block_valid(sbi, EXT2_I(inode)->i_file_acl, 0)) {
810
+ if (!ext2_data_block_valid(sbi, EXT2_I(inode)->i_file_acl, 1)) {
773811 ext2_error(inode->i_sb, "ext2_xattr_delete_inode",
774812 "inode %ld: xattr block %d is out of data blocks range",
775813 inode->i_ino, EXT2_I(inode)->i_file_acl);
....@@ -784,8 +822,7 @@
784822 goto cleanup;
785823 }
786824 ea_bdebug(bh, "b_count=%d", atomic_read(&(bh->b_count)));
787
- if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||
788
- HDR(bh)->h_blocks != cpu_to_le32(1)) {
825
+ if (!ext2_xattr_header_valid(HDR(bh))) {
789826 ext2_error(inode->i_sb, "ext2_xattr_delete_inode",
790827 "inode %ld: bad block %d", inode->i_ino,
791828 EXT2_I(inode)->i_file_acl);
....@@ -836,7 +873,8 @@
836873 __u32 hash = le32_to_cpu(HDR(bh)->h_hash);
837874 int error;
838875
839
- error = mb_cache_entry_create(cache, GFP_NOFS, hash, bh->b_blocknr, 1);
876
+ error = mb_cache_entry_create(cache, GFP_NOFS, hash, bh->b_blocknr,
877
+ true);
840878 if (error) {
841879 if (error == -EBUSY) {
842880 ea_bdebug(bh, "already in cache");