// SPDX-License-Identifier: GPL-2.0-only
/*
* This file is part of UBIFS.
*
* Copyright (C) 2006-2008 Nokia Corporation
*
* Authors: Adrian Hunter
* Artem Bityutskiy (Битюцкий Артём)
*/
/*
* This file implements functions needed to recover from unclean un-mounts.
* When UBIFS is mounted, it checks a flag on the master node to determine if
* an un-mount was completed successfully. If not, the process of mounting
* incorporates additional checking and fixing of on-flash data structures.
* UBIFS always cleans away all remnants of an unclean un-mount, so that
* errors do not accumulate. However UBIFS defers recovery if it is mounted
* read-only, and the flash is not modified in that case.
*
* The general UBIFS approach to the recovery is that it recovers from
* corruptions which could be caused by power cuts, but it refuses to recover
* from corruption caused by other reasons. And UBIFS tries to distinguish
* between these 2 reasons of corruptions and silently recover in the former
* case and loudly complain in the latter case.
*
* UBIFS writes only to erased LEBs, so it writes only to the flash space
* containing only 0xFFs. UBIFS also always writes strictly from the beginning
* of the LEB to the end. And UBIFS assumes that the underlying flash media
* writes in @c->max_write_size bytes at a time.
*
* Hence, if UBIFS finds a corrupted node at offset X, it expects only the min.
* I/O unit corresponding to offset X to contain corrupted data, all the
* following min. I/O units have to contain empty space (all 0xFFs). If this is
* not true, the corruption cannot be the result of a power cut, and UBIFS
* refuses to mount.
*/
#include <linux/crc32.h>
#include <linux/slab.h>
#include "ubifs.h"
/**
* is_empty - determine whether a buffer is empty (contains all 0xff).
* @buf: buffer to clean
* @len: length of buffer
*
* This function returns %1 if the buffer is empty (contains all 0xff) otherwise
* %0 is returned.
*/
static int is_empty(void *buf, int len)
{
uint8_t *p = buf;
int i;
for (i = 0; i < len; i++)
if (*p++ != 0xff)
return 0;
return 1;
}
/**
* first_non_ff - find offset of the first non-0xff byte.
* @buf: buffer to search in
* @len: length of buffer
*
* This function returns offset of the first non-0xff byte in @buf or %-1 if
* the buffer contains only 0xff bytes.
*/
static int first_non_ff(void *buf, int len)
{
uint8_t *p = buf;
int i;
for (i = 0; i < len; i++)
if (*p++ != 0xff)
return i;
return -1;
}
/**
* get_master_node - get the last valid master node allowing for corruption.
* @c: UBIFS file-system description object
* @lnum: LEB number
* @pbuf: buffer containing the LEB read, is returned here
* @mst: master node, if found, is returned here
* @cor: corruption, if found, is returned here
*
* This function allocates a buffer, reads the LEB into it, and finds and
* returns the last valid master node allowing for one area of corruption.
* The corrupt area, if there is one, must be consistent with the assumption
* that it is the result of an unclean unmount while the master node was being
* written. Under those circumstances, it is valid to use the previously written
* master node.
*
* This function returns %0 on success and a negative error code on failure.
*/
static int get_master_node(const struct ubifs_info *c, int lnum, void **pbuf,
struct ubifs_mst_node **mst, void **cor)
{
const int sz = c->mst_node_alsz;
int err, offs, len;
void *sbuf, *buf;
sbuf = vmalloc(c->leb_size);
if (!sbuf)
return -ENOMEM;
err = ubifs_leb_read(c, lnum, sbuf, 0, c->leb_size, 0);
if (err && err != -EBADMSG)
goto out_free;
/* Find the first position that is definitely not a node */
offs = 0;
buf = sbuf;
len = c->leb_size;
while (offs + UBIFS_MST_NODE_SZ <= c->leb_size) {
struct ubifs_ch *ch = buf;
if (le32_to_cpu(ch->magic) != UBIFS_NODE_MAGIC)
break;
offs += sz;
buf += sz;
len -= sz;
}
/* See if there was a valid master node before that */
if (offs) {
int ret;
offs -= sz;
buf -= sz;
len += sz;
ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 1);
if (ret != SCANNED_A_NODE && offs) {
/* Could have been corruption so check one place back */
offs -= sz;
buf -= sz;
len += sz;
ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 1);
if (ret != SCANNED_A_NODE)
/*
* We accept only one area of corruption because
* we are assuming that it was caused while
* trying to write a master node.
*/
goto out_err;
}
if (ret == SCANNED_A_NODE) {
struct ubifs_ch *ch = buf;
if (ch->node_type != UBIFS_MST_NODE)
goto out_err;
dbg_rcvry("found a master node at %d:%d", lnum, offs);
*mst = buf;
offs += sz;
buf += sz;
len -= sz;
}
}
/* Check for corruption */
if (offs < c->leb_size) {
if (!is_empty(buf, min_t(int, len, sz))) {
*cor = buf;
dbg_rcvry("found corruption at %d:%d", lnum, offs);
}
offs += sz;
buf += sz;
len -= sz;
}
/* Check remaining empty space */
if (offs < c->leb_size)
if (!is_empty(buf, len))
goto out_err;
*pbuf = sbuf;
return 0;
out_err:
err = -EINVAL;
out_free:
vfree(sbuf);
*mst = NULL;
*cor = NULL;
return err;
}
/**
* write_rcvrd_mst_node - write recovered master node.
* @c: UBIFS file-system description object
* @mst: master node
*
* This function returns %0 on success and a negative error code on failure.
|