aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ubifs-utils/fsck.ubifs/fsck.ubifs.c174
-rw-r--r--ubifs-utils/fsck.ubifs/fsck.ubifs.h5
-rw-r--r--ubifs-utils/libubifs/io.c4
-rw-r--r--ubifs-utils/libubifs/lpt.c66
-rw-r--r--ubifs-utils/libubifs/lpt_commit.c5
-rw-r--r--ubifs-utils/libubifs/master.c18
-rw-r--r--ubifs-utils/libubifs/orphan.c4
-rw-r--r--ubifs-utils/libubifs/recovery.c4
-rw-r--r--ubifs-utils/libubifs/replay.c1
-rw-r--r--ubifs-utils/libubifs/sb.c2
-rw-r--r--ubifs-utils/libubifs/scan.c1
-rw-r--r--ubifs-utils/libubifs/tnc_misc.c4
-rw-r--r--ubifs-utils/libubifs/ubifs.h98
13 files changed, 366 insertions, 20 deletions
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index 721976b..a367241 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -168,6 +168,172 @@ static void fsck_assert_failed(__unused const struct ubifs_info *c)
exit(exit_code);
}
+static void fsck_set_failure_reason(const struct ubifs_info *c,
+ unsigned int reason)
+{
+ if (FSCK(c)->mode == REBUILD_MODE)
+ return;
+
+ FSCK(c)->failure_reason = reason;
+ if (reason & FR_LPT_CORRUPTED) {
+ log_out(c, "Found corrupted pnode/nnode, set lpt corrupted");
+ FSCK(c)->lpt_status |= FR_LPT_CORRUPTED;
+ }
+ if (reason & FR_LPT_INCORRECT) {
+ log_out(c, "Bad space statistics, set lpt incorrect");
+ FSCK(c)->lpt_status |= FR_LPT_INCORRECT;
+ }
+}
+
+static unsigned int fsck_get_failure_reason(const struct ubifs_info *c)
+{
+ ubifs_assert(c, FSCK(c)->mode != REBUILD_MODE);
+
+ return FSCK(c)->failure_reason;
+}
+
+static void fsck_clear_failure_reason(const struct ubifs_info *c)
+{
+ ubifs_assert(c, FSCK(c)->mode != REBUILD_MODE);
+
+ FSCK(c)->failure_reason = 0;
+}
+
+static bool fsck_test_and_clear_failure_reason(const struct ubifs_info *c,
+ unsigned int reason)
+{
+ bool res = (FSCK(c)->failure_reason & reason) != 0;
+
+ ubifs_assert(c, FSCK(c)->mode != REBUILD_MODE);
+ ubifs_assert(c, !(FSCK(c)->failure_reason & (~reason)));
+
+ FSCK(c)->failure_reason = 0;
+
+ return res;
+}
+
+static void fsck_set_lpt_invalid(const struct ubifs_info *c,
+ unsigned int reason)
+{
+ ubifs_assert(c, FSCK(c)->mode != REBUILD_MODE);
+
+ if (reason & FR_LPT_CORRUPTED) {
+ log_out(c, "Found corrupted pnode/nnode, set lpt corrupted");
+ FSCK(c)->lpt_status |= FR_LPT_CORRUPTED;
+ }
+ if (reason & FR_LPT_INCORRECT) {
+ log_out(c, "Bad space statistics, set lpt incorrect");
+ FSCK(c)->lpt_status |= FR_LPT_INCORRECT;
+ }
+}
+
+static bool fsck_test_lpt_valid(const struct ubifs_info *c, int lnum,
+ int old_free, int old_dirty,
+ int free, int dirty)
+{
+ ubifs_assert(c, FSCK(c)->mode != REBUILD_MODE);
+
+ if (c->cmt_state != COMMIT_RESTING)
+ /* Don't skip updating lpt when do commit. */
+ goto out;
+
+ if (FSCK(c)->lpt_status)
+ return false;
+
+ if (c->lst.empty_lebs < 0 || c->lst.empty_lebs > c->main_lebs) {
+ log_out(c, "Bad empty_lebs %d(main_lebs %d), set lpt incorrect",
+ c->lst.empty_lebs, c->main_lebs);
+ goto out_invalid;
+ }
+ if (c->freeable_cnt < 0 || c->freeable_cnt > c->main_lebs) {
+ log_out(c, "Bad freeable_cnt %d(main_lebs %d), set lpt incorrect",
+ c->freeable_cnt, c->main_lebs);
+ goto out_invalid;
+ }
+ if (c->lst.taken_empty_lebs < 0 ||
+ c->lst.taken_empty_lebs > c->lst.empty_lebs) {
+ log_out(c, "Bad taken_empty_lebs %d(empty_lebs %d), set lpt incorrect",
+ c->lst.taken_empty_lebs, c->lst.empty_lebs);
+ goto out_invalid;
+ }
+ if (c->lst.total_free & 7) {
+ log_out(c, "total_free(%lld) is not 8 bytes aligned, set lpt incorrect",
+ c->lst.total_free);
+ goto out_invalid;
+ }
+ if (c->lst.total_dirty & 7) {
+ log_out(c, "total_dirty(%lld) is not 8 bytes aligned, set lpt incorrect",
+ c->lst.total_dirty);
+ goto out_invalid;
+ }
+ if (c->lst.total_dead & 7) {
+ log_out(c, "total_dead(%lld) is not 8 bytes aligned, set lpt incorrect",
+ c->lst.total_dead);
+ goto out_invalid;
+ }
+ if (c->lst.total_dark & 7) {
+ log_out(c, "total_dark(%lld) is not 8 bytes aligned, set lpt incorrect",
+ c->lst.total_dark);
+ goto out_invalid;
+ }
+ if (c->lst.total_used & 7) {
+ log_out(c, "total_used(%lld) is not 8 bytes aligned, set lpt incorrect",
+ c->lst.total_used);
+ goto out_invalid;
+ }
+ if (old_free != LPROPS_NC && (old_free & 7)) {
+ log_out(c, "LEB %d old_free(%d) is not 8 bytes aligned, set lpt incorrect",
+ lnum, old_free);
+ goto out_invalid;
+ }
+ if (old_dirty != LPROPS_NC && (old_dirty & 7)) {
+ log_out(c, "LEB %d old_dirty(%d) is not 8 bytes aligned, set lpt incorrect",
+ lnum, old_dirty);
+ goto out_invalid;
+ }
+ if (free != LPROPS_NC && (free < 0 || free > c->leb_size)) {
+ log_out(c, "LEB %d bad free %d(leb_size %d), set lpt incorrect",
+ lnum, free, c->leb_size);
+ goto out_invalid;
+ }
+ if (dirty != LPROPS_NC && dirty < 0) {
+ /* Dirty may be more than c->leb_size before set_bud_lprops. */
+ log_out(c, "LEB %d bad dirty %d(leb_size %d), set lpt incorrect",
+ lnum, dirty, c->leb_size);
+ goto out_invalid;
+ }
+
+out:
+ return true;
+
+out_invalid:
+ FSCK(c)->lpt_status |= FR_LPT_INCORRECT;
+ return false;
+}
+
+static bool fsck_can_ignore_failure(const struct ubifs_info *c,
+ unsigned int reason)
+{
+ ubifs_assert(c, FSCK(c)->mode != REBUILD_MODE);
+
+ if (c->cmt_state != COMMIT_RESTING)
+ /* Don't ignore failure when do commit. */
+ return false;
+ if (reason & (FR_LPT_CORRUPTED | FR_LPT_INCORRECT))
+ return true;
+
+ return false;
+}
+
+static const unsigned int reason_mapping_table[] = {};
+
+static bool fsck_handle_failure(const struct ubifs_info *c, unsigned int reason)
+{
+ ubifs_assert(c, FSCK(c)->mode != REBUILD_MODE);
+
+ return fix_problem(c, reason_mapping_table[reason]);
+}
+
static void signal_cancel(int sig)
{
ubifs_warn(c, "killed by signo %d", sig);
@@ -199,6 +365,14 @@ static int init_fsck_info(struct ubifs_info *c, int argc, char *argv[])
c->private = fsck;
FSCK(c)->mode = mode;
c->assert_failed_cb = fsck_assert_failed;
+ c->set_failure_reason_cb = fsck_set_failure_reason;
+ c->get_failure_reason_cb = fsck_get_failure_reason;
+ c->clear_failure_reason_cb = fsck_clear_failure_reason;
+ c->test_and_clear_failure_reason_cb = fsck_test_and_clear_failure_reason;
+ c->set_lpt_invalid_cb = fsck_set_lpt_invalid;
+ c->test_lpt_valid_cb = fsck_test_lpt_valid;
+ c->can_ignore_failure_cb = fsck_can_ignore_failure;
+ c->handle_failure_cb = fsck_handle_failure;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = signal_cancel;
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index 033a601..762745f 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -39,9 +39,14 @@ enum { NORMAL_MODE = 0, SAFE_MODE, DANGER_MODE0,
/**
* struct ubifs_fsck_info - UBIFS fsck information.
* @mode: working mode
+ * @failure_reason: reasons for failed operations
+ * @lpt_status: the status of lpt, could be: %0(OK), %FR_LPT_CORRUPTED or
+ * %FR_LPT_INCORRECT
*/
struct ubifs_fsck_info {
int mode;
+ unsigned int failure_reason;
+ unsigned int lpt_status;
};
#define FSCK(c) ((struct ubifs_fsck_info*)c->private)
diff --git a/ubifs-utils/libubifs/io.c b/ubifs-utils/libubifs/io.c
index 6f17017..b6fb331 100644
--- a/ubifs-utils/libubifs/io.c
+++ b/ubifs-utils/libubifs/io.c
@@ -964,6 +964,7 @@ int ubifs_read_node_wbuf(struct ubifs_wbuf *wbuf, void *buf, int type, int len,
err = ubifs_check_node(c, buf, len, lnum, offs, 0, 0);
if (err) {
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
ubifs_err(c, "expected node type %d", type);
return err;
}
@@ -977,6 +978,7 @@ int ubifs_read_node_wbuf(struct ubifs_wbuf *wbuf, void *buf, int type, int len,
return 0;
out:
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
ubifs_err(c, "bad node at LEB %d:%d", lnum, offs);
ubifs_dump_node(c, buf, len);
dump_stack();
@@ -1020,6 +1022,7 @@ int ubifs_read_node(const struct ubifs_info *c, void *buf, int type, int len,
err = ubifs_check_node(c, buf, len, lnum, offs, 0, 0);
if (err) {
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
ubifs_err(c, "expected node type %d", type);
return err;
}
@@ -1033,6 +1036,7 @@ int ubifs_read_node(const struct ubifs_info *c, void *buf, int type, int len,
return 0;
out:
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
ubifs_err(c, "bad node at LEB %d:%d, LEB mapping status %d", lnum,
offs, ubi_is_mapped(c->dev_fd, lnum));
ubifs_dump_node(c, buf, len);
diff --git a/ubifs-utils/libubifs/lpt.c b/ubifs-utils/libubifs/lpt.c
index 92b3fec..c0df7c7 100644
--- a/ubifs-utils/libubifs/lpt.c
+++ b/ubifs-utils/libubifs/lpt.c
@@ -983,6 +983,7 @@ static int check_lpt_crc(const struct ubifs_info *c, void *buf, int len)
calc_crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
len - UBIFS_LPT_CRC_BYTES);
if (crc != calc_crc) {
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
ubifs_err(c, "invalid crc in LPT node: crc %hx calc %hx",
crc, calc_crc);
dump_stack();
@@ -1007,6 +1008,7 @@ static int check_lpt_type(const struct ubifs_info *c, uint8_t **addr,
node_type = ubifs_unpack_bits(c, addr, pos, UBIFS_LPT_TYPE_BITS);
if (node_type != type) {
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
ubifs_err(c, "invalid type (%d) in LPT node type %d",
node_type, type);
dump_stack();
@@ -1106,8 +1108,10 @@ static int unpack_ltab(const struct ubifs_info *c, void *buf)
int dirty = ubifs_unpack_bits(c, &addr, &pos, c->lpt_spc_bits);
if (free < 0 || free > c->leb_size || dirty < 0 ||
- dirty > c->leb_size || free + dirty > c->leb_size)
+ dirty > c->leb_size || free + dirty > c->leb_size) {
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
return -EINVAL;
+ }
c->ltab[i].free = free;
c->ltab[i].dirty = dirty;
@@ -1136,8 +1140,10 @@ static int unpack_lsave(const struct ubifs_info *c, void *buf)
for (i = 0; i < c->lsave_cnt; i++) {
int lnum = ubifs_unpack_bits(c, &addr, &pos, c->lnum_bits);
- if (lnum < c->main_first || lnum >= c->leb_cnt)
+ if (lnum < c->main_first || lnum >= c->leb_cnt) {
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
return -EINVAL;
+ }
c->lsave[i] = lnum;
}
err = check_lpt_crc(c, buf, c->lsave_sz);
@@ -1162,11 +1168,11 @@ static int validate_nnode(const struct ubifs_info *c, struct ubifs_nnode *nnode,
int num = calc_nnode_num_from_parent(c, parent, iip);
if (nnode->num != num)
- return -EINVAL;
+ goto out_invalid;
}
lvl = parent ? parent->level - 1 : c->lpt_hght;
if (lvl < 1)
- return -EINVAL;
+ goto out_invalid;
if (lvl == 1)
max_offs = c->leb_size - c->pnode_sz;
else
@@ -1177,15 +1183,19 @@ static int validate_nnode(const struct ubifs_info *c, struct ubifs_nnode *nnode,
if (lnum == 0) {
if (offs != 0)
- return -EINVAL;
+ goto out_invalid;
continue;
}
if (lnum < c->lpt_first || lnum > c->lpt_last)
- return -EINVAL;
+ goto out_invalid;
if (offs < 0 || offs > max_offs)
- return -EINVAL;
+ goto out_invalid;
}
return 0;
+
+out_invalid:
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
+ return -EINVAL;
}
/**
@@ -1206,7 +1216,7 @@ static int validate_pnode(const struct ubifs_info *c, struct ubifs_pnode *pnode,
int num = calc_pnode_num_from_parent(c, parent, iip);
if (pnode->num != num)
- return -EINVAL;
+ goto out_invalid;
}
for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
int free = pnode->lprops[i].free;
@@ -1214,13 +1224,17 @@ static int validate_pnode(const struct ubifs_info *c, struct ubifs_pnode *pnode,
if (free < 0 || free > c->leb_size || free % c->min_io_size ||
(free & 7))
- return -EINVAL;
+ goto out_invalid;
if (dirty < 0 || dirty > c->leb_size || (dirty & 7))
- return -EINVAL;
+ goto out_invalid;
if (dirty + free > c->leb_size)
- return -EINVAL;
+ goto out_invalid;
}
return 0;
+
+out_invalid:
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
+ return -EINVAL;
}
/**
@@ -1283,8 +1297,11 @@ int ubifs_read_nnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip)
nnode->num = calc_nnode_num_from_parent(c, parent, iip);
} else {
err = ubifs_leb_read(c, lnum, buf, offs, c->nnode_sz, 1);
- if (err)
+ if (err) {
+ if (err == -EBADMSG)
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
goto out;
+ }
err = ubifs_unpack_nnode(c, buf, nnode);
if (err)
goto out;
@@ -1352,8 +1369,11 @@ static int read_pnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip)
}
} else {
err = ubifs_leb_read(c, lnum, buf, offs, c->pnode_sz, 1);
- if (err)
+ if (err) {
+ if (err == -EBADMSG)
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
goto out;
+ }
err = unpack_pnode(c, buf, pnode);
if (err)
goto out;
@@ -1394,8 +1414,11 @@ static int read_ltab(struct ubifs_info *c)
if (!buf)
return -ENOMEM;
err = ubifs_leb_read(c, c->ltab_lnum, buf, c->ltab_offs, c->ltab_sz, 1);
- if (err)
+ if (err) {
+ if (err == -EBADMSG)
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
goto out;
+ }
err = unpack_ltab(c, buf);
out:
vfree(buf);
@@ -1418,8 +1441,11 @@ static int read_lsave(struct ubifs_info *c)
return -ENOMEM;
err = ubifs_leb_read(c, c->lsave_lnum, buf, c->lsave_offs,
c->lsave_sz, 1);
- if (err)
+ if (err) {
+ if (err == -EBADMSG)
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
goto out;
+ }
err = unpack_lsave(c, buf);
if (err)
goto out;
@@ -2031,8 +2057,11 @@ static struct ubifs_nnode *scan_get_nnode(struct ubifs_info *c,
} else {
err = ubifs_leb_read(c, branch->lnum, buf, branch->offs,
c->nnode_sz, 1);
- if (err)
+ if (err) {
+ if (err == -EBADMSG)
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
return ERR_PTR(err);
+ }
err = ubifs_unpack_nnode(c, buf, nnode);
if (err)
return ERR_PTR(err);
@@ -2100,8 +2129,11 @@ static struct ubifs_pnode *scan_get_pnode(struct ubifs_info *c,
ubifs_assert(c, branch->offs >= 0 && branch->offs < c->leb_size);
err = ubifs_leb_read(c, branch->lnum, buf, branch->offs,
c->pnode_sz, 1);
- if (err)
+ if (err) {
+ if (err == -EBADMSG)
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
return ERR_PTR(err);
+ }
err = unpack_pnode(c, buf, pnode);
if (err)
return ERR_PTR(err);
diff --git a/ubifs-utils/libubifs/lpt_commit.c b/ubifs-utils/libubifs/lpt_commit.c
index b00f75f..43eb7a6 100644
--- a/ubifs-utils/libubifs/lpt_commit.c
+++ b/ubifs-utils/libubifs/lpt_commit.c
@@ -1607,8 +1607,11 @@ static int dbg_check_ltab_lnum(struct ubifs_info *c, int lnum)
dbg_lp("LEB %d", lnum);
err = ubifs_leb_read(c, lnum, buf, 0, c->leb_size, 1);
- if (err)
+ if (err) {
+ if (err == -EBADMSG)
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
goto out;
+ }
while (1) {
if (!is_a_node(c, p, len)) {
diff --git a/ubifs-utils/libubifs/master.c b/ubifs-utils/libubifs/master.c
index cce1a41..61ff4ce 100644
--- a/ubifs-utils/libubifs/master.c
+++ b/ubifs-utils/libubifs/master.c
@@ -146,10 +146,12 @@ static int scan_for_master(struct ubifs_info *c)
return 0;
out:
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
ubifs_scan_destroy(sleb);
return -EUCLEAN;
out_dump:
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
ubifs_err(c, "unexpected node type %d master LEB %d:%d",
snod->type, lnum, snod->offs);
ubifs_scan_destroy(sleb);
@@ -165,6 +167,7 @@ out_dump:
*/
static int validate_master(const struct ubifs_info *c)
{
+ unsigned int reason = FR_DATA_CORRUPTED;
long long main_sz;
int err;
@@ -254,39 +257,46 @@ static int validate_master(const struct ubifs_info *c)
}
if (c->lst.empty_lebs < 0 || c->lst.empty_lebs > c->main_lebs - 2) {
+ reason = FR_LPT_INCORRECT;
err = 15;
goto out;
}
if (c->lst.idx_lebs < 0 || c->lst.idx_lebs > c->main_lebs - 1) {
+ reason = FR_LPT_INCORRECT;
err = 16;
goto out;
}
if (c->lst.total_free < 0 || c->lst.total_free > main_sz ||
c->lst.total_free & 7) {
+ reason = FR_LPT_INCORRECT;
err = 17;
goto out;
}
if (c->lst.total_dirty < 0 || (c->lst.total_dirty & 7)) {
+ reason = FR_LPT_INCORRECT;
err = 18;
goto out;
}
if (c->lst.total_used < 0 || (c->lst.total_used & 7)) {
+ reason = FR_LPT_INCORRECT;
err = 19;
goto out;
}
if (c->lst.total_free + c->lst.total_dirty +
c->lst.total_used > main_sz) {
+ reason = FR_LPT_INCORRECT;
err = 20;
goto out;
}
if (c->lst.total_dead + c->lst.total_dark +
c->lst.total_used + c->bi.old_idx_sz > main_sz) {
+ reason = FR_LPT_INCORRECT;
err = 21;
goto out;
}
@@ -294,6 +304,7 @@ static int validate_master(const struct ubifs_info *c)
if (c->lst.total_dead < 0 ||
c->lst.total_dead > c->lst.total_free + c->lst.total_dirty ||
c->lst.total_dead & 7) {
+ reason = FR_LPT_INCORRECT;
err = 22;
goto out;
}
@@ -301,6 +312,7 @@ static int validate_master(const struct ubifs_info *c)
if (c->lst.total_dark < 0 ||
c->lst.total_dark > c->lst.total_free + c->lst.total_dirty ||
c->lst.total_dark & 7) {
+ reason = FR_LPT_INCORRECT;
err = 23;
goto out;
}
@@ -308,6 +320,7 @@ static int validate_master(const struct ubifs_info *c)
return 0;
out:
+ set_failure_reason_callback(c, reason);
ubifs_err(c, "bad master node at offset %d error %d", c->mst_offs, err);
ubifs_dump_node(c, c->mst_node, c->mst_node_alsz);
return -EINVAL;
@@ -331,8 +344,10 @@ int ubifs_read_master(struct ubifs_info *c)
err = scan_for_master(c);
if (err) {
- if (err == -EUCLEAN)
+ if (err == -EUCLEAN) {
+ clear_failure_reason_callback(c);
err = ubifs_recover_master_node(c);
+ }
if (err)
/*
* Note, we do not free 'c->mst_node' here because the
@@ -386,6 +401,7 @@ int ubifs_read_master(struct ubifs_info *c)
if (c->leb_cnt < old_leb_cnt ||
c->leb_cnt < UBIFS_MIN_LEB_CNT) {
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
ubifs_err(c, "bad leb_cnt on master node");
ubifs_dump_node(c, c->mst_node, c->mst_node_alsz);
return -EINVAL;
diff --git a/ubifs-utils/libubifs/orphan.c b/ubifs-utils/libubifs/orphan.c
index 2f31874..26668cb 100644
--- a/ubifs-utils/libubifs/orphan.c
+++ b/ubifs-utils/libubifs/orphan.c
@@ -547,9 +547,11 @@ static int kill_orphans(struct ubifs_info *c)
dbg_rcvry("LEB %d", lnum);
sleb = ubifs_scan(c, lnum, 0, c->sbuf, 1);
if (IS_ERR(sleb)) {
- if (PTR_ERR(sleb) == -EUCLEAN)
+ if (PTR_ERR(sleb) == -EUCLEAN) {
+ clear_failure_reason_callback(c);
sleb = ubifs_recover_leb(c, lnum, 0,
c->sbuf, -1);
+ }
if (IS_ERR(sleb)) {
err = PTR_ERR(sleb);
break;
diff --git a/ubifs-utils/libubifs/recovery.c b/ubifs-utils/libubifs/recovery.c
index 910414c..9115b17 100644
--- a/ubifs-utils/libubifs/recovery.c
+++ b/ubifs-utils/libubifs/recovery.c
@@ -182,6 +182,7 @@ static int get_master_node(const struct ubifs_info *c, int lnum, void **pbuf,
return 0;
out_err:
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
err = -EINVAL;
out_free:
vfree(sbuf);
@@ -355,6 +356,7 @@ int ubifs_recover_master_node(struct ubifs_info *c)
return 0;
out_err:
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
err = -EINVAL;
out_free:
ubifs_err(c, "failed to recover master node");
@@ -762,6 +764,7 @@ corrupted_rescan:
ubifs_err(c, "corruption %d", ret);
ubifs_scan_a_node(c, buf, len, lnum, offs, 0);
corrupted:
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
ubifs_scanned_corruption(c, lnum, offs, buf);
err = -EUCLEAN;
error:
@@ -817,6 +820,7 @@ static int get_cs_sqnum(struct ubifs_info *c, int lnum, int offs,
out_err:
err = -EINVAL;
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
out_free:
ubifs_err(c, "failed to get CS sqnum");
kfree(cs_node);
diff --git a/ubifs-utils/libubifs/replay.c b/ubifs-utils/libubifs/replay.c
index b1d0164..30ed282 100644
--- a/ubifs-utils/libubifs/replay.c
+++ b/ubifs-utils/libubifs/replay.c
@@ -928,6 +928,7 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf)
if (IS_ERR(sleb)) {
if (PTR_ERR(sleb) != -EUCLEAN || !c->need_recovery)
return PTR_ERR(sleb);
+ clear_failure_reason_callback(c);
/*
* Note, the below function will recover this log LEB only if
* it is the last, because unclean reboots can possibly corrupt
diff --git a/ubifs-utils/libubifs/sb.c b/ubifs-utils/libubifs/sb.c
index 312661d..2147280 100644
--- a/ubifs-utils/libubifs/sb.c
+++ b/ubifs-utils/libubifs/sb.c
@@ -360,6 +360,8 @@ int ubifs_read_superblock(struct ubifs_info *c)
err = validate_sb(c, sup);
out:
+ if (err)
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
return err;
}
diff --git a/ubifs-utils/libubifs/scan.c b/ubifs-utils/libubifs/scan.c
index 74509fd..e9581a6 100644
--- a/ubifs-utils/libubifs/scan.c
+++ b/ubifs-utils/libubifs/scan.c
@@ -344,6 +344,7 @@ corrupted:
}
err = -EUCLEAN;
ubifs_scan_destroy(sleb);
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
return ERR_PTR(err);
error:
diff --git a/ubifs-utils/libubifs/tnc_misc.c b/ubifs-utils/libubifs/tnc_misc.c
index 8c38f15..0ffb434 100644
--- a/ubifs-utils/libubifs/tnc_misc.c
+++ b/ubifs-utils/libubifs/tnc_misc.c
@@ -222,6 +222,8 @@ static int read_znode(struct ubifs_info *c, struct ubifs_zbranch *zzbr,
err = ubifs_read_node(c, idx, UBIFS_IDX_NODE, len, lnum, offs);
if (err < 0) {
+ if (test_and_clear_failure_reason_callback(c, FR_DATA_CORRUPTED))
+ set_failure_reason_callback(c, FR_TNC_CORRUPTED);
kfree(idx);
return err;
}
@@ -335,6 +337,7 @@ static int read_znode(struct ubifs_info *c, struct ubifs_zbranch *zzbr,
return 0;
out_dump:
+ set_failure_reason_callback(c, FR_TNC_CORRUPTED);
ubifs_err(c, "bad indexing node at LEB %d:%d, error %d", lnum, offs, err);
ubifs_dump_node(c, idx, c->max_idx_node_sz);
kfree(idx);
@@ -430,6 +433,7 @@ int ubifs_tnc_read_node(struct ubifs_info *c, struct ubifs_zbranch *zbr,
/* Make sure the key of the read node is correct */
key_read(c, node + UBIFS_KEY_OFFSET, &key1);
if (!keys_eq(c, key, &key1)) {
+ set_failure_reason_callback(c, FR_DATA_CORRUPTED);
ubifs_err(c, "bad key in node at LEB %d:%d",
zbr->lnum, zbr->offs);
dbg_tnck(key, "looked for key ");
diff --git a/ubifs-utils/libubifs/ubifs.h b/ubifs-utils/libubifs/ubifs.h
index babaae8..21b0ce0 100644
--- a/ubifs-utils/libubifs/ubifs.h
+++ b/ubifs-utils/libubifs/ubifs.h
@@ -1029,6 +1029,20 @@ struct ubifs_budg_info {
*
* @private: private information related to specific situation, eg. fsck.
* @assert_failed_cb: callback function to handle assertion failure
+ * @set_failure_reason_cb: record reasons while certain failure happens
+ * @get_failure_reason_cb: get failure reasons
+ * @clear_failure_reason_cb: callback function to clear the error which is
+ * caused by reading corrupted data or invalid lpt
+ * @test_and_clear_failure_reason_cb: callback function to check and clear the
+ * error which is caused by reading corrupted
+ * data or invalid lpt
+ * @set_lpt_invalid_cb: callback function to set the invalid lpt status
+ * @test_lpt_valid_cb: callback function to check whether lpt is corrupted or
+ * incorrect, should be called before updating lpt
+ * @can_ignore_failure_cb: callback function to decide whether the failure
+ * can be ignored
+ * @handle_failure_cb: callback function to decide whether the failure can be
+ * handled
*/
struct ubifs_info {
struct ubifs_sb_node *sup_node;
@@ -1254,6 +1268,21 @@ struct ubifs_info {
void *private;
void (*assert_failed_cb)(const struct ubifs_info *c);
+ void (*set_failure_reason_cb)(const struct ubifs_info *c,
+ unsigned int reason);
+ unsigned int (*get_failure_reason_cb)(const struct ubifs_info *c);
+ void (*clear_failure_reason_cb)(const struct ubifs_info *c);
+ bool (*test_and_clear_failure_reason_cb)(const struct ubifs_info *c,
+ unsigned int reason);
+ void (*set_lpt_invalid_cb)(const struct ubifs_info *c,
+ unsigned int reason);
+ bool (*test_lpt_valid_cb)(const struct ubifs_info *c, int lnum,
+ int old_free, int old_dirty,
+ int free, int dirty);
+ bool (*can_ignore_failure_cb)(const struct ubifs_info *c,
+ unsigned int reason);
+ bool (*handle_failure_cb)(const struct ubifs_info *c,
+ unsigned int reason);
};
extern atomic_long_t ubifs_clean_zn_cnt;
@@ -1502,6 +1531,75 @@ int ubifs_add_snod(const struct ubifs_info *c, struct ubifs_scan_leb *sleb,
void ubifs_scanned_corruption(const struct ubifs_info *c, int lnum, int offs,
void *buf);
+/* Failure reasons which are checked by fsck. */
+enum {
+ FR_DATA_CORRUPTED = 1, /* Data is corrupted(master/log/orphan/main) */
+ FR_TNC_CORRUPTED = 2, /* TNC is corrupted */
+ FR_LPT_CORRUPTED = 4, /* LPT is corrupted */
+ FR_LPT_INCORRECT = 8 /* Space statistics are wrong */
+};
+/* Callback functions for failure(which can be handled by fsck) happens. */
+static inline void set_failure_reason_callback(const struct ubifs_info *c,
+ unsigned int reason)
+{
+ if (c->set_failure_reason_cb)
+ c->set_failure_reason_cb(c, reason);
+}
+static inline unsigned int get_failure_reason_callback(
+ const struct ubifs_info *c)
+{
+ if (c->get_failure_reason_cb)
+ return c->get_failure_reason_cb(c);
+
+ return 0;
+}
+static inline void clear_failure_reason_callback(const struct ubifs_info *c)
+{
+ if (c->clear_failure_reason_cb)
+ c->clear_failure_reason_cb(c);
+}
+static inline bool test_and_clear_failure_reason_callback(
+ const struct ubifs_info *c,
+ unsigned int reason)
+{
+ if (c->test_and_clear_failure_reason_cb)
+ return c->test_and_clear_failure_reason_cb(c, reason);
+
+ return false;
+}
+static inline void set_lpt_invalid_callback(const struct ubifs_info *c,
+ unsigned int reason)
+{
+ if (c->set_lpt_invalid_cb)
+ c->set_lpt_invalid_cb(c, reason);
+}
+static inline bool test_lpt_valid_callback(const struct ubifs_info *c, int lnum,
+ int old_free, int old_dirty,
+ int free, int dirty)
+{
+ if (c->test_lpt_valid_cb)
+ return c->test_lpt_valid_cb(c, lnum,
+ old_free, old_dirty, free, dirty);
+
+ return false;
+}
+static inline bool can_ignore_failure_callback(const struct ubifs_info *c,
+ unsigned int reason)
+{
+ if (c->can_ignore_failure_cb)
+ return c->can_ignore_failure_cb(c, reason);
+
+ return false;
+}
+static inline bool handle_failure_callback(const struct ubifs_info *c,
+ unsigned int reason)
+{
+ if (c->handle_failure_cb)
+ return c->handle_failure_cb(c, reason);
+
+ return false;
+}
+
/* log.c */
void ubifs_add_bud(struct ubifs_info *c, struct ubifs_bud *bud);
void ubifs_create_buds_lists(struct ubifs_info *c);