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: Adrian Hunter 862306a36Sopenharmony_ci * Artem Bityutskiy (Битюцкий Артём) 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/* 1262306a36Sopenharmony_ci * This file implements the LEB properties tree (LPT) area. The LPT area 1362306a36Sopenharmony_ci * contains the LEB properties tree, a table of LPT area eraseblocks (ltab), and 1462306a36Sopenharmony_ci * (for the "big" model) a table of saved LEB numbers (lsave). The LPT area sits 1562306a36Sopenharmony_ci * between the log and the orphan area. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * The LPT area is like a miniature self-contained file system. It is required 1862306a36Sopenharmony_ci * that it never runs out of space, is fast to access and update, and scales 1962306a36Sopenharmony_ci * logarithmically. The LEB properties tree is implemented as a wandering tree 2062306a36Sopenharmony_ci * much like the TNC, and the LPT area has its own garbage collection. 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * The LPT has two slightly different forms called the "small model" and the 2362306a36Sopenharmony_ci * "big model". The small model is used when the entire LEB properties table 2462306a36Sopenharmony_ci * can be written into a single eraseblock. In that case, garbage collection 2562306a36Sopenharmony_ci * consists of just writing the whole table, which therefore makes all other 2662306a36Sopenharmony_ci * eraseblocks reusable. In the case of the big model, dirty eraseblocks are 2762306a36Sopenharmony_ci * selected for garbage collection, which consists of marking the clean nodes in 2862306a36Sopenharmony_ci * that LEB as dirty, and then only the dirty nodes are written out. Also, in 2962306a36Sopenharmony_ci * the case of the big model, a table of LEB numbers is saved so that the entire 3062306a36Sopenharmony_ci * LPT does not to be scanned looking for empty eraseblocks when UBIFS is first 3162306a36Sopenharmony_ci * mounted. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include "ubifs.h" 3562306a36Sopenharmony_ci#include <linux/crc16.h> 3662306a36Sopenharmony_ci#include <linux/math64.h> 3762306a36Sopenharmony_ci#include <linux/slab.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/** 4062306a36Sopenharmony_ci * do_calc_lpt_geom - calculate sizes for the LPT area. 4162306a36Sopenharmony_ci * @c: the UBIFS file-system description object 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * Calculate the sizes of LPT bit fields, nodes, and tree, based on the 4462306a36Sopenharmony_ci * properties of the flash and whether LPT is "big" (c->big_lpt). 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_cistatic void do_calc_lpt_geom(struct ubifs_info *c) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci int i, n, bits, per_leb_wastage, max_pnode_cnt; 4962306a36Sopenharmony_ci long long sz, tot_wastage; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci n = c->main_lebs + c->max_leb_cnt - c->leb_cnt; 5262306a36Sopenharmony_ci max_pnode_cnt = DIV_ROUND_UP(n, UBIFS_LPT_FANOUT); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci c->lpt_hght = 1; 5562306a36Sopenharmony_ci n = UBIFS_LPT_FANOUT; 5662306a36Sopenharmony_ci while (n < max_pnode_cnt) { 5762306a36Sopenharmony_ci c->lpt_hght += 1; 5862306a36Sopenharmony_ci n <<= UBIFS_LPT_FANOUT_SHIFT; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci c->pnode_cnt = DIV_ROUND_UP(c->main_lebs, UBIFS_LPT_FANOUT); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci n = DIV_ROUND_UP(c->pnode_cnt, UBIFS_LPT_FANOUT); 6462306a36Sopenharmony_ci c->nnode_cnt = n; 6562306a36Sopenharmony_ci for (i = 1; i < c->lpt_hght; i++) { 6662306a36Sopenharmony_ci n = DIV_ROUND_UP(n, UBIFS_LPT_FANOUT); 6762306a36Sopenharmony_ci c->nnode_cnt += n; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci c->space_bits = fls(c->leb_size) - 3; 7162306a36Sopenharmony_ci c->lpt_lnum_bits = fls(c->lpt_lebs); 7262306a36Sopenharmony_ci c->lpt_offs_bits = fls(c->leb_size - 1); 7362306a36Sopenharmony_ci c->lpt_spc_bits = fls(c->leb_size); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci n = DIV_ROUND_UP(c->max_leb_cnt, UBIFS_LPT_FANOUT); 7662306a36Sopenharmony_ci c->pcnt_bits = fls(n - 1); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci c->lnum_bits = fls(c->max_leb_cnt - 1); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + 8162306a36Sopenharmony_ci (c->big_lpt ? c->pcnt_bits : 0) + 8262306a36Sopenharmony_ci (c->space_bits * 2 + 1) * UBIFS_LPT_FANOUT; 8362306a36Sopenharmony_ci c->pnode_sz = (bits + 7) / 8; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + 8662306a36Sopenharmony_ci (c->big_lpt ? c->pcnt_bits : 0) + 8762306a36Sopenharmony_ci (c->lpt_lnum_bits + c->lpt_offs_bits) * UBIFS_LPT_FANOUT; 8862306a36Sopenharmony_ci c->nnode_sz = (bits + 7) / 8; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + 9162306a36Sopenharmony_ci c->lpt_lebs * c->lpt_spc_bits * 2; 9262306a36Sopenharmony_ci c->ltab_sz = (bits + 7) / 8; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + 9562306a36Sopenharmony_ci c->lnum_bits * c->lsave_cnt; 9662306a36Sopenharmony_ci c->lsave_sz = (bits + 7) / 8; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* Calculate the minimum LPT size */ 9962306a36Sopenharmony_ci c->lpt_sz = (long long)c->pnode_cnt * c->pnode_sz; 10062306a36Sopenharmony_ci c->lpt_sz += (long long)c->nnode_cnt * c->nnode_sz; 10162306a36Sopenharmony_ci c->lpt_sz += c->ltab_sz; 10262306a36Sopenharmony_ci if (c->big_lpt) 10362306a36Sopenharmony_ci c->lpt_sz += c->lsave_sz; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* Add wastage */ 10662306a36Sopenharmony_ci sz = c->lpt_sz; 10762306a36Sopenharmony_ci per_leb_wastage = max_t(int, c->pnode_sz, c->nnode_sz); 10862306a36Sopenharmony_ci sz += per_leb_wastage; 10962306a36Sopenharmony_ci tot_wastage = per_leb_wastage; 11062306a36Sopenharmony_ci while (sz > c->leb_size) { 11162306a36Sopenharmony_ci sz += per_leb_wastage; 11262306a36Sopenharmony_ci sz -= c->leb_size; 11362306a36Sopenharmony_ci tot_wastage += per_leb_wastage; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci tot_wastage += ALIGN(sz, c->min_io_size) - sz; 11662306a36Sopenharmony_ci c->lpt_sz += tot_wastage; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/** 12062306a36Sopenharmony_ci * ubifs_calc_lpt_geom - calculate and check sizes for the LPT area. 12162306a36Sopenharmony_ci * @c: the UBIFS file-system description object 12262306a36Sopenharmony_ci * 12362306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_ciint ubifs_calc_lpt_geom(struct ubifs_info *c) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci int lebs_needed; 12862306a36Sopenharmony_ci long long sz; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci do_calc_lpt_geom(c); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* Verify that lpt_lebs is big enough */ 13362306a36Sopenharmony_ci sz = c->lpt_sz * 2; /* Must have at least 2 times the size */ 13462306a36Sopenharmony_ci lebs_needed = div_u64(sz + c->leb_size - 1, c->leb_size); 13562306a36Sopenharmony_ci if (lebs_needed > c->lpt_lebs) { 13662306a36Sopenharmony_ci ubifs_err(c, "too few LPT LEBs"); 13762306a36Sopenharmony_ci return -EINVAL; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* Verify that ltab fits in a single LEB (since ltab is a single node */ 14162306a36Sopenharmony_ci if (c->ltab_sz > c->leb_size) { 14262306a36Sopenharmony_ci ubifs_err(c, "LPT ltab too big"); 14362306a36Sopenharmony_ci return -EINVAL; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci c->check_lpt_free = c->big_lpt; 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/** 15162306a36Sopenharmony_ci * calc_dflt_lpt_geom - calculate default LPT geometry. 15262306a36Sopenharmony_ci * @c: the UBIFS file-system description object 15362306a36Sopenharmony_ci * @main_lebs: number of main area LEBs is passed and returned here 15462306a36Sopenharmony_ci * @big_lpt: whether the LPT area is "big" is returned here 15562306a36Sopenharmony_ci * 15662306a36Sopenharmony_ci * The size of the LPT area depends on parameters that themselves are dependent 15762306a36Sopenharmony_ci * on the size of the LPT area. This function, successively recalculates the LPT 15862306a36Sopenharmony_ci * area geometry until the parameters and resultant geometry are consistent. 15962306a36Sopenharmony_ci * 16062306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_cistatic int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, 16362306a36Sopenharmony_ci int *big_lpt) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci int i, lebs_needed; 16662306a36Sopenharmony_ci long long sz; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* Start by assuming the minimum number of LPT LEBs */ 16962306a36Sopenharmony_ci c->lpt_lebs = UBIFS_MIN_LPT_LEBS; 17062306a36Sopenharmony_ci c->main_lebs = *main_lebs - c->lpt_lebs; 17162306a36Sopenharmony_ci if (c->main_lebs <= 0) 17262306a36Sopenharmony_ci return -EINVAL; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* And assume we will use the small LPT model */ 17562306a36Sopenharmony_ci c->big_lpt = 0; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* 17862306a36Sopenharmony_ci * Calculate the geometry based on assumptions above and then see if it 17962306a36Sopenharmony_ci * makes sense 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ci do_calc_lpt_geom(c); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* Small LPT model must have lpt_sz < leb_size */ 18462306a36Sopenharmony_ci if (c->lpt_sz > c->leb_size) { 18562306a36Sopenharmony_ci /* Nope, so try again using big LPT model */ 18662306a36Sopenharmony_ci c->big_lpt = 1; 18762306a36Sopenharmony_ci do_calc_lpt_geom(c); 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* Now check there are enough LPT LEBs */ 19162306a36Sopenharmony_ci for (i = 0; i < 64 ; i++) { 19262306a36Sopenharmony_ci sz = c->lpt_sz * 4; /* Allow 4 times the size */ 19362306a36Sopenharmony_ci lebs_needed = div_u64(sz + c->leb_size - 1, c->leb_size); 19462306a36Sopenharmony_ci if (lebs_needed > c->lpt_lebs) { 19562306a36Sopenharmony_ci /* Not enough LPT LEBs so try again with more */ 19662306a36Sopenharmony_ci c->lpt_lebs = lebs_needed; 19762306a36Sopenharmony_ci c->main_lebs = *main_lebs - c->lpt_lebs; 19862306a36Sopenharmony_ci if (c->main_lebs <= 0) 19962306a36Sopenharmony_ci return -EINVAL; 20062306a36Sopenharmony_ci do_calc_lpt_geom(c); 20162306a36Sopenharmony_ci continue; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci if (c->ltab_sz > c->leb_size) { 20462306a36Sopenharmony_ci ubifs_err(c, "LPT ltab too big"); 20562306a36Sopenharmony_ci return -EINVAL; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci *main_lebs = c->main_lebs; 20862306a36Sopenharmony_ci *big_lpt = c->big_lpt; 20962306a36Sopenharmony_ci return 0; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci return -EINVAL; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/** 21562306a36Sopenharmony_ci * pack_bits - pack bit fields end-to-end. 21662306a36Sopenharmony_ci * @c: UBIFS file-system description object 21762306a36Sopenharmony_ci * @addr: address at which to pack (passed and next address returned) 21862306a36Sopenharmony_ci * @pos: bit position at which to pack (passed and next position returned) 21962306a36Sopenharmony_ci * @val: value to pack 22062306a36Sopenharmony_ci * @nrbits: number of bits of value to pack (1-32) 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_cistatic void pack_bits(const struct ubifs_info *c, uint8_t **addr, int *pos, uint32_t val, int nrbits) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci uint8_t *p = *addr; 22562306a36Sopenharmony_ci int b = *pos; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci ubifs_assert(c, nrbits > 0); 22862306a36Sopenharmony_ci ubifs_assert(c, nrbits <= 32); 22962306a36Sopenharmony_ci ubifs_assert(c, *pos >= 0); 23062306a36Sopenharmony_ci ubifs_assert(c, *pos < 8); 23162306a36Sopenharmony_ci ubifs_assert(c, (val >> nrbits) == 0 || nrbits == 32); 23262306a36Sopenharmony_ci if (b) { 23362306a36Sopenharmony_ci *p |= ((uint8_t)val) << b; 23462306a36Sopenharmony_ci nrbits += b; 23562306a36Sopenharmony_ci if (nrbits > 8) { 23662306a36Sopenharmony_ci *++p = (uint8_t)(val >>= (8 - b)); 23762306a36Sopenharmony_ci if (nrbits > 16) { 23862306a36Sopenharmony_ci *++p = (uint8_t)(val >>= 8); 23962306a36Sopenharmony_ci if (nrbits > 24) { 24062306a36Sopenharmony_ci *++p = (uint8_t)(val >>= 8); 24162306a36Sopenharmony_ci if (nrbits > 32) 24262306a36Sopenharmony_ci *++p = (uint8_t)(val >>= 8); 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci } else { 24762306a36Sopenharmony_ci *p = (uint8_t)val; 24862306a36Sopenharmony_ci if (nrbits > 8) { 24962306a36Sopenharmony_ci *++p = (uint8_t)(val >>= 8); 25062306a36Sopenharmony_ci if (nrbits > 16) { 25162306a36Sopenharmony_ci *++p = (uint8_t)(val >>= 8); 25262306a36Sopenharmony_ci if (nrbits > 24) 25362306a36Sopenharmony_ci *++p = (uint8_t)(val >>= 8); 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci b = nrbits & 7; 25862306a36Sopenharmony_ci if (b == 0) 25962306a36Sopenharmony_ci p++; 26062306a36Sopenharmony_ci *addr = p; 26162306a36Sopenharmony_ci *pos = b; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci/** 26562306a36Sopenharmony_ci * ubifs_unpack_bits - unpack bit fields. 26662306a36Sopenharmony_ci * @c: UBIFS file-system description object 26762306a36Sopenharmony_ci * @addr: address at which to unpack (passed and next address returned) 26862306a36Sopenharmony_ci * @pos: bit position at which to unpack (passed and next position returned) 26962306a36Sopenharmony_ci * @nrbits: number of bits of value to unpack (1-32) 27062306a36Sopenharmony_ci * 27162306a36Sopenharmony_ci * This functions returns the value unpacked. 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_ciuint32_t ubifs_unpack_bits(const struct ubifs_info *c, uint8_t **addr, int *pos, int nrbits) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci const int k = 32 - nrbits; 27662306a36Sopenharmony_ci uint8_t *p = *addr; 27762306a36Sopenharmony_ci int b = *pos; 27862306a36Sopenharmony_ci uint32_t val; 27962306a36Sopenharmony_ci const int bytes = (nrbits + b + 7) >> 3; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci ubifs_assert(c, nrbits > 0); 28262306a36Sopenharmony_ci ubifs_assert(c, nrbits <= 32); 28362306a36Sopenharmony_ci ubifs_assert(c, *pos >= 0); 28462306a36Sopenharmony_ci ubifs_assert(c, *pos < 8); 28562306a36Sopenharmony_ci if (b) { 28662306a36Sopenharmony_ci switch (bytes) { 28762306a36Sopenharmony_ci case 2: 28862306a36Sopenharmony_ci val = p[1]; 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci case 3: 29162306a36Sopenharmony_ci val = p[1] | ((uint32_t)p[2] << 8); 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci case 4: 29462306a36Sopenharmony_ci val = p[1] | ((uint32_t)p[2] << 8) | 29562306a36Sopenharmony_ci ((uint32_t)p[3] << 16); 29662306a36Sopenharmony_ci break; 29762306a36Sopenharmony_ci case 5: 29862306a36Sopenharmony_ci val = p[1] | ((uint32_t)p[2] << 8) | 29962306a36Sopenharmony_ci ((uint32_t)p[3] << 16) | 30062306a36Sopenharmony_ci ((uint32_t)p[4] << 24); 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci val <<= (8 - b); 30362306a36Sopenharmony_ci val |= *p >> b; 30462306a36Sopenharmony_ci nrbits += b; 30562306a36Sopenharmony_ci } else { 30662306a36Sopenharmony_ci switch (bytes) { 30762306a36Sopenharmony_ci case 1: 30862306a36Sopenharmony_ci val = p[0]; 30962306a36Sopenharmony_ci break; 31062306a36Sopenharmony_ci case 2: 31162306a36Sopenharmony_ci val = p[0] | ((uint32_t)p[1] << 8); 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci case 3: 31462306a36Sopenharmony_ci val = p[0] | ((uint32_t)p[1] << 8) | 31562306a36Sopenharmony_ci ((uint32_t)p[2] << 16); 31662306a36Sopenharmony_ci break; 31762306a36Sopenharmony_ci case 4: 31862306a36Sopenharmony_ci val = p[0] | ((uint32_t)p[1] << 8) | 31962306a36Sopenharmony_ci ((uint32_t)p[2] << 16) | 32062306a36Sopenharmony_ci ((uint32_t)p[3] << 24); 32162306a36Sopenharmony_ci break; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci val <<= k; 32562306a36Sopenharmony_ci val >>= k; 32662306a36Sopenharmony_ci b = nrbits & 7; 32762306a36Sopenharmony_ci p += nrbits >> 3; 32862306a36Sopenharmony_ci *addr = p; 32962306a36Sopenharmony_ci *pos = b; 33062306a36Sopenharmony_ci ubifs_assert(c, (val >> nrbits) == 0 || nrbits - b == 32); 33162306a36Sopenharmony_ci return val; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci/** 33562306a36Sopenharmony_ci * ubifs_pack_pnode - pack all the bit fields of a pnode. 33662306a36Sopenharmony_ci * @c: UBIFS file-system description object 33762306a36Sopenharmony_ci * @buf: buffer into which to pack 33862306a36Sopenharmony_ci * @pnode: pnode to pack 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_civoid ubifs_pack_pnode(struct ubifs_info *c, void *buf, 34162306a36Sopenharmony_ci struct ubifs_pnode *pnode) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; 34462306a36Sopenharmony_ci int i, pos = 0; 34562306a36Sopenharmony_ci uint16_t crc; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci pack_bits(c, &addr, &pos, UBIFS_LPT_PNODE, UBIFS_LPT_TYPE_BITS); 34862306a36Sopenharmony_ci if (c->big_lpt) 34962306a36Sopenharmony_ci pack_bits(c, &addr, &pos, pnode->num, c->pcnt_bits); 35062306a36Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 35162306a36Sopenharmony_ci pack_bits(c, &addr, &pos, pnode->lprops[i].free >> 3, 35262306a36Sopenharmony_ci c->space_bits); 35362306a36Sopenharmony_ci pack_bits(c, &addr, &pos, pnode->lprops[i].dirty >> 3, 35462306a36Sopenharmony_ci c->space_bits); 35562306a36Sopenharmony_ci if (pnode->lprops[i].flags & LPROPS_INDEX) 35662306a36Sopenharmony_ci pack_bits(c, &addr, &pos, 1, 1); 35762306a36Sopenharmony_ci else 35862306a36Sopenharmony_ci pack_bits(c, &addr, &pos, 0, 1); 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, 36162306a36Sopenharmony_ci c->pnode_sz - UBIFS_LPT_CRC_BYTES); 36262306a36Sopenharmony_ci addr = buf; 36362306a36Sopenharmony_ci pos = 0; 36462306a36Sopenharmony_ci pack_bits(c, &addr, &pos, crc, UBIFS_LPT_CRC_BITS); 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci/** 36862306a36Sopenharmony_ci * ubifs_pack_nnode - pack all the bit fields of a nnode. 36962306a36Sopenharmony_ci * @c: UBIFS file-system description object 37062306a36Sopenharmony_ci * @buf: buffer into which to pack 37162306a36Sopenharmony_ci * @nnode: nnode to pack 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_civoid ubifs_pack_nnode(struct ubifs_info *c, void *buf, 37462306a36Sopenharmony_ci struct ubifs_nnode *nnode) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; 37762306a36Sopenharmony_ci int i, pos = 0; 37862306a36Sopenharmony_ci uint16_t crc; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci pack_bits(c, &addr, &pos, UBIFS_LPT_NNODE, UBIFS_LPT_TYPE_BITS); 38162306a36Sopenharmony_ci if (c->big_lpt) 38262306a36Sopenharmony_ci pack_bits(c, &addr, &pos, nnode->num, c->pcnt_bits); 38362306a36Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 38462306a36Sopenharmony_ci int lnum = nnode->nbranch[i].lnum; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (lnum == 0) 38762306a36Sopenharmony_ci lnum = c->lpt_last + 1; 38862306a36Sopenharmony_ci pack_bits(c, &addr, &pos, lnum - c->lpt_first, c->lpt_lnum_bits); 38962306a36Sopenharmony_ci pack_bits(c, &addr, &pos, nnode->nbranch[i].offs, 39062306a36Sopenharmony_ci c->lpt_offs_bits); 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, 39362306a36Sopenharmony_ci c->nnode_sz - UBIFS_LPT_CRC_BYTES); 39462306a36Sopenharmony_ci addr = buf; 39562306a36Sopenharmony_ci pos = 0; 39662306a36Sopenharmony_ci pack_bits(c, &addr, &pos, crc, UBIFS_LPT_CRC_BITS); 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci/** 40062306a36Sopenharmony_ci * ubifs_pack_ltab - pack the LPT's own lprops table. 40162306a36Sopenharmony_ci * @c: UBIFS file-system description object 40262306a36Sopenharmony_ci * @buf: buffer into which to pack 40362306a36Sopenharmony_ci * @ltab: LPT's own lprops table to pack 40462306a36Sopenharmony_ci */ 40562306a36Sopenharmony_civoid ubifs_pack_ltab(struct ubifs_info *c, void *buf, 40662306a36Sopenharmony_ci struct ubifs_lpt_lprops *ltab) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; 40962306a36Sopenharmony_ci int i, pos = 0; 41062306a36Sopenharmony_ci uint16_t crc; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci pack_bits(c, &addr, &pos, UBIFS_LPT_LTAB, UBIFS_LPT_TYPE_BITS); 41362306a36Sopenharmony_ci for (i = 0; i < c->lpt_lebs; i++) { 41462306a36Sopenharmony_ci pack_bits(c, &addr, &pos, ltab[i].free, c->lpt_spc_bits); 41562306a36Sopenharmony_ci pack_bits(c, &addr, &pos, ltab[i].dirty, c->lpt_spc_bits); 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, 41862306a36Sopenharmony_ci c->ltab_sz - UBIFS_LPT_CRC_BYTES); 41962306a36Sopenharmony_ci addr = buf; 42062306a36Sopenharmony_ci pos = 0; 42162306a36Sopenharmony_ci pack_bits(c, &addr, &pos, crc, UBIFS_LPT_CRC_BITS); 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci/** 42562306a36Sopenharmony_ci * ubifs_pack_lsave - pack the LPT's save table. 42662306a36Sopenharmony_ci * @c: UBIFS file-system description object 42762306a36Sopenharmony_ci * @buf: buffer into which to pack 42862306a36Sopenharmony_ci * @lsave: LPT's save table to pack 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_civoid ubifs_pack_lsave(struct ubifs_info *c, void *buf, int *lsave) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; 43362306a36Sopenharmony_ci int i, pos = 0; 43462306a36Sopenharmony_ci uint16_t crc; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci pack_bits(c, &addr, &pos, UBIFS_LPT_LSAVE, UBIFS_LPT_TYPE_BITS); 43762306a36Sopenharmony_ci for (i = 0; i < c->lsave_cnt; i++) 43862306a36Sopenharmony_ci pack_bits(c, &addr, &pos, lsave[i], c->lnum_bits); 43962306a36Sopenharmony_ci crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, 44062306a36Sopenharmony_ci c->lsave_sz - UBIFS_LPT_CRC_BYTES); 44162306a36Sopenharmony_ci addr = buf; 44262306a36Sopenharmony_ci pos = 0; 44362306a36Sopenharmony_ci pack_bits(c, &addr, &pos, crc, UBIFS_LPT_CRC_BITS); 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci/** 44762306a36Sopenharmony_ci * ubifs_add_lpt_dirt - add dirty space to LPT LEB properties. 44862306a36Sopenharmony_ci * @c: UBIFS file-system description object 44962306a36Sopenharmony_ci * @lnum: LEB number to which to add dirty space 45062306a36Sopenharmony_ci * @dirty: amount of dirty space to add 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_civoid ubifs_add_lpt_dirt(struct ubifs_info *c, int lnum, int dirty) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci if (!dirty || !lnum) 45562306a36Sopenharmony_ci return; 45662306a36Sopenharmony_ci dbg_lp("LEB %d add %d to %d", 45762306a36Sopenharmony_ci lnum, dirty, c->ltab[lnum - c->lpt_first].dirty); 45862306a36Sopenharmony_ci ubifs_assert(c, lnum >= c->lpt_first && lnum <= c->lpt_last); 45962306a36Sopenharmony_ci c->ltab[lnum - c->lpt_first].dirty += dirty; 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci/** 46362306a36Sopenharmony_ci * set_ltab - set LPT LEB properties. 46462306a36Sopenharmony_ci * @c: UBIFS file-system description object 46562306a36Sopenharmony_ci * @lnum: LEB number 46662306a36Sopenharmony_ci * @free: amount of free space 46762306a36Sopenharmony_ci * @dirty: amount of dirty space 46862306a36Sopenharmony_ci */ 46962306a36Sopenharmony_cistatic void set_ltab(struct ubifs_info *c, int lnum, int free, int dirty) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci dbg_lp("LEB %d free %d dirty %d to %d %d", 47262306a36Sopenharmony_ci lnum, c->ltab[lnum - c->lpt_first].free, 47362306a36Sopenharmony_ci c->ltab[lnum - c->lpt_first].dirty, free, dirty); 47462306a36Sopenharmony_ci ubifs_assert(c, lnum >= c->lpt_first && lnum <= c->lpt_last); 47562306a36Sopenharmony_ci c->ltab[lnum - c->lpt_first].free = free; 47662306a36Sopenharmony_ci c->ltab[lnum - c->lpt_first].dirty = dirty; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci/** 48062306a36Sopenharmony_ci * ubifs_add_nnode_dirt - add dirty space to LPT LEB properties. 48162306a36Sopenharmony_ci * @c: UBIFS file-system description object 48262306a36Sopenharmony_ci * @nnode: nnode for which to add dirt 48362306a36Sopenharmony_ci */ 48462306a36Sopenharmony_civoid ubifs_add_nnode_dirt(struct ubifs_info *c, struct ubifs_nnode *nnode) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct ubifs_nnode *np = nnode->parent; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (np) 48962306a36Sopenharmony_ci ubifs_add_lpt_dirt(c, np->nbranch[nnode->iip].lnum, 49062306a36Sopenharmony_ci c->nnode_sz); 49162306a36Sopenharmony_ci else { 49262306a36Sopenharmony_ci ubifs_add_lpt_dirt(c, c->lpt_lnum, c->nnode_sz); 49362306a36Sopenharmony_ci if (!(c->lpt_drty_flgs & LTAB_DIRTY)) { 49462306a36Sopenharmony_ci c->lpt_drty_flgs |= LTAB_DIRTY; 49562306a36Sopenharmony_ci ubifs_add_lpt_dirt(c, c->ltab_lnum, c->ltab_sz); 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci/** 50162306a36Sopenharmony_ci * add_pnode_dirt - add dirty space to LPT LEB properties. 50262306a36Sopenharmony_ci * @c: UBIFS file-system description object 50362306a36Sopenharmony_ci * @pnode: pnode for which to add dirt 50462306a36Sopenharmony_ci */ 50562306a36Sopenharmony_cistatic void add_pnode_dirt(struct ubifs_info *c, struct ubifs_pnode *pnode) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci ubifs_add_lpt_dirt(c, pnode->parent->nbranch[pnode->iip].lnum, 50862306a36Sopenharmony_ci c->pnode_sz); 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci/** 51262306a36Sopenharmony_ci * calc_nnode_num - calculate nnode number. 51362306a36Sopenharmony_ci * @row: the row in the tree (root is zero) 51462306a36Sopenharmony_ci * @col: the column in the row (leftmost is zero) 51562306a36Sopenharmony_ci * 51662306a36Sopenharmony_ci * The nnode number is a number that uniquely identifies a nnode and can be used 51762306a36Sopenharmony_ci * easily to traverse the tree from the root to that nnode. 51862306a36Sopenharmony_ci * 51962306a36Sopenharmony_ci * This function calculates and returns the nnode number for the nnode at @row 52062306a36Sopenharmony_ci * and @col. 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_cistatic int calc_nnode_num(int row, int col) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci int num, bits; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci num = 1; 52762306a36Sopenharmony_ci while (row--) { 52862306a36Sopenharmony_ci bits = (col & (UBIFS_LPT_FANOUT - 1)); 52962306a36Sopenharmony_ci col >>= UBIFS_LPT_FANOUT_SHIFT; 53062306a36Sopenharmony_ci num <<= UBIFS_LPT_FANOUT_SHIFT; 53162306a36Sopenharmony_ci num |= bits; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci return num; 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci/** 53762306a36Sopenharmony_ci * calc_nnode_num_from_parent - calculate nnode number. 53862306a36Sopenharmony_ci * @c: UBIFS file-system description object 53962306a36Sopenharmony_ci * @parent: parent nnode 54062306a36Sopenharmony_ci * @iip: index in parent 54162306a36Sopenharmony_ci * 54262306a36Sopenharmony_ci * The nnode number is a number that uniquely identifies a nnode and can be used 54362306a36Sopenharmony_ci * easily to traverse the tree from the root to that nnode. 54462306a36Sopenharmony_ci * 54562306a36Sopenharmony_ci * This function calculates and returns the nnode number based on the parent's 54662306a36Sopenharmony_ci * nnode number and the index in parent. 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_cistatic int calc_nnode_num_from_parent(const struct ubifs_info *c, 54962306a36Sopenharmony_ci struct ubifs_nnode *parent, int iip) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci int num, shft; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (!parent) 55462306a36Sopenharmony_ci return 1; 55562306a36Sopenharmony_ci shft = (c->lpt_hght - parent->level) * UBIFS_LPT_FANOUT_SHIFT; 55662306a36Sopenharmony_ci num = parent->num ^ (1 << shft); 55762306a36Sopenharmony_ci num |= (UBIFS_LPT_FANOUT + iip) << shft; 55862306a36Sopenharmony_ci return num; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci/** 56262306a36Sopenharmony_ci * calc_pnode_num_from_parent - calculate pnode number. 56362306a36Sopenharmony_ci * @c: UBIFS file-system description object 56462306a36Sopenharmony_ci * @parent: parent nnode 56562306a36Sopenharmony_ci * @iip: index in parent 56662306a36Sopenharmony_ci * 56762306a36Sopenharmony_ci * The pnode number is a number that uniquely identifies a pnode and can be used 56862306a36Sopenharmony_ci * easily to traverse the tree from the root to that pnode. 56962306a36Sopenharmony_ci * 57062306a36Sopenharmony_ci * This function calculates and returns the pnode number based on the parent's 57162306a36Sopenharmony_ci * nnode number and the index in parent. 57262306a36Sopenharmony_ci */ 57362306a36Sopenharmony_cistatic int calc_pnode_num_from_parent(const struct ubifs_info *c, 57462306a36Sopenharmony_ci struct ubifs_nnode *parent, int iip) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci int i, n = c->lpt_hght - 1, pnum = parent->num, num = 0; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci for (i = 0; i < n; i++) { 57962306a36Sopenharmony_ci num <<= UBIFS_LPT_FANOUT_SHIFT; 58062306a36Sopenharmony_ci num |= pnum & (UBIFS_LPT_FANOUT - 1); 58162306a36Sopenharmony_ci pnum >>= UBIFS_LPT_FANOUT_SHIFT; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci num <<= UBIFS_LPT_FANOUT_SHIFT; 58462306a36Sopenharmony_ci num |= iip; 58562306a36Sopenharmony_ci return num; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci/** 58962306a36Sopenharmony_ci * ubifs_create_dflt_lpt - create default LPT. 59062306a36Sopenharmony_ci * @c: UBIFS file-system description object 59162306a36Sopenharmony_ci * @main_lebs: number of main area LEBs is passed and returned here 59262306a36Sopenharmony_ci * @lpt_first: LEB number of first LPT LEB 59362306a36Sopenharmony_ci * @lpt_lebs: number of LEBs for LPT is passed and returned here 59462306a36Sopenharmony_ci * @big_lpt: use big LPT model is passed and returned here 59562306a36Sopenharmony_ci * @hash: hash of the LPT is returned here 59662306a36Sopenharmony_ci * 59762306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 59862306a36Sopenharmony_ci */ 59962306a36Sopenharmony_ciint ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first, 60062306a36Sopenharmony_ci int *lpt_lebs, int *big_lpt, u8 *hash) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci int lnum, err = 0, node_sz, iopos, i, j, cnt, len, alen, row; 60362306a36Sopenharmony_ci int blnum, boffs, bsz, bcnt; 60462306a36Sopenharmony_ci struct ubifs_pnode *pnode = NULL; 60562306a36Sopenharmony_ci struct ubifs_nnode *nnode = NULL; 60662306a36Sopenharmony_ci void *buf = NULL, *p; 60762306a36Sopenharmony_ci struct ubifs_lpt_lprops *ltab = NULL; 60862306a36Sopenharmony_ci int *lsave = NULL; 60962306a36Sopenharmony_ci struct shash_desc *desc; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci err = calc_dflt_lpt_geom(c, main_lebs, big_lpt); 61262306a36Sopenharmony_ci if (err) 61362306a36Sopenharmony_ci return err; 61462306a36Sopenharmony_ci *lpt_lebs = c->lpt_lebs; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* Needed by 'ubifs_pack_nnode()' and 'set_ltab()' */ 61762306a36Sopenharmony_ci c->lpt_first = lpt_first; 61862306a36Sopenharmony_ci /* Needed by 'set_ltab()' */ 61962306a36Sopenharmony_ci c->lpt_last = lpt_first + c->lpt_lebs - 1; 62062306a36Sopenharmony_ci /* Needed by 'ubifs_pack_lsave()' */ 62162306a36Sopenharmony_ci c->main_first = c->leb_cnt - *main_lebs; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci desc = ubifs_hash_get_desc(c); 62462306a36Sopenharmony_ci if (IS_ERR(desc)) 62562306a36Sopenharmony_ci return PTR_ERR(desc); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci lsave = kmalloc_array(c->lsave_cnt, sizeof(int), GFP_KERNEL); 62862306a36Sopenharmony_ci pnode = kzalloc(sizeof(struct ubifs_pnode), GFP_KERNEL); 62962306a36Sopenharmony_ci nnode = kzalloc(sizeof(struct ubifs_nnode), GFP_KERNEL); 63062306a36Sopenharmony_ci buf = vmalloc(c->leb_size); 63162306a36Sopenharmony_ci ltab = vmalloc(array_size(sizeof(struct ubifs_lpt_lprops), 63262306a36Sopenharmony_ci c->lpt_lebs)); 63362306a36Sopenharmony_ci if (!pnode || !nnode || !buf || !ltab || !lsave) { 63462306a36Sopenharmony_ci err = -ENOMEM; 63562306a36Sopenharmony_ci goto out; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci ubifs_assert(c, !c->ltab); 63962306a36Sopenharmony_ci c->ltab = ltab; /* Needed by set_ltab */ 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci /* Initialize LPT's own lprops */ 64262306a36Sopenharmony_ci for (i = 0; i < c->lpt_lebs; i++) { 64362306a36Sopenharmony_ci ltab[i].free = c->leb_size; 64462306a36Sopenharmony_ci ltab[i].dirty = 0; 64562306a36Sopenharmony_ci ltab[i].tgc = 0; 64662306a36Sopenharmony_ci ltab[i].cmt = 0; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci lnum = lpt_first; 65062306a36Sopenharmony_ci p = buf; 65162306a36Sopenharmony_ci /* Number of leaf nodes (pnodes) */ 65262306a36Sopenharmony_ci cnt = c->pnode_cnt; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci /* 65562306a36Sopenharmony_ci * The first pnode contains the LEB properties for the LEBs that contain 65662306a36Sopenharmony_ci * the root inode node and the root index node of the index tree. 65762306a36Sopenharmony_ci */ 65862306a36Sopenharmony_ci node_sz = ALIGN(ubifs_idx_node_sz(c, 1), 8); 65962306a36Sopenharmony_ci iopos = ALIGN(node_sz, c->min_io_size); 66062306a36Sopenharmony_ci pnode->lprops[0].free = c->leb_size - iopos; 66162306a36Sopenharmony_ci pnode->lprops[0].dirty = iopos - node_sz; 66262306a36Sopenharmony_ci pnode->lprops[0].flags = LPROPS_INDEX; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci node_sz = UBIFS_INO_NODE_SZ; 66562306a36Sopenharmony_ci iopos = ALIGN(node_sz, c->min_io_size); 66662306a36Sopenharmony_ci pnode->lprops[1].free = c->leb_size - iopos; 66762306a36Sopenharmony_ci pnode->lprops[1].dirty = iopos - node_sz; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci for (i = 2; i < UBIFS_LPT_FANOUT; i++) 67062306a36Sopenharmony_ci pnode->lprops[i].free = c->leb_size; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci /* Add first pnode */ 67362306a36Sopenharmony_ci ubifs_pack_pnode(c, p, pnode); 67462306a36Sopenharmony_ci err = ubifs_shash_update(c, desc, p, c->pnode_sz); 67562306a36Sopenharmony_ci if (err) 67662306a36Sopenharmony_ci goto out; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci p += c->pnode_sz; 67962306a36Sopenharmony_ci len = c->pnode_sz; 68062306a36Sopenharmony_ci pnode->num += 1; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* Reset pnode values for remaining pnodes */ 68362306a36Sopenharmony_ci pnode->lprops[0].free = c->leb_size; 68462306a36Sopenharmony_ci pnode->lprops[0].dirty = 0; 68562306a36Sopenharmony_ci pnode->lprops[0].flags = 0; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci pnode->lprops[1].free = c->leb_size; 68862306a36Sopenharmony_ci pnode->lprops[1].dirty = 0; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci /* 69162306a36Sopenharmony_ci * To calculate the internal node branches, we keep information about 69262306a36Sopenharmony_ci * the level below. 69362306a36Sopenharmony_ci */ 69462306a36Sopenharmony_ci blnum = lnum; /* LEB number of level below */ 69562306a36Sopenharmony_ci boffs = 0; /* Offset of level below */ 69662306a36Sopenharmony_ci bcnt = cnt; /* Number of nodes in level below */ 69762306a36Sopenharmony_ci bsz = c->pnode_sz; /* Size of nodes in level below */ 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* Add all remaining pnodes */ 70062306a36Sopenharmony_ci for (i = 1; i < cnt; i++) { 70162306a36Sopenharmony_ci if (len + c->pnode_sz > c->leb_size) { 70262306a36Sopenharmony_ci alen = ALIGN(len, c->min_io_size); 70362306a36Sopenharmony_ci set_ltab(c, lnum, c->leb_size - alen, alen - len); 70462306a36Sopenharmony_ci memset(p, 0xff, alen - len); 70562306a36Sopenharmony_ci err = ubifs_leb_change(c, lnum++, buf, alen); 70662306a36Sopenharmony_ci if (err) 70762306a36Sopenharmony_ci goto out; 70862306a36Sopenharmony_ci p = buf; 70962306a36Sopenharmony_ci len = 0; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci ubifs_pack_pnode(c, p, pnode); 71262306a36Sopenharmony_ci err = ubifs_shash_update(c, desc, p, c->pnode_sz); 71362306a36Sopenharmony_ci if (err) 71462306a36Sopenharmony_ci goto out; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci p += c->pnode_sz; 71762306a36Sopenharmony_ci len += c->pnode_sz; 71862306a36Sopenharmony_ci /* 71962306a36Sopenharmony_ci * pnodes are simply numbered left to right starting at zero, 72062306a36Sopenharmony_ci * which means the pnode number can be used easily to traverse 72162306a36Sopenharmony_ci * down the tree to the corresponding pnode. 72262306a36Sopenharmony_ci */ 72362306a36Sopenharmony_ci pnode->num += 1; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci row = 0; 72762306a36Sopenharmony_ci for (i = UBIFS_LPT_FANOUT; cnt > i; i <<= UBIFS_LPT_FANOUT_SHIFT) 72862306a36Sopenharmony_ci row += 1; 72962306a36Sopenharmony_ci /* Add all nnodes, one level at a time */ 73062306a36Sopenharmony_ci while (1) { 73162306a36Sopenharmony_ci /* Number of internal nodes (nnodes) at next level */ 73262306a36Sopenharmony_ci cnt = DIV_ROUND_UP(cnt, UBIFS_LPT_FANOUT); 73362306a36Sopenharmony_ci for (i = 0; i < cnt; i++) { 73462306a36Sopenharmony_ci if (len + c->nnode_sz > c->leb_size) { 73562306a36Sopenharmony_ci alen = ALIGN(len, c->min_io_size); 73662306a36Sopenharmony_ci set_ltab(c, lnum, c->leb_size - alen, 73762306a36Sopenharmony_ci alen - len); 73862306a36Sopenharmony_ci memset(p, 0xff, alen - len); 73962306a36Sopenharmony_ci err = ubifs_leb_change(c, lnum++, buf, alen); 74062306a36Sopenharmony_ci if (err) 74162306a36Sopenharmony_ci goto out; 74262306a36Sopenharmony_ci p = buf; 74362306a36Sopenharmony_ci len = 0; 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci /* Only 1 nnode at this level, so it is the root */ 74662306a36Sopenharmony_ci if (cnt == 1) { 74762306a36Sopenharmony_ci c->lpt_lnum = lnum; 74862306a36Sopenharmony_ci c->lpt_offs = len; 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci /* Set branches to the level below */ 75162306a36Sopenharmony_ci for (j = 0; j < UBIFS_LPT_FANOUT; j++) { 75262306a36Sopenharmony_ci if (bcnt) { 75362306a36Sopenharmony_ci if (boffs + bsz > c->leb_size) { 75462306a36Sopenharmony_ci blnum += 1; 75562306a36Sopenharmony_ci boffs = 0; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci nnode->nbranch[j].lnum = blnum; 75862306a36Sopenharmony_ci nnode->nbranch[j].offs = boffs; 75962306a36Sopenharmony_ci boffs += bsz; 76062306a36Sopenharmony_ci bcnt--; 76162306a36Sopenharmony_ci } else { 76262306a36Sopenharmony_ci nnode->nbranch[j].lnum = 0; 76362306a36Sopenharmony_ci nnode->nbranch[j].offs = 0; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci nnode->num = calc_nnode_num(row, i); 76762306a36Sopenharmony_ci ubifs_pack_nnode(c, p, nnode); 76862306a36Sopenharmony_ci p += c->nnode_sz; 76962306a36Sopenharmony_ci len += c->nnode_sz; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci /* Only 1 nnode at this level, so it is the root */ 77262306a36Sopenharmony_ci if (cnt == 1) 77362306a36Sopenharmony_ci break; 77462306a36Sopenharmony_ci /* Update the information about the level below */ 77562306a36Sopenharmony_ci bcnt = cnt; 77662306a36Sopenharmony_ci bsz = c->nnode_sz; 77762306a36Sopenharmony_ci row -= 1; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (*big_lpt) { 78162306a36Sopenharmony_ci /* Need to add LPT's save table */ 78262306a36Sopenharmony_ci if (len + c->lsave_sz > c->leb_size) { 78362306a36Sopenharmony_ci alen = ALIGN(len, c->min_io_size); 78462306a36Sopenharmony_ci set_ltab(c, lnum, c->leb_size - alen, alen - len); 78562306a36Sopenharmony_ci memset(p, 0xff, alen - len); 78662306a36Sopenharmony_ci err = ubifs_leb_change(c, lnum++, buf, alen); 78762306a36Sopenharmony_ci if (err) 78862306a36Sopenharmony_ci goto out; 78962306a36Sopenharmony_ci p = buf; 79062306a36Sopenharmony_ci len = 0; 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci c->lsave_lnum = lnum; 79462306a36Sopenharmony_ci c->lsave_offs = len; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci for (i = 0; i < c->lsave_cnt && i < *main_lebs; i++) 79762306a36Sopenharmony_ci lsave[i] = c->main_first + i; 79862306a36Sopenharmony_ci for (; i < c->lsave_cnt; i++) 79962306a36Sopenharmony_ci lsave[i] = c->main_first; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci ubifs_pack_lsave(c, p, lsave); 80262306a36Sopenharmony_ci p += c->lsave_sz; 80362306a36Sopenharmony_ci len += c->lsave_sz; 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci /* Need to add LPT's own LEB properties table */ 80762306a36Sopenharmony_ci if (len + c->ltab_sz > c->leb_size) { 80862306a36Sopenharmony_ci alen = ALIGN(len, c->min_io_size); 80962306a36Sopenharmony_ci set_ltab(c, lnum, c->leb_size - alen, alen - len); 81062306a36Sopenharmony_ci memset(p, 0xff, alen - len); 81162306a36Sopenharmony_ci err = ubifs_leb_change(c, lnum++, buf, alen); 81262306a36Sopenharmony_ci if (err) 81362306a36Sopenharmony_ci goto out; 81462306a36Sopenharmony_ci p = buf; 81562306a36Sopenharmony_ci len = 0; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci c->ltab_lnum = lnum; 81962306a36Sopenharmony_ci c->ltab_offs = len; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci /* Update ltab before packing it */ 82262306a36Sopenharmony_ci len += c->ltab_sz; 82362306a36Sopenharmony_ci alen = ALIGN(len, c->min_io_size); 82462306a36Sopenharmony_ci set_ltab(c, lnum, c->leb_size - alen, alen - len); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci ubifs_pack_ltab(c, p, ltab); 82762306a36Sopenharmony_ci p += c->ltab_sz; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci /* Write remaining buffer */ 83062306a36Sopenharmony_ci memset(p, 0xff, alen - len); 83162306a36Sopenharmony_ci err = ubifs_leb_change(c, lnum, buf, alen); 83262306a36Sopenharmony_ci if (err) 83362306a36Sopenharmony_ci goto out; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci err = ubifs_shash_final(c, desc, hash); 83662306a36Sopenharmony_ci if (err) 83762306a36Sopenharmony_ci goto out; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci c->nhead_lnum = lnum; 84062306a36Sopenharmony_ci c->nhead_offs = ALIGN(len, c->min_io_size); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci dbg_lp("space_bits %d", c->space_bits); 84362306a36Sopenharmony_ci dbg_lp("lpt_lnum_bits %d", c->lpt_lnum_bits); 84462306a36Sopenharmony_ci dbg_lp("lpt_offs_bits %d", c->lpt_offs_bits); 84562306a36Sopenharmony_ci dbg_lp("lpt_spc_bits %d", c->lpt_spc_bits); 84662306a36Sopenharmony_ci dbg_lp("pcnt_bits %d", c->pcnt_bits); 84762306a36Sopenharmony_ci dbg_lp("lnum_bits %d", c->lnum_bits); 84862306a36Sopenharmony_ci dbg_lp("pnode_sz %d", c->pnode_sz); 84962306a36Sopenharmony_ci dbg_lp("nnode_sz %d", c->nnode_sz); 85062306a36Sopenharmony_ci dbg_lp("ltab_sz %d", c->ltab_sz); 85162306a36Sopenharmony_ci dbg_lp("lsave_sz %d", c->lsave_sz); 85262306a36Sopenharmony_ci dbg_lp("lsave_cnt %d", c->lsave_cnt); 85362306a36Sopenharmony_ci dbg_lp("lpt_hght %d", c->lpt_hght); 85462306a36Sopenharmony_ci dbg_lp("big_lpt %u", c->big_lpt); 85562306a36Sopenharmony_ci dbg_lp("LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs); 85662306a36Sopenharmony_ci dbg_lp("LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs); 85762306a36Sopenharmony_ci dbg_lp("LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs); 85862306a36Sopenharmony_ci if (c->big_lpt) 85962306a36Sopenharmony_ci dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs); 86062306a36Sopenharmony_ciout: 86162306a36Sopenharmony_ci c->ltab = NULL; 86262306a36Sopenharmony_ci kfree(desc); 86362306a36Sopenharmony_ci kfree(lsave); 86462306a36Sopenharmony_ci vfree(ltab); 86562306a36Sopenharmony_ci vfree(buf); 86662306a36Sopenharmony_ci kfree(nnode); 86762306a36Sopenharmony_ci kfree(pnode); 86862306a36Sopenharmony_ci return err; 86962306a36Sopenharmony_ci} 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci/** 87262306a36Sopenharmony_ci * update_cats - add LEB properties of a pnode to LEB category lists and heaps. 87362306a36Sopenharmony_ci * @c: UBIFS file-system description object 87462306a36Sopenharmony_ci * @pnode: pnode 87562306a36Sopenharmony_ci * 87662306a36Sopenharmony_ci * When a pnode is loaded into memory, the LEB properties it contains are added, 87762306a36Sopenharmony_ci * by this function, to the LEB category lists and heaps. 87862306a36Sopenharmony_ci */ 87962306a36Sopenharmony_cistatic void update_cats(struct ubifs_info *c, struct ubifs_pnode *pnode) 88062306a36Sopenharmony_ci{ 88162306a36Sopenharmony_ci int i; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 88462306a36Sopenharmony_ci int cat = pnode->lprops[i].flags & LPROPS_CAT_MASK; 88562306a36Sopenharmony_ci int lnum = pnode->lprops[i].lnum; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci if (!lnum) 88862306a36Sopenharmony_ci return; 88962306a36Sopenharmony_ci ubifs_add_to_cat(c, &pnode->lprops[i], cat); 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci/** 89462306a36Sopenharmony_ci * replace_cats - add LEB properties of a pnode to LEB category lists and heaps. 89562306a36Sopenharmony_ci * @c: UBIFS file-system description object 89662306a36Sopenharmony_ci * @old_pnode: pnode copied 89762306a36Sopenharmony_ci * @new_pnode: pnode copy 89862306a36Sopenharmony_ci * 89962306a36Sopenharmony_ci * During commit it is sometimes necessary to copy a pnode 90062306a36Sopenharmony_ci * (see dirty_cow_pnode). When that happens, references in 90162306a36Sopenharmony_ci * category lists and heaps must be replaced. This function does that. 90262306a36Sopenharmony_ci */ 90362306a36Sopenharmony_cistatic void replace_cats(struct ubifs_info *c, struct ubifs_pnode *old_pnode, 90462306a36Sopenharmony_ci struct ubifs_pnode *new_pnode) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci int i; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 90962306a36Sopenharmony_ci if (!new_pnode->lprops[i].lnum) 91062306a36Sopenharmony_ci return; 91162306a36Sopenharmony_ci ubifs_replace_cat(c, &old_pnode->lprops[i], 91262306a36Sopenharmony_ci &new_pnode->lprops[i]); 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci/** 91762306a36Sopenharmony_ci * check_lpt_crc - check LPT node crc is correct. 91862306a36Sopenharmony_ci * @c: UBIFS file-system description object 91962306a36Sopenharmony_ci * @buf: buffer containing node 92062306a36Sopenharmony_ci * @len: length of node 92162306a36Sopenharmony_ci * 92262306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 92362306a36Sopenharmony_ci */ 92462306a36Sopenharmony_cistatic int check_lpt_crc(const struct ubifs_info *c, void *buf, int len) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci int pos = 0; 92762306a36Sopenharmony_ci uint8_t *addr = buf; 92862306a36Sopenharmony_ci uint16_t crc, calc_crc; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci crc = ubifs_unpack_bits(c, &addr, &pos, UBIFS_LPT_CRC_BITS); 93162306a36Sopenharmony_ci calc_crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, 93262306a36Sopenharmony_ci len - UBIFS_LPT_CRC_BYTES); 93362306a36Sopenharmony_ci if (crc != calc_crc) { 93462306a36Sopenharmony_ci ubifs_err(c, "invalid crc in LPT node: crc %hx calc %hx", 93562306a36Sopenharmony_ci crc, calc_crc); 93662306a36Sopenharmony_ci dump_stack(); 93762306a36Sopenharmony_ci return -EINVAL; 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci return 0; 94062306a36Sopenharmony_ci} 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci/** 94362306a36Sopenharmony_ci * check_lpt_type - check LPT node type is correct. 94462306a36Sopenharmony_ci * @c: UBIFS file-system description object 94562306a36Sopenharmony_ci * @addr: address of type bit field is passed and returned updated here 94662306a36Sopenharmony_ci * @pos: position of type bit field is passed and returned updated here 94762306a36Sopenharmony_ci * @type: expected type 94862306a36Sopenharmony_ci * 94962306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 95062306a36Sopenharmony_ci */ 95162306a36Sopenharmony_cistatic int check_lpt_type(const struct ubifs_info *c, uint8_t **addr, 95262306a36Sopenharmony_ci int *pos, int type) 95362306a36Sopenharmony_ci{ 95462306a36Sopenharmony_ci int node_type; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci node_type = ubifs_unpack_bits(c, addr, pos, UBIFS_LPT_TYPE_BITS); 95762306a36Sopenharmony_ci if (node_type != type) { 95862306a36Sopenharmony_ci ubifs_err(c, "invalid type (%d) in LPT node type %d", 95962306a36Sopenharmony_ci node_type, type); 96062306a36Sopenharmony_ci dump_stack(); 96162306a36Sopenharmony_ci return -EINVAL; 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci return 0; 96462306a36Sopenharmony_ci} 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci/** 96762306a36Sopenharmony_ci * unpack_pnode - unpack a pnode. 96862306a36Sopenharmony_ci * @c: UBIFS file-system description object 96962306a36Sopenharmony_ci * @buf: buffer containing packed pnode to unpack 97062306a36Sopenharmony_ci * @pnode: pnode structure to fill 97162306a36Sopenharmony_ci * 97262306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 97362306a36Sopenharmony_ci */ 97462306a36Sopenharmony_cistatic int unpack_pnode(const struct ubifs_info *c, void *buf, 97562306a36Sopenharmony_ci struct ubifs_pnode *pnode) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; 97862306a36Sopenharmony_ci int i, pos = 0, err; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_PNODE); 98162306a36Sopenharmony_ci if (err) 98262306a36Sopenharmony_ci return err; 98362306a36Sopenharmony_ci if (c->big_lpt) 98462306a36Sopenharmony_ci pnode->num = ubifs_unpack_bits(c, &addr, &pos, c->pcnt_bits); 98562306a36Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 98662306a36Sopenharmony_ci struct ubifs_lprops * const lprops = &pnode->lprops[i]; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci lprops->free = ubifs_unpack_bits(c, &addr, &pos, c->space_bits); 98962306a36Sopenharmony_ci lprops->free <<= 3; 99062306a36Sopenharmony_ci lprops->dirty = ubifs_unpack_bits(c, &addr, &pos, c->space_bits); 99162306a36Sopenharmony_ci lprops->dirty <<= 3; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci if (ubifs_unpack_bits(c, &addr, &pos, 1)) 99462306a36Sopenharmony_ci lprops->flags = LPROPS_INDEX; 99562306a36Sopenharmony_ci else 99662306a36Sopenharmony_ci lprops->flags = 0; 99762306a36Sopenharmony_ci lprops->flags |= ubifs_categorize_lprops(c, lprops); 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_ci err = check_lpt_crc(c, buf, c->pnode_sz); 100062306a36Sopenharmony_ci return err; 100162306a36Sopenharmony_ci} 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci/** 100462306a36Sopenharmony_ci * ubifs_unpack_nnode - unpack a nnode. 100562306a36Sopenharmony_ci * @c: UBIFS file-system description object 100662306a36Sopenharmony_ci * @buf: buffer containing packed nnode to unpack 100762306a36Sopenharmony_ci * @nnode: nnode structure to fill 100862306a36Sopenharmony_ci * 100962306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 101062306a36Sopenharmony_ci */ 101162306a36Sopenharmony_ciint ubifs_unpack_nnode(const struct ubifs_info *c, void *buf, 101262306a36Sopenharmony_ci struct ubifs_nnode *nnode) 101362306a36Sopenharmony_ci{ 101462306a36Sopenharmony_ci uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; 101562306a36Sopenharmony_ci int i, pos = 0, err; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_NNODE); 101862306a36Sopenharmony_ci if (err) 101962306a36Sopenharmony_ci return err; 102062306a36Sopenharmony_ci if (c->big_lpt) 102162306a36Sopenharmony_ci nnode->num = ubifs_unpack_bits(c, &addr, &pos, c->pcnt_bits); 102262306a36Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 102362306a36Sopenharmony_ci int lnum; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci lnum = ubifs_unpack_bits(c, &addr, &pos, c->lpt_lnum_bits) + 102662306a36Sopenharmony_ci c->lpt_first; 102762306a36Sopenharmony_ci if (lnum == c->lpt_last + 1) 102862306a36Sopenharmony_ci lnum = 0; 102962306a36Sopenharmony_ci nnode->nbranch[i].lnum = lnum; 103062306a36Sopenharmony_ci nnode->nbranch[i].offs = ubifs_unpack_bits(c, &addr, &pos, 103162306a36Sopenharmony_ci c->lpt_offs_bits); 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci err = check_lpt_crc(c, buf, c->nnode_sz); 103462306a36Sopenharmony_ci return err; 103562306a36Sopenharmony_ci} 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci/** 103862306a36Sopenharmony_ci * unpack_ltab - unpack the LPT's own lprops table. 103962306a36Sopenharmony_ci * @c: UBIFS file-system description object 104062306a36Sopenharmony_ci * @buf: buffer from which to unpack 104162306a36Sopenharmony_ci * 104262306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 104362306a36Sopenharmony_ci */ 104462306a36Sopenharmony_cistatic int unpack_ltab(const struct ubifs_info *c, void *buf) 104562306a36Sopenharmony_ci{ 104662306a36Sopenharmony_ci uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; 104762306a36Sopenharmony_ci int i, pos = 0, err; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_LTAB); 105062306a36Sopenharmony_ci if (err) 105162306a36Sopenharmony_ci return err; 105262306a36Sopenharmony_ci for (i = 0; i < c->lpt_lebs; i++) { 105362306a36Sopenharmony_ci int free = ubifs_unpack_bits(c, &addr, &pos, c->lpt_spc_bits); 105462306a36Sopenharmony_ci int dirty = ubifs_unpack_bits(c, &addr, &pos, c->lpt_spc_bits); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci if (free < 0 || free > c->leb_size || dirty < 0 || 105762306a36Sopenharmony_ci dirty > c->leb_size || free + dirty > c->leb_size) 105862306a36Sopenharmony_ci return -EINVAL; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci c->ltab[i].free = free; 106162306a36Sopenharmony_ci c->ltab[i].dirty = dirty; 106262306a36Sopenharmony_ci c->ltab[i].tgc = 0; 106362306a36Sopenharmony_ci c->ltab[i].cmt = 0; 106462306a36Sopenharmony_ci } 106562306a36Sopenharmony_ci err = check_lpt_crc(c, buf, c->ltab_sz); 106662306a36Sopenharmony_ci return err; 106762306a36Sopenharmony_ci} 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci/** 107062306a36Sopenharmony_ci * unpack_lsave - unpack the LPT's save table. 107162306a36Sopenharmony_ci * @c: UBIFS file-system description object 107262306a36Sopenharmony_ci * @buf: buffer from which to unpack 107362306a36Sopenharmony_ci * 107462306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 107562306a36Sopenharmony_ci */ 107662306a36Sopenharmony_cistatic int unpack_lsave(const struct ubifs_info *c, void *buf) 107762306a36Sopenharmony_ci{ 107862306a36Sopenharmony_ci uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; 107962306a36Sopenharmony_ci int i, pos = 0, err; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_LSAVE); 108262306a36Sopenharmony_ci if (err) 108362306a36Sopenharmony_ci return err; 108462306a36Sopenharmony_ci for (i = 0; i < c->lsave_cnt; i++) { 108562306a36Sopenharmony_ci int lnum = ubifs_unpack_bits(c, &addr, &pos, c->lnum_bits); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci if (lnum < c->main_first || lnum >= c->leb_cnt) 108862306a36Sopenharmony_ci return -EINVAL; 108962306a36Sopenharmony_ci c->lsave[i] = lnum; 109062306a36Sopenharmony_ci } 109162306a36Sopenharmony_ci err = check_lpt_crc(c, buf, c->lsave_sz); 109262306a36Sopenharmony_ci return err; 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci/** 109662306a36Sopenharmony_ci * validate_nnode - validate a nnode. 109762306a36Sopenharmony_ci * @c: UBIFS file-system description object 109862306a36Sopenharmony_ci * @nnode: nnode to validate 109962306a36Sopenharmony_ci * @parent: parent nnode (or NULL for the root nnode) 110062306a36Sopenharmony_ci * @iip: index in parent 110162306a36Sopenharmony_ci * 110262306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 110362306a36Sopenharmony_ci */ 110462306a36Sopenharmony_cistatic int validate_nnode(const struct ubifs_info *c, struct ubifs_nnode *nnode, 110562306a36Sopenharmony_ci struct ubifs_nnode *parent, int iip) 110662306a36Sopenharmony_ci{ 110762306a36Sopenharmony_ci int i, lvl, max_offs; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci if (c->big_lpt) { 111062306a36Sopenharmony_ci int num = calc_nnode_num_from_parent(c, parent, iip); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci if (nnode->num != num) 111362306a36Sopenharmony_ci return -EINVAL; 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci lvl = parent ? parent->level - 1 : c->lpt_hght; 111662306a36Sopenharmony_ci if (lvl < 1) 111762306a36Sopenharmony_ci return -EINVAL; 111862306a36Sopenharmony_ci if (lvl == 1) 111962306a36Sopenharmony_ci max_offs = c->leb_size - c->pnode_sz; 112062306a36Sopenharmony_ci else 112162306a36Sopenharmony_ci max_offs = c->leb_size - c->nnode_sz; 112262306a36Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 112362306a36Sopenharmony_ci int lnum = nnode->nbranch[i].lnum; 112462306a36Sopenharmony_ci int offs = nnode->nbranch[i].offs; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci if (lnum == 0) { 112762306a36Sopenharmony_ci if (offs != 0) 112862306a36Sopenharmony_ci return -EINVAL; 112962306a36Sopenharmony_ci continue; 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci if (lnum < c->lpt_first || lnum > c->lpt_last) 113262306a36Sopenharmony_ci return -EINVAL; 113362306a36Sopenharmony_ci if (offs < 0 || offs > max_offs) 113462306a36Sopenharmony_ci return -EINVAL; 113562306a36Sopenharmony_ci } 113662306a36Sopenharmony_ci return 0; 113762306a36Sopenharmony_ci} 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci/** 114062306a36Sopenharmony_ci * validate_pnode - validate a pnode. 114162306a36Sopenharmony_ci * @c: UBIFS file-system description object 114262306a36Sopenharmony_ci * @pnode: pnode to validate 114362306a36Sopenharmony_ci * @parent: parent nnode 114462306a36Sopenharmony_ci * @iip: index in parent 114562306a36Sopenharmony_ci * 114662306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 114762306a36Sopenharmony_ci */ 114862306a36Sopenharmony_cistatic int validate_pnode(const struct ubifs_info *c, struct ubifs_pnode *pnode, 114962306a36Sopenharmony_ci struct ubifs_nnode *parent, int iip) 115062306a36Sopenharmony_ci{ 115162306a36Sopenharmony_ci int i; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci if (c->big_lpt) { 115462306a36Sopenharmony_ci int num = calc_pnode_num_from_parent(c, parent, iip); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci if (pnode->num != num) 115762306a36Sopenharmony_ci return -EINVAL; 115862306a36Sopenharmony_ci } 115962306a36Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 116062306a36Sopenharmony_ci int free = pnode->lprops[i].free; 116162306a36Sopenharmony_ci int dirty = pnode->lprops[i].dirty; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci if (free < 0 || free > c->leb_size || free % c->min_io_size || 116462306a36Sopenharmony_ci (free & 7)) 116562306a36Sopenharmony_ci return -EINVAL; 116662306a36Sopenharmony_ci if (dirty < 0 || dirty > c->leb_size || (dirty & 7)) 116762306a36Sopenharmony_ci return -EINVAL; 116862306a36Sopenharmony_ci if (dirty + free > c->leb_size) 116962306a36Sopenharmony_ci return -EINVAL; 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci return 0; 117262306a36Sopenharmony_ci} 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci/** 117562306a36Sopenharmony_ci * set_pnode_lnum - set LEB numbers on a pnode. 117662306a36Sopenharmony_ci * @c: UBIFS file-system description object 117762306a36Sopenharmony_ci * @pnode: pnode to update 117862306a36Sopenharmony_ci * 117962306a36Sopenharmony_ci * This function calculates the LEB numbers for the LEB properties it contains 118062306a36Sopenharmony_ci * based on the pnode number. 118162306a36Sopenharmony_ci */ 118262306a36Sopenharmony_cistatic void set_pnode_lnum(const struct ubifs_info *c, 118362306a36Sopenharmony_ci struct ubifs_pnode *pnode) 118462306a36Sopenharmony_ci{ 118562306a36Sopenharmony_ci int i, lnum; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci lnum = (pnode->num << UBIFS_LPT_FANOUT_SHIFT) + c->main_first; 118862306a36Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 118962306a36Sopenharmony_ci if (lnum >= c->leb_cnt) 119062306a36Sopenharmony_ci return; 119162306a36Sopenharmony_ci pnode->lprops[i].lnum = lnum++; 119262306a36Sopenharmony_ci } 119362306a36Sopenharmony_ci} 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci/** 119662306a36Sopenharmony_ci * ubifs_read_nnode - read a nnode from flash and link it to the tree in memory. 119762306a36Sopenharmony_ci * @c: UBIFS file-system description object 119862306a36Sopenharmony_ci * @parent: parent nnode (or NULL for the root) 119962306a36Sopenharmony_ci * @iip: index in parent 120062306a36Sopenharmony_ci * 120162306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 120262306a36Sopenharmony_ci */ 120362306a36Sopenharmony_ciint ubifs_read_nnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip) 120462306a36Sopenharmony_ci{ 120562306a36Sopenharmony_ci struct ubifs_nbranch *branch = NULL; 120662306a36Sopenharmony_ci struct ubifs_nnode *nnode = NULL; 120762306a36Sopenharmony_ci void *buf = c->lpt_nod_buf; 120862306a36Sopenharmony_ci int err, lnum, offs; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci if (parent) { 121162306a36Sopenharmony_ci branch = &parent->nbranch[iip]; 121262306a36Sopenharmony_ci lnum = branch->lnum; 121362306a36Sopenharmony_ci offs = branch->offs; 121462306a36Sopenharmony_ci } else { 121562306a36Sopenharmony_ci lnum = c->lpt_lnum; 121662306a36Sopenharmony_ci offs = c->lpt_offs; 121762306a36Sopenharmony_ci } 121862306a36Sopenharmony_ci nnode = kzalloc(sizeof(struct ubifs_nnode), GFP_NOFS); 121962306a36Sopenharmony_ci if (!nnode) { 122062306a36Sopenharmony_ci err = -ENOMEM; 122162306a36Sopenharmony_ci goto out; 122262306a36Sopenharmony_ci } 122362306a36Sopenharmony_ci if (lnum == 0) { 122462306a36Sopenharmony_ci /* 122562306a36Sopenharmony_ci * This nnode was not written which just means that the LEB 122662306a36Sopenharmony_ci * properties in the subtree below it describe empty LEBs. We 122762306a36Sopenharmony_ci * make the nnode as though we had read it, which in fact means 122862306a36Sopenharmony_ci * doing almost nothing. 122962306a36Sopenharmony_ci */ 123062306a36Sopenharmony_ci if (c->big_lpt) 123162306a36Sopenharmony_ci nnode->num = calc_nnode_num_from_parent(c, parent, iip); 123262306a36Sopenharmony_ci } else { 123362306a36Sopenharmony_ci err = ubifs_leb_read(c, lnum, buf, offs, c->nnode_sz, 1); 123462306a36Sopenharmony_ci if (err) 123562306a36Sopenharmony_ci goto out; 123662306a36Sopenharmony_ci err = ubifs_unpack_nnode(c, buf, nnode); 123762306a36Sopenharmony_ci if (err) 123862306a36Sopenharmony_ci goto out; 123962306a36Sopenharmony_ci } 124062306a36Sopenharmony_ci err = validate_nnode(c, nnode, parent, iip); 124162306a36Sopenharmony_ci if (err) 124262306a36Sopenharmony_ci goto out; 124362306a36Sopenharmony_ci if (!c->big_lpt) 124462306a36Sopenharmony_ci nnode->num = calc_nnode_num_from_parent(c, parent, iip); 124562306a36Sopenharmony_ci if (parent) { 124662306a36Sopenharmony_ci branch->nnode = nnode; 124762306a36Sopenharmony_ci nnode->level = parent->level - 1; 124862306a36Sopenharmony_ci } else { 124962306a36Sopenharmony_ci c->nroot = nnode; 125062306a36Sopenharmony_ci nnode->level = c->lpt_hght; 125162306a36Sopenharmony_ci } 125262306a36Sopenharmony_ci nnode->parent = parent; 125362306a36Sopenharmony_ci nnode->iip = iip; 125462306a36Sopenharmony_ci return 0; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ciout: 125762306a36Sopenharmony_ci ubifs_err(c, "error %d reading nnode at %d:%d", err, lnum, offs); 125862306a36Sopenharmony_ci dump_stack(); 125962306a36Sopenharmony_ci kfree(nnode); 126062306a36Sopenharmony_ci return err; 126162306a36Sopenharmony_ci} 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci/** 126462306a36Sopenharmony_ci * read_pnode - read a pnode from flash and link it to the tree in memory. 126562306a36Sopenharmony_ci * @c: UBIFS file-system description object 126662306a36Sopenharmony_ci * @parent: parent nnode 126762306a36Sopenharmony_ci * @iip: index in parent 126862306a36Sopenharmony_ci * 126962306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 127062306a36Sopenharmony_ci */ 127162306a36Sopenharmony_cistatic int read_pnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip) 127262306a36Sopenharmony_ci{ 127362306a36Sopenharmony_ci struct ubifs_nbranch *branch; 127462306a36Sopenharmony_ci struct ubifs_pnode *pnode = NULL; 127562306a36Sopenharmony_ci void *buf = c->lpt_nod_buf; 127662306a36Sopenharmony_ci int err, lnum, offs; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci branch = &parent->nbranch[iip]; 127962306a36Sopenharmony_ci lnum = branch->lnum; 128062306a36Sopenharmony_ci offs = branch->offs; 128162306a36Sopenharmony_ci pnode = kzalloc(sizeof(struct ubifs_pnode), GFP_NOFS); 128262306a36Sopenharmony_ci if (!pnode) 128362306a36Sopenharmony_ci return -ENOMEM; 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci if (lnum == 0) { 128662306a36Sopenharmony_ci /* 128762306a36Sopenharmony_ci * This pnode was not written which just means that the LEB 128862306a36Sopenharmony_ci * properties in it describe empty LEBs. We make the pnode as 128962306a36Sopenharmony_ci * though we had read it. 129062306a36Sopenharmony_ci */ 129162306a36Sopenharmony_ci int i; 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci if (c->big_lpt) 129462306a36Sopenharmony_ci pnode->num = calc_pnode_num_from_parent(c, parent, iip); 129562306a36Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 129662306a36Sopenharmony_ci struct ubifs_lprops * const lprops = &pnode->lprops[i]; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci lprops->free = c->leb_size; 129962306a36Sopenharmony_ci lprops->flags = ubifs_categorize_lprops(c, lprops); 130062306a36Sopenharmony_ci } 130162306a36Sopenharmony_ci } else { 130262306a36Sopenharmony_ci err = ubifs_leb_read(c, lnum, buf, offs, c->pnode_sz, 1); 130362306a36Sopenharmony_ci if (err) 130462306a36Sopenharmony_ci goto out; 130562306a36Sopenharmony_ci err = unpack_pnode(c, buf, pnode); 130662306a36Sopenharmony_ci if (err) 130762306a36Sopenharmony_ci goto out; 130862306a36Sopenharmony_ci } 130962306a36Sopenharmony_ci err = validate_pnode(c, pnode, parent, iip); 131062306a36Sopenharmony_ci if (err) 131162306a36Sopenharmony_ci goto out; 131262306a36Sopenharmony_ci if (!c->big_lpt) 131362306a36Sopenharmony_ci pnode->num = calc_pnode_num_from_parent(c, parent, iip); 131462306a36Sopenharmony_ci branch->pnode = pnode; 131562306a36Sopenharmony_ci pnode->parent = parent; 131662306a36Sopenharmony_ci pnode->iip = iip; 131762306a36Sopenharmony_ci set_pnode_lnum(c, pnode); 131862306a36Sopenharmony_ci c->pnodes_have += 1; 131962306a36Sopenharmony_ci return 0; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ciout: 132262306a36Sopenharmony_ci ubifs_err(c, "error %d reading pnode at %d:%d", err, lnum, offs); 132362306a36Sopenharmony_ci ubifs_dump_pnode(c, pnode, parent, iip); 132462306a36Sopenharmony_ci dump_stack(); 132562306a36Sopenharmony_ci ubifs_err(c, "calc num: %d", calc_pnode_num_from_parent(c, parent, iip)); 132662306a36Sopenharmony_ci kfree(pnode); 132762306a36Sopenharmony_ci return err; 132862306a36Sopenharmony_ci} 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci/** 133162306a36Sopenharmony_ci * read_ltab - read LPT's own lprops table. 133262306a36Sopenharmony_ci * @c: UBIFS file-system description object 133362306a36Sopenharmony_ci * 133462306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 133562306a36Sopenharmony_ci */ 133662306a36Sopenharmony_cistatic int read_ltab(struct ubifs_info *c) 133762306a36Sopenharmony_ci{ 133862306a36Sopenharmony_ci int err; 133962306a36Sopenharmony_ci void *buf; 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci buf = vmalloc(c->ltab_sz); 134262306a36Sopenharmony_ci if (!buf) 134362306a36Sopenharmony_ci return -ENOMEM; 134462306a36Sopenharmony_ci err = ubifs_leb_read(c, c->ltab_lnum, buf, c->ltab_offs, c->ltab_sz, 1); 134562306a36Sopenharmony_ci if (err) 134662306a36Sopenharmony_ci goto out; 134762306a36Sopenharmony_ci err = unpack_ltab(c, buf); 134862306a36Sopenharmony_ciout: 134962306a36Sopenharmony_ci vfree(buf); 135062306a36Sopenharmony_ci return err; 135162306a36Sopenharmony_ci} 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci/** 135462306a36Sopenharmony_ci * read_lsave - read LPT's save table. 135562306a36Sopenharmony_ci * @c: UBIFS file-system description object 135662306a36Sopenharmony_ci * 135762306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 135862306a36Sopenharmony_ci */ 135962306a36Sopenharmony_cistatic int read_lsave(struct ubifs_info *c) 136062306a36Sopenharmony_ci{ 136162306a36Sopenharmony_ci int err, i; 136262306a36Sopenharmony_ci void *buf; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci buf = vmalloc(c->lsave_sz); 136562306a36Sopenharmony_ci if (!buf) 136662306a36Sopenharmony_ci return -ENOMEM; 136762306a36Sopenharmony_ci err = ubifs_leb_read(c, c->lsave_lnum, buf, c->lsave_offs, 136862306a36Sopenharmony_ci c->lsave_sz, 1); 136962306a36Sopenharmony_ci if (err) 137062306a36Sopenharmony_ci goto out; 137162306a36Sopenharmony_ci err = unpack_lsave(c, buf); 137262306a36Sopenharmony_ci if (err) 137362306a36Sopenharmony_ci goto out; 137462306a36Sopenharmony_ci for (i = 0; i < c->lsave_cnt; i++) { 137562306a36Sopenharmony_ci int lnum = c->lsave[i]; 137662306a36Sopenharmony_ci struct ubifs_lprops *lprops; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci /* 137962306a36Sopenharmony_ci * Due to automatic resizing, the values in the lsave table 138062306a36Sopenharmony_ci * could be beyond the volume size - just ignore them. 138162306a36Sopenharmony_ci */ 138262306a36Sopenharmony_ci if (lnum >= c->leb_cnt) 138362306a36Sopenharmony_ci continue; 138462306a36Sopenharmony_ci lprops = ubifs_lpt_lookup(c, lnum); 138562306a36Sopenharmony_ci if (IS_ERR(lprops)) { 138662306a36Sopenharmony_ci err = PTR_ERR(lprops); 138762306a36Sopenharmony_ci goto out; 138862306a36Sopenharmony_ci } 138962306a36Sopenharmony_ci } 139062306a36Sopenharmony_ciout: 139162306a36Sopenharmony_ci vfree(buf); 139262306a36Sopenharmony_ci return err; 139362306a36Sopenharmony_ci} 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci/** 139662306a36Sopenharmony_ci * ubifs_get_nnode - get a nnode. 139762306a36Sopenharmony_ci * @c: UBIFS file-system description object 139862306a36Sopenharmony_ci * @parent: parent nnode (or NULL for the root) 139962306a36Sopenharmony_ci * @iip: index in parent 140062306a36Sopenharmony_ci * 140162306a36Sopenharmony_ci * This function returns a pointer to the nnode on success or a negative error 140262306a36Sopenharmony_ci * code on failure. 140362306a36Sopenharmony_ci */ 140462306a36Sopenharmony_cistruct ubifs_nnode *ubifs_get_nnode(struct ubifs_info *c, 140562306a36Sopenharmony_ci struct ubifs_nnode *parent, int iip) 140662306a36Sopenharmony_ci{ 140762306a36Sopenharmony_ci struct ubifs_nbranch *branch; 140862306a36Sopenharmony_ci struct ubifs_nnode *nnode; 140962306a36Sopenharmony_ci int err; 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci branch = &parent->nbranch[iip]; 141262306a36Sopenharmony_ci nnode = branch->nnode; 141362306a36Sopenharmony_ci if (nnode) 141462306a36Sopenharmony_ci return nnode; 141562306a36Sopenharmony_ci err = ubifs_read_nnode(c, parent, iip); 141662306a36Sopenharmony_ci if (err) 141762306a36Sopenharmony_ci return ERR_PTR(err); 141862306a36Sopenharmony_ci return branch->nnode; 141962306a36Sopenharmony_ci} 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci/** 142262306a36Sopenharmony_ci * ubifs_get_pnode - get a pnode. 142362306a36Sopenharmony_ci * @c: UBIFS file-system description object 142462306a36Sopenharmony_ci * @parent: parent nnode 142562306a36Sopenharmony_ci * @iip: index in parent 142662306a36Sopenharmony_ci * 142762306a36Sopenharmony_ci * This function returns a pointer to the pnode on success or a negative error 142862306a36Sopenharmony_ci * code on failure. 142962306a36Sopenharmony_ci */ 143062306a36Sopenharmony_cistruct ubifs_pnode *ubifs_get_pnode(struct ubifs_info *c, 143162306a36Sopenharmony_ci struct ubifs_nnode *parent, int iip) 143262306a36Sopenharmony_ci{ 143362306a36Sopenharmony_ci struct ubifs_nbranch *branch; 143462306a36Sopenharmony_ci struct ubifs_pnode *pnode; 143562306a36Sopenharmony_ci int err; 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci branch = &parent->nbranch[iip]; 143862306a36Sopenharmony_ci pnode = branch->pnode; 143962306a36Sopenharmony_ci if (pnode) 144062306a36Sopenharmony_ci return pnode; 144162306a36Sopenharmony_ci err = read_pnode(c, parent, iip); 144262306a36Sopenharmony_ci if (err) 144362306a36Sopenharmony_ci return ERR_PTR(err); 144462306a36Sopenharmony_ci update_cats(c, branch->pnode); 144562306a36Sopenharmony_ci return branch->pnode; 144662306a36Sopenharmony_ci} 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci/** 144962306a36Sopenharmony_ci * ubifs_pnode_lookup - lookup a pnode in the LPT. 145062306a36Sopenharmony_ci * @c: UBIFS file-system description object 145162306a36Sopenharmony_ci * @i: pnode number (0 to (main_lebs - 1) / UBIFS_LPT_FANOUT) 145262306a36Sopenharmony_ci * 145362306a36Sopenharmony_ci * This function returns a pointer to the pnode on success or a negative 145462306a36Sopenharmony_ci * error code on failure. 145562306a36Sopenharmony_ci */ 145662306a36Sopenharmony_cistruct ubifs_pnode *ubifs_pnode_lookup(struct ubifs_info *c, int i) 145762306a36Sopenharmony_ci{ 145862306a36Sopenharmony_ci int err, h, iip, shft; 145962306a36Sopenharmony_ci struct ubifs_nnode *nnode; 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci if (!c->nroot) { 146262306a36Sopenharmony_ci err = ubifs_read_nnode(c, NULL, 0); 146362306a36Sopenharmony_ci if (err) 146462306a36Sopenharmony_ci return ERR_PTR(err); 146562306a36Sopenharmony_ci } 146662306a36Sopenharmony_ci i <<= UBIFS_LPT_FANOUT_SHIFT; 146762306a36Sopenharmony_ci nnode = c->nroot; 146862306a36Sopenharmony_ci shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT; 146962306a36Sopenharmony_ci for (h = 1; h < c->lpt_hght; h++) { 147062306a36Sopenharmony_ci iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); 147162306a36Sopenharmony_ci shft -= UBIFS_LPT_FANOUT_SHIFT; 147262306a36Sopenharmony_ci nnode = ubifs_get_nnode(c, nnode, iip); 147362306a36Sopenharmony_ci if (IS_ERR(nnode)) 147462306a36Sopenharmony_ci return ERR_CAST(nnode); 147562306a36Sopenharmony_ci } 147662306a36Sopenharmony_ci iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); 147762306a36Sopenharmony_ci return ubifs_get_pnode(c, nnode, iip); 147862306a36Sopenharmony_ci} 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci/** 148162306a36Sopenharmony_ci * ubifs_lpt_lookup - lookup LEB properties in the LPT. 148262306a36Sopenharmony_ci * @c: UBIFS file-system description object 148362306a36Sopenharmony_ci * @lnum: LEB number to lookup 148462306a36Sopenharmony_ci * 148562306a36Sopenharmony_ci * This function returns a pointer to the LEB properties on success or a 148662306a36Sopenharmony_ci * negative error code on failure. 148762306a36Sopenharmony_ci */ 148862306a36Sopenharmony_cistruct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum) 148962306a36Sopenharmony_ci{ 149062306a36Sopenharmony_ci int i, iip; 149162306a36Sopenharmony_ci struct ubifs_pnode *pnode; 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci i = lnum - c->main_first; 149462306a36Sopenharmony_ci pnode = ubifs_pnode_lookup(c, i >> UBIFS_LPT_FANOUT_SHIFT); 149562306a36Sopenharmony_ci if (IS_ERR(pnode)) 149662306a36Sopenharmony_ci return ERR_CAST(pnode); 149762306a36Sopenharmony_ci iip = (i & (UBIFS_LPT_FANOUT - 1)); 149862306a36Sopenharmony_ci dbg_lp("LEB %d, free %d, dirty %d, flags %d", lnum, 149962306a36Sopenharmony_ci pnode->lprops[iip].free, pnode->lprops[iip].dirty, 150062306a36Sopenharmony_ci pnode->lprops[iip].flags); 150162306a36Sopenharmony_ci return &pnode->lprops[iip]; 150262306a36Sopenharmony_ci} 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci/** 150562306a36Sopenharmony_ci * dirty_cow_nnode - ensure a nnode is not being committed. 150662306a36Sopenharmony_ci * @c: UBIFS file-system description object 150762306a36Sopenharmony_ci * @nnode: nnode to check 150862306a36Sopenharmony_ci * 150962306a36Sopenharmony_ci * Returns dirtied nnode on success or negative error code on failure. 151062306a36Sopenharmony_ci */ 151162306a36Sopenharmony_cistatic struct ubifs_nnode *dirty_cow_nnode(struct ubifs_info *c, 151262306a36Sopenharmony_ci struct ubifs_nnode *nnode) 151362306a36Sopenharmony_ci{ 151462306a36Sopenharmony_ci struct ubifs_nnode *n; 151562306a36Sopenharmony_ci int i; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci if (!test_bit(COW_CNODE, &nnode->flags)) { 151862306a36Sopenharmony_ci /* nnode is not being committed */ 151962306a36Sopenharmony_ci if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) { 152062306a36Sopenharmony_ci c->dirty_nn_cnt += 1; 152162306a36Sopenharmony_ci ubifs_add_nnode_dirt(c, nnode); 152262306a36Sopenharmony_ci } 152362306a36Sopenharmony_ci return nnode; 152462306a36Sopenharmony_ci } 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci /* nnode is being committed, so copy it */ 152762306a36Sopenharmony_ci n = kmemdup(nnode, sizeof(struct ubifs_nnode), GFP_NOFS); 152862306a36Sopenharmony_ci if (unlikely(!n)) 152962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci n->cnext = NULL; 153262306a36Sopenharmony_ci __set_bit(DIRTY_CNODE, &n->flags); 153362306a36Sopenharmony_ci __clear_bit(COW_CNODE, &n->flags); 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci /* The children now have new parent */ 153662306a36Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 153762306a36Sopenharmony_ci struct ubifs_nbranch *branch = &n->nbranch[i]; 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci if (branch->cnode) 154062306a36Sopenharmony_ci branch->cnode->parent = n; 154162306a36Sopenharmony_ci } 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci ubifs_assert(c, !test_bit(OBSOLETE_CNODE, &nnode->flags)); 154462306a36Sopenharmony_ci __set_bit(OBSOLETE_CNODE, &nnode->flags); 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci c->dirty_nn_cnt += 1; 154762306a36Sopenharmony_ci ubifs_add_nnode_dirt(c, nnode); 154862306a36Sopenharmony_ci if (nnode->parent) 154962306a36Sopenharmony_ci nnode->parent->nbranch[n->iip].nnode = n; 155062306a36Sopenharmony_ci else 155162306a36Sopenharmony_ci c->nroot = n; 155262306a36Sopenharmony_ci return n; 155362306a36Sopenharmony_ci} 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci/** 155662306a36Sopenharmony_ci * dirty_cow_pnode - ensure a pnode is not being committed. 155762306a36Sopenharmony_ci * @c: UBIFS file-system description object 155862306a36Sopenharmony_ci * @pnode: pnode to check 155962306a36Sopenharmony_ci * 156062306a36Sopenharmony_ci * Returns dirtied pnode on success or negative error code on failure. 156162306a36Sopenharmony_ci */ 156262306a36Sopenharmony_cistatic struct ubifs_pnode *dirty_cow_pnode(struct ubifs_info *c, 156362306a36Sopenharmony_ci struct ubifs_pnode *pnode) 156462306a36Sopenharmony_ci{ 156562306a36Sopenharmony_ci struct ubifs_pnode *p; 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci if (!test_bit(COW_CNODE, &pnode->flags)) { 156862306a36Sopenharmony_ci /* pnode is not being committed */ 156962306a36Sopenharmony_ci if (!test_and_set_bit(DIRTY_CNODE, &pnode->flags)) { 157062306a36Sopenharmony_ci c->dirty_pn_cnt += 1; 157162306a36Sopenharmony_ci add_pnode_dirt(c, pnode); 157262306a36Sopenharmony_ci } 157362306a36Sopenharmony_ci return pnode; 157462306a36Sopenharmony_ci } 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci /* pnode is being committed, so copy it */ 157762306a36Sopenharmony_ci p = kmemdup(pnode, sizeof(struct ubifs_pnode), GFP_NOFS); 157862306a36Sopenharmony_ci if (unlikely(!p)) 157962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci p->cnext = NULL; 158262306a36Sopenharmony_ci __set_bit(DIRTY_CNODE, &p->flags); 158362306a36Sopenharmony_ci __clear_bit(COW_CNODE, &p->flags); 158462306a36Sopenharmony_ci replace_cats(c, pnode, p); 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci ubifs_assert(c, !test_bit(OBSOLETE_CNODE, &pnode->flags)); 158762306a36Sopenharmony_ci __set_bit(OBSOLETE_CNODE, &pnode->flags); 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci c->dirty_pn_cnt += 1; 159062306a36Sopenharmony_ci add_pnode_dirt(c, pnode); 159162306a36Sopenharmony_ci pnode->parent->nbranch[p->iip].pnode = p; 159262306a36Sopenharmony_ci return p; 159362306a36Sopenharmony_ci} 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci/** 159662306a36Sopenharmony_ci * ubifs_lpt_lookup_dirty - lookup LEB properties in the LPT. 159762306a36Sopenharmony_ci * @c: UBIFS file-system description object 159862306a36Sopenharmony_ci * @lnum: LEB number to lookup 159962306a36Sopenharmony_ci * 160062306a36Sopenharmony_ci * This function returns a pointer to the LEB properties on success or a 160162306a36Sopenharmony_ci * negative error code on failure. 160262306a36Sopenharmony_ci */ 160362306a36Sopenharmony_cistruct ubifs_lprops *ubifs_lpt_lookup_dirty(struct ubifs_info *c, int lnum) 160462306a36Sopenharmony_ci{ 160562306a36Sopenharmony_ci int err, i, h, iip, shft; 160662306a36Sopenharmony_ci struct ubifs_nnode *nnode; 160762306a36Sopenharmony_ci struct ubifs_pnode *pnode; 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci if (!c->nroot) { 161062306a36Sopenharmony_ci err = ubifs_read_nnode(c, NULL, 0); 161162306a36Sopenharmony_ci if (err) 161262306a36Sopenharmony_ci return ERR_PTR(err); 161362306a36Sopenharmony_ci } 161462306a36Sopenharmony_ci nnode = c->nroot; 161562306a36Sopenharmony_ci nnode = dirty_cow_nnode(c, nnode); 161662306a36Sopenharmony_ci if (IS_ERR(nnode)) 161762306a36Sopenharmony_ci return ERR_CAST(nnode); 161862306a36Sopenharmony_ci i = lnum - c->main_first; 161962306a36Sopenharmony_ci shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT; 162062306a36Sopenharmony_ci for (h = 1; h < c->lpt_hght; h++) { 162162306a36Sopenharmony_ci iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); 162262306a36Sopenharmony_ci shft -= UBIFS_LPT_FANOUT_SHIFT; 162362306a36Sopenharmony_ci nnode = ubifs_get_nnode(c, nnode, iip); 162462306a36Sopenharmony_ci if (IS_ERR(nnode)) 162562306a36Sopenharmony_ci return ERR_CAST(nnode); 162662306a36Sopenharmony_ci nnode = dirty_cow_nnode(c, nnode); 162762306a36Sopenharmony_ci if (IS_ERR(nnode)) 162862306a36Sopenharmony_ci return ERR_CAST(nnode); 162962306a36Sopenharmony_ci } 163062306a36Sopenharmony_ci iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); 163162306a36Sopenharmony_ci pnode = ubifs_get_pnode(c, nnode, iip); 163262306a36Sopenharmony_ci if (IS_ERR(pnode)) 163362306a36Sopenharmony_ci return ERR_CAST(pnode); 163462306a36Sopenharmony_ci pnode = dirty_cow_pnode(c, pnode); 163562306a36Sopenharmony_ci if (IS_ERR(pnode)) 163662306a36Sopenharmony_ci return ERR_CAST(pnode); 163762306a36Sopenharmony_ci iip = (i & (UBIFS_LPT_FANOUT - 1)); 163862306a36Sopenharmony_ci dbg_lp("LEB %d, free %d, dirty %d, flags %d", lnum, 163962306a36Sopenharmony_ci pnode->lprops[iip].free, pnode->lprops[iip].dirty, 164062306a36Sopenharmony_ci pnode->lprops[iip].flags); 164162306a36Sopenharmony_ci ubifs_assert(c, test_bit(DIRTY_CNODE, &pnode->flags)); 164262306a36Sopenharmony_ci return &pnode->lprops[iip]; 164362306a36Sopenharmony_ci} 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci/** 164662306a36Sopenharmony_ci * ubifs_lpt_calc_hash - Calculate hash of the LPT pnodes 164762306a36Sopenharmony_ci * @c: UBIFS file-system description object 164862306a36Sopenharmony_ci * @hash: the returned hash of the LPT pnodes 164962306a36Sopenharmony_ci * 165062306a36Sopenharmony_ci * This function iterates over the LPT pnodes and creates a hash over them. 165162306a36Sopenharmony_ci * Returns 0 for success or a negative error code otherwise. 165262306a36Sopenharmony_ci */ 165362306a36Sopenharmony_ciint ubifs_lpt_calc_hash(struct ubifs_info *c, u8 *hash) 165462306a36Sopenharmony_ci{ 165562306a36Sopenharmony_ci struct ubifs_nnode *nnode, *nn; 165662306a36Sopenharmony_ci struct ubifs_cnode *cnode; 165762306a36Sopenharmony_ci struct shash_desc *desc; 165862306a36Sopenharmony_ci int iip = 0, i; 165962306a36Sopenharmony_ci int bufsiz = max_t(int, c->nnode_sz, c->pnode_sz); 166062306a36Sopenharmony_ci void *buf; 166162306a36Sopenharmony_ci int err; 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci if (!ubifs_authenticated(c)) 166462306a36Sopenharmony_ci return 0; 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci if (!c->nroot) { 166762306a36Sopenharmony_ci err = ubifs_read_nnode(c, NULL, 0); 166862306a36Sopenharmony_ci if (err) 166962306a36Sopenharmony_ci return err; 167062306a36Sopenharmony_ci } 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci desc = ubifs_hash_get_desc(c); 167362306a36Sopenharmony_ci if (IS_ERR(desc)) 167462306a36Sopenharmony_ci return PTR_ERR(desc); 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci buf = kmalloc(bufsiz, GFP_NOFS); 167762306a36Sopenharmony_ci if (!buf) { 167862306a36Sopenharmony_ci err = -ENOMEM; 167962306a36Sopenharmony_ci goto out; 168062306a36Sopenharmony_ci } 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci cnode = (struct ubifs_cnode *)c->nroot; 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci while (cnode) { 168562306a36Sopenharmony_ci nnode = cnode->parent; 168662306a36Sopenharmony_ci nn = (struct ubifs_nnode *)cnode; 168762306a36Sopenharmony_ci if (cnode->level > 1) { 168862306a36Sopenharmony_ci while (iip < UBIFS_LPT_FANOUT) { 168962306a36Sopenharmony_ci if (nn->nbranch[iip].lnum == 0) { 169062306a36Sopenharmony_ci /* Go right */ 169162306a36Sopenharmony_ci iip++; 169262306a36Sopenharmony_ci continue; 169362306a36Sopenharmony_ci } 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci nnode = ubifs_get_nnode(c, nn, iip); 169662306a36Sopenharmony_ci if (IS_ERR(nnode)) { 169762306a36Sopenharmony_ci err = PTR_ERR(nnode); 169862306a36Sopenharmony_ci goto out; 169962306a36Sopenharmony_ci } 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci /* Go down */ 170262306a36Sopenharmony_ci iip = 0; 170362306a36Sopenharmony_ci cnode = (struct ubifs_cnode *)nnode; 170462306a36Sopenharmony_ci break; 170562306a36Sopenharmony_ci } 170662306a36Sopenharmony_ci if (iip < UBIFS_LPT_FANOUT) 170762306a36Sopenharmony_ci continue; 170862306a36Sopenharmony_ci } else { 170962306a36Sopenharmony_ci struct ubifs_pnode *pnode; 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 171262306a36Sopenharmony_ci if (nn->nbranch[i].lnum == 0) 171362306a36Sopenharmony_ci continue; 171462306a36Sopenharmony_ci pnode = ubifs_get_pnode(c, nn, i); 171562306a36Sopenharmony_ci if (IS_ERR(pnode)) { 171662306a36Sopenharmony_ci err = PTR_ERR(pnode); 171762306a36Sopenharmony_ci goto out; 171862306a36Sopenharmony_ci } 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci ubifs_pack_pnode(c, buf, pnode); 172162306a36Sopenharmony_ci err = ubifs_shash_update(c, desc, buf, 172262306a36Sopenharmony_ci c->pnode_sz); 172362306a36Sopenharmony_ci if (err) 172462306a36Sopenharmony_ci goto out; 172562306a36Sopenharmony_ci } 172662306a36Sopenharmony_ci } 172762306a36Sopenharmony_ci /* Go up and to the right */ 172862306a36Sopenharmony_ci iip = cnode->iip + 1; 172962306a36Sopenharmony_ci cnode = (struct ubifs_cnode *)nnode; 173062306a36Sopenharmony_ci } 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci err = ubifs_shash_final(c, desc, hash); 173362306a36Sopenharmony_ciout: 173462306a36Sopenharmony_ci kfree(desc); 173562306a36Sopenharmony_ci kfree(buf); 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci return err; 173862306a36Sopenharmony_ci} 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci/** 174162306a36Sopenharmony_ci * lpt_check_hash - check the hash of the LPT. 174262306a36Sopenharmony_ci * @c: UBIFS file-system description object 174362306a36Sopenharmony_ci * 174462306a36Sopenharmony_ci * This function calculates a hash over all pnodes in the LPT and compares it with 174562306a36Sopenharmony_ci * the hash stored in the master node. Returns %0 on success and a negative error 174662306a36Sopenharmony_ci * code on failure. 174762306a36Sopenharmony_ci */ 174862306a36Sopenharmony_cistatic int lpt_check_hash(struct ubifs_info *c) 174962306a36Sopenharmony_ci{ 175062306a36Sopenharmony_ci int err; 175162306a36Sopenharmony_ci u8 hash[UBIFS_HASH_ARR_SZ]; 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci if (!ubifs_authenticated(c)) 175462306a36Sopenharmony_ci return 0; 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci err = ubifs_lpt_calc_hash(c, hash); 175762306a36Sopenharmony_ci if (err) 175862306a36Sopenharmony_ci return err; 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci if (ubifs_check_hash(c, c->mst_node->hash_lpt, hash)) { 176162306a36Sopenharmony_ci err = -EPERM; 176262306a36Sopenharmony_ci ubifs_err(c, "Failed to authenticate LPT"); 176362306a36Sopenharmony_ci } else { 176462306a36Sopenharmony_ci err = 0; 176562306a36Sopenharmony_ci } 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci return err; 176862306a36Sopenharmony_ci} 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci/** 177162306a36Sopenharmony_ci * lpt_init_rd - initialize the LPT for reading. 177262306a36Sopenharmony_ci * @c: UBIFS file-system description object 177362306a36Sopenharmony_ci * 177462306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 177562306a36Sopenharmony_ci */ 177662306a36Sopenharmony_cistatic int lpt_init_rd(struct ubifs_info *c) 177762306a36Sopenharmony_ci{ 177862306a36Sopenharmony_ci int err, i; 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci c->ltab = vmalloc(array_size(sizeof(struct ubifs_lpt_lprops), 178162306a36Sopenharmony_ci c->lpt_lebs)); 178262306a36Sopenharmony_ci if (!c->ltab) 178362306a36Sopenharmony_ci return -ENOMEM; 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci i = max_t(int, c->nnode_sz, c->pnode_sz); 178662306a36Sopenharmony_ci c->lpt_nod_buf = kmalloc(i, GFP_KERNEL); 178762306a36Sopenharmony_ci if (!c->lpt_nod_buf) 178862306a36Sopenharmony_ci return -ENOMEM; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci for (i = 0; i < LPROPS_HEAP_CNT; i++) { 179162306a36Sopenharmony_ci c->lpt_heap[i].arr = kmalloc_array(LPT_HEAP_SZ, 179262306a36Sopenharmony_ci sizeof(void *), 179362306a36Sopenharmony_ci GFP_KERNEL); 179462306a36Sopenharmony_ci if (!c->lpt_heap[i].arr) 179562306a36Sopenharmony_ci return -ENOMEM; 179662306a36Sopenharmony_ci c->lpt_heap[i].cnt = 0; 179762306a36Sopenharmony_ci c->lpt_heap[i].max_cnt = LPT_HEAP_SZ; 179862306a36Sopenharmony_ci } 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci c->dirty_idx.arr = kmalloc_array(LPT_HEAP_SZ, sizeof(void *), 180162306a36Sopenharmony_ci GFP_KERNEL); 180262306a36Sopenharmony_ci if (!c->dirty_idx.arr) 180362306a36Sopenharmony_ci return -ENOMEM; 180462306a36Sopenharmony_ci c->dirty_idx.cnt = 0; 180562306a36Sopenharmony_ci c->dirty_idx.max_cnt = LPT_HEAP_SZ; 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci err = read_ltab(c); 180862306a36Sopenharmony_ci if (err) 180962306a36Sopenharmony_ci return err; 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci err = lpt_check_hash(c); 181262306a36Sopenharmony_ci if (err) 181362306a36Sopenharmony_ci return err; 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci dbg_lp("space_bits %d", c->space_bits); 181662306a36Sopenharmony_ci dbg_lp("lpt_lnum_bits %d", c->lpt_lnum_bits); 181762306a36Sopenharmony_ci dbg_lp("lpt_offs_bits %d", c->lpt_offs_bits); 181862306a36Sopenharmony_ci dbg_lp("lpt_spc_bits %d", c->lpt_spc_bits); 181962306a36Sopenharmony_ci dbg_lp("pcnt_bits %d", c->pcnt_bits); 182062306a36Sopenharmony_ci dbg_lp("lnum_bits %d", c->lnum_bits); 182162306a36Sopenharmony_ci dbg_lp("pnode_sz %d", c->pnode_sz); 182262306a36Sopenharmony_ci dbg_lp("nnode_sz %d", c->nnode_sz); 182362306a36Sopenharmony_ci dbg_lp("ltab_sz %d", c->ltab_sz); 182462306a36Sopenharmony_ci dbg_lp("lsave_sz %d", c->lsave_sz); 182562306a36Sopenharmony_ci dbg_lp("lsave_cnt %d", c->lsave_cnt); 182662306a36Sopenharmony_ci dbg_lp("lpt_hght %d", c->lpt_hght); 182762306a36Sopenharmony_ci dbg_lp("big_lpt %u", c->big_lpt); 182862306a36Sopenharmony_ci dbg_lp("LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs); 182962306a36Sopenharmony_ci dbg_lp("LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs); 183062306a36Sopenharmony_ci dbg_lp("LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs); 183162306a36Sopenharmony_ci if (c->big_lpt) 183262306a36Sopenharmony_ci dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs); 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci return 0; 183562306a36Sopenharmony_ci} 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci/** 183862306a36Sopenharmony_ci * lpt_init_wr - initialize the LPT for writing. 183962306a36Sopenharmony_ci * @c: UBIFS file-system description object 184062306a36Sopenharmony_ci * 184162306a36Sopenharmony_ci * 'lpt_init_rd()' must have been called already. 184262306a36Sopenharmony_ci * 184362306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 184462306a36Sopenharmony_ci */ 184562306a36Sopenharmony_cistatic int lpt_init_wr(struct ubifs_info *c) 184662306a36Sopenharmony_ci{ 184762306a36Sopenharmony_ci int err, i; 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci c->ltab_cmt = vmalloc(array_size(sizeof(struct ubifs_lpt_lprops), 185062306a36Sopenharmony_ci c->lpt_lebs)); 185162306a36Sopenharmony_ci if (!c->ltab_cmt) 185262306a36Sopenharmony_ci return -ENOMEM; 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci c->lpt_buf = vmalloc(c->leb_size); 185562306a36Sopenharmony_ci if (!c->lpt_buf) 185662306a36Sopenharmony_ci return -ENOMEM; 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci if (c->big_lpt) { 185962306a36Sopenharmony_ci c->lsave = kmalloc_array(c->lsave_cnt, sizeof(int), GFP_NOFS); 186062306a36Sopenharmony_ci if (!c->lsave) 186162306a36Sopenharmony_ci return -ENOMEM; 186262306a36Sopenharmony_ci err = read_lsave(c); 186362306a36Sopenharmony_ci if (err) 186462306a36Sopenharmony_ci return err; 186562306a36Sopenharmony_ci } 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci for (i = 0; i < c->lpt_lebs; i++) 186862306a36Sopenharmony_ci if (c->ltab[i].free == c->leb_size) { 186962306a36Sopenharmony_ci err = ubifs_leb_unmap(c, i + c->lpt_first); 187062306a36Sopenharmony_ci if (err) 187162306a36Sopenharmony_ci return err; 187262306a36Sopenharmony_ci } 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci return 0; 187562306a36Sopenharmony_ci} 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci/** 187862306a36Sopenharmony_ci * ubifs_lpt_init - initialize the LPT. 187962306a36Sopenharmony_ci * @c: UBIFS file-system description object 188062306a36Sopenharmony_ci * @rd: whether to initialize lpt for reading 188162306a36Sopenharmony_ci * @wr: whether to initialize lpt for writing 188262306a36Sopenharmony_ci * 188362306a36Sopenharmony_ci * For mounting 'rw', @rd and @wr are both true. For mounting 'ro', @rd is true 188462306a36Sopenharmony_ci * and @wr is false. For mounting from 'ro' to 'rw', @rd is false and @wr is 188562306a36Sopenharmony_ci * true. 188662306a36Sopenharmony_ci * 188762306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 188862306a36Sopenharmony_ci */ 188962306a36Sopenharmony_ciint ubifs_lpt_init(struct ubifs_info *c, int rd, int wr) 189062306a36Sopenharmony_ci{ 189162306a36Sopenharmony_ci int err; 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci if (rd) { 189462306a36Sopenharmony_ci err = lpt_init_rd(c); 189562306a36Sopenharmony_ci if (err) 189662306a36Sopenharmony_ci goto out_err; 189762306a36Sopenharmony_ci } 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci if (wr) { 190062306a36Sopenharmony_ci err = lpt_init_wr(c); 190162306a36Sopenharmony_ci if (err) 190262306a36Sopenharmony_ci goto out_err; 190362306a36Sopenharmony_ci } 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci return 0; 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_ciout_err: 190862306a36Sopenharmony_ci if (wr) 190962306a36Sopenharmony_ci ubifs_lpt_free(c, 1); 191062306a36Sopenharmony_ci if (rd) 191162306a36Sopenharmony_ci ubifs_lpt_free(c, 0); 191262306a36Sopenharmony_ci return err; 191362306a36Sopenharmony_ci} 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci/** 191662306a36Sopenharmony_ci * struct lpt_scan_node - somewhere to put nodes while we scan LPT. 191762306a36Sopenharmony_ci * @nnode: where to keep a nnode 191862306a36Sopenharmony_ci * @pnode: where to keep a pnode 191962306a36Sopenharmony_ci * @cnode: where to keep a cnode 192062306a36Sopenharmony_ci * @in_tree: is the node in the tree in memory 192162306a36Sopenharmony_ci * @ptr.nnode: pointer to the nnode (if it is an nnode) which may be here or in 192262306a36Sopenharmony_ci * the tree 192362306a36Sopenharmony_ci * @ptr.pnode: ditto for pnode 192462306a36Sopenharmony_ci * @ptr.cnode: ditto for cnode 192562306a36Sopenharmony_ci */ 192662306a36Sopenharmony_cistruct lpt_scan_node { 192762306a36Sopenharmony_ci union { 192862306a36Sopenharmony_ci struct ubifs_nnode nnode; 192962306a36Sopenharmony_ci struct ubifs_pnode pnode; 193062306a36Sopenharmony_ci struct ubifs_cnode cnode; 193162306a36Sopenharmony_ci }; 193262306a36Sopenharmony_ci int in_tree; 193362306a36Sopenharmony_ci union { 193462306a36Sopenharmony_ci struct ubifs_nnode *nnode; 193562306a36Sopenharmony_ci struct ubifs_pnode *pnode; 193662306a36Sopenharmony_ci struct ubifs_cnode *cnode; 193762306a36Sopenharmony_ci } ptr; 193862306a36Sopenharmony_ci}; 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci/** 194162306a36Sopenharmony_ci * scan_get_nnode - for the scan, get a nnode from either the tree or flash. 194262306a36Sopenharmony_ci * @c: the UBIFS file-system description object 194362306a36Sopenharmony_ci * @path: where to put the nnode 194462306a36Sopenharmony_ci * @parent: parent of the nnode 194562306a36Sopenharmony_ci * @iip: index in parent of the nnode 194662306a36Sopenharmony_ci * 194762306a36Sopenharmony_ci * This function returns a pointer to the nnode on success or a negative error 194862306a36Sopenharmony_ci * code on failure. 194962306a36Sopenharmony_ci */ 195062306a36Sopenharmony_cistatic struct ubifs_nnode *scan_get_nnode(struct ubifs_info *c, 195162306a36Sopenharmony_ci struct lpt_scan_node *path, 195262306a36Sopenharmony_ci struct ubifs_nnode *parent, int iip) 195362306a36Sopenharmony_ci{ 195462306a36Sopenharmony_ci struct ubifs_nbranch *branch; 195562306a36Sopenharmony_ci struct ubifs_nnode *nnode; 195662306a36Sopenharmony_ci void *buf = c->lpt_nod_buf; 195762306a36Sopenharmony_ci int err; 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci branch = &parent->nbranch[iip]; 196062306a36Sopenharmony_ci nnode = branch->nnode; 196162306a36Sopenharmony_ci if (nnode) { 196262306a36Sopenharmony_ci path->in_tree = 1; 196362306a36Sopenharmony_ci path->ptr.nnode = nnode; 196462306a36Sopenharmony_ci return nnode; 196562306a36Sopenharmony_ci } 196662306a36Sopenharmony_ci nnode = &path->nnode; 196762306a36Sopenharmony_ci path->in_tree = 0; 196862306a36Sopenharmony_ci path->ptr.nnode = nnode; 196962306a36Sopenharmony_ci memset(nnode, 0, sizeof(struct ubifs_nnode)); 197062306a36Sopenharmony_ci if (branch->lnum == 0) { 197162306a36Sopenharmony_ci /* 197262306a36Sopenharmony_ci * This nnode was not written which just means that the LEB 197362306a36Sopenharmony_ci * properties in the subtree below it describe empty LEBs. We 197462306a36Sopenharmony_ci * make the nnode as though we had read it, which in fact means 197562306a36Sopenharmony_ci * doing almost nothing. 197662306a36Sopenharmony_ci */ 197762306a36Sopenharmony_ci if (c->big_lpt) 197862306a36Sopenharmony_ci nnode->num = calc_nnode_num_from_parent(c, parent, iip); 197962306a36Sopenharmony_ci } else { 198062306a36Sopenharmony_ci err = ubifs_leb_read(c, branch->lnum, buf, branch->offs, 198162306a36Sopenharmony_ci c->nnode_sz, 1); 198262306a36Sopenharmony_ci if (err) 198362306a36Sopenharmony_ci return ERR_PTR(err); 198462306a36Sopenharmony_ci err = ubifs_unpack_nnode(c, buf, nnode); 198562306a36Sopenharmony_ci if (err) 198662306a36Sopenharmony_ci return ERR_PTR(err); 198762306a36Sopenharmony_ci } 198862306a36Sopenharmony_ci err = validate_nnode(c, nnode, parent, iip); 198962306a36Sopenharmony_ci if (err) 199062306a36Sopenharmony_ci return ERR_PTR(err); 199162306a36Sopenharmony_ci if (!c->big_lpt) 199262306a36Sopenharmony_ci nnode->num = calc_nnode_num_from_parent(c, parent, iip); 199362306a36Sopenharmony_ci nnode->level = parent->level - 1; 199462306a36Sopenharmony_ci nnode->parent = parent; 199562306a36Sopenharmony_ci nnode->iip = iip; 199662306a36Sopenharmony_ci return nnode; 199762306a36Sopenharmony_ci} 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci/** 200062306a36Sopenharmony_ci * scan_get_pnode - for the scan, get a pnode from either the tree or flash. 200162306a36Sopenharmony_ci * @c: the UBIFS file-system description object 200262306a36Sopenharmony_ci * @path: where to put the pnode 200362306a36Sopenharmony_ci * @parent: parent of the pnode 200462306a36Sopenharmony_ci * @iip: index in parent of the pnode 200562306a36Sopenharmony_ci * 200662306a36Sopenharmony_ci * This function returns a pointer to the pnode on success or a negative error 200762306a36Sopenharmony_ci * code on failure. 200862306a36Sopenharmony_ci */ 200962306a36Sopenharmony_cistatic struct ubifs_pnode *scan_get_pnode(struct ubifs_info *c, 201062306a36Sopenharmony_ci struct lpt_scan_node *path, 201162306a36Sopenharmony_ci struct ubifs_nnode *parent, int iip) 201262306a36Sopenharmony_ci{ 201362306a36Sopenharmony_ci struct ubifs_nbranch *branch; 201462306a36Sopenharmony_ci struct ubifs_pnode *pnode; 201562306a36Sopenharmony_ci void *buf = c->lpt_nod_buf; 201662306a36Sopenharmony_ci int err; 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci branch = &parent->nbranch[iip]; 201962306a36Sopenharmony_ci pnode = branch->pnode; 202062306a36Sopenharmony_ci if (pnode) { 202162306a36Sopenharmony_ci path->in_tree = 1; 202262306a36Sopenharmony_ci path->ptr.pnode = pnode; 202362306a36Sopenharmony_ci return pnode; 202462306a36Sopenharmony_ci } 202562306a36Sopenharmony_ci pnode = &path->pnode; 202662306a36Sopenharmony_ci path->in_tree = 0; 202762306a36Sopenharmony_ci path->ptr.pnode = pnode; 202862306a36Sopenharmony_ci memset(pnode, 0, sizeof(struct ubifs_pnode)); 202962306a36Sopenharmony_ci if (branch->lnum == 0) { 203062306a36Sopenharmony_ci /* 203162306a36Sopenharmony_ci * This pnode was not written which just means that the LEB 203262306a36Sopenharmony_ci * properties in it describe empty LEBs. We make the pnode as 203362306a36Sopenharmony_ci * though we had read it. 203462306a36Sopenharmony_ci */ 203562306a36Sopenharmony_ci int i; 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci if (c->big_lpt) 203862306a36Sopenharmony_ci pnode->num = calc_pnode_num_from_parent(c, parent, iip); 203962306a36Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 204062306a36Sopenharmony_ci struct ubifs_lprops * const lprops = &pnode->lprops[i]; 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci lprops->free = c->leb_size; 204362306a36Sopenharmony_ci lprops->flags = ubifs_categorize_lprops(c, lprops); 204462306a36Sopenharmony_ci } 204562306a36Sopenharmony_ci } else { 204662306a36Sopenharmony_ci ubifs_assert(c, branch->lnum >= c->lpt_first && 204762306a36Sopenharmony_ci branch->lnum <= c->lpt_last); 204862306a36Sopenharmony_ci ubifs_assert(c, branch->offs >= 0 && branch->offs < c->leb_size); 204962306a36Sopenharmony_ci err = ubifs_leb_read(c, branch->lnum, buf, branch->offs, 205062306a36Sopenharmony_ci c->pnode_sz, 1); 205162306a36Sopenharmony_ci if (err) 205262306a36Sopenharmony_ci return ERR_PTR(err); 205362306a36Sopenharmony_ci err = unpack_pnode(c, buf, pnode); 205462306a36Sopenharmony_ci if (err) 205562306a36Sopenharmony_ci return ERR_PTR(err); 205662306a36Sopenharmony_ci } 205762306a36Sopenharmony_ci err = validate_pnode(c, pnode, parent, iip); 205862306a36Sopenharmony_ci if (err) 205962306a36Sopenharmony_ci return ERR_PTR(err); 206062306a36Sopenharmony_ci if (!c->big_lpt) 206162306a36Sopenharmony_ci pnode->num = calc_pnode_num_from_parent(c, parent, iip); 206262306a36Sopenharmony_ci pnode->parent = parent; 206362306a36Sopenharmony_ci pnode->iip = iip; 206462306a36Sopenharmony_ci set_pnode_lnum(c, pnode); 206562306a36Sopenharmony_ci return pnode; 206662306a36Sopenharmony_ci} 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci/** 206962306a36Sopenharmony_ci * ubifs_lpt_scan_nolock - scan the LPT. 207062306a36Sopenharmony_ci * @c: the UBIFS file-system description object 207162306a36Sopenharmony_ci * @start_lnum: LEB number from which to start scanning 207262306a36Sopenharmony_ci * @end_lnum: LEB number at which to stop scanning 207362306a36Sopenharmony_ci * @scan_cb: callback function called for each lprops 207462306a36Sopenharmony_ci * @data: data to be passed to the callback function 207562306a36Sopenharmony_ci * 207662306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 207762306a36Sopenharmony_ci */ 207862306a36Sopenharmony_ciint ubifs_lpt_scan_nolock(struct ubifs_info *c, int start_lnum, int end_lnum, 207962306a36Sopenharmony_ci ubifs_lpt_scan_callback scan_cb, void *data) 208062306a36Sopenharmony_ci{ 208162306a36Sopenharmony_ci int err = 0, i, h, iip, shft; 208262306a36Sopenharmony_ci struct ubifs_nnode *nnode; 208362306a36Sopenharmony_ci struct ubifs_pnode *pnode; 208462306a36Sopenharmony_ci struct lpt_scan_node *path; 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci if (start_lnum == -1) { 208762306a36Sopenharmony_ci start_lnum = end_lnum + 1; 208862306a36Sopenharmony_ci if (start_lnum >= c->leb_cnt) 208962306a36Sopenharmony_ci start_lnum = c->main_first; 209062306a36Sopenharmony_ci } 209162306a36Sopenharmony_ci 209262306a36Sopenharmony_ci ubifs_assert(c, start_lnum >= c->main_first && start_lnum < c->leb_cnt); 209362306a36Sopenharmony_ci ubifs_assert(c, end_lnum >= c->main_first && end_lnum < c->leb_cnt); 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_ci if (!c->nroot) { 209662306a36Sopenharmony_ci err = ubifs_read_nnode(c, NULL, 0); 209762306a36Sopenharmony_ci if (err) 209862306a36Sopenharmony_ci return err; 209962306a36Sopenharmony_ci } 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci path = kmalloc_array(c->lpt_hght + 1, sizeof(struct lpt_scan_node), 210262306a36Sopenharmony_ci GFP_NOFS); 210362306a36Sopenharmony_ci if (!path) 210462306a36Sopenharmony_ci return -ENOMEM; 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci path[0].ptr.nnode = c->nroot; 210762306a36Sopenharmony_ci path[0].in_tree = 1; 210862306a36Sopenharmony_ciagain: 210962306a36Sopenharmony_ci /* Descend to the pnode containing start_lnum */ 211062306a36Sopenharmony_ci nnode = c->nroot; 211162306a36Sopenharmony_ci i = start_lnum - c->main_first; 211262306a36Sopenharmony_ci shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT; 211362306a36Sopenharmony_ci for (h = 1; h < c->lpt_hght; h++) { 211462306a36Sopenharmony_ci iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); 211562306a36Sopenharmony_ci shft -= UBIFS_LPT_FANOUT_SHIFT; 211662306a36Sopenharmony_ci nnode = scan_get_nnode(c, path + h, nnode, iip); 211762306a36Sopenharmony_ci if (IS_ERR(nnode)) { 211862306a36Sopenharmony_ci err = PTR_ERR(nnode); 211962306a36Sopenharmony_ci goto out; 212062306a36Sopenharmony_ci } 212162306a36Sopenharmony_ci } 212262306a36Sopenharmony_ci iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); 212362306a36Sopenharmony_ci pnode = scan_get_pnode(c, path + h, nnode, iip); 212462306a36Sopenharmony_ci if (IS_ERR(pnode)) { 212562306a36Sopenharmony_ci err = PTR_ERR(pnode); 212662306a36Sopenharmony_ci goto out; 212762306a36Sopenharmony_ci } 212862306a36Sopenharmony_ci iip = (i & (UBIFS_LPT_FANOUT - 1)); 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci /* Loop for each lprops */ 213162306a36Sopenharmony_ci while (1) { 213262306a36Sopenharmony_ci struct ubifs_lprops *lprops = &pnode->lprops[iip]; 213362306a36Sopenharmony_ci int ret, lnum = lprops->lnum; 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci ret = scan_cb(c, lprops, path[h].in_tree, data); 213662306a36Sopenharmony_ci if (ret < 0) { 213762306a36Sopenharmony_ci err = ret; 213862306a36Sopenharmony_ci goto out; 213962306a36Sopenharmony_ci } 214062306a36Sopenharmony_ci if (ret & LPT_SCAN_ADD) { 214162306a36Sopenharmony_ci /* Add all the nodes in path to the tree in memory */ 214262306a36Sopenharmony_ci for (h = 1; h < c->lpt_hght; h++) { 214362306a36Sopenharmony_ci const size_t sz = sizeof(struct ubifs_nnode); 214462306a36Sopenharmony_ci struct ubifs_nnode *parent; 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci if (path[h].in_tree) 214762306a36Sopenharmony_ci continue; 214862306a36Sopenharmony_ci nnode = kmemdup(&path[h].nnode, sz, GFP_NOFS); 214962306a36Sopenharmony_ci if (!nnode) { 215062306a36Sopenharmony_ci err = -ENOMEM; 215162306a36Sopenharmony_ci goto out; 215262306a36Sopenharmony_ci } 215362306a36Sopenharmony_ci parent = nnode->parent; 215462306a36Sopenharmony_ci parent->nbranch[nnode->iip].nnode = nnode; 215562306a36Sopenharmony_ci path[h].ptr.nnode = nnode; 215662306a36Sopenharmony_ci path[h].in_tree = 1; 215762306a36Sopenharmony_ci path[h + 1].cnode.parent = nnode; 215862306a36Sopenharmony_ci } 215962306a36Sopenharmony_ci if (path[h].in_tree) 216062306a36Sopenharmony_ci ubifs_ensure_cat(c, lprops); 216162306a36Sopenharmony_ci else { 216262306a36Sopenharmony_ci const size_t sz = sizeof(struct ubifs_pnode); 216362306a36Sopenharmony_ci struct ubifs_nnode *parent; 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci pnode = kmemdup(&path[h].pnode, sz, GFP_NOFS); 216662306a36Sopenharmony_ci if (!pnode) { 216762306a36Sopenharmony_ci err = -ENOMEM; 216862306a36Sopenharmony_ci goto out; 216962306a36Sopenharmony_ci } 217062306a36Sopenharmony_ci parent = pnode->parent; 217162306a36Sopenharmony_ci parent->nbranch[pnode->iip].pnode = pnode; 217262306a36Sopenharmony_ci path[h].ptr.pnode = pnode; 217362306a36Sopenharmony_ci path[h].in_tree = 1; 217462306a36Sopenharmony_ci update_cats(c, pnode); 217562306a36Sopenharmony_ci c->pnodes_have += 1; 217662306a36Sopenharmony_ci } 217762306a36Sopenharmony_ci err = dbg_check_lpt_nodes(c, (struct ubifs_cnode *) 217862306a36Sopenharmony_ci c->nroot, 0, 0); 217962306a36Sopenharmony_ci if (err) 218062306a36Sopenharmony_ci goto out; 218162306a36Sopenharmony_ci err = dbg_check_cats(c); 218262306a36Sopenharmony_ci if (err) 218362306a36Sopenharmony_ci goto out; 218462306a36Sopenharmony_ci } 218562306a36Sopenharmony_ci if (ret & LPT_SCAN_STOP) { 218662306a36Sopenharmony_ci err = 0; 218762306a36Sopenharmony_ci break; 218862306a36Sopenharmony_ci } 218962306a36Sopenharmony_ci /* Get the next lprops */ 219062306a36Sopenharmony_ci if (lnum == end_lnum) { 219162306a36Sopenharmony_ci /* 219262306a36Sopenharmony_ci * We got to the end without finding what we were 219362306a36Sopenharmony_ci * looking for 219462306a36Sopenharmony_ci */ 219562306a36Sopenharmony_ci err = -ENOSPC; 219662306a36Sopenharmony_ci goto out; 219762306a36Sopenharmony_ci } 219862306a36Sopenharmony_ci if (lnum + 1 >= c->leb_cnt) { 219962306a36Sopenharmony_ci /* Wrap-around to the beginning */ 220062306a36Sopenharmony_ci start_lnum = c->main_first; 220162306a36Sopenharmony_ci goto again; 220262306a36Sopenharmony_ci } 220362306a36Sopenharmony_ci if (iip + 1 < UBIFS_LPT_FANOUT) { 220462306a36Sopenharmony_ci /* Next lprops is in the same pnode */ 220562306a36Sopenharmony_ci iip += 1; 220662306a36Sopenharmony_ci continue; 220762306a36Sopenharmony_ci } 220862306a36Sopenharmony_ci /* We need to get the next pnode. Go up until we can go right */ 220962306a36Sopenharmony_ci iip = pnode->iip; 221062306a36Sopenharmony_ci while (1) { 221162306a36Sopenharmony_ci h -= 1; 221262306a36Sopenharmony_ci ubifs_assert(c, h >= 0); 221362306a36Sopenharmony_ci nnode = path[h].ptr.nnode; 221462306a36Sopenharmony_ci if (iip + 1 < UBIFS_LPT_FANOUT) 221562306a36Sopenharmony_ci break; 221662306a36Sopenharmony_ci iip = nnode->iip; 221762306a36Sopenharmony_ci } 221862306a36Sopenharmony_ci /* Go right */ 221962306a36Sopenharmony_ci iip += 1; 222062306a36Sopenharmony_ci /* Descend to the pnode */ 222162306a36Sopenharmony_ci h += 1; 222262306a36Sopenharmony_ci for (; h < c->lpt_hght; h++) { 222362306a36Sopenharmony_ci nnode = scan_get_nnode(c, path + h, nnode, iip); 222462306a36Sopenharmony_ci if (IS_ERR(nnode)) { 222562306a36Sopenharmony_ci err = PTR_ERR(nnode); 222662306a36Sopenharmony_ci goto out; 222762306a36Sopenharmony_ci } 222862306a36Sopenharmony_ci iip = 0; 222962306a36Sopenharmony_ci } 223062306a36Sopenharmony_ci pnode = scan_get_pnode(c, path + h, nnode, iip); 223162306a36Sopenharmony_ci if (IS_ERR(pnode)) { 223262306a36Sopenharmony_ci err = PTR_ERR(pnode); 223362306a36Sopenharmony_ci goto out; 223462306a36Sopenharmony_ci } 223562306a36Sopenharmony_ci iip = 0; 223662306a36Sopenharmony_ci } 223762306a36Sopenharmony_ciout: 223862306a36Sopenharmony_ci kfree(path); 223962306a36Sopenharmony_ci return err; 224062306a36Sopenharmony_ci} 224162306a36Sopenharmony_ci 224262306a36Sopenharmony_ci/** 224362306a36Sopenharmony_ci * dbg_chk_pnode - check a pnode. 224462306a36Sopenharmony_ci * @c: the UBIFS file-system description object 224562306a36Sopenharmony_ci * @pnode: pnode to check 224662306a36Sopenharmony_ci * @col: pnode column 224762306a36Sopenharmony_ci * 224862306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 224962306a36Sopenharmony_ci */ 225062306a36Sopenharmony_cistatic int dbg_chk_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode, 225162306a36Sopenharmony_ci int col) 225262306a36Sopenharmony_ci{ 225362306a36Sopenharmony_ci int i; 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci if (pnode->num != col) { 225662306a36Sopenharmony_ci ubifs_err(c, "pnode num %d expected %d parent num %d iip %d", 225762306a36Sopenharmony_ci pnode->num, col, pnode->parent->num, pnode->iip); 225862306a36Sopenharmony_ci return -EINVAL; 225962306a36Sopenharmony_ci } 226062306a36Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 226162306a36Sopenharmony_ci struct ubifs_lprops *lp, *lprops = &pnode->lprops[i]; 226262306a36Sopenharmony_ci int lnum = (pnode->num << UBIFS_LPT_FANOUT_SHIFT) + i + 226362306a36Sopenharmony_ci c->main_first; 226462306a36Sopenharmony_ci int found, cat = lprops->flags & LPROPS_CAT_MASK; 226562306a36Sopenharmony_ci struct ubifs_lpt_heap *heap; 226662306a36Sopenharmony_ci struct list_head *list = NULL; 226762306a36Sopenharmony_ci 226862306a36Sopenharmony_ci if (lnum >= c->leb_cnt) 226962306a36Sopenharmony_ci continue; 227062306a36Sopenharmony_ci if (lprops->lnum != lnum) { 227162306a36Sopenharmony_ci ubifs_err(c, "bad LEB number %d expected %d", 227262306a36Sopenharmony_ci lprops->lnum, lnum); 227362306a36Sopenharmony_ci return -EINVAL; 227462306a36Sopenharmony_ci } 227562306a36Sopenharmony_ci if (lprops->flags & LPROPS_TAKEN) { 227662306a36Sopenharmony_ci if (cat != LPROPS_UNCAT) { 227762306a36Sopenharmony_ci ubifs_err(c, "LEB %d taken but not uncat %d", 227862306a36Sopenharmony_ci lprops->lnum, cat); 227962306a36Sopenharmony_ci return -EINVAL; 228062306a36Sopenharmony_ci } 228162306a36Sopenharmony_ci continue; 228262306a36Sopenharmony_ci } 228362306a36Sopenharmony_ci if (lprops->flags & LPROPS_INDEX) { 228462306a36Sopenharmony_ci switch (cat) { 228562306a36Sopenharmony_ci case LPROPS_UNCAT: 228662306a36Sopenharmony_ci case LPROPS_DIRTY_IDX: 228762306a36Sopenharmony_ci case LPROPS_FRDI_IDX: 228862306a36Sopenharmony_ci break; 228962306a36Sopenharmony_ci default: 229062306a36Sopenharmony_ci ubifs_err(c, "LEB %d index but cat %d", 229162306a36Sopenharmony_ci lprops->lnum, cat); 229262306a36Sopenharmony_ci return -EINVAL; 229362306a36Sopenharmony_ci } 229462306a36Sopenharmony_ci } else { 229562306a36Sopenharmony_ci switch (cat) { 229662306a36Sopenharmony_ci case LPROPS_UNCAT: 229762306a36Sopenharmony_ci case LPROPS_DIRTY: 229862306a36Sopenharmony_ci case LPROPS_FREE: 229962306a36Sopenharmony_ci case LPROPS_EMPTY: 230062306a36Sopenharmony_ci case LPROPS_FREEABLE: 230162306a36Sopenharmony_ci break; 230262306a36Sopenharmony_ci default: 230362306a36Sopenharmony_ci ubifs_err(c, "LEB %d not index but cat %d", 230462306a36Sopenharmony_ci lprops->lnum, cat); 230562306a36Sopenharmony_ci return -EINVAL; 230662306a36Sopenharmony_ci } 230762306a36Sopenharmony_ci } 230862306a36Sopenharmony_ci switch (cat) { 230962306a36Sopenharmony_ci case LPROPS_UNCAT: 231062306a36Sopenharmony_ci list = &c->uncat_list; 231162306a36Sopenharmony_ci break; 231262306a36Sopenharmony_ci case LPROPS_EMPTY: 231362306a36Sopenharmony_ci list = &c->empty_list; 231462306a36Sopenharmony_ci break; 231562306a36Sopenharmony_ci case LPROPS_FREEABLE: 231662306a36Sopenharmony_ci list = &c->freeable_list; 231762306a36Sopenharmony_ci break; 231862306a36Sopenharmony_ci case LPROPS_FRDI_IDX: 231962306a36Sopenharmony_ci list = &c->frdi_idx_list; 232062306a36Sopenharmony_ci break; 232162306a36Sopenharmony_ci } 232262306a36Sopenharmony_ci found = 0; 232362306a36Sopenharmony_ci switch (cat) { 232462306a36Sopenharmony_ci case LPROPS_DIRTY: 232562306a36Sopenharmony_ci case LPROPS_DIRTY_IDX: 232662306a36Sopenharmony_ci case LPROPS_FREE: 232762306a36Sopenharmony_ci heap = &c->lpt_heap[cat - 1]; 232862306a36Sopenharmony_ci if (lprops->hpos < heap->cnt && 232962306a36Sopenharmony_ci heap->arr[lprops->hpos] == lprops) 233062306a36Sopenharmony_ci found = 1; 233162306a36Sopenharmony_ci break; 233262306a36Sopenharmony_ci case LPROPS_UNCAT: 233362306a36Sopenharmony_ci case LPROPS_EMPTY: 233462306a36Sopenharmony_ci case LPROPS_FREEABLE: 233562306a36Sopenharmony_ci case LPROPS_FRDI_IDX: 233662306a36Sopenharmony_ci list_for_each_entry(lp, list, list) 233762306a36Sopenharmony_ci if (lprops == lp) { 233862306a36Sopenharmony_ci found = 1; 233962306a36Sopenharmony_ci break; 234062306a36Sopenharmony_ci } 234162306a36Sopenharmony_ci break; 234262306a36Sopenharmony_ci } 234362306a36Sopenharmony_ci if (!found) { 234462306a36Sopenharmony_ci ubifs_err(c, "LEB %d cat %d not found in cat heap/list", 234562306a36Sopenharmony_ci lprops->lnum, cat); 234662306a36Sopenharmony_ci return -EINVAL; 234762306a36Sopenharmony_ci } 234862306a36Sopenharmony_ci switch (cat) { 234962306a36Sopenharmony_ci case LPROPS_EMPTY: 235062306a36Sopenharmony_ci if (lprops->free != c->leb_size) { 235162306a36Sopenharmony_ci ubifs_err(c, "LEB %d cat %d free %d dirty %d", 235262306a36Sopenharmony_ci lprops->lnum, cat, lprops->free, 235362306a36Sopenharmony_ci lprops->dirty); 235462306a36Sopenharmony_ci return -EINVAL; 235562306a36Sopenharmony_ci } 235662306a36Sopenharmony_ci break; 235762306a36Sopenharmony_ci case LPROPS_FREEABLE: 235862306a36Sopenharmony_ci case LPROPS_FRDI_IDX: 235962306a36Sopenharmony_ci if (lprops->free + lprops->dirty != c->leb_size) { 236062306a36Sopenharmony_ci ubifs_err(c, "LEB %d cat %d free %d dirty %d", 236162306a36Sopenharmony_ci lprops->lnum, cat, lprops->free, 236262306a36Sopenharmony_ci lprops->dirty); 236362306a36Sopenharmony_ci return -EINVAL; 236462306a36Sopenharmony_ci } 236562306a36Sopenharmony_ci break; 236662306a36Sopenharmony_ci } 236762306a36Sopenharmony_ci } 236862306a36Sopenharmony_ci return 0; 236962306a36Sopenharmony_ci} 237062306a36Sopenharmony_ci 237162306a36Sopenharmony_ci/** 237262306a36Sopenharmony_ci * dbg_check_lpt_nodes - check nnodes and pnodes. 237362306a36Sopenharmony_ci * @c: the UBIFS file-system description object 237462306a36Sopenharmony_ci * @cnode: next cnode (nnode or pnode) to check 237562306a36Sopenharmony_ci * @row: row of cnode (root is zero) 237662306a36Sopenharmony_ci * @col: column of cnode (leftmost is zero) 237762306a36Sopenharmony_ci * 237862306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 237962306a36Sopenharmony_ci */ 238062306a36Sopenharmony_ciint dbg_check_lpt_nodes(struct ubifs_info *c, struct ubifs_cnode *cnode, 238162306a36Sopenharmony_ci int row, int col) 238262306a36Sopenharmony_ci{ 238362306a36Sopenharmony_ci struct ubifs_nnode *nnode, *nn; 238462306a36Sopenharmony_ci struct ubifs_cnode *cn; 238562306a36Sopenharmony_ci int num, iip = 0, err; 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_ci if (!dbg_is_chk_lprops(c)) 238862306a36Sopenharmony_ci return 0; 238962306a36Sopenharmony_ci 239062306a36Sopenharmony_ci while (cnode) { 239162306a36Sopenharmony_ci ubifs_assert(c, row >= 0); 239262306a36Sopenharmony_ci nnode = cnode->parent; 239362306a36Sopenharmony_ci if (cnode->level) { 239462306a36Sopenharmony_ci /* cnode is a nnode */ 239562306a36Sopenharmony_ci num = calc_nnode_num(row, col); 239662306a36Sopenharmony_ci if (cnode->num != num) { 239762306a36Sopenharmony_ci ubifs_err(c, "nnode num %d expected %d parent num %d iip %d", 239862306a36Sopenharmony_ci cnode->num, num, 239962306a36Sopenharmony_ci (nnode ? nnode->num : 0), cnode->iip); 240062306a36Sopenharmony_ci return -EINVAL; 240162306a36Sopenharmony_ci } 240262306a36Sopenharmony_ci nn = (struct ubifs_nnode *)cnode; 240362306a36Sopenharmony_ci while (iip < UBIFS_LPT_FANOUT) { 240462306a36Sopenharmony_ci cn = nn->nbranch[iip].cnode; 240562306a36Sopenharmony_ci if (cn) { 240662306a36Sopenharmony_ci /* Go down */ 240762306a36Sopenharmony_ci row += 1; 240862306a36Sopenharmony_ci col <<= UBIFS_LPT_FANOUT_SHIFT; 240962306a36Sopenharmony_ci col += iip; 241062306a36Sopenharmony_ci iip = 0; 241162306a36Sopenharmony_ci cnode = cn; 241262306a36Sopenharmony_ci break; 241362306a36Sopenharmony_ci } 241462306a36Sopenharmony_ci /* Go right */ 241562306a36Sopenharmony_ci iip += 1; 241662306a36Sopenharmony_ci } 241762306a36Sopenharmony_ci if (iip < UBIFS_LPT_FANOUT) 241862306a36Sopenharmony_ci continue; 241962306a36Sopenharmony_ci } else { 242062306a36Sopenharmony_ci struct ubifs_pnode *pnode; 242162306a36Sopenharmony_ci 242262306a36Sopenharmony_ci /* cnode is a pnode */ 242362306a36Sopenharmony_ci pnode = (struct ubifs_pnode *)cnode; 242462306a36Sopenharmony_ci err = dbg_chk_pnode(c, pnode, col); 242562306a36Sopenharmony_ci if (err) 242662306a36Sopenharmony_ci return err; 242762306a36Sopenharmony_ci } 242862306a36Sopenharmony_ci /* Go up and to the right */ 242962306a36Sopenharmony_ci row -= 1; 243062306a36Sopenharmony_ci col >>= UBIFS_LPT_FANOUT_SHIFT; 243162306a36Sopenharmony_ci iip = cnode->iip + 1; 243262306a36Sopenharmony_ci cnode = (struct ubifs_cnode *)nnode; 243362306a36Sopenharmony_ci } 243462306a36Sopenharmony_ci return 0; 243562306a36Sopenharmony_ci} 2436