// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2024, Huawei Technologies Co, Ltd. * * Authors: Zhihao Cheng */ #include #include #include "bitops.h" #include "kmem.h" #include "ubifs.h" #include "defs.h" #include "debug.h" #include "key.h" #include "misc.h" #include "fsck.ubifs.h" int ubifs_load_filesystem(struct ubifs_info *c) { int err; size_t sz; err = init_constants_early(c); if (err) { exit_code |= FSCK_ERROR; return err; } err = check_volume_empty(c); if (err <= 0) { exit_code |= FSCK_ERROR; log_err(c, 0, "%s UBI volume!", err < 0 ? "bad" : "empty"); return -EINVAL; } if (c->ro_media && !c->ro_mount) { exit_code |= FSCK_ERROR; log_err(c, 0, "cannot read-write on read-only media"); return -EROFS; } err = -ENOMEM; c->bottom_up_buf = kmalloc_array(BOTTOM_UP_HEIGHT, sizeof(int), GFP_KERNEL); if (!c->bottom_up_buf) { exit_code |= FSCK_ERROR; log_err(c, errno, "cannot allocate bottom_up_buf"); goto out_free; } c->sbuf = vmalloc(c->leb_size); if (!c->sbuf) { exit_code |= FSCK_ERROR; log_err(c, errno, "cannot allocate sbuf"); goto out_free; } if (!c->ro_mount) { c->ileb_buf = vmalloc(c->leb_size); if (!c->ileb_buf) { exit_code |= FSCK_ERROR; log_err(c, errno, "cannot allocate ileb_buf"); goto out_free; } } c->mounting = 1; log_out(c, "Read superblock"); err = ubifs_read_superblock(c); if (err) { if (test_and_clear_failure_reason_callback(c, FR_DATA_CORRUPTED)) fix_problem(c, SB_CORRUPTED, NULL); exit_code |= FSCK_ERROR; goto out_mounting; } err = init_constants_sb(c); if (err) { exit_code |= FSCK_ERROR; goto out_mounting; } sz = ALIGN(c->max_idx_node_sz, c->min_io_size) * 2; c->cbuf = kmalloc(sz, GFP_NOFS); if (!c->cbuf) { err = -ENOMEM; exit_code |= FSCK_ERROR; log_err(c, errno, "cannot allocate cbuf"); goto out_mounting; } err = alloc_wbufs(c); if (err) { exit_code |= FSCK_ERROR; log_err(c, 0, "cannot allocate wbuf"); goto out_mounting; } log_out(c, "Read master & init lpt"); err = ubifs_read_master(c); if (err) { if (test_and_clear_failure_reason_callback(c, FR_DATA_CORRUPTED)) { if (fix_problem(c, MST_CORRUPTED, NULL)) FSCK(c)->try_rebuild = true; } else exit_code |= FSCK_ERROR; goto out_master; } init_constants_master(c); if ((c->mst_node->flags & cpu_to_le32(UBIFS_MST_DIRTY)) != 0) { ubifs_msg(c, "recovery needed"); c->need_recovery = 1; } if (c->need_recovery && !c->ro_mount) { err = ubifs_recover_inl_heads(c, c->sbuf); if (err) { exit_code |= FSCK_ERROR; goto out_master; } } err = ubifs_lpt_init(c, 1, !c->ro_mount); if (err) { exit_code |= FSCK_ERROR; goto out_master; } if (!c->ro_mount && c->space_fixup) { err = ubifs_fixup_free_space(c); if (err) { exit_code |= FSCK_ERROR; goto out_lpt; } } if (!c->ro_mount && !c->need_recovery) { /* * Set the "dirty" flag so that if we reboot uncleanly we * will notice this immediately on the next mount. */ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY); err = ubifs_write_master(c); if (err) { exit_code |= FSCK_ERROR; goto out_lpt; } } if (!c->ro_mount && c->superblock_need_write) { err = ubifs_write_sb_node(c, c->sup_node); if (err) { exit_code |= FSCK_ERROR; goto out_lpt; } c->superblock_need_write = 0; } log_out(c, "Replay journal"); err = ubifs_replay_journal(c); if (err) { handle_error(c, HAS_DATA_CORRUPTED | HAS_TNC_CORRUPTED); goto out_journal; } /* Calculate 'min_idx_lebs' after journal replay */ c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c); log_out(c, "Handle orphan nodes"); err = ubifs_mount_orphans(c, c->need_recovery, c->ro_mount); if (err) { handle_error(c, HAS_TNC_CORRUPTED); goto out_orphans; } if (!c->ro_mount) { int lnum; /* Check for enough log space */ lnum = c->lhead_lnum + 1; if (lnum >= UBIFS_LOG_LNUM + c->log_lebs) lnum = UBIFS_LOG_LNUM; if (lnum == c->ltail_lnum) { log_out(c, "Consolidate log"); err = ubifs_consolidate_log(c); if (err) { handle_error(c, HAS_DATA_CORRUPTED); goto out_orphans; } } if (c->need_recovery) { log_out(c, "Recover isize"); err = ubifs_recover_size(c, true); if (err) { handle_error(c, HAS_TNC_CORRUPTED); goto out_orphans; } } } else if (c->need_recovery) { log_out(c, "Recover isize"); err = ubifs_recover_size(c, false); if (err) { handle_error(c, HAS_TNC_CORRUPTED); goto out_orphans; } } c->mounting = 0; return 0; out_orphans: free_orphans(c); out_journal: destroy_journal(c); out_lpt: ubifs_lpt_free(c, 0); out_master: c->max_sqnum = 0; c->highest_inum = 0; c->calc_idx_sz = 0; kfree(c->mst_node); kfree(c->rcvrd_mst_node); free_wbufs(c); out_mounting: c->mounting = 0; out_free: kfree(c->cbuf); kfree(c->ileb_buf); kfree(c->sbuf); kfree(c->bottom_up_buf); kfree(c->sup_node); return err; } void ubifs_destroy_filesystem(struct ubifs_info *c) { destroy_journal(c); free_wbufs(c); free_orphans(c); ubifs_lpt_free(c, 0); c->max_sqnum = 0; c->highest_inum = 0; c->calc_idx_sz = 0; kfree(c->cbuf); kfree(c->rcvrd_mst_node); kfree(c->mst_node); kfree(c->ileb_buf); kfree(c->sbuf); kfree(c->bottom_up_buf); kfree(c->sup_node); }