Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit de097578 authored by Adrian Hunter's avatar Adrian Hunter Committed by Artem Bityutskiy
Browse files

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: default avatarAdrian Hunter <adrian.hunter@nokia.com>
parent 963f0cf6
Loading
Loading
Loading
Loading
+23 −47
Original line number Diff line number Diff line
@@ -425,59 +425,35 @@ static void clean_buf(const struct ubifs_info *c, void **buf, int lnum,
 * @lnum: LEB number of the LEB from which @buf was read
 * @offs: offset from which @buf was read
 *
 * This function scans @buf for more nodes and returns %0 is a node is found and
 * %1 if no more nodes are found.
 * This function ensures that the corrupted node at @offs is the last thing
 * written to a LEB. This function returns %1 if more data is not found and
 * %0 if more data is found.
 */
static int no_more_nodes(const struct ubifs_info *c, void *buf, int len,
			int lnum, int offs)
{
	int skip, next_offs = 0;

	if (len > UBIFS_DATA_NODE_SZ) {
		struct ubifs_ch *ch = buf;
		int dlen = le32_to_cpu(ch->len);

		if (ch->node_type == UBIFS_DATA_NODE && dlen >= UBIFS_CH_SZ &&
		    dlen <= UBIFS_MAX_DATA_NODE_SZ)
			/* The corrupt node looks like a data node */
			next_offs = ALIGN(offs + dlen, 8);
	}

	if (c->min_io_size == 1)
		skip = 8;
	else
		skip = ALIGN(offs + 1, c->min_io_size) - offs;

	offs += skip;
	buf += skip;
	len -= skip;
	while (len > 8) {
	struct ubifs_ch *ch = buf;
		uint32_t magic = le32_to_cpu(ch->magic);
		int ret;
	int skip, dlen = le32_to_cpu(ch->len);

		if (magic == UBIFS_NODE_MAGIC) {
			ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 1);
			if (ret == SCANNED_A_NODE || ret > 0) {
	/* Check for empty space after the corrupt node's common header */
	skip = ALIGN(offs + UBIFS_CH_SZ, c->min_io_size) - offs;
	if (is_empty(buf + skip, len - skip))
		return 1;
	/*
				 * There is a small chance this is just data in
				 * a data node, so check that possibility. e.g.
				 * this is part of a file that itself contains
				 * a UBIFS image.
	 * The area after the common header size is not empty, so the common
	 * header must be intact. Check it.
	 */
				if (next_offs && offs + le32_to_cpu(ch->len) <=
				    next_offs)
					continue;
				dbg_rcvry("unexpected node at %d:%d", lnum,
					  offs);
	if (ubifs_check_node(c, buf, lnum, offs, 1, 0) != -EUCLEAN) {
		dbg_rcvry("unexpected bad common header at %d:%d", lnum, offs);
		return 0;
	}
		}
		offs += 8;
		buf += 8;
		len -= 8;
	}
	/* Now we know the corrupt node's length we can skip over it */
	skip = ALIGN(offs + dlen, c->min_io_size) - offs;
	/* After which there should be empty space */
	if (is_empty(buf + skip, len - skip))
		return 1;
	dbg_rcvry("unexpected data at %d:%d", lnum, offs + skip);
	return 0;
}

/**