diff options
-rw-r--r-- | ubifs-utils/fsck.ubifs/check_space.c | 576 | ||||
-rw-r--r-- | ubifs-utils/fsck.ubifs/fsck.ubifs.c | 13 | ||||
-rw-r--r-- | ubifs-utils/fsck.ubifs/fsck.ubifs.h | 58 | ||||
-rw-r--r-- | ubifs-utils/fsck.ubifs/problem.c | 51 | ||||
-rw-r--r-- | ubifs-utils/fsck.ubifs/rebuild_fs.c | 5 | ||||
-rw-r--r-- | ubifs-utils/libubifs/lpt.c | 9 | ||||
-rw-r--r-- | ubifs-utils/libubifs/lpt_commit.c | 18 | ||||
-rw-r--r-- | ubifs-utils/libubifs/replay.c | 2 | ||||
-rw-r--r-- | ubifs-utils/libubifs/ubifs.h | 5 | ||||
-rw-r--r-- | ubifs-utils/mkfs.ubifs/mkfs.ubifs.c | 2 |
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; |