aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ubifs-utils/fsck.ubifs/check_space.c576
-rw-r--r--ubifs-utils/fsck.ubifs/fsck.ubifs.c13
-rw-r--r--ubifs-utils/fsck.ubifs/fsck.ubifs.h58
-rw-r--r--ubifs-utils/fsck.ubifs/problem.c51
-rw-r--r--ubifs-utils/fsck.ubifs/rebuild_fs.c5
-rw-r--r--ubifs-utils/libubifs/lpt.c9
-rw-r--r--ubifs-utils/libubifs/lpt_commit.c18
-rw-r--r--ubifs-utils/libubifs/replay.c2
-rw-r--r--ubifs-utils/libubifs/ubifs.h5
-rw-r--r--ubifs-utils/mkfs.ubifs/mkfs.ubifs.c2
10 files changed, 720 insertions, 19 deletions
diff --git a/ubifs-utils/fsck.ubifs/check_space.c b/ubifs-utils/fsck.ubifs/check_space.c
index f758bf1..afe6ba0 100644
--- a/ubifs-utils/fsck.ubifs/check_space.c
+++ b/ubifs-utils/fsck.ubifs/check_space.c
@@ -44,18 +44,20 @@ int get_free_leb(struct ubifs_info *c)
* build_lpt - construct LPT and write it into flash.
* @c: UBIFS file-system description object
* @calculate_lp_cb: callback function to calculate the properties for given LEB
+ * @free_ltab: %true means to release c->ltab after creating lpt
*
* This function builds LPT according to the calculated results by
* @calculate_lp_cb and writes LPT into flash. Returns zero in case of success,
* a negative error code in case of failure.
*/
-int build_lpt(struct ubifs_info *c, calculate_lp_callback calculate_lp_cb)
+int build_lpt(struct ubifs_info *c, calculate_lp_callback calculate_lp_cb,
+ bool free_ltab)
{
int i, err, lnum, free, dirty;
u8 hash_lpt[UBIFS_HASH_ARR_SZ];
memset(&c->lst, 0, sizeof(struct ubifs_lp_stats));
- /* Set gc lnum. */
+ /* Set gc lnum, equivalent to ubifs_rcvry_gc_commit/take_gc_lnum. */
lnum = get_free_leb(c);
if (lnum < 0)
return lnum;
@@ -63,7 +65,7 @@ int build_lpt(struct ubifs_info *c, calculate_lp_callback calculate_lp_cb)
/* Update LPT. */
for (i = 0; i < c->main_lebs; i++) {
- err = calculate_lp_cb(c, i, &free, &dirty);
+ err = calculate_lp_cb(c, i, &free, &dirty, NULL);
if (err)
return err;
@@ -87,8 +89,574 @@ int build_lpt(struct ubifs_info *c, calculate_lp_callback calculate_lp_cb)
c->lst.total_dark += ubifs_calc_dark(c, spc);
c->lst.total_used += c->leb_size - spc;
}
+
+ dbg_fsck("build properties for LEB %d, free %d dirty %d is_idx %d, in %s",
+ i + c->main_first, free, dirty,
+ FSCK(c)->lpts[i].flags & LPROPS_INDEX ? 1 : 0,
+ c->dev_name);
}
/* Write LPT. */
- return ubifs_create_lpt(c, FSCK(c)->lpts, c->main_lebs, hash_lpt);
+ return ubifs_create_lpt(c, FSCK(c)->lpts, c->main_lebs, hash_lpt, free_ltab);
+}
+
+static int scan_get_lp(struct ubifs_info *c, int index, int *free, int *dirty,
+ int *is_idx)
+{
+ struct ubifs_scan_leb *sleb;
+ struct ubifs_scan_node *snod;
+ int used, idx_leb, lnum = index + c->main_first, err = 0;
+ bool is_build_lpt = FSCK(c)->lpt_status & FR_LPT_CORRUPTED;
+
+ if (is_build_lpt) {
+ if (!test_bit(index, FSCK(c)->used_lebs) || c->gc_lnum == lnum) {
+ *free = c->leb_size;
+ *dirty = 0;
+ return 0;
+ }
+ } else {
+ if (!test_bit(index, FSCK(c)->used_lebs)) {
+ *free = c->leb_size;
+ *dirty = 0;
+ return 0;
+ }
+ }
+
+ sleb = ubifs_scan(c, lnum, 0, c->sbuf, 0);
+ if (IS_ERR(sleb)) {
+ /* All TNC LEBs have passed ubifs_scan in previous steps. */
+ ubifs_assert(c, !get_failure_reason_callback(c));
+ return PTR_ERR(sleb);
+ }
+
+ idx_leb = -1;
+ used = 0;
+ list_for_each_entry(snod, &sleb->nodes, list) {
+ int found, level = 0;
+
+ if (idx_leb == -1)
+ idx_leb = (snod->type == UBIFS_IDX_NODE) ? 1 : 0;
+
+ if (idx_leb)
+ /*
+ * Previous steps have ensured that every TNC LEB
+ * contains only index nodes or non-index nodes.
+ */
+ ubifs_assert(c, snod->type == UBIFS_IDX_NODE);
+
+ if (snod->type == UBIFS_IDX_NODE) {
+ struct ubifs_idx_node *idx = snod->node;
+
+ key_read(c, ubifs_idx_key(c, idx), &snod->key);
+ level = le16_to_cpu(idx->level);
+ }
+
+ found = ubifs_tnc_has_node(c, &snod->key, level, lnum,
+ snod->offs, idx_leb);
+ if (found) {
+ if (found < 0) {
+ err = found;
+ /*
+ * TNC traversing is finished in previous steps,
+ * any TNC path is accessible.
+ */
+ ubifs_assert(c, !get_failure_reason_callback(c));
+ goto out;
+ }
+ used += ALIGN(snod->len, 8);
+ }
+ }
+
+ if (is_build_lpt && !used) {
+ *free = c->leb_size;
+ *dirty = 0;
+ } else {
+ *free = c->leb_size - sleb->endpt;
+ *dirty = sleb->endpt - used;
+ if (idx_leb == 1) {
+ if (is_build_lpt)
+ FSCK(c)->lpts[index].flags = LPROPS_INDEX;
+ else
+ *is_idx = 1;
+ }
+ }
+
+out:
+ ubifs_scan_destroy(sleb);
+ return err;
+}
+
+static void clear_buds(struct ubifs_info *c)
+{
+ int i;
+
+ /*
+ * Since lpt is invalid, space statistics cannot be trusted, the buds
+ * were used to trace taken LEBs(LPT related), and fsck makes sure that
+ * there will be no new journal writings(no space allocations) before
+ * committing, so we should clear buds to prevent wrong lpt updating in
+ * committing stage(eg. ubifs_return_leb operation for @c->old_buds).
+ */
+ free_buds(c, true);
+ for (i = 0; i < c->jhead_cnt; i++) {
+ c->jheads[i].wbuf.lnum = -1;
+ c->jheads[i].wbuf.offs = -1;
+ }
+}
+
+static void clear_lp_lists_and_heaps(struct ubifs_info *c)
+{
+ int i;
+
+ /*
+ * Since lpt is invalid, clear in-memory fast accessing paths (lp
+ * lists & heaps).
+ */
+ c->freeable_cnt = 0;
+ c->in_a_category_cnt = 0;
+ for (i = 0; i < LPROPS_HEAP_CNT; i++) {
+ memset(c->lpt_heap[i].arr, 0, LPT_HEAP_SZ * sizeof(void *));
+ c->lpt_heap[i].cnt = 0;
+ c->lpt_heap[i].max_cnt = LPT_HEAP_SZ;
+ }
+ memset(c->dirty_idx.arr, 0, LPT_HEAP_SZ * sizeof(void *));
+ c->dirty_idx.cnt = 0;
+ c->dirty_idx.max_cnt = LPT_HEAP_SZ;
+ INIT_LIST_HEAD(&c->uncat_list);
+ INIT_LIST_HEAD(&c->empty_list);
+ INIT_LIST_HEAD(&c->freeable_list);
+ INIT_LIST_HEAD(&c->frdi_idx_list);
+}
+
+static int retake_ihead(struct ubifs_info *c)
+{
+ int err = take_ihead(c);
+
+ if (err < 0) {
+ /* All LPT nodes must be accessible. */
+ ubifs_assert(c, !get_failure_reason_callback(c));
+ ubifs_assert(c, FSCK(c)->lpt_status == 0);
+ } else
+ err = 0;
+
+ return err;
+}
+
+static int rebuild_lpt(struct ubifs_info *c)
+{
+ int err;
+
+ /* Clear buds. */
+ clear_buds(c);
+ /* Clear stale in-memory lpt data. */
+ c->lpt_drty_flgs = 0;
+ c->dirty_nn_cnt = 0;
+ c->dirty_pn_cnt = 0;
+ clear_lp_lists_and_heaps(c);
+ ubifs_free_lpt_nodes(c);
+ kfree(c->ltab);
+ c->ltab = NULL;
+
+ FSCK(c)->lpts = kzalloc(sizeof(struct ubifs_lprops) * c->main_lebs,
+ GFP_KERNEL);
+ if (!FSCK(c)->lpts) {
+ log_err(c, errno, "can not allocate lpts");
+ return -ENOMEM;
+ }
+
+ err = build_lpt(c, scan_get_lp, false);
+ if (err)
+ goto out;
+
+ err = retake_ihead(c);
+ if (err)
+ goto out;
+
+ FSCK(c)->lpt_status = 0;
+
+out:
+ kfree(FSCK(c)->lpts);
+ return err;
+}
+
+static void check_and_correct_nnode(struct ubifs_info *c,
+ struct ubifs_nnode *nnode,
+ struct ubifs_nnode *parent_nnode,
+ int row, int col, int *corrected)
+{
+ int num = ubifs_calc_nnode_num(row, col);
+
+ if (nnode->num != num) {
+ struct nnode_problem nnp = {
+ .nnode = nnode,
+ .parent_nnode = parent_nnode,
+ .num = num,
+ };
+
+ /*
+ * The nnode number is read from disk in big lpt mode, which
+ * could lead to the wrong nnode number, otherwise, ther nnode
+ * number cannot be wrong.
+ */
+ ubifs_assert(c, c->big_lpt);
+ FSCK(c)->lpt_status |= FR_LPT_INCORRECT;
+ if (fix_problem(c, NNODE_INCORRECT, &nnp)) {
+ nnode->num = num;
+ *corrected = 1;
+ }
+ }
+}
+
+static int check_and_correct_pnode(struct ubifs_info *c,
+ struct ubifs_pnode *pnode, int col,
+ struct ubifs_lp_stats *lst,
+ int *freeable_cnt, int *corrected)
+{
+ int i, index, lnum;
+ const int lp_cnt = UBIFS_LPT_FANOUT;
+
+ if (pnode->num != col) {
+ struct pnode_problem pnp = {
+ .pnode = pnode,
+ .num = col,
+ };
+
+ /*
+ * The pnode number is read from disk in big lpt mode, which
+ * could lead to the wrong pnode number, otherwise, ther pnode
+ * number cannot be wrong.
+ */
+ ubifs_assert(c, c->big_lpt);
+ FSCK(c)->lpt_status |= FR_LPT_INCORRECT;
+ if (fix_problem(c, PNODE_INCORRECT, &pnp)) {
+ pnode->num = col;
+ *corrected = 1;
+ }
+ }
+
+ index = pnode->num << UBIFS_LPT_FANOUT_SHIFT;
+ lnum = index + c->main_first;
+ for (i = 0; i < lp_cnt && lnum < c->leb_cnt; i++, index++, lnum++) {
+ int err, cat, free, dirty, is_idx = 0;
+ struct ubifs_lprops *lp = &pnode->lprops[i];
+
+ err = scan_get_lp(c, index, &free, &dirty, &is_idx);
+ if (err)
+ return err;
+
+ dbg_fsck("calculate properties for LEB %d, free %d dirty %d is_idx %d, in %s",
+ lnum, free, dirty, is_idx, c->dev_name);
+
+ if (!FSCK(c)->lpt_status && lp->free + lp->dirty == c->leb_size
+ && !test_bit(index, FSCK(c)->used_lebs)) {
+ /*
+ * Some LEBs may become freeable in the following cases:
+ * a. LEBs become freeable after replaying the journal.
+ * b. Unclean reboot while doing gc for a freeable
+ * non-index LEB
+ * c. Freeable index LEBs in an uncompleted commit due
+ * to an unclean unmount.
+ * , which makes that these LEBs won't be accounted into
+ * the FSCK(c)->used_lebs, but they actually have
+ * free/dirty space statistics. So we should skip
+ * checking space for these LEBs.
+ */
+ free = lp->free;
+ dirty = lp->dirty;
+ is_idx = (lp->flags & LPROPS_INDEX) ? 1 : 0;
+ }
+ if (lnum != lp->lnum ||
+ free != lp->free || dirty != lp->dirty ||
+ (is_idx && !(lp->flags & LPROPS_INDEX)) ||
+ (!is_idx && (lp->flags & LPROPS_INDEX))) {
+ struct lp_problem lpp = {
+ .lnum = lnum,
+ .lp = lp,
+ .free = free,
+ .dirty = dirty,
+ .is_idx = is_idx,
+ };
+
+ FSCK(c)->lpt_status |= FR_LPT_INCORRECT;
+ if (fix_problem(c, LP_INCORRECT, &lpp)) {
+ lp->lnum = lnum;
+ lp->free = free;
+ lp->dirty = dirty;
+ lp->flags = is_idx ? LPROPS_INDEX : 0;
+ *corrected = 1;
+ }
+ }
+
+ cat = ubifs_categorize_lprops(c, lp);
+ if (cat != (lp->flags & LPROPS_CAT_MASK)) {
+ if (FSCK(c)->lpt_status & FR_LPT_INCORRECT) {
+ lp->flags &= ~LPROPS_CAT_MASK;
+ lp->flags |= cat;
+ } else {
+ /* lp could be in the heap or un-categorized(add heap failed). */
+ ubifs_assert(c, (lp->flags & LPROPS_CAT_MASK) == LPROPS_UNCAT);
+ }
+ }
+ if (cat == LPROPS_FREEABLE)
+ *freeable_cnt = *freeable_cnt + 1;
+ if ((lp->flags & LPROPS_TAKEN) && free == c->leb_size)
+ lst->taken_empty_lebs += 1;
+
+ lst->total_free += free;
+ lst->total_dirty += dirty;
+
+ if (free == c->leb_size)
+ lst->empty_lebs++;
+
+ if (is_idx) {
+ lst->idx_lebs += 1;
+ } else {
+ int spc;
+
+ spc = free + dirty;
+ if (spc < c->dead_wm)
+ lst->total_dead += spc;
+ else
+ lst->total_dark += ubifs_calc_dark(c, spc);
+ lst->total_used += c->leb_size - spc;
+ }
+ }
+
+ return 0;
+}
+
+static int check_and_correct_lpt(struct ubifs_info *c, int *lpt_corrected)
+{
+ int err, i, cnt, iip, row, col, corrected, lnum, max_num, freeable_cnt;
+ struct ubifs_cnode *cn, *cnode;
+ struct ubifs_nnode *nnode, *nn;
+ struct ubifs_pnode *pnode;
+ struct ubifs_lp_stats lst;
+
+ max_num = 0;
+ freeable_cnt = 0;
+ memset(&lst, 0, sizeof(struct ubifs_lp_stats));
+
+ /* Load the entire LPT tree, check whether there are corrupted nodes. */
+ cnt = DIV_ROUND_UP(c->main_lebs, UBIFS_LPT_FANOUT);
+ for (i = 0; i < cnt; i++) {
+ pnode = ubifs_pnode_lookup(c, i);
+ if (IS_ERR(pnode))
+ return PTR_ERR(pnode);
+ if (pnode->num > max_num)
+ max_num = pnode->num;
+ }
+
+ /* Check whether there are pnodes exceeding the 'c->main_lebs'. */
+ pnode = ubifs_pnode_lookup(c, 0);
+ if (IS_ERR(pnode))
+ return PTR_ERR(pnode);
+ while (pnode) {
+ if (pnode->num > max_num) {
+ ubifs_err(c, "pnode(%d) exceeds max number(%d)",
+ pnode->num, max_num);
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
+ return -EINVAL;
+ }
+ pnode = ubifs_find_next_pnode(c, pnode);
+ if (IS_ERR(pnode))
+ return PTR_ERR(pnode);
+ }
+
+ /* Check & correct nnodes and pnodes(including LEB properties). */
+ row = col = iip = 0;
+ cnode = (struct ubifs_cnode *)c->nroot;
+ while (cnode) {
+ ubifs_assert(c, row >= 0);
+ nnode = cnode->parent;
+ if (cnode->level) {
+ corrected = 0;
+ /* cnode is a nnode */
+ nn = (struct ubifs_nnode *)cnode;
+ check_and_correct_nnode(c, nn, nnode, row, col,
+ &corrected);
+ if (corrected)
+ ubifs_make_nnode_dirty(c, nn);
+ while (iip < UBIFS_LPT_FANOUT) {
+ cn = nn->nbranch[iip].cnode;
+ if (cn) {
+ /* Go down */
+ row += 1;
+ col <<= UBIFS_LPT_FANOUT_SHIFT;
+ col += iip;
+ iip = 0;
+ cnode = cn;
+ break;
+ }
+ /* Go right */
+ iip += 1;
+ }
+ if (iip < UBIFS_LPT_FANOUT)
+ continue;
+ } else {
+ corrected = 0;
+ /* cnode is a pnode */
+ pnode = (struct ubifs_pnode *)cnode;
+ err = check_and_correct_pnode(c, pnode, col, &lst,
+ &freeable_cnt, &corrected);
+ if (err)
+ return err;
+ if (corrected)
+ ubifs_make_pnode_dirty(c, pnode);
+ }
+ /* Go up and to the right */
+ row -= 1;
+ col >>= UBIFS_LPT_FANOUT_SHIFT;
+ iip = cnode->iip + 1;
+ cnode = (struct ubifs_cnode *)nnode;
+ }
+
+ dbg_fsck("empty_lebs %d, idx_lebs %d, total_free %lld, total_dirty %lld,"
+ " total_used %lld, total_dead %lld, total_dark %lld,"
+ " taken_empty_lebs %d, freeable_cnt %d, in %s",
+ lst.empty_lebs, lst.idx_lebs, lst.total_free, lst.total_dirty,
+ lst.total_used, lst.total_dead, lst.total_dark,
+ lst.taken_empty_lebs, freeable_cnt, c->dev_name);
+
+ /* Check & correct the global space statistics. */
+ if (lst.empty_lebs != c->lst.empty_lebs ||
+ lst.idx_lebs != c->lst.idx_lebs ||
+ lst.total_free != c->lst.total_free ||
+ lst.total_dirty != c->lst.total_dirty ||
+ lst.total_used != c->lst.total_used ||
+ lst.total_dead != c->lst.total_dead ||
+ lst.total_dark != c->lst.total_dark) {
+ struct space_stat_problem ssp = {
+ .lst = &c->lst,
+ .calc_lst = &lst,
+ };
+
+ FSCK(c)->lpt_status |= FR_LPT_INCORRECT;
+ if (fix_problem(c, SPACE_STAT_INCORRECT, &ssp)) {
+ c->lst.empty_lebs = lst.empty_lebs;
+ c->lst.idx_lebs = lst.idx_lebs;
+ c->lst.total_free = lst.total_free;
+ c->lst.total_dirty = lst.total_dirty;
+ c->lst.total_used = lst.total_used;
+ c->lst.total_dead = lst.total_dead;
+ c->lst.total_dark = lst.total_dark;
+ }
+ }
+
+ /* Check & correct the lprops table information. */
+ for (lnum = c->lpt_first; lnum <= c->lpt_last; lnum++) {
+ err = dbg_check_ltab_lnum(c, lnum);
+ if (err)
+ return err;
+ }
+
+ if (FSCK(c)->lpt_status & FR_LPT_INCORRECT) {
+ /* Reset the taken_empty_lebs. */
+ c->lst.taken_empty_lebs = 0;
+ /* Clear buds. */
+ clear_buds(c);
+ /* Clear lp lists & heaps. */
+ clear_lp_lists_and_heaps(c);
+ /*
+ * Build lp lists & heaps, subsequent steps could recover
+ * disconnected files by allocating free space.
+ */
+ for (lnum = c->main_first; lnum < c->leb_cnt; lnum++) {
+ int cat;
+ struct ubifs_lprops *lp = ubifs_lpt_lookup(c, lnum);
+ if (IS_ERR(lp))
+ return PTR_ERR(lp);
+
+ /* Clear %LPROPS_TAKEN flag for all LEBs. */
+ lp->flags &= ~LPROPS_TAKEN;
+ cat = lp->flags & LPROPS_CAT_MASK;
+ ubifs_add_to_cat(c, lp, cat);
+ }
+ /*
+ * The %LPROPS_TAKEN flag is cleared in LEB properties, just
+ * remark it for c->ihead_lnum LEB.
+ */
+ err = retake_ihead(c);
+ if (err)
+ return err;
+
+ *lpt_corrected = 1;
+ FSCK(c)->lpt_status &= ~FR_LPT_INCORRECT;
+ } else {
+ ubifs_assert(c, c->freeable_cnt == freeable_cnt);
+ ubifs_assert(c, c->lst.taken_empty_lebs == lst.taken_empty_lebs);
+ ubifs_assert(c, c->in_a_category_cnt == c->main_lebs);
+ }
+
+ return 0;
+}
+
+/**
+ * check_and_correct_space - check & correct the space statistics.
+ * @c: UBIFS file-system description object
+ *
+ * This function does following things:
+ * 1. Check fsck mode, exit program if current mode is check mode.
+ * 2. Check space statistics by comparing lpt records with scanning results
+ * for all main LEBs. There could be following problems:
+ * a) comparison result is inconsistent: correct the lpt records by LEB
+ * scanning results.
+ * b) lpt is corrupted: rebuild lpt.
+ * 3. Set the gc lnum.
+ * Returns zero in case of success, a negative error code in case of failure.
+ */
+int check_and_correct_space(struct ubifs_info *c)
+{
+ int err, lpt_corrected = 0;
+
+ if (FSCK(c)->mode == CHECK_MODE) {
+ /*
+ * The check mode will exit, because unclean LEBs are not
+ * rewritten for readonly mode in previous steps.
+ */
+ if (FSCK(c)->lpt_status)
+ exit_code |= FSCK_UNCORRECTED;
+ dbg_fsck("skip checking & correcting space%s, in %s",
+ mode_name(c), c->dev_name);
+ exit(exit_code);
+ }
+
+ log_out(c, "Check and correct the space statistics");
+
+ if (FSCK(c)->lpt_status & FR_LPT_CORRUPTED) {
+rebuild:
+ if (fix_problem(c, LPT_CORRUPTED, NULL))
+ return rebuild_lpt(c);
+ }
+
+ err = check_and_correct_lpt(c, &lpt_corrected);
+ if (err) {
+ if (test_and_clear_failure_reason_callback(c, FR_LPT_CORRUPTED))
+ goto rebuild;
+ return err;
+ }
+
+ /* Set gc lnum. */
+ if (c->need_recovery || lpt_corrected) {
+ err = ubifs_rcvry_gc_commit(c);
+ if (err) {
+ /* All LPT nodes must be accessible. */
+ ubifs_assert(c, !get_failure_reason_callback(c));
+ ubifs_assert(c, FSCK(c)->lpt_status == 0);
+ return err;
+ }
+ } else {
+ err = take_gc_lnum(c);
+ if (err) {
+ /* All LPT nodes must be accessible. */
+ ubifs_assert(c, !get_failure_reason_callback(c));
+ ubifs_assert(c, FSCK(c)->lpt_status == 0);
+ return err;
+ }
+ err = ubifs_leb_unmap(c, c->gc_lnum);
+ if (err)
+ return err;
+ }
+
+ return err;
}
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.c b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
index cd498f5..ba1b500 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.c
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.c
@@ -58,6 +58,7 @@ static const char *helptext =
"-b, --rebuild Forcedly repair the filesystem even by rebuilding filesystem.\n"
" Depends on -y option\n"
"-n, --nochange Make no changes to the filesystem, only check filesystem.\n"
+" This mode don't check space, because unclean LEBs are not rewritten in readonly mode.\n"
" Can not be specified at the same time as the -a or -y options\n"
"Examples:\n"
"\t1. Check and repair filesystem from UBI volume /dev/ubi0_0\n"
@@ -329,6 +330,7 @@ static const unsigned int reason_mapping_table[] = {
BUD_CORRUPTED, /* FR_H_BUD_CORRUPTED */
TNC_DATA_CORRUPTED, /* FR_H_TNC_DATA_CORRUPTED */
ORPHAN_CORRUPTED, /* FR_H_ORPHAN_CORRUPTED */
+ LTAB_INCORRECT, /* FR_H_LTAB_INCORRECT */
};
static bool fsck_handle_failure(const struct ubifs_info *c, unsigned int reason,
@@ -471,8 +473,18 @@ static int do_fsck(void)
if (tnc_is_empty(c) && fix_problem(c, EMPTY_TNC, NULL)) {
err = -EINVAL;
FSCK(c)->try_rebuild = true;
+ goto free_disconnected_files;
}
+ err = check_and_correct_space(c);
+ kfree(FSCK(c)->used_lebs);
+ destroy_file_tree(c, &FSCK(c)->scanned_files);
+ if (err)
+ exit_code |= FSCK_ERROR;
+
+ destroy_file_list(c, &FSCK(c)->disconnected_files);
+ return err;
+
free_disconnected_files:
destroy_file_list(c, &FSCK(c)->disconnected_files);
free_used_lebs:
@@ -519,6 +531,7 @@ int main(int argc, char *argv[])
* Step 9: Check and handle unreachable files
* Step 10: Check and correct files
* Step 11: Check whether the TNC is empty
+ * Step 12: Check and correct the space statistics
*/
err = do_fsck();
if (err && FSCK(c)->try_rebuild) {
diff --git a/ubifs-utils/fsck.ubifs/fsck.ubifs.h b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
index ade27fe..00c157a 100644
--- a/ubifs-utils/fsck.ubifs/fsck.ubifs.h
+++ b/ubifs-utils/fsck.ubifs/fsck.ubifs.h
@@ -44,12 +44,14 @@ enum { SB_CORRUPTED = 0, MST_CORRUPTED, LOG_CORRUPTED, BUD_CORRUPTED,
FILE_SHOULDNT_HAVE_DATA, FILE_HAS_NO_DENT, XATTR_HAS_NO_HOST,
XATTR_HAS_WRONG_HOST, FILE_HAS_NO_ENCRYPT, FILE_IS_DISCONNECTED,
FILE_ROOT_HAS_DENT, DENTRY_IS_UNREACHABLE, FILE_IS_INCONSISTENT,
- EMPTY_TNC };
+ EMPTY_TNC, LPT_CORRUPTED, NNODE_INCORRECT, PNODE_INCORRECT,
+ LP_INCORRECT, SPACE_STAT_INCORRECT, LTAB_INCORRECT };
enum { HAS_DATA_CORRUPTED = 1, HAS_TNC_CORRUPTED = 2 };
typedef int (*calculate_lp_callback)(struct ubifs_info *c,
- int index, int *free, int *dirty);
+ int index, int *free, int *dirty,
+ int *is_idx);
struct scanned_file;
@@ -201,6 +203,54 @@ struct invalid_file_problem {
};
/**
+ * nnode_problem - problem instance for incorrect nnode
+ * @nnode: incorrect nnode
+ * @parent_nnode: the parent nnode of @nnode, could be NULL if @nnode is root
+ * @num: calculated num
+ */
+struct nnode_problem {
+ struct ubifs_nnode *nnode;
+ struct ubifs_nnode *parent_nnode;
+ int num;
+};
+
+/**
+ * pnode_problem - problem instance for incorrect pnode
+ * @pnode: incorrect pnode
+ * @num: calculated num
+ */
+struct pnode_problem {
+ struct ubifs_pnode *pnode;
+ int num;
+};
+
+/**
+ * lp_problem - problem instance for incorrect LEB proerties
+ * @lp: incorrect LEB properties
+ * @lnum: LEB number
+ * @free: calculated free space in LEB
+ * @dirty: calculated dirty bytes in LEB
+ * @is_idx: %true means that the LEB is an index LEB
+ */
+struct lp_problem {
+ struct ubifs_lprops *lp;
+ int lnum;
+ int free;
+ int dirty;
+ int is_idx;
+};
+
+/**
+ * space_stat_problem - problem instance for incorrect space statistics
+ * @lst: current space statistics
+ * @calc_lst: calculated space statistics
+ */
+struct space_stat_problem {
+ struct ubifs_lp_stats *lst;
+ struct ubifs_lp_stats *calc_lst;
+};
+
+/**
* ubifs_rebuild_info - UBIFS rebuilding information.
* @write_buf: write buffer for LEB @head_lnum
* @head_lnum: current writing LEB number
@@ -326,6 +376,8 @@ bool tnc_is_empty(struct ubifs_info *c);
/* check_space.c */
int get_free_leb(struct ubifs_info *c);
-int build_lpt(struct ubifs_info *c, calculate_lp_callback calculate_lp_cb);
+int build_lpt(struct ubifs_info *c, calculate_lp_callback calculate_lp_cb,
+ bool free_ltab);
+int check_and_correct_space(struct ubifs_info *c);
#endif
diff --git a/ubifs-utils/fsck.ubifs/problem.c b/ubifs-utils/fsck.ubifs/problem.c
index 795f05f..f987e48 100644
--- a/ubifs-utils/fsck.ubifs/problem.c
+++ b/ubifs-utils/fsck.ubifs/problem.c
@@ -61,6 +61,12 @@ static const struct fsck_problem problem_table[] = {
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Dentry is unreachable"}, // DENTRY_IS_UNREACHABLE
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "File is inconsistent"}, // FILE_IS_INCONSISTENT
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA | PROBLEM_NEED_REBUILD, "TNC is empty"}, // EMPTY_TNC
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Corrupted pnode/nnode"}, // LPT_CORRUPTED
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Inconsistent properties for nnode"}, // NNODE_INCORRECT
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Inconsistent properties for pnode"}, // PNODE_INCORRECT
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Inconsistent properties for LEB"}, // LP_INCORRECT
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Incorrect space statistics"}, // SPACE_STAT_INCORRECT
+ {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Inconsistent properties for lprops table"}, // LTAB_INCORRECT
};
static const char *get_question(const struct fsck_problem *problem,
@@ -96,6 +102,8 @@ static const char *get_question(const struct fsck_problem *problem,
return "Remove data block?";
case FILE_IS_DISCONNECTED:
return "Put it into disconnected list?";
+ case LPT_CORRUPTED:
+ return "Rebuild LPT?";
}
return "Fix it?";
@@ -229,6 +237,49 @@ static void print_problem(const struct ubifs_info *c,
file->calc_xnms, file->calc_size);
break;
}
+ case NNODE_INCORRECT:
+ {
+ const struct nnode_problem *nnp = (const struct nnode_problem *)priv;
+
+ log_out(c, "problem: %s, nnode num %d expected %d parent num %d iip %d",
+ problem->desc, nnp->nnode->num, nnp->num,
+ nnp->parent_nnode ? nnp->parent_nnode->num : 0,
+ nnp->nnode->iip);
+ break;
+ }
+ case PNODE_INCORRECT:
+ {
+ const struct pnode_problem *pnp = (const struct pnode_problem *)priv;
+
+ log_out(c, "problem: %s, pnode num %d expected %d parent num %d iip %d",
+ problem->desc, pnp->pnode->num, pnp->num,
+ pnp->pnode->parent->num, pnp->pnode->iip);
+ break;
+ }
+ case LP_INCORRECT:
+ {
+ const struct lp_problem *lpp = (const struct lp_problem *)priv;
+
+ log_out(c, "problem: %s %d, free %d dirty %d is_idx %d, should be lnum %d free %d dirty %d is_idx %d",
+ problem->desc, lpp->lp->lnum, lpp->lp->free,
+ lpp->lp->dirty, lpp->lp->flags & LPROPS_INDEX ? 1 : 0,
+ lpp->lnum, lpp->free, lpp->dirty, lpp->is_idx);
+ break;
+ }
+ case SPACE_STAT_INCORRECT:
+ {
+ const struct space_stat_problem *ssp = (const struct space_stat_problem *)priv;
+
+ log_out(c, "problem: %s, empty_lebs %d idx_lebs %d total_free %lld total_dirty %lld total_used %lld total_dead %lld total_dark %lld, should be empty_lebs %d idx_lebs %d total_free %lld total_dirty %lld total_used %lld total_dead %lld total_dark %lld",
+ problem->desc, ssp->lst->empty_lebs, ssp->lst->idx_lebs,
+ ssp->lst->total_free, ssp->lst->total_dirty,
+ ssp->lst->total_used, ssp->lst->total_dead,
+ ssp->lst->total_dark, ssp->calc_lst->empty_lebs,
+ ssp->calc_lst->idx_lebs, ssp->calc_lst->total_free,
+ ssp->calc_lst->total_dirty, ssp->calc_lst->total_used,
+ ssp->calc_lst->total_dead, ssp->calc_lst->total_dark);
+ break;
+ }
default:
log_out(c, "problem: %s", problem->desc);
break;
diff --git a/ubifs-utils/fsck.ubifs/rebuild_fs.c b/ubifs-utils/fsck.ubifs/rebuild_fs.c
index 1161f5a..e65d655 100644
--- a/ubifs-utils/fsck.ubifs/rebuild_fs.c
+++ b/ubifs-utils/fsck.ubifs/rebuild_fs.c
@@ -1224,7 +1224,8 @@ out_idx_list:
return err;
}
-static int calculate_lp(struct ubifs_info *c, int index, int *free, int *dirty)
+static int calculate_lp(struct ubifs_info *c, int index, int *free, int *dirty,
+ __unused int *is_idx)
{
if (!test_bit(index, FSCK(c)->used_lebs) ||
c->gc_lnum == index + c->main_first) {
@@ -1421,7 +1422,7 @@ int ubifs_rebuild_filesystem(struct ubifs_info *c)
/* Step 10. Build LPT. */
log_out(c, "Build LPT");
- err = build_lpt(c, calculate_lp);
+ err = build_lpt(c, calculate_lp, true);
if (err) {
exit_code |= FSCK_ERROR;
goto out;
diff --git a/ubifs-utils/libubifs/lpt.c b/ubifs-utils/libubifs/lpt.c
index fc70cad..8e20a17 100644
--- a/ubifs-utils/libubifs/lpt.c
+++ b/ubifs-utils/libubifs/lpt.c
@@ -605,6 +605,7 @@ static int calc_pnode_num_from_parent(const struct ubifs_info *c,
* @lps: array of logical eraseblock properties
* @lp_cnt: the length of @lps
* @hash: hash of the LPT is returned here
+ * @free_ltab: %true means to release c->ltab after creating lpt
*
* This function creates lpt, the pnode will be initialized based on
* corresponding elements in @lps. If there are no corresponding lprops
@@ -612,7 +613,7 @@ static int calc_pnode_num_from_parent(const struct ubifs_info *c,
* as free state.
*/
int ubifs_create_lpt(struct ubifs_info *c, struct ubifs_lprops *lps, int lp_cnt,
- u8 *hash)
+ u8 *hash, bool free_ltab)
{
int lnum, err = 0, i, j, cnt, len, alen, row;
int blnum, boffs, bsz, bcnt;
@@ -910,10 +911,12 @@ int ubifs_create_lpt(struct ubifs_info *c, struct ubifs_lprops *lps, int lp_cnt,
if (c->big_lpt)
dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs);
out:
- c->ltab = NULL;
+ if (free_ltab || err) {
+ c->ltab = NULL;
+ vfree(ltab);
+ }
kfree(desc);
kfree(lsave);
- vfree(ltab);
vfree(buf);
kfree(nnode);
kfree(pnode);
diff --git a/ubifs-utils/libubifs/lpt_commit.c b/ubifs-utils/libubifs/lpt_commit.c
index 8a44546..ee84f80 100644
--- a/ubifs-utils/libubifs/lpt_commit.c
+++ b/ubifs-utils/libubifs/lpt_commit.c
@@ -1599,7 +1599,7 @@ static int dbg_is_node_dirty(struct ubifs_info *c, int node_type, int lnum,
*
* This function returns %0 on success and a negative error code on failure.
*/
-static int dbg_check_ltab_lnum(struct ubifs_info *c, int lnum)
+int dbg_check_ltab_lnum(struct ubifs_info *c, int lnum)
{
int err, len = c->leb_size, dirty = 0, node_type, node_num, node_len;
int ret;
@@ -1608,7 +1608,7 @@ static int dbg_check_ltab_lnum(struct ubifs_info *c, int lnum)
buf = p = __vmalloc(c->leb_size, GFP_NOFS);
if (!buf) {
ubifs_err(c, "cannot allocate memory for ltab checking");
- return 0;
+ return -ENOMEM;
}
dbg_lp("LEB %d", lnum);
@@ -1632,20 +1632,30 @@ static int dbg_check_ltab_lnum(struct ubifs_info *c, int lnum)
continue;
}
if (!dbg_is_all_ff(p, len)) {
+ set_failure_reason_callback(c, FR_LPT_CORRUPTED);
ubifs_err(c, "invalid empty space in LEB %d at %d",
lnum, c->leb_size - len);
err = -EINVAL;
+ goto out;
}
i = lnum - c->lpt_first;
if (len != c->ltab[i].free) {
ubifs_err(c, "invalid free space in LEB %d (free %d, expected %d)",
- lnum, len, c->ltab[i].free);
+ lnum, c->ltab[i].free, len);
err = -EINVAL;
+ if (handle_failure_callback(c, FR_H_LTAB_INCORRECT, NULL)) {
+ c->ltab[i].free = len;
+ err = 0;
+ }
}
if (dirty != c->ltab[i].dirty) {
ubifs_err(c, "invalid dirty space in LEB %d (dirty %d, expected %d)",
- lnum, dirty, c->ltab[i].dirty);
+ lnum, c->ltab[i].dirty, dirty);
err = -EINVAL;
+ if (handle_failure_callback(c, FR_H_LTAB_INCORRECT, NULL)) {
+ c->ltab[i].dirty = dirty;
+ err = 0;
+ }
}
goto out;
}
diff --git a/ubifs-utils/libubifs/replay.c b/ubifs-utils/libubifs/replay.c
index 2741742..3943b32 100644
--- a/ubifs-utils/libubifs/replay.c
+++ b/ubifs-utils/libubifs/replay.c
@@ -1099,7 +1099,7 @@ out_dump:
* This function returns the amount of free space in the index head LEB or a
* negative error code.
*/
-static int take_ihead(struct ubifs_info *c)
+int take_ihead(struct ubifs_info *c)
{
const struct ubifs_lprops *lp;
int err, free;
diff --git a/ubifs-utils/libubifs/ubifs.h b/ubifs-utils/libubifs/ubifs.h
index 45c4105..a4b05a6 100644
--- a/ubifs-utils/libubifs/ubifs.h
+++ b/ubifs-utils/libubifs/ubifs.h
@@ -1563,6 +1563,7 @@ enum {
FR_H_BUD_CORRUPTED = 0, /* Bud LEB is corrupted */
FR_H_TNC_DATA_CORRUPTED, /* Data searched from TNC is corrupted */
FR_H_ORPHAN_CORRUPTED, /* Orphan LEB is corrupted */
+ FR_H_LTAB_INCORRECT, /* Lprops table is incorrect */
};
/* Callback functions for failure(which can be handled by fsck) happens. */
static inline void set_failure_reason_callback(const struct ubifs_info *c,
@@ -1734,6 +1735,7 @@ int ubifs_fixup_free_space(struct ubifs_info *c);
/* replay.c */
int ubifs_validate_entry(struct ubifs_info *c,
const struct ubifs_dent_node *dent);
+int take_ihead(struct ubifs_info *c);
int ubifs_replay_journal(struct ubifs_info *c);
/* gc.c */
@@ -1754,7 +1756,7 @@ int ubifs_clear_orphans(struct ubifs_info *c);
int ubifs_calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, int *big_lpt);
int ubifs_calc_lpt_geom(struct ubifs_info *c);
int ubifs_create_lpt(struct ubifs_info *c, struct ubifs_lprops *lps, int lp_cnt,
- u8 *hash);
+ u8 *hash, bool free_ltab);
int ubifs_lpt_init(struct ubifs_info *c, int rd, int wr);
struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum);
struct ubifs_lprops *ubifs_lpt_lookup_dirty(struct ubifs_info *c, int lnum);
@@ -1795,6 +1797,7 @@ int ubifs_lpt_end_commit(struct ubifs_info *c);
int ubifs_lpt_post_commit(struct ubifs_info *c);
void ubifs_free_lpt_nodes(struct ubifs_info *c);
void ubifs_lpt_free(struct ubifs_info *c, int wr_only);
+int dbg_check_ltab_lnum(struct ubifs_info *c, int lnum);
/* lprops.c */
const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c,
diff --git a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
index 2817b6c..b5f3892 100644
--- a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
+++ b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
@@ -2675,7 +2675,7 @@ static int write_lpt(void)
int err, lnum;
c->lscan_lnum = c->main_first;
- err = ubifs_create_lpt(c, c->lpt, c->main_lebs, c->lpt_hash);
+ err = ubifs_create_lpt(c, c->lpt, c->main_lebs, c->lpt_hash, true);
if (err)
return err;