diff options
author | Adrian Hunter <adrian.hunter@nokia.com> | 2009-03-20 06:09:04 -0400 |
---|---|---|
committer | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2009-03-31 07:58:40 -0400 |
commit | de0975781a1a8bc92e07eb7681d10ef9bb5e6df9 (patch) | |
tree | 0612ff6bcb230a317013ae0796606640dc862bb7 /fs/ubifs | |
parent | 963f0cf6d116d83c558a8efe9045c1c5ad7aed34 (diff) |
UBIFS: fix recovery bug
UBIFS did not recovery in a situation in which it could
have. The relevant function assumed there could not be
more nodes in an eraseblock after a corrupted node, but
in fact the last (NAND) page written might contain anything.
The correct approach is to check for empty space (0xFF bytes)
from then on.
Signed-off-by: Adrian Hunter <adrian.hunter@nokia.com>
Diffstat (limited to 'fs/ubifs')
-rw-r--r-- | fs/ubifs/recovery.c | 70 |
1 files changed, 23 insertions, 47 deletions
diff --git a/fs/ubifs/recovery.c b/fs/ubifs/recovery.c index 90acac603e63..10662975d2ef 100644 --- a/fs/ubifs/recovery.c +++ b/fs/ubifs/recovery.c | |||
@@ -425,59 +425,35 @@ static void clean_buf(const struct ubifs_info *c, void **buf, int lnum, | |||
425 | * @lnum: LEB number of the LEB from which @buf was read | 425 | * @lnum: LEB number of the LEB from which @buf was read |
426 | * @offs: offset from which @buf was read | 426 | * @offs: offset from which @buf was read |
427 | * | 427 | * |
428 | * This function scans @buf for more nodes and returns %0 is a node is found and | 428 | * This function ensures that the corrupted node at @offs is the last thing |
429 | * %1 if no more nodes are found. | 429 | * written to a LEB. This function returns %1 if more data is not found and |
430 | * %0 if more data is found. | ||
430 | */ | 431 | */ |
431 | static int no_more_nodes(const struct ubifs_info *c, void *buf, int len, | 432 | static int no_more_nodes(const struct ubifs_info *c, void *buf, int len, |
432 | int lnum, int offs) | 433 | int lnum, int offs) |
433 | { | 434 | { |
434 | int skip, next_offs = 0; | 435 | struct ubifs_ch *ch = buf; |
436 | int skip, dlen = le32_to_cpu(ch->len); | ||
435 | 437 | ||
436 | if (len > UBIFS_DATA_NODE_SZ) { | 438 | /* Check for empty space after the corrupt node's common header */ |
437 | struct ubifs_ch *ch = buf; | 439 | skip = ALIGN(offs + UBIFS_CH_SZ, c->min_io_size) - offs; |
438 | int dlen = le32_to_cpu(ch->len); | 440 | if (is_empty(buf + skip, len - skip)) |
439 | 441 | return 1; | |
440 | if (ch->node_type == UBIFS_DATA_NODE && dlen >= UBIFS_CH_SZ && | 442 | /* |
441 | dlen <= UBIFS_MAX_DATA_NODE_SZ) | 443 | * The area after the common header size is not empty, so the common |
442 | /* The corrupt node looks like a data node */ | 444 | * header must be intact. Check it. |
443 | next_offs = ALIGN(offs + dlen, 8); | 445 | */ |
444 | } | 446 | if (ubifs_check_node(c, buf, lnum, offs, 1, 0) != -EUCLEAN) { |
445 | 447 | dbg_rcvry("unexpected bad common header at %d:%d", lnum, offs); | |
446 | if (c->min_io_size == 1) | 448 | return 0; |
447 | skip = 8; | ||
448 | else | ||
449 | skip = ALIGN(offs + 1, c->min_io_size) - offs; | ||
450 | |||
451 | offs += skip; | ||
452 | buf += skip; | ||
453 | len -= skip; | ||
454 | while (len > 8) { | ||
455 | struct ubifs_ch *ch = buf; | ||
456 | uint32_t magic = le32_to_cpu(ch->magic); | ||
457 | int ret; | ||
458 | |||
459 | if (magic == UBIFS_NODE_MAGIC) { | ||
460 | ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 1); | ||
461 | if (ret == SCANNED_A_NODE || ret > 0) { | ||
462 | /* | ||
463 | * There is a small chance this is just data in | ||
464 | * a data node, so check that possibility. e.g. | ||
465 | * this is part of a file that itself contains | ||
466 | * a UBIFS image. | ||
467 | */ | ||
468 | if (next_offs && offs + le32_to_cpu(ch->len) <= | ||
469 | next_offs) | ||
470 | continue; | ||
471 | dbg_rcvry("unexpected node at %d:%d", lnum, | ||
472 | offs); | ||
473 | return 0; | ||
474 | } | ||
475 | } | ||
476 | offs += 8; | ||
477 | buf += 8; | ||
478 | len -= 8; | ||
479 | } | 449 | } |
480 | return 1; | 450 | /* Now we know the corrupt node's length we can skip over it */ |
451 | skip = ALIGN(offs + dlen, c->min_io_size) - offs; | ||
452 | /* After which there should be empty space */ | ||
453 | if (is_empty(buf + skip, len - skip)) | ||
454 | return 1; | ||
455 | dbg_rcvry("unexpected data at %d:%d", lnum, offs + skip); | ||
456 | return 0; | ||
481 | } | 457 | } |
482 | 458 | ||
483 | /** | 459 | /** |