162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This file is part of UBIFS. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2006-2008 Nokia Corporation. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Authors: Artem Bityutskiy (Битюцкий Артём) 862306a36Sopenharmony_ci * Adrian Hunter 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/* This file implements reading and writing the master node */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "ubifs.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/** 1662306a36Sopenharmony_ci * ubifs_compare_master_node - compare two UBIFS master nodes 1762306a36Sopenharmony_ci * @c: UBIFS file-system description object 1862306a36Sopenharmony_ci * @m1: the first node 1962306a36Sopenharmony_ci * @m2: the second node 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * This function compares two UBIFS master nodes. Returns 0 if they are equal 2262306a36Sopenharmony_ci * and nonzero if not. 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ciint ubifs_compare_master_node(struct ubifs_info *c, void *m1, void *m2) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci int ret; 2762306a36Sopenharmony_ci int behind; 2862306a36Sopenharmony_ci int hmac_offs = offsetof(struct ubifs_mst_node, hmac); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci /* 3162306a36Sopenharmony_ci * Do not compare the common node header since the sequence number and 3262306a36Sopenharmony_ci * hence the CRC are different. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci ret = memcmp(m1 + UBIFS_CH_SZ, m2 + UBIFS_CH_SZ, 3562306a36Sopenharmony_ci hmac_offs - UBIFS_CH_SZ); 3662306a36Sopenharmony_ci if (ret) 3762306a36Sopenharmony_ci return ret; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci /* 4062306a36Sopenharmony_ci * Do not compare the embedded HMAC as well which also must be different 4162306a36Sopenharmony_ci * due to the different common node header. 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_ci behind = hmac_offs + UBIFS_MAX_HMAC_LEN; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (UBIFS_MST_NODE_SZ > behind) 4662306a36Sopenharmony_ci return memcmp(m1 + behind, m2 + behind, UBIFS_MST_NODE_SZ - behind); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci return 0; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* mst_node_check_hash - Check hash of a master node 5262306a36Sopenharmony_ci * @c: UBIFS file-system description object 5362306a36Sopenharmony_ci * @mst: The master node 5462306a36Sopenharmony_ci * @expected: The expected hash of the master node 5562306a36Sopenharmony_ci * 5662306a36Sopenharmony_ci * This checks the hash of a master node against a given expected hash. 5762306a36Sopenharmony_ci * Note that we have two master nodes on a UBIFS image which have different 5862306a36Sopenharmony_ci * sequence numbers and consequently different CRCs. To be able to match 5962306a36Sopenharmony_ci * both master nodes we exclude the common node header containing the sequence 6062306a36Sopenharmony_ci * number and CRC from the hash. 6162306a36Sopenharmony_ci * 6262306a36Sopenharmony_ci * Returns 0 if the hashes are equal, a negative error code otherwise. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_cistatic int mst_node_check_hash(const struct ubifs_info *c, 6562306a36Sopenharmony_ci const struct ubifs_mst_node *mst, 6662306a36Sopenharmony_ci const u8 *expected) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci u8 calc[UBIFS_MAX_HASH_LEN]; 6962306a36Sopenharmony_ci const void *node = mst; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci crypto_shash_tfm_digest(c->hash_tfm, node + sizeof(struct ubifs_ch), 7262306a36Sopenharmony_ci UBIFS_MST_NODE_SZ - sizeof(struct ubifs_ch), 7362306a36Sopenharmony_ci calc); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (ubifs_check_hash(c, expected, calc)) 7662306a36Sopenharmony_ci return -EPERM; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return 0; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/** 8262306a36Sopenharmony_ci * scan_for_master - search the valid master node. 8362306a36Sopenharmony_ci * @c: UBIFS file-system description object 8462306a36Sopenharmony_ci * 8562306a36Sopenharmony_ci * This function scans the master node LEBs and search for the latest master 8662306a36Sopenharmony_ci * node. Returns zero in case of success, %-EUCLEAN if there master area is 8762306a36Sopenharmony_ci * corrupted and requires recovery, and a negative error code in case of 8862306a36Sopenharmony_ci * failure. 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_cistatic int scan_for_master(struct ubifs_info *c) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct ubifs_scan_leb *sleb; 9362306a36Sopenharmony_ci struct ubifs_scan_node *snod; 9462306a36Sopenharmony_ci int lnum, offs = 0, nodes_cnt, err; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci lnum = UBIFS_MST_LNUM; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci sleb = ubifs_scan(c, lnum, 0, c->sbuf, 1); 9962306a36Sopenharmony_ci if (IS_ERR(sleb)) 10062306a36Sopenharmony_ci return PTR_ERR(sleb); 10162306a36Sopenharmony_ci nodes_cnt = sleb->nodes_cnt; 10262306a36Sopenharmony_ci if (nodes_cnt > 0) { 10362306a36Sopenharmony_ci snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node, 10462306a36Sopenharmony_ci list); 10562306a36Sopenharmony_ci if (snod->type != UBIFS_MST_NODE) 10662306a36Sopenharmony_ci goto out_dump; 10762306a36Sopenharmony_ci memcpy(c->mst_node, snod->node, snod->len); 10862306a36Sopenharmony_ci offs = snod->offs; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci ubifs_scan_destroy(sleb); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci lnum += 1; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci sleb = ubifs_scan(c, lnum, 0, c->sbuf, 1); 11562306a36Sopenharmony_ci if (IS_ERR(sleb)) 11662306a36Sopenharmony_ci return PTR_ERR(sleb); 11762306a36Sopenharmony_ci if (sleb->nodes_cnt != nodes_cnt) 11862306a36Sopenharmony_ci goto out; 11962306a36Sopenharmony_ci if (!sleb->nodes_cnt) 12062306a36Sopenharmony_ci goto out; 12162306a36Sopenharmony_ci snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node, list); 12262306a36Sopenharmony_ci if (snod->type != UBIFS_MST_NODE) 12362306a36Sopenharmony_ci goto out_dump; 12462306a36Sopenharmony_ci if (snod->offs != offs) 12562306a36Sopenharmony_ci goto out; 12662306a36Sopenharmony_ci if (ubifs_compare_master_node(c, c->mst_node, snod->node)) 12762306a36Sopenharmony_ci goto out; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci c->mst_offs = offs; 13062306a36Sopenharmony_ci ubifs_scan_destroy(sleb); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (!ubifs_authenticated(c)) 13362306a36Sopenharmony_ci return 0; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (ubifs_hmac_zero(c, c->mst_node->hmac)) { 13662306a36Sopenharmony_ci err = mst_node_check_hash(c, c->mst_node, 13762306a36Sopenharmony_ci c->sup_node->hash_mst); 13862306a36Sopenharmony_ci if (err) 13962306a36Sopenharmony_ci ubifs_err(c, "Failed to verify master node hash"); 14062306a36Sopenharmony_ci } else { 14162306a36Sopenharmony_ci err = ubifs_node_verify_hmac(c, c->mst_node, 14262306a36Sopenharmony_ci sizeof(struct ubifs_mst_node), 14362306a36Sopenharmony_ci offsetof(struct ubifs_mst_node, hmac)); 14462306a36Sopenharmony_ci if (err) 14562306a36Sopenharmony_ci ubifs_err(c, "Failed to verify master node HMAC"); 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (err) 14962306a36Sopenharmony_ci return -EPERM; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ciout: 15462306a36Sopenharmony_ci ubifs_scan_destroy(sleb); 15562306a36Sopenharmony_ci return -EUCLEAN; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ciout_dump: 15862306a36Sopenharmony_ci ubifs_err(c, "unexpected node type %d master LEB %d:%d", 15962306a36Sopenharmony_ci snod->type, lnum, snod->offs); 16062306a36Sopenharmony_ci ubifs_scan_destroy(sleb); 16162306a36Sopenharmony_ci return -EINVAL; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci/** 16562306a36Sopenharmony_ci * validate_master - validate master node. 16662306a36Sopenharmony_ci * @c: UBIFS file-system description object 16762306a36Sopenharmony_ci * 16862306a36Sopenharmony_ci * This function validates data which was read from master node. Returns zero 16962306a36Sopenharmony_ci * if the data is all right and %-EINVAL if not. 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_cistatic int validate_master(const struct ubifs_info *c) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci long long main_sz; 17462306a36Sopenharmony_ci int err; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (c->max_sqnum >= SQNUM_WATERMARK) { 17762306a36Sopenharmony_ci err = 1; 17862306a36Sopenharmony_ci goto out; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (c->cmt_no >= c->max_sqnum) { 18262306a36Sopenharmony_ci err = 2; 18362306a36Sopenharmony_ci goto out; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (c->highest_inum >= INUM_WATERMARK) { 18762306a36Sopenharmony_ci err = 3; 18862306a36Sopenharmony_ci goto out; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (c->lhead_lnum < UBIFS_LOG_LNUM || 19262306a36Sopenharmony_ci c->lhead_lnum >= UBIFS_LOG_LNUM + c->log_lebs || 19362306a36Sopenharmony_ci c->lhead_offs < 0 || c->lhead_offs >= c->leb_size || 19462306a36Sopenharmony_ci c->lhead_offs & (c->min_io_size - 1)) { 19562306a36Sopenharmony_ci err = 4; 19662306a36Sopenharmony_ci goto out; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (c->zroot.lnum >= c->leb_cnt || c->zroot.lnum < c->main_first || 20062306a36Sopenharmony_ci c->zroot.offs >= c->leb_size || c->zroot.offs & 7) { 20162306a36Sopenharmony_ci err = 5; 20262306a36Sopenharmony_ci goto out; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (c->zroot.len < c->ranges[UBIFS_IDX_NODE].min_len || 20662306a36Sopenharmony_ci c->zroot.len > c->ranges[UBIFS_IDX_NODE].max_len) { 20762306a36Sopenharmony_ci err = 6; 20862306a36Sopenharmony_ci goto out; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (c->gc_lnum >= c->leb_cnt || c->gc_lnum < c->main_first) { 21262306a36Sopenharmony_ci err = 7; 21362306a36Sopenharmony_ci goto out; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (c->ihead_lnum >= c->leb_cnt || c->ihead_lnum < c->main_first || 21762306a36Sopenharmony_ci c->ihead_offs % c->min_io_size || c->ihead_offs < 0 || 21862306a36Sopenharmony_ci c->ihead_offs > c->leb_size || c->ihead_offs & 7) { 21962306a36Sopenharmony_ci err = 8; 22062306a36Sopenharmony_ci goto out; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci main_sz = (long long)c->main_lebs * c->leb_size; 22462306a36Sopenharmony_ci if (c->bi.old_idx_sz & 7 || c->bi.old_idx_sz >= main_sz) { 22562306a36Sopenharmony_ci err = 9; 22662306a36Sopenharmony_ci goto out; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (c->lpt_lnum < c->lpt_first || c->lpt_lnum > c->lpt_last || 23062306a36Sopenharmony_ci c->lpt_offs < 0 || c->lpt_offs + c->nnode_sz > c->leb_size) { 23162306a36Sopenharmony_ci err = 10; 23262306a36Sopenharmony_ci goto out; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (c->nhead_lnum < c->lpt_first || c->nhead_lnum > c->lpt_last || 23662306a36Sopenharmony_ci c->nhead_offs < 0 || c->nhead_offs % c->min_io_size || 23762306a36Sopenharmony_ci c->nhead_offs > c->leb_size) { 23862306a36Sopenharmony_ci err = 11; 23962306a36Sopenharmony_ci goto out; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (c->ltab_lnum < c->lpt_first || c->ltab_lnum > c->lpt_last || 24362306a36Sopenharmony_ci c->ltab_offs < 0 || 24462306a36Sopenharmony_ci c->ltab_offs + c->ltab_sz > c->leb_size) { 24562306a36Sopenharmony_ci err = 12; 24662306a36Sopenharmony_ci goto out; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (c->big_lpt && (c->lsave_lnum < c->lpt_first || 25062306a36Sopenharmony_ci c->lsave_lnum > c->lpt_last || c->lsave_offs < 0 || 25162306a36Sopenharmony_ci c->lsave_offs + c->lsave_sz > c->leb_size)) { 25262306a36Sopenharmony_ci err = 13; 25362306a36Sopenharmony_ci goto out; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (c->lscan_lnum < c->main_first || c->lscan_lnum >= c->leb_cnt) { 25762306a36Sopenharmony_ci err = 14; 25862306a36Sopenharmony_ci goto out; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (c->lst.empty_lebs < 0 || c->lst.empty_lebs > c->main_lebs - 2) { 26262306a36Sopenharmony_ci err = 15; 26362306a36Sopenharmony_ci goto out; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (c->lst.idx_lebs < 0 || c->lst.idx_lebs > c->main_lebs - 1) { 26762306a36Sopenharmony_ci err = 16; 26862306a36Sopenharmony_ci goto out; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (c->lst.total_free < 0 || c->lst.total_free > main_sz || 27262306a36Sopenharmony_ci c->lst.total_free & 7) { 27362306a36Sopenharmony_ci err = 17; 27462306a36Sopenharmony_ci goto out; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (c->lst.total_dirty < 0 || (c->lst.total_dirty & 7)) { 27862306a36Sopenharmony_ci err = 18; 27962306a36Sopenharmony_ci goto out; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (c->lst.total_used < 0 || (c->lst.total_used & 7)) { 28362306a36Sopenharmony_ci err = 19; 28462306a36Sopenharmony_ci goto out; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (c->lst.total_free + c->lst.total_dirty + 28862306a36Sopenharmony_ci c->lst.total_used > main_sz) { 28962306a36Sopenharmony_ci err = 20; 29062306a36Sopenharmony_ci goto out; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (c->lst.total_dead + c->lst.total_dark + 29462306a36Sopenharmony_ci c->lst.total_used + c->bi.old_idx_sz > main_sz) { 29562306a36Sopenharmony_ci err = 21; 29662306a36Sopenharmony_ci goto out; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (c->lst.total_dead < 0 || 30062306a36Sopenharmony_ci c->lst.total_dead > c->lst.total_free + c->lst.total_dirty || 30162306a36Sopenharmony_ci c->lst.total_dead & 7) { 30262306a36Sopenharmony_ci err = 22; 30362306a36Sopenharmony_ci goto out; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (c->lst.total_dark < 0 || 30762306a36Sopenharmony_ci c->lst.total_dark > c->lst.total_free + c->lst.total_dirty || 30862306a36Sopenharmony_ci c->lst.total_dark & 7) { 30962306a36Sopenharmony_ci err = 23; 31062306a36Sopenharmony_ci goto out; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return 0; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ciout: 31662306a36Sopenharmony_ci ubifs_err(c, "bad master node at offset %d error %d", c->mst_offs, err); 31762306a36Sopenharmony_ci ubifs_dump_node(c, c->mst_node, c->mst_node_alsz); 31862306a36Sopenharmony_ci return -EINVAL; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci/** 32262306a36Sopenharmony_ci * ubifs_read_master - read master node. 32362306a36Sopenharmony_ci * @c: UBIFS file-system description object 32462306a36Sopenharmony_ci * 32562306a36Sopenharmony_ci * This function finds and reads the master node during file-system mount. If 32662306a36Sopenharmony_ci * the flash is empty, it creates default master node as well. Returns zero in 32762306a36Sopenharmony_ci * case of success and a negative error code in case of failure. 32862306a36Sopenharmony_ci */ 32962306a36Sopenharmony_ciint ubifs_read_master(struct ubifs_info *c) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci int err, old_leb_cnt; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci c->mst_node = kzalloc(c->mst_node_alsz, GFP_KERNEL); 33462306a36Sopenharmony_ci if (!c->mst_node) 33562306a36Sopenharmony_ci return -ENOMEM; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci err = scan_for_master(c); 33862306a36Sopenharmony_ci if (err) { 33962306a36Sopenharmony_ci if (err == -EUCLEAN) 34062306a36Sopenharmony_ci err = ubifs_recover_master_node(c); 34162306a36Sopenharmony_ci if (err) 34262306a36Sopenharmony_ci /* 34362306a36Sopenharmony_ci * Note, we do not free 'c->mst_node' here because the 34462306a36Sopenharmony_ci * unmount routine will take care of this. 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_ci return err; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* Make sure that the recovery flag is clear */ 35062306a36Sopenharmony_ci c->mst_node->flags &= cpu_to_le32(~UBIFS_MST_RCVRY); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci c->max_sqnum = le64_to_cpu(c->mst_node->ch.sqnum); 35362306a36Sopenharmony_ci c->highest_inum = le64_to_cpu(c->mst_node->highest_inum); 35462306a36Sopenharmony_ci c->cmt_no = le64_to_cpu(c->mst_node->cmt_no); 35562306a36Sopenharmony_ci c->zroot.lnum = le32_to_cpu(c->mst_node->root_lnum); 35662306a36Sopenharmony_ci c->zroot.offs = le32_to_cpu(c->mst_node->root_offs); 35762306a36Sopenharmony_ci c->zroot.len = le32_to_cpu(c->mst_node->root_len); 35862306a36Sopenharmony_ci c->lhead_lnum = le32_to_cpu(c->mst_node->log_lnum); 35962306a36Sopenharmony_ci c->gc_lnum = le32_to_cpu(c->mst_node->gc_lnum); 36062306a36Sopenharmony_ci c->ihead_lnum = le32_to_cpu(c->mst_node->ihead_lnum); 36162306a36Sopenharmony_ci c->ihead_offs = le32_to_cpu(c->mst_node->ihead_offs); 36262306a36Sopenharmony_ci c->bi.old_idx_sz = le64_to_cpu(c->mst_node->index_size); 36362306a36Sopenharmony_ci c->lpt_lnum = le32_to_cpu(c->mst_node->lpt_lnum); 36462306a36Sopenharmony_ci c->lpt_offs = le32_to_cpu(c->mst_node->lpt_offs); 36562306a36Sopenharmony_ci c->nhead_lnum = le32_to_cpu(c->mst_node->nhead_lnum); 36662306a36Sopenharmony_ci c->nhead_offs = le32_to_cpu(c->mst_node->nhead_offs); 36762306a36Sopenharmony_ci c->ltab_lnum = le32_to_cpu(c->mst_node->ltab_lnum); 36862306a36Sopenharmony_ci c->ltab_offs = le32_to_cpu(c->mst_node->ltab_offs); 36962306a36Sopenharmony_ci c->lsave_lnum = le32_to_cpu(c->mst_node->lsave_lnum); 37062306a36Sopenharmony_ci c->lsave_offs = le32_to_cpu(c->mst_node->lsave_offs); 37162306a36Sopenharmony_ci c->lscan_lnum = le32_to_cpu(c->mst_node->lscan_lnum); 37262306a36Sopenharmony_ci c->lst.empty_lebs = le32_to_cpu(c->mst_node->empty_lebs); 37362306a36Sopenharmony_ci c->lst.idx_lebs = le32_to_cpu(c->mst_node->idx_lebs); 37462306a36Sopenharmony_ci old_leb_cnt = le32_to_cpu(c->mst_node->leb_cnt); 37562306a36Sopenharmony_ci c->lst.total_free = le64_to_cpu(c->mst_node->total_free); 37662306a36Sopenharmony_ci c->lst.total_dirty = le64_to_cpu(c->mst_node->total_dirty); 37762306a36Sopenharmony_ci c->lst.total_used = le64_to_cpu(c->mst_node->total_used); 37862306a36Sopenharmony_ci c->lst.total_dead = le64_to_cpu(c->mst_node->total_dead); 37962306a36Sopenharmony_ci c->lst.total_dark = le64_to_cpu(c->mst_node->total_dark); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci ubifs_copy_hash(c, c->mst_node->hash_root_idx, c->zroot.hash); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci c->calc_idx_sz = c->bi.old_idx_sz; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (c->mst_node->flags & cpu_to_le32(UBIFS_MST_NO_ORPHS)) 38662306a36Sopenharmony_ci c->no_orphs = 1; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (old_leb_cnt != c->leb_cnt) { 38962306a36Sopenharmony_ci /* The file system has been resized */ 39062306a36Sopenharmony_ci int growth = c->leb_cnt - old_leb_cnt; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (c->leb_cnt < old_leb_cnt || 39362306a36Sopenharmony_ci c->leb_cnt < UBIFS_MIN_LEB_CNT) { 39462306a36Sopenharmony_ci ubifs_err(c, "bad leb_cnt on master node"); 39562306a36Sopenharmony_ci ubifs_dump_node(c, c->mst_node, c->mst_node_alsz); 39662306a36Sopenharmony_ci return -EINVAL; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci dbg_mnt("Auto resizing (master) from %d LEBs to %d LEBs", 40062306a36Sopenharmony_ci old_leb_cnt, c->leb_cnt); 40162306a36Sopenharmony_ci c->lst.empty_lebs += growth; 40262306a36Sopenharmony_ci c->lst.total_free += growth * (long long)c->leb_size; 40362306a36Sopenharmony_ci c->lst.total_dark += growth * (long long)c->dark_wm; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* 40662306a36Sopenharmony_ci * Reflect changes back onto the master node. N.B. the master 40762306a36Sopenharmony_ci * node gets written immediately whenever mounting (or 40862306a36Sopenharmony_ci * remounting) in read-write mode, so we do not need to write it 40962306a36Sopenharmony_ci * here. 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_ci c->mst_node->leb_cnt = cpu_to_le32(c->leb_cnt); 41262306a36Sopenharmony_ci c->mst_node->empty_lebs = cpu_to_le32(c->lst.empty_lebs); 41362306a36Sopenharmony_ci c->mst_node->total_free = cpu_to_le64(c->lst.total_free); 41462306a36Sopenharmony_ci c->mst_node->total_dark = cpu_to_le64(c->lst.total_dark); 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci err = validate_master(c); 41862306a36Sopenharmony_ci if (err) 41962306a36Sopenharmony_ci return err; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci err = dbg_old_index_check_init(c, &c->zroot); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return err; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci/** 42762306a36Sopenharmony_ci * ubifs_write_master - write master node. 42862306a36Sopenharmony_ci * @c: UBIFS file-system description object 42962306a36Sopenharmony_ci * 43062306a36Sopenharmony_ci * This function writes the master node. Returns zero in case of success and a 43162306a36Sopenharmony_ci * negative error code in case of failure. The master node is written twice to 43262306a36Sopenharmony_ci * enable recovery. 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_ciint ubifs_write_master(struct ubifs_info *c) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci int err, lnum, offs, len; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci ubifs_assert(c, !c->ro_media && !c->ro_mount); 43962306a36Sopenharmony_ci if (c->ro_error) 44062306a36Sopenharmony_ci return -EROFS; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci lnum = UBIFS_MST_LNUM; 44362306a36Sopenharmony_ci offs = c->mst_offs + c->mst_node_alsz; 44462306a36Sopenharmony_ci len = UBIFS_MST_NODE_SZ; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (offs + UBIFS_MST_NODE_SZ > c->leb_size) { 44762306a36Sopenharmony_ci err = ubifs_leb_unmap(c, lnum); 44862306a36Sopenharmony_ci if (err) 44962306a36Sopenharmony_ci return err; 45062306a36Sopenharmony_ci offs = 0; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci c->mst_offs = offs; 45462306a36Sopenharmony_ci c->mst_node->highest_inum = cpu_to_le64(c->highest_inum); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci ubifs_copy_hash(c, c->zroot.hash, c->mst_node->hash_root_idx); 45762306a36Sopenharmony_ci err = ubifs_write_node_hmac(c, c->mst_node, len, lnum, offs, 45862306a36Sopenharmony_ci offsetof(struct ubifs_mst_node, hmac)); 45962306a36Sopenharmony_ci if (err) 46062306a36Sopenharmony_ci return err; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci lnum += 1; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (offs == 0) { 46562306a36Sopenharmony_ci err = ubifs_leb_unmap(c, lnum); 46662306a36Sopenharmony_ci if (err) 46762306a36Sopenharmony_ci return err; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci err = ubifs_write_node_hmac(c, c->mst_node, len, lnum, offs, 47062306a36Sopenharmony_ci offsetof(struct ubifs_mst_node, hmac)); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci return err; 47362306a36Sopenharmony_ci} 474