18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * This file is part of UBIFS.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2006-2008 Nokia Corporation.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Authors: Artem Bityutskiy (Битюцкий Артём)
88c2ecf20Sopenharmony_ci *          Adrian Hunter
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci/* This file implements reading and writing the master node */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "ubifs.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci/**
168c2ecf20Sopenharmony_ci * ubifs_compare_master_node - compare two UBIFS master nodes
178c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object
188c2ecf20Sopenharmony_ci * @m1: the first node
198c2ecf20Sopenharmony_ci * @m2: the second node
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * This function compares two UBIFS master nodes. Returns 0 if they are equal
228c2ecf20Sopenharmony_ci * and nonzero if not.
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ciint ubifs_compare_master_node(struct ubifs_info *c, void *m1, void *m2)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	int ret;
278c2ecf20Sopenharmony_ci	int behind;
288c2ecf20Sopenharmony_ci	int hmac_offs = offsetof(struct ubifs_mst_node, hmac);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	/*
318c2ecf20Sopenharmony_ci	 * Do not compare the common node header since the sequence number and
328c2ecf20Sopenharmony_ci	 * hence the CRC are different.
338c2ecf20Sopenharmony_ci	 */
348c2ecf20Sopenharmony_ci	ret = memcmp(m1 + UBIFS_CH_SZ, m2 + UBIFS_CH_SZ,
358c2ecf20Sopenharmony_ci		     hmac_offs - UBIFS_CH_SZ);
368c2ecf20Sopenharmony_ci	if (ret)
378c2ecf20Sopenharmony_ci		return ret;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	/*
408c2ecf20Sopenharmony_ci	 * Do not compare the embedded HMAC aswell which also must be different
418c2ecf20Sopenharmony_ci	 * due to the different common node header.
428c2ecf20Sopenharmony_ci	 */
438c2ecf20Sopenharmony_ci	behind = hmac_offs + UBIFS_MAX_HMAC_LEN;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	if (UBIFS_MST_NODE_SZ > behind)
468c2ecf20Sopenharmony_ci		return memcmp(m1 + behind, m2 + behind, UBIFS_MST_NODE_SZ - behind);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	return 0;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/* mst_node_check_hash - Check hash of a master node
528c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object
538c2ecf20Sopenharmony_ci * @mst: The master node
548c2ecf20Sopenharmony_ci * @expected: The expected hash of the master node
558c2ecf20Sopenharmony_ci *
568c2ecf20Sopenharmony_ci * This checks the hash of a master node against a given expected hash.
578c2ecf20Sopenharmony_ci * Note that we have two master nodes on a UBIFS image which have different
588c2ecf20Sopenharmony_ci * sequence numbers and consequently different CRCs. To be able to match
598c2ecf20Sopenharmony_ci * both master nodes we exclude the common node header containing the sequence
608c2ecf20Sopenharmony_ci * number and CRC from the hash.
618c2ecf20Sopenharmony_ci *
628c2ecf20Sopenharmony_ci * Returns 0 if the hashes are equal, a negative error code otherwise.
638c2ecf20Sopenharmony_ci */
648c2ecf20Sopenharmony_cistatic int mst_node_check_hash(const struct ubifs_info *c,
658c2ecf20Sopenharmony_ci			       const struct ubifs_mst_node *mst,
668c2ecf20Sopenharmony_ci			       const u8 *expected)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	u8 calc[UBIFS_MAX_HASH_LEN];
698c2ecf20Sopenharmony_ci	const void *node = mst;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	crypto_shash_tfm_digest(c->hash_tfm, node + sizeof(struct ubifs_ch),
728c2ecf20Sopenharmony_ci				UBIFS_MST_NODE_SZ - sizeof(struct ubifs_ch),
738c2ecf20Sopenharmony_ci				calc);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (ubifs_check_hash(c, expected, calc))
768c2ecf20Sopenharmony_ci		return -EPERM;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	return 0;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/**
828c2ecf20Sopenharmony_ci * scan_for_master - search the valid master node.
838c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object
848c2ecf20Sopenharmony_ci *
858c2ecf20Sopenharmony_ci * This function scans the master node LEBs and search for the latest master
868c2ecf20Sopenharmony_ci * node. Returns zero in case of success, %-EUCLEAN if there master area is
878c2ecf20Sopenharmony_ci * corrupted and requires recovery, and a negative error code in case of
888c2ecf20Sopenharmony_ci * failure.
898c2ecf20Sopenharmony_ci */
908c2ecf20Sopenharmony_cistatic int scan_for_master(struct ubifs_info *c)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	struct ubifs_scan_leb *sleb;
938c2ecf20Sopenharmony_ci	struct ubifs_scan_node *snod;
948c2ecf20Sopenharmony_ci	int lnum, offs = 0, nodes_cnt, err;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	lnum = UBIFS_MST_LNUM;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	sleb = ubifs_scan(c, lnum, 0, c->sbuf, 1);
998c2ecf20Sopenharmony_ci	if (IS_ERR(sleb))
1008c2ecf20Sopenharmony_ci		return PTR_ERR(sleb);
1018c2ecf20Sopenharmony_ci	nodes_cnt = sleb->nodes_cnt;
1028c2ecf20Sopenharmony_ci	if (nodes_cnt > 0) {
1038c2ecf20Sopenharmony_ci		snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node,
1048c2ecf20Sopenharmony_ci				  list);
1058c2ecf20Sopenharmony_ci		if (snod->type != UBIFS_MST_NODE)
1068c2ecf20Sopenharmony_ci			goto out_dump;
1078c2ecf20Sopenharmony_ci		memcpy(c->mst_node, snod->node, snod->len);
1088c2ecf20Sopenharmony_ci		offs = snod->offs;
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci	ubifs_scan_destroy(sleb);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	lnum += 1;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	sleb = ubifs_scan(c, lnum, 0, c->sbuf, 1);
1158c2ecf20Sopenharmony_ci	if (IS_ERR(sleb))
1168c2ecf20Sopenharmony_ci		return PTR_ERR(sleb);
1178c2ecf20Sopenharmony_ci	if (sleb->nodes_cnt != nodes_cnt)
1188c2ecf20Sopenharmony_ci		goto out;
1198c2ecf20Sopenharmony_ci	if (!sleb->nodes_cnt)
1208c2ecf20Sopenharmony_ci		goto out;
1218c2ecf20Sopenharmony_ci	snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node, list);
1228c2ecf20Sopenharmony_ci	if (snod->type != UBIFS_MST_NODE)
1238c2ecf20Sopenharmony_ci		goto out_dump;
1248c2ecf20Sopenharmony_ci	if (snod->offs != offs)
1258c2ecf20Sopenharmony_ci		goto out;
1268c2ecf20Sopenharmony_ci	if (ubifs_compare_master_node(c, c->mst_node, snod->node))
1278c2ecf20Sopenharmony_ci		goto out;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	c->mst_offs = offs;
1308c2ecf20Sopenharmony_ci	ubifs_scan_destroy(sleb);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	if (!ubifs_authenticated(c))
1338c2ecf20Sopenharmony_ci		return 0;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	if (ubifs_hmac_zero(c, c->mst_node->hmac)) {
1368c2ecf20Sopenharmony_ci		err = mst_node_check_hash(c, c->mst_node,
1378c2ecf20Sopenharmony_ci					  c->sup_node->hash_mst);
1388c2ecf20Sopenharmony_ci		if (err)
1398c2ecf20Sopenharmony_ci			ubifs_err(c, "Failed to verify master node hash");
1408c2ecf20Sopenharmony_ci	} else {
1418c2ecf20Sopenharmony_ci		err = ubifs_node_verify_hmac(c, c->mst_node,
1428c2ecf20Sopenharmony_ci					sizeof(struct ubifs_mst_node),
1438c2ecf20Sopenharmony_ci					offsetof(struct ubifs_mst_node, hmac));
1448c2ecf20Sopenharmony_ci		if (err)
1458c2ecf20Sopenharmony_ci			ubifs_err(c, "Failed to verify master node HMAC");
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if (err)
1498c2ecf20Sopenharmony_ci		return -EPERM;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	return 0;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ciout:
1548c2ecf20Sopenharmony_ci	ubifs_scan_destroy(sleb);
1558c2ecf20Sopenharmony_ci	return -EUCLEAN;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ciout_dump:
1588c2ecf20Sopenharmony_ci	ubifs_err(c, "unexpected node type %d master LEB %d:%d",
1598c2ecf20Sopenharmony_ci		  snod->type, lnum, snod->offs);
1608c2ecf20Sopenharmony_ci	ubifs_scan_destroy(sleb);
1618c2ecf20Sopenharmony_ci	return -EINVAL;
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci/**
1658c2ecf20Sopenharmony_ci * validate_master - validate master node.
1668c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object
1678c2ecf20Sopenharmony_ci *
1688c2ecf20Sopenharmony_ci * This function validates data which was read from master node. Returns zero
1698c2ecf20Sopenharmony_ci * if the data is all right and %-EINVAL if not.
1708c2ecf20Sopenharmony_ci */
1718c2ecf20Sopenharmony_cistatic int validate_master(const struct ubifs_info *c)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	long long main_sz;
1748c2ecf20Sopenharmony_ci	int err;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	if (c->max_sqnum >= SQNUM_WATERMARK) {
1778c2ecf20Sopenharmony_ci		err = 1;
1788c2ecf20Sopenharmony_ci		goto out;
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (c->cmt_no >= c->max_sqnum) {
1828c2ecf20Sopenharmony_ci		err = 2;
1838c2ecf20Sopenharmony_ci		goto out;
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	if (c->highest_inum >= INUM_WATERMARK) {
1878c2ecf20Sopenharmony_ci		err = 3;
1888c2ecf20Sopenharmony_ci		goto out;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if (c->lhead_lnum < UBIFS_LOG_LNUM ||
1928c2ecf20Sopenharmony_ci	    c->lhead_lnum >= UBIFS_LOG_LNUM + c->log_lebs ||
1938c2ecf20Sopenharmony_ci	    c->lhead_offs < 0 || c->lhead_offs >= c->leb_size ||
1948c2ecf20Sopenharmony_ci	    c->lhead_offs & (c->min_io_size - 1)) {
1958c2ecf20Sopenharmony_ci		err = 4;
1968c2ecf20Sopenharmony_ci		goto out;
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	if (c->zroot.lnum >= c->leb_cnt || c->zroot.lnum < c->main_first ||
2008c2ecf20Sopenharmony_ci	    c->zroot.offs >= c->leb_size || c->zroot.offs & 7) {
2018c2ecf20Sopenharmony_ci		err = 5;
2028c2ecf20Sopenharmony_ci		goto out;
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (c->zroot.len < c->ranges[UBIFS_IDX_NODE].min_len ||
2068c2ecf20Sopenharmony_ci	    c->zroot.len > c->ranges[UBIFS_IDX_NODE].max_len) {
2078c2ecf20Sopenharmony_ci		err = 6;
2088c2ecf20Sopenharmony_ci		goto out;
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (c->gc_lnum >= c->leb_cnt || c->gc_lnum < c->main_first) {
2128c2ecf20Sopenharmony_ci		err = 7;
2138c2ecf20Sopenharmony_ci		goto out;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (c->ihead_lnum >= c->leb_cnt || c->ihead_lnum < c->main_first ||
2178c2ecf20Sopenharmony_ci	    c->ihead_offs % c->min_io_size || c->ihead_offs < 0 ||
2188c2ecf20Sopenharmony_ci	    c->ihead_offs > c->leb_size || c->ihead_offs & 7) {
2198c2ecf20Sopenharmony_ci		err = 8;
2208c2ecf20Sopenharmony_ci		goto out;
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	main_sz = (long long)c->main_lebs * c->leb_size;
2248c2ecf20Sopenharmony_ci	if (c->bi.old_idx_sz & 7 || c->bi.old_idx_sz >= main_sz) {
2258c2ecf20Sopenharmony_ci		err = 9;
2268c2ecf20Sopenharmony_ci		goto out;
2278c2ecf20Sopenharmony_ci	}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	if (c->lpt_lnum < c->lpt_first || c->lpt_lnum > c->lpt_last ||
2308c2ecf20Sopenharmony_ci	    c->lpt_offs < 0 || c->lpt_offs + c->nnode_sz > c->leb_size) {
2318c2ecf20Sopenharmony_ci		err = 10;
2328c2ecf20Sopenharmony_ci		goto out;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if (c->nhead_lnum < c->lpt_first || c->nhead_lnum > c->lpt_last ||
2368c2ecf20Sopenharmony_ci	    c->nhead_offs < 0 || c->nhead_offs % c->min_io_size ||
2378c2ecf20Sopenharmony_ci	    c->nhead_offs > c->leb_size) {
2388c2ecf20Sopenharmony_ci		err = 11;
2398c2ecf20Sopenharmony_ci		goto out;
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if (c->ltab_lnum < c->lpt_first || c->ltab_lnum > c->lpt_last ||
2438c2ecf20Sopenharmony_ci	    c->ltab_offs < 0 ||
2448c2ecf20Sopenharmony_ci	    c->ltab_offs + c->ltab_sz > c->leb_size) {
2458c2ecf20Sopenharmony_ci		err = 12;
2468c2ecf20Sopenharmony_ci		goto out;
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	if (c->big_lpt && (c->lsave_lnum < c->lpt_first ||
2508c2ecf20Sopenharmony_ci	    c->lsave_lnum > c->lpt_last || c->lsave_offs < 0 ||
2518c2ecf20Sopenharmony_ci	    c->lsave_offs + c->lsave_sz > c->leb_size)) {
2528c2ecf20Sopenharmony_ci		err = 13;
2538c2ecf20Sopenharmony_ci		goto out;
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	if (c->lscan_lnum < c->main_first || c->lscan_lnum >= c->leb_cnt) {
2578c2ecf20Sopenharmony_ci		err = 14;
2588c2ecf20Sopenharmony_ci		goto out;
2598c2ecf20Sopenharmony_ci	}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	if (c->lst.empty_lebs < 0 || c->lst.empty_lebs > c->main_lebs - 2) {
2628c2ecf20Sopenharmony_ci		err = 15;
2638c2ecf20Sopenharmony_ci		goto out;
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (c->lst.idx_lebs < 0 || c->lst.idx_lebs > c->main_lebs - 1) {
2678c2ecf20Sopenharmony_ci		err = 16;
2688c2ecf20Sopenharmony_ci		goto out;
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	if (c->lst.total_free < 0 || c->lst.total_free > main_sz ||
2728c2ecf20Sopenharmony_ci	    c->lst.total_free & 7) {
2738c2ecf20Sopenharmony_ci		err = 17;
2748c2ecf20Sopenharmony_ci		goto out;
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (c->lst.total_dirty < 0 || (c->lst.total_dirty & 7)) {
2788c2ecf20Sopenharmony_ci		err = 18;
2798c2ecf20Sopenharmony_ci		goto out;
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	if (c->lst.total_used < 0 || (c->lst.total_used & 7)) {
2838c2ecf20Sopenharmony_ci		err = 19;
2848c2ecf20Sopenharmony_ci		goto out;
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	if (c->lst.total_free + c->lst.total_dirty +
2888c2ecf20Sopenharmony_ci	    c->lst.total_used > main_sz) {
2898c2ecf20Sopenharmony_ci		err = 20;
2908c2ecf20Sopenharmony_ci		goto out;
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	if (c->lst.total_dead + c->lst.total_dark +
2948c2ecf20Sopenharmony_ci	    c->lst.total_used + c->bi.old_idx_sz > main_sz) {
2958c2ecf20Sopenharmony_ci		err = 21;
2968c2ecf20Sopenharmony_ci		goto out;
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	if (c->lst.total_dead < 0 ||
3008c2ecf20Sopenharmony_ci	    c->lst.total_dead > c->lst.total_free + c->lst.total_dirty ||
3018c2ecf20Sopenharmony_ci	    c->lst.total_dead & 7) {
3028c2ecf20Sopenharmony_ci		err = 22;
3038c2ecf20Sopenharmony_ci		goto out;
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	if (c->lst.total_dark < 0 ||
3078c2ecf20Sopenharmony_ci	    c->lst.total_dark > c->lst.total_free + c->lst.total_dirty ||
3088c2ecf20Sopenharmony_ci	    c->lst.total_dark & 7) {
3098c2ecf20Sopenharmony_ci		err = 23;
3108c2ecf20Sopenharmony_ci		goto out;
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	return 0;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ciout:
3168c2ecf20Sopenharmony_ci	ubifs_err(c, "bad master node at offset %d error %d", c->mst_offs, err);
3178c2ecf20Sopenharmony_ci	ubifs_dump_node(c, c->mst_node, c->mst_node_alsz);
3188c2ecf20Sopenharmony_ci	return -EINVAL;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci/**
3228c2ecf20Sopenharmony_ci * ubifs_read_master - read master node.
3238c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object
3248c2ecf20Sopenharmony_ci *
3258c2ecf20Sopenharmony_ci * This function finds and reads the master node during file-system mount. If
3268c2ecf20Sopenharmony_ci * the flash is empty, it creates default master node as well. Returns zero in
3278c2ecf20Sopenharmony_ci * case of success and a negative error code in case of failure.
3288c2ecf20Sopenharmony_ci */
3298c2ecf20Sopenharmony_ciint ubifs_read_master(struct ubifs_info *c)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	int err, old_leb_cnt;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	c->mst_node = kzalloc(c->mst_node_alsz, GFP_KERNEL);
3348c2ecf20Sopenharmony_ci	if (!c->mst_node)
3358c2ecf20Sopenharmony_ci		return -ENOMEM;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	err = scan_for_master(c);
3388c2ecf20Sopenharmony_ci	if (err) {
3398c2ecf20Sopenharmony_ci		if (err == -EUCLEAN)
3408c2ecf20Sopenharmony_ci			err = ubifs_recover_master_node(c);
3418c2ecf20Sopenharmony_ci		if (err)
3428c2ecf20Sopenharmony_ci			/*
3438c2ecf20Sopenharmony_ci			 * Note, we do not free 'c->mst_node' here because the
3448c2ecf20Sopenharmony_ci			 * unmount routine will take care of this.
3458c2ecf20Sopenharmony_ci			 */
3468c2ecf20Sopenharmony_ci			return err;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	/* Make sure that the recovery flag is clear */
3508c2ecf20Sopenharmony_ci	c->mst_node->flags &= cpu_to_le32(~UBIFS_MST_RCVRY);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	c->max_sqnum       = le64_to_cpu(c->mst_node->ch.sqnum);
3538c2ecf20Sopenharmony_ci	c->highest_inum    = le64_to_cpu(c->mst_node->highest_inum);
3548c2ecf20Sopenharmony_ci	c->cmt_no          = le64_to_cpu(c->mst_node->cmt_no);
3558c2ecf20Sopenharmony_ci	c->zroot.lnum      = le32_to_cpu(c->mst_node->root_lnum);
3568c2ecf20Sopenharmony_ci	c->zroot.offs      = le32_to_cpu(c->mst_node->root_offs);
3578c2ecf20Sopenharmony_ci	c->zroot.len       = le32_to_cpu(c->mst_node->root_len);
3588c2ecf20Sopenharmony_ci	c->lhead_lnum      = le32_to_cpu(c->mst_node->log_lnum);
3598c2ecf20Sopenharmony_ci	c->gc_lnum         = le32_to_cpu(c->mst_node->gc_lnum);
3608c2ecf20Sopenharmony_ci	c->ihead_lnum      = le32_to_cpu(c->mst_node->ihead_lnum);
3618c2ecf20Sopenharmony_ci	c->ihead_offs      = le32_to_cpu(c->mst_node->ihead_offs);
3628c2ecf20Sopenharmony_ci	c->bi.old_idx_sz   = le64_to_cpu(c->mst_node->index_size);
3638c2ecf20Sopenharmony_ci	c->lpt_lnum        = le32_to_cpu(c->mst_node->lpt_lnum);
3648c2ecf20Sopenharmony_ci	c->lpt_offs        = le32_to_cpu(c->mst_node->lpt_offs);
3658c2ecf20Sopenharmony_ci	c->nhead_lnum      = le32_to_cpu(c->mst_node->nhead_lnum);
3668c2ecf20Sopenharmony_ci	c->nhead_offs      = le32_to_cpu(c->mst_node->nhead_offs);
3678c2ecf20Sopenharmony_ci	c->ltab_lnum       = le32_to_cpu(c->mst_node->ltab_lnum);
3688c2ecf20Sopenharmony_ci	c->ltab_offs       = le32_to_cpu(c->mst_node->ltab_offs);
3698c2ecf20Sopenharmony_ci	c->lsave_lnum      = le32_to_cpu(c->mst_node->lsave_lnum);
3708c2ecf20Sopenharmony_ci	c->lsave_offs      = le32_to_cpu(c->mst_node->lsave_offs);
3718c2ecf20Sopenharmony_ci	c->lscan_lnum      = le32_to_cpu(c->mst_node->lscan_lnum);
3728c2ecf20Sopenharmony_ci	c->lst.empty_lebs  = le32_to_cpu(c->mst_node->empty_lebs);
3738c2ecf20Sopenharmony_ci	c->lst.idx_lebs    = le32_to_cpu(c->mst_node->idx_lebs);
3748c2ecf20Sopenharmony_ci	old_leb_cnt        = le32_to_cpu(c->mst_node->leb_cnt);
3758c2ecf20Sopenharmony_ci	c->lst.total_free  = le64_to_cpu(c->mst_node->total_free);
3768c2ecf20Sopenharmony_ci	c->lst.total_dirty = le64_to_cpu(c->mst_node->total_dirty);
3778c2ecf20Sopenharmony_ci	c->lst.total_used  = le64_to_cpu(c->mst_node->total_used);
3788c2ecf20Sopenharmony_ci	c->lst.total_dead  = le64_to_cpu(c->mst_node->total_dead);
3798c2ecf20Sopenharmony_ci	c->lst.total_dark  = le64_to_cpu(c->mst_node->total_dark);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	ubifs_copy_hash(c, c->mst_node->hash_root_idx, c->zroot.hash);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	c->calc_idx_sz = c->bi.old_idx_sz;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	if (c->mst_node->flags & cpu_to_le32(UBIFS_MST_NO_ORPHS))
3868c2ecf20Sopenharmony_ci		c->no_orphs = 1;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	if (old_leb_cnt != c->leb_cnt) {
3898c2ecf20Sopenharmony_ci		/* The file system has been resized */
3908c2ecf20Sopenharmony_ci		int growth = c->leb_cnt - old_leb_cnt;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci		if (c->leb_cnt < old_leb_cnt ||
3938c2ecf20Sopenharmony_ci		    c->leb_cnt < UBIFS_MIN_LEB_CNT) {
3948c2ecf20Sopenharmony_ci			ubifs_err(c, "bad leb_cnt on master node");
3958c2ecf20Sopenharmony_ci			ubifs_dump_node(c, c->mst_node, c->mst_node_alsz);
3968c2ecf20Sopenharmony_ci			return -EINVAL;
3978c2ecf20Sopenharmony_ci		}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci		dbg_mnt("Auto resizing (master) from %d LEBs to %d LEBs",
4008c2ecf20Sopenharmony_ci			old_leb_cnt, c->leb_cnt);
4018c2ecf20Sopenharmony_ci		c->lst.empty_lebs += growth;
4028c2ecf20Sopenharmony_ci		c->lst.total_free += growth * (long long)c->leb_size;
4038c2ecf20Sopenharmony_ci		c->lst.total_dark += growth * (long long)c->dark_wm;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		/*
4068c2ecf20Sopenharmony_ci		 * Reflect changes back onto the master node. N.B. the master
4078c2ecf20Sopenharmony_ci		 * node gets written immediately whenever mounting (or
4088c2ecf20Sopenharmony_ci		 * remounting) in read-write mode, so we do not need to write it
4098c2ecf20Sopenharmony_ci		 * here.
4108c2ecf20Sopenharmony_ci		 */
4118c2ecf20Sopenharmony_ci		c->mst_node->leb_cnt = cpu_to_le32(c->leb_cnt);
4128c2ecf20Sopenharmony_ci		c->mst_node->empty_lebs = cpu_to_le32(c->lst.empty_lebs);
4138c2ecf20Sopenharmony_ci		c->mst_node->total_free = cpu_to_le64(c->lst.total_free);
4148c2ecf20Sopenharmony_ci		c->mst_node->total_dark = cpu_to_le64(c->lst.total_dark);
4158c2ecf20Sopenharmony_ci	}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	err = validate_master(c);
4188c2ecf20Sopenharmony_ci	if (err)
4198c2ecf20Sopenharmony_ci		return err;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	err = dbg_old_index_check_init(c, &c->zroot);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	return err;
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci/**
4278c2ecf20Sopenharmony_ci * ubifs_write_master - write master node.
4288c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object
4298c2ecf20Sopenharmony_ci *
4308c2ecf20Sopenharmony_ci * This function writes the master node. Returns zero in case of success and a
4318c2ecf20Sopenharmony_ci * negative error code in case of failure. The master node is written twice to
4328c2ecf20Sopenharmony_ci * enable recovery.
4338c2ecf20Sopenharmony_ci */
4348c2ecf20Sopenharmony_ciint ubifs_write_master(struct ubifs_info *c)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	int err, lnum, offs, len;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	ubifs_assert(c, !c->ro_media && !c->ro_mount);
4398c2ecf20Sopenharmony_ci	if (c->ro_error)
4408c2ecf20Sopenharmony_ci		return -EROFS;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	lnum = UBIFS_MST_LNUM;
4438c2ecf20Sopenharmony_ci	offs = c->mst_offs + c->mst_node_alsz;
4448c2ecf20Sopenharmony_ci	len = UBIFS_MST_NODE_SZ;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	if (offs + UBIFS_MST_NODE_SZ > c->leb_size) {
4478c2ecf20Sopenharmony_ci		err = ubifs_leb_unmap(c, lnum);
4488c2ecf20Sopenharmony_ci		if (err)
4498c2ecf20Sopenharmony_ci			return err;
4508c2ecf20Sopenharmony_ci		offs = 0;
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	c->mst_offs = offs;
4548c2ecf20Sopenharmony_ci	c->mst_node->highest_inum = cpu_to_le64(c->highest_inum);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	ubifs_copy_hash(c, c->zroot.hash, c->mst_node->hash_root_idx);
4578c2ecf20Sopenharmony_ci	err = ubifs_write_node_hmac(c, c->mst_node, len, lnum, offs,
4588c2ecf20Sopenharmony_ci				    offsetof(struct ubifs_mst_node, hmac));
4598c2ecf20Sopenharmony_ci	if (err)
4608c2ecf20Sopenharmony_ci		return err;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	lnum += 1;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	if (offs == 0) {
4658c2ecf20Sopenharmony_ci		err = ubifs_leb_unmap(c, lnum);
4668c2ecf20Sopenharmony_ci		if (err)
4678c2ecf20Sopenharmony_ci			return err;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci	err = ubifs_write_node_hmac(c, c->mst_node, len, lnum, offs,
4708c2ecf20Sopenharmony_ci				    offsetof(struct ubifs_mst_node, hmac));
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	return err;
4738c2ecf20Sopenharmony_ci}
474