xref: /kernel/linux/linux-6.6/fs/ubifs/master.c (revision 62306a36)
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