18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * This file is part of UBIFS. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2006-2008 Nokia Corporation. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Authors: Adrian Hunter 88c2ecf20Sopenharmony_ci * Artem Bityutskiy (Битюцкий Артём) 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci/* 128c2ecf20Sopenharmony_ci * This file implements the LEB properties tree (LPT) area. The LPT area 138c2ecf20Sopenharmony_ci * contains the LEB properties tree, a table of LPT area eraseblocks (ltab), and 148c2ecf20Sopenharmony_ci * (for the "big" model) a table of saved LEB numbers (lsave). The LPT area sits 158c2ecf20Sopenharmony_ci * between the log and the orphan area. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * The LPT area is like a miniature self-contained file system. It is required 188c2ecf20Sopenharmony_ci * that it never runs out of space, is fast to access and update, and scales 198c2ecf20Sopenharmony_ci * logarithmically. The LEB properties tree is implemented as a wandering tree 208c2ecf20Sopenharmony_ci * much like the TNC, and the LPT area has its own garbage collection. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * The LPT has two slightly different forms called the "small model" and the 238c2ecf20Sopenharmony_ci * "big model". The small model is used when the entire LEB properties table 248c2ecf20Sopenharmony_ci * can be written into a single eraseblock. In that case, garbage collection 258c2ecf20Sopenharmony_ci * consists of just writing the whole table, which therefore makes all other 268c2ecf20Sopenharmony_ci * eraseblocks reusable. In the case of the big model, dirty eraseblocks are 278c2ecf20Sopenharmony_ci * selected for garbage collection, which consists of marking the clean nodes in 288c2ecf20Sopenharmony_ci * that LEB as dirty, and then only the dirty nodes are written out. Also, in 298c2ecf20Sopenharmony_ci * the case of the big model, a table of LEB numbers is saved so that the entire 308c2ecf20Sopenharmony_ci * LPT does not to be scanned looking for empty eraseblocks when UBIFS is first 318c2ecf20Sopenharmony_ci * mounted. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include "ubifs.h" 358c2ecf20Sopenharmony_ci#include <linux/crc16.h> 368c2ecf20Sopenharmony_ci#include <linux/math64.h> 378c2ecf20Sopenharmony_ci#include <linux/slab.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/** 408c2ecf20Sopenharmony_ci * do_calc_lpt_geom - calculate sizes for the LPT area. 418c2ecf20Sopenharmony_ci * @c: the UBIFS file-system description object 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * Calculate the sizes of LPT bit fields, nodes, and tree, based on the 448c2ecf20Sopenharmony_ci * properties of the flash and whether LPT is "big" (c->big_lpt). 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_cistatic void do_calc_lpt_geom(struct ubifs_info *c) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci int i, n, bits, per_leb_wastage, max_pnode_cnt; 498c2ecf20Sopenharmony_ci long long sz, tot_wastage; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci n = c->main_lebs + c->max_leb_cnt - c->leb_cnt; 528c2ecf20Sopenharmony_ci max_pnode_cnt = DIV_ROUND_UP(n, UBIFS_LPT_FANOUT); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci c->lpt_hght = 1; 558c2ecf20Sopenharmony_ci n = UBIFS_LPT_FANOUT; 568c2ecf20Sopenharmony_ci while (n < max_pnode_cnt) { 578c2ecf20Sopenharmony_ci c->lpt_hght += 1; 588c2ecf20Sopenharmony_ci n <<= UBIFS_LPT_FANOUT_SHIFT; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci c->pnode_cnt = DIV_ROUND_UP(c->main_lebs, UBIFS_LPT_FANOUT); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci n = DIV_ROUND_UP(c->pnode_cnt, UBIFS_LPT_FANOUT); 648c2ecf20Sopenharmony_ci c->nnode_cnt = n; 658c2ecf20Sopenharmony_ci for (i = 1; i < c->lpt_hght; i++) { 668c2ecf20Sopenharmony_ci n = DIV_ROUND_UP(n, UBIFS_LPT_FANOUT); 678c2ecf20Sopenharmony_ci c->nnode_cnt += n; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci c->space_bits = fls(c->leb_size) - 3; 718c2ecf20Sopenharmony_ci c->lpt_lnum_bits = fls(c->lpt_lebs); 728c2ecf20Sopenharmony_ci c->lpt_offs_bits = fls(c->leb_size - 1); 738c2ecf20Sopenharmony_ci c->lpt_spc_bits = fls(c->leb_size); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci n = DIV_ROUND_UP(c->max_leb_cnt, UBIFS_LPT_FANOUT); 768c2ecf20Sopenharmony_ci c->pcnt_bits = fls(n - 1); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci c->lnum_bits = fls(c->max_leb_cnt - 1); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + 818c2ecf20Sopenharmony_ci (c->big_lpt ? c->pcnt_bits : 0) + 828c2ecf20Sopenharmony_ci (c->space_bits * 2 + 1) * UBIFS_LPT_FANOUT; 838c2ecf20Sopenharmony_ci c->pnode_sz = (bits + 7) / 8; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + 868c2ecf20Sopenharmony_ci (c->big_lpt ? c->pcnt_bits : 0) + 878c2ecf20Sopenharmony_ci (c->lpt_lnum_bits + c->lpt_offs_bits) * UBIFS_LPT_FANOUT; 888c2ecf20Sopenharmony_ci c->nnode_sz = (bits + 7) / 8; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + 918c2ecf20Sopenharmony_ci c->lpt_lebs * c->lpt_spc_bits * 2; 928c2ecf20Sopenharmony_ci c->ltab_sz = (bits + 7) / 8; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + 958c2ecf20Sopenharmony_ci c->lnum_bits * c->lsave_cnt; 968c2ecf20Sopenharmony_ci c->lsave_sz = (bits + 7) / 8; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* Calculate the minimum LPT size */ 998c2ecf20Sopenharmony_ci c->lpt_sz = (long long)c->pnode_cnt * c->pnode_sz; 1008c2ecf20Sopenharmony_ci c->lpt_sz += (long long)c->nnode_cnt * c->nnode_sz; 1018c2ecf20Sopenharmony_ci c->lpt_sz += c->ltab_sz; 1028c2ecf20Sopenharmony_ci if (c->big_lpt) 1038c2ecf20Sopenharmony_ci c->lpt_sz += c->lsave_sz; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* Add wastage */ 1068c2ecf20Sopenharmony_ci sz = c->lpt_sz; 1078c2ecf20Sopenharmony_ci per_leb_wastage = max_t(int, c->pnode_sz, c->nnode_sz); 1088c2ecf20Sopenharmony_ci sz += per_leb_wastage; 1098c2ecf20Sopenharmony_ci tot_wastage = per_leb_wastage; 1108c2ecf20Sopenharmony_ci while (sz > c->leb_size) { 1118c2ecf20Sopenharmony_ci sz += per_leb_wastage; 1128c2ecf20Sopenharmony_ci sz -= c->leb_size; 1138c2ecf20Sopenharmony_ci tot_wastage += per_leb_wastage; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci tot_wastage += ALIGN(sz, c->min_io_size) - sz; 1168c2ecf20Sopenharmony_ci c->lpt_sz += tot_wastage; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/** 1208c2ecf20Sopenharmony_ci * ubifs_calc_lpt_geom - calculate and check sizes for the LPT area. 1218c2ecf20Sopenharmony_ci * @c: the UBIFS file-system description object 1228c2ecf20Sopenharmony_ci * 1238c2ecf20Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_ciint ubifs_calc_lpt_geom(struct ubifs_info *c) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci int lebs_needed; 1288c2ecf20Sopenharmony_ci long long sz; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci do_calc_lpt_geom(c); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* Verify that lpt_lebs is big enough */ 1338c2ecf20Sopenharmony_ci sz = c->lpt_sz * 2; /* Must have at least 2 times the size */ 1348c2ecf20Sopenharmony_ci lebs_needed = div_u64(sz + c->leb_size - 1, c->leb_size); 1358c2ecf20Sopenharmony_ci if (lebs_needed > c->lpt_lebs) { 1368c2ecf20Sopenharmony_ci ubifs_err(c, "too few LPT LEBs"); 1378c2ecf20Sopenharmony_ci return -EINVAL; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* Verify that ltab fits in a single LEB (since ltab is a single node */ 1418c2ecf20Sopenharmony_ci if (c->ltab_sz > c->leb_size) { 1428c2ecf20Sopenharmony_ci ubifs_err(c, "LPT ltab too big"); 1438c2ecf20Sopenharmony_ci return -EINVAL; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci c->check_lpt_free = c->big_lpt; 1478c2ecf20Sopenharmony_ci return 0; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci/** 1518c2ecf20Sopenharmony_ci * calc_dflt_lpt_geom - calculate default LPT geometry. 1528c2ecf20Sopenharmony_ci * @c: the UBIFS file-system description object 1538c2ecf20Sopenharmony_ci * @main_lebs: number of main area LEBs is passed and returned here 1548c2ecf20Sopenharmony_ci * @big_lpt: whether the LPT area is "big" is returned here 1558c2ecf20Sopenharmony_ci * 1568c2ecf20Sopenharmony_ci * The size of the LPT area depends on parameters that themselves are dependent 1578c2ecf20Sopenharmony_ci * on the size of the LPT area. This function, successively recalculates the LPT 1588c2ecf20Sopenharmony_ci * area geometry until the parameters and resultant geometry are consistent. 1598c2ecf20Sopenharmony_ci * 1608c2ecf20Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 1618c2ecf20Sopenharmony_ci */ 1628c2ecf20Sopenharmony_cistatic int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, 1638c2ecf20Sopenharmony_ci int *big_lpt) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci int i, lebs_needed; 1668c2ecf20Sopenharmony_ci long long sz; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* Start by assuming the minimum number of LPT LEBs */ 1698c2ecf20Sopenharmony_ci c->lpt_lebs = UBIFS_MIN_LPT_LEBS; 1708c2ecf20Sopenharmony_ci c->main_lebs = *main_lebs - c->lpt_lebs; 1718c2ecf20Sopenharmony_ci if (c->main_lebs <= 0) 1728c2ecf20Sopenharmony_ci return -EINVAL; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* And assume we will use the small LPT model */ 1758c2ecf20Sopenharmony_ci c->big_lpt = 0; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* 1788c2ecf20Sopenharmony_ci * Calculate the geometry based on assumptions above and then see if it 1798c2ecf20Sopenharmony_ci * makes sense 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_ci do_calc_lpt_geom(c); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* Small LPT model must have lpt_sz < leb_size */ 1848c2ecf20Sopenharmony_ci if (c->lpt_sz > c->leb_size) { 1858c2ecf20Sopenharmony_ci /* Nope, so try again using big LPT model */ 1868c2ecf20Sopenharmony_ci c->big_lpt = 1; 1878c2ecf20Sopenharmony_ci do_calc_lpt_geom(c); 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* Now check there are enough LPT LEBs */ 1918c2ecf20Sopenharmony_ci for (i = 0; i < 64 ; i++) { 1928c2ecf20Sopenharmony_ci sz = c->lpt_sz * 4; /* Allow 4 times the size */ 1938c2ecf20Sopenharmony_ci lebs_needed = div_u64(sz + c->leb_size - 1, c->leb_size); 1948c2ecf20Sopenharmony_ci if (lebs_needed > c->lpt_lebs) { 1958c2ecf20Sopenharmony_ci /* Not enough LPT LEBs so try again with more */ 1968c2ecf20Sopenharmony_ci c->lpt_lebs = lebs_needed; 1978c2ecf20Sopenharmony_ci c->main_lebs = *main_lebs - c->lpt_lebs; 1988c2ecf20Sopenharmony_ci if (c->main_lebs <= 0) 1998c2ecf20Sopenharmony_ci return -EINVAL; 2008c2ecf20Sopenharmony_ci do_calc_lpt_geom(c); 2018c2ecf20Sopenharmony_ci continue; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci if (c->ltab_sz > c->leb_size) { 2048c2ecf20Sopenharmony_ci ubifs_err(c, "LPT ltab too big"); 2058c2ecf20Sopenharmony_ci return -EINVAL; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci *main_lebs = c->main_lebs; 2088c2ecf20Sopenharmony_ci *big_lpt = c->big_lpt; 2098c2ecf20Sopenharmony_ci return 0; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci return -EINVAL; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci/** 2158c2ecf20Sopenharmony_ci * pack_bits - pack bit fields end-to-end. 2168c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 2178c2ecf20Sopenharmony_ci * @addr: address at which to pack (passed and next address returned) 2188c2ecf20Sopenharmony_ci * @pos: bit position at which to pack (passed and next position returned) 2198c2ecf20Sopenharmony_ci * @val: value to pack 2208c2ecf20Sopenharmony_ci * @nrbits: number of bits of value to pack (1-32) 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_cistatic void pack_bits(const struct ubifs_info *c, uint8_t **addr, int *pos, uint32_t val, int nrbits) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci uint8_t *p = *addr; 2258c2ecf20Sopenharmony_ci int b = *pos; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci ubifs_assert(c, nrbits > 0); 2288c2ecf20Sopenharmony_ci ubifs_assert(c, nrbits <= 32); 2298c2ecf20Sopenharmony_ci ubifs_assert(c, *pos >= 0); 2308c2ecf20Sopenharmony_ci ubifs_assert(c, *pos < 8); 2318c2ecf20Sopenharmony_ci ubifs_assert(c, (val >> nrbits) == 0 || nrbits == 32); 2328c2ecf20Sopenharmony_ci if (b) { 2338c2ecf20Sopenharmony_ci *p |= ((uint8_t)val) << b; 2348c2ecf20Sopenharmony_ci nrbits += b; 2358c2ecf20Sopenharmony_ci if (nrbits > 8) { 2368c2ecf20Sopenharmony_ci *++p = (uint8_t)(val >>= (8 - b)); 2378c2ecf20Sopenharmony_ci if (nrbits > 16) { 2388c2ecf20Sopenharmony_ci *++p = (uint8_t)(val >>= 8); 2398c2ecf20Sopenharmony_ci if (nrbits > 24) { 2408c2ecf20Sopenharmony_ci *++p = (uint8_t)(val >>= 8); 2418c2ecf20Sopenharmony_ci if (nrbits > 32) 2428c2ecf20Sopenharmony_ci *++p = (uint8_t)(val >>= 8); 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci } else { 2478c2ecf20Sopenharmony_ci *p = (uint8_t)val; 2488c2ecf20Sopenharmony_ci if (nrbits > 8) { 2498c2ecf20Sopenharmony_ci *++p = (uint8_t)(val >>= 8); 2508c2ecf20Sopenharmony_ci if (nrbits > 16) { 2518c2ecf20Sopenharmony_ci *++p = (uint8_t)(val >>= 8); 2528c2ecf20Sopenharmony_ci if (nrbits > 24) 2538c2ecf20Sopenharmony_ci *++p = (uint8_t)(val >>= 8); 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci b = nrbits & 7; 2588c2ecf20Sopenharmony_ci if (b == 0) 2598c2ecf20Sopenharmony_ci p++; 2608c2ecf20Sopenharmony_ci *addr = p; 2618c2ecf20Sopenharmony_ci *pos = b; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci/** 2658c2ecf20Sopenharmony_ci * ubifs_unpack_bits - unpack bit fields. 2668c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 2678c2ecf20Sopenharmony_ci * @addr: address at which to unpack (passed and next address returned) 2688c2ecf20Sopenharmony_ci * @pos: bit position at which to unpack (passed and next position returned) 2698c2ecf20Sopenharmony_ci * @nrbits: number of bits of value to unpack (1-32) 2708c2ecf20Sopenharmony_ci * 2718c2ecf20Sopenharmony_ci * This functions returns the value unpacked. 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_ciuint32_t ubifs_unpack_bits(const struct ubifs_info *c, uint8_t **addr, int *pos, int nrbits) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci const int k = 32 - nrbits; 2768c2ecf20Sopenharmony_ci uint8_t *p = *addr; 2778c2ecf20Sopenharmony_ci int b = *pos; 2788c2ecf20Sopenharmony_ci uint32_t val; 2798c2ecf20Sopenharmony_ci const int bytes = (nrbits + b + 7) >> 3; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci ubifs_assert(c, nrbits > 0); 2828c2ecf20Sopenharmony_ci ubifs_assert(c, nrbits <= 32); 2838c2ecf20Sopenharmony_ci ubifs_assert(c, *pos >= 0); 2848c2ecf20Sopenharmony_ci ubifs_assert(c, *pos < 8); 2858c2ecf20Sopenharmony_ci if (b) { 2868c2ecf20Sopenharmony_ci switch (bytes) { 2878c2ecf20Sopenharmony_ci case 2: 2888c2ecf20Sopenharmony_ci val = p[1]; 2898c2ecf20Sopenharmony_ci break; 2908c2ecf20Sopenharmony_ci case 3: 2918c2ecf20Sopenharmony_ci val = p[1] | ((uint32_t)p[2] << 8); 2928c2ecf20Sopenharmony_ci break; 2938c2ecf20Sopenharmony_ci case 4: 2948c2ecf20Sopenharmony_ci val = p[1] | ((uint32_t)p[2] << 8) | 2958c2ecf20Sopenharmony_ci ((uint32_t)p[3] << 16); 2968c2ecf20Sopenharmony_ci break; 2978c2ecf20Sopenharmony_ci case 5: 2988c2ecf20Sopenharmony_ci val = p[1] | ((uint32_t)p[2] << 8) | 2998c2ecf20Sopenharmony_ci ((uint32_t)p[3] << 16) | 3008c2ecf20Sopenharmony_ci ((uint32_t)p[4] << 24); 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci val <<= (8 - b); 3038c2ecf20Sopenharmony_ci val |= *p >> b; 3048c2ecf20Sopenharmony_ci nrbits += b; 3058c2ecf20Sopenharmony_ci } else { 3068c2ecf20Sopenharmony_ci switch (bytes) { 3078c2ecf20Sopenharmony_ci case 1: 3088c2ecf20Sopenharmony_ci val = p[0]; 3098c2ecf20Sopenharmony_ci break; 3108c2ecf20Sopenharmony_ci case 2: 3118c2ecf20Sopenharmony_ci val = p[0] | ((uint32_t)p[1] << 8); 3128c2ecf20Sopenharmony_ci break; 3138c2ecf20Sopenharmony_ci case 3: 3148c2ecf20Sopenharmony_ci val = p[0] | ((uint32_t)p[1] << 8) | 3158c2ecf20Sopenharmony_ci ((uint32_t)p[2] << 16); 3168c2ecf20Sopenharmony_ci break; 3178c2ecf20Sopenharmony_ci case 4: 3188c2ecf20Sopenharmony_ci val = p[0] | ((uint32_t)p[1] << 8) | 3198c2ecf20Sopenharmony_ci ((uint32_t)p[2] << 16) | 3208c2ecf20Sopenharmony_ci ((uint32_t)p[3] << 24); 3218c2ecf20Sopenharmony_ci break; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci val <<= k; 3258c2ecf20Sopenharmony_ci val >>= k; 3268c2ecf20Sopenharmony_ci b = nrbits & 7; 3278c2ecf20Sopenharmony_ci p += nrbits >> 3; 3288c2ecf20Sopenharmony_ci *addr = p; 3298c2ecf20Sopenharmony_ci *pos = b; 3308c2ecf20Sopenharmony_ci ubifs_assert(c, (val >> nrbits) == 0 || nrbits - b == 32); 3318c2ecf20Sopenharmony_ci return val; 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci/** 3358c2ecf20Sopenharmony_ci * ubifs_pack_pnode - pack all the bit fields of a pnode. 3368c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 3378c2ecf20Sopenharmony_ci * @buf: buffer into which to pack 3388c2ecf20Sopenharmony_ci * @pnode: pnode to pack 3398c2ecf20Sopenharmony_ci */ 3408c2ecf20Sopenharmony_civoid ubifs_pack_pnode(struct ubifs_info *c, void *buf, 3418c2ecf20Sopenharmony_ci struct ubifs_pnode *pnode) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; 3448c2ecf20Sopenharmony_ci int i, pos = 0; 3458c2ecf20Sopenharmony_ci uint16_t crc; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci pack_bits(c, &addr, &pos, UBIFS_LPT_PNODE, UBIFS_LPT_TYPE_BITS); 3488c2ecf20Sopenharmony_ci if (c->big_lpt) 3498c2ecf20Sopenharmony_ci pack_bits(c, &addr, &pos, pnode->num, c->pcnt_bits); 3508c2ecf20Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 3518c2ecf20Sopenharmony_ci pack_bits(c, &addr, &pos, pnode->lprops[i].free >> 3, 3528c2ecf20Sopenharmony_ci c->space_bits); 3538c2ecf20Sopenharmony_ci pack_bits(c, &addr, &pos, pnode->lprops[i].dirty >> 3, 3548c2ecf20Sopenharmony_ci c->space_bits); 3558c2ecf20Sopenharmony_ci if (pnode->lprops[i].flags & LPROPS_INDEX) 3568c2ecf20Sopenharmony_ci pack_bits(c, &addr, &pos, 1, 1); 3578c2ecf20Sopenharmony_ci else 3588c2ecf20Sopenharmony_ci pack_bits(c, &addr, &pos, 0, 1); 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, 3618c2ecf20Sopenharmony_ci c->pnode_sz - UBIFS_LPT_CRC_BYTES); 3628c2ecf20Sopenharmony_ci addr = buf; 3638c2ecf20Sopenharmony_ci pos = 0; 3648c2ecf20Sopenharmony_ci pack_bits(c, &addr, &pos, crc, UBIFS_LPT_CRC_BITS); 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci/** 3688c2ecf20Sopenharmony_ci * ubifs_pack_nnode - pack all the bit fields of a nnode. 3698c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 3708c2ecf20Sopenharmony_ci * @buf: buffer into which to pack 3718c2ecf20Sopenharmony_ci * @nnode: nnode to pack 3728c2ecf20Sopenharmony_ci */ 3738c2ecf20Sopenharmony_civoid ubifs_pack_nnode(struct ubifs_info *c, void *buf, 3748c2ecf20Sopenharmony_ci struct ubifs_nnode *nnode) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; 3778c2ecf20Sopenharmony_ci int i, pos = 0; 3788c2ecf20Sopenharmony_ci uint16_t crc; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci pack_bits(c, &addr, &pos, UBIFS_LPT_NNODE, UBIFS_LPT_TYPE_BITS); 3818c2ecf20Sopenharmony_ci if (c->big_lpt) 3828c2ecf20Sopenharmony_ci pack_bits(c, &addr, &pos, nnode->num, c->pcnt_bits); 3838c2ecf20Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 3848c2ecf20Sopenharmony_ci int lnum = nnode->nbranch[i].lnum; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (lnum == 0) 3878c2ecf20Sopenharmony_ci lnum = c->lpt_last + 1; 3888c2ecf20Sopenharmony_ci pack_bits(c, &addr, &pos, lnum - c->lpt_first, c->lpt_lnum_bits); 3898c2ecf20Sopenharmony_ci pack_bits(c, &addr, &pos, nnode->nbranch[i].offs, 3908c2ecf20Sopenharmony_ci c->lpt_offs_bits); 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, 3938c2ecf20Sopenharmony_ci c->nnode_sz - UBIFS_LPT_CRC_BYTES); 3948c2ecf20Sopenharmony_ci addr = buf; 3958c2ecf20Sopenharmony_ci pos = 0; 3968c2ecf20Sopenharmony_ci pack_bits(c, &addr, &pos, crc, UBIFS_LPT_CRC_BITS); 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci/** 4008c2ecf20Sopenharmony_ci * ubifs_pack_ltab - pack the LPT's own lprops table. 4018c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 4028c2ecf20Sopenharmony_ci * @buf: buffer into which to pack 4038c2ecf20Sopenharmony_ci * @ltab: LPT's own lprops table to pack 4048c2ecf20Sopenharmony_ci */ 4058c2ecf20Sopenharmony_civoid ubifs_pack_ltab(struct ubifs_info *c, void *buf, 4068c2ecf20Sopenharmony_ci struct ubifs_lpt_lprops *ltab) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; 4098c2ecf20Sopenharmony_ci int i, pos = 0; 4108c2ecf20Sopenharmony_ci uint16_t crc; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci pack_bits(c, &addr, &pos, UBIFS_LPT_LTAB, UBIFS_LPT_TYPE_BITS); 4138c2ecf20Sopenharmony_ci for (i = 0; i < c->lpt_lebs; i++) { 4148c2ecf20Sopenharmony_ci pack_bits(c, &addr, &pos, ltab[i].free, c->lpt_spc_bits); 4158c2ecf20Sopenharmony_ci pack_bits(c, &addr, &pos, ltab[i].dirty, c->lpt_spc_bits); 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, 4188c2ecf20Sopenharmony_ci c->ltab_sz - UBIFS_LPT_CRC_BYTES); 4198c2ecf20Sopenharmony_ci addr = buf; 4208c2ecf20Sopenharmony_ci pos = 0; 4218c2ecf20Sopenharmony_ci pack_bits(c, &addr, &pos, crc, UBIFS_LPT_CRC_BITS); 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci/** 4258c2ecf20Sopenharmony_ci * ubifs_pack_lsave - pack the LPT's save table. 4268c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 4278c2ecf20Sopenharmony_ci * @buf: buffer into which to pack 4288c2ecf20Sopenharmony_ci * @lsave: LPT's save table to pack 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_civoid ubifs_pack_lsave(struct ubifs_info *c, void *buf, int *lsave) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; 4338c2ecf20Sopenharmony_ci int i, pos = 0; 4348c2ecf20Sopenharmony_ci uint16_t crc; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci pack_bits(c, &addr, &pos, UBIFS_LPT_LSAVE, UBIFS_LPT_TYPE_BITS); 4378c2ecf20Sopenharmony_ci for (i = 0; i < c->lsave_cnt; i++) 4388c2ecf20Sopenharmony_ci pack_bits(c, &addr, &pos, lsave[i], c->lnum_bits); 4398c2ecf20Sopenharmony_ci crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, 4408c2ecf20Sopenharmony_ci c->lsave_sz - UBIFS_LPT_CRC_BYTES); 4418c2ecf20Sopenharmony_ci addr = buf; 4428c2ecf20Sopenharmony_ci pos = 0; 4438c2ecf20Sopenharmony_ci pack_bits(c, &addr, &pos, crc, UBIFS_LPT_CRC_BITS); 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci/** 4478c2ecf20Sopenharmony_ci * ubifs_add_lpt_dirt - add dirty space to LPT LEB properties. 4488c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 4498c2ecf20Sopenharmony_ci * @lnum: LEB number to which to add dirty space 4508c2ecf20Sopenharmony_ci * @dirty: amount of dirty space to add 4518c2ecf20Sopenharmony_ci */ 4528c2ecf20Sopenharmony_civoid ubifs_add_lpt_dirt(struct ubifs_info *c, int lnum, int dirty) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci if (!dirty || !lnum) 4558c2ecf20Sopenharmony_ci return; 4568c2ecf20Sopenharmony_ci dbg_lp("LEB %d add %d to %d", 4578c2ecf20Sopenharmony_ci lnum, dirty, c->ltab[lnum - c->lpt_first].dirty); 4588c2ecf20Sopenharmony_ci ubifs_assert(c, lnum >= c->lpt_first && lnum <= c->lpt_last); 4598c2ecf20Sopenharmony_ci c->ltab[lnum - c->lpt_first].dirty += dirty; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci/** 4638c2ecf20Sopenharmony_ci * set_ltab - set LPT LEB properties. 4648c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 4658c2ecf20Sopenharmony_ci * @lnum: LEB number 4668c2ecf20Sopenharmony_ci * @free: amount of free space 4678c2ecf20Sopenharmony_ci * @dirty: amount of dirty space 4688c2ecf20Sopenharmony_ci */ 4698c2ecf20Sopenharmony_cistatic void set_ltab(struct ubifs_info *c, int lnum, int free, int dirty) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci dbg_lp("LEB %d free %d dirty %d to %d %d", 4728c2ecf20Sopenharmony_ci lnum, c->ltab[lnum - c->lpt_first].free, 4738c2ecf20Sopenharmony_ci c->ltab[lnum - c->lpt_first].dirty, free, dirty); 4748c2ecf20Sopenharmony_ci ubifs_assert(c, lnum >= c->lpt_first && lnum <= c->lpt_last); 4758c2ecf20Sopenharmony_ci c->ltab[lnum - c->lpt_first].free = free; 4768c2ecf20Sopenharmony_ci c->ltab[lnum - c->lpt_first].dirty = dirty; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci/** 4808c2ecf20Sopenharmony_ci * ubifs_add_nnode_dirt - add dirty space to LPT LEB properties. 4818c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 4828c2ecf20Sopenharmony_ci * @nnode: nnode for which to add dirt 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_civoid ubifs_add_nnode_dirt(struct ubifs_info *c, struct ubifs_nnode *nnode) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci struct ubifs_nnode *np = nnode->parent; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci if (np) 4898c2ecf20Sopenharmony_ci ubifs_add_lpt_dirt(c, np->nbranch[nnode->iip].lnum, 4908c2ecf20Sopenharmony_ci c->nnode_sz); 4918c2ecf20Sopenharmony_ci else { 4928c2ecf20Sopenharmony_ci ubifs_add_lpt_dirt(c, c->lpt_lnum, c->nnode_sz); 4938c2ecf20Sopenharmony_ci if (!(c->lpt_drty_flgs & LTAB_DIRTY)) { 4948c2ecf20Sopenharmony_ci c->lpt_drty_flgs |= LTAB_DIRTY; 4958c2ecf20Sopenharmony_ci ubifs_add_lpt_dirt(c, c->ltab_lnum, c->ltab_sz); 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci/** 5018c2ecf20Sopenharmony_ci * add_pnode_dirt - add dirty space to LPT LEB properties. 5028c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 5038c2ecf20Sopenharmony_ci * @pnode: pnode for which to add dirt 5048c2ecf20Sopenharmony_ci */ 5058c2ecf20Sopenharmony_cistatic void add_pnode_dirt(struct ubifs_info *c, struct ubifs_pnode *pnode) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci ubifs_add_lpt_dirt(c, pnode->parent->nbranch[pnode->iip].lnum, 5088c2ecf20Sopenharmony_ci c->pnode_sz); 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci/** 5128c2ecf20Sopenharmony_ci * calc_nnode_num - calculate nnode number. 5138c2ecf20Sopenharmony_ci * @row: the row in the tree (root is zero) 5148c2ecf20Sopenharmony_ci * @col: the column in the row (leftmost is zero) 5158c2ecf20Sopenharmony_ci * 5168c2ecf20Sopenharmony_ci * The nnode number is a number that uniquely identifies a nnode and can be used 5178c2ecf20Sopenharmony_ci * easily to traverse the tree from the root to that nnode. 5188c2ecf20Sopenharmony_ci * 5198c2ecf20Sopenharmony_ci * This function calculates and returns the nnode number for the nnode at @row 5208c2ecf20Sopenharmony_ci * and @col. 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_cistatic int calc_nnode_num(int row, int col) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci int num, bits; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci num = 1; 5278c2ecf20Sopenharmony_ci while (row--) { 5288c2ecf20Sopenharmony_ci bits = (col & (UBIFS_LPT_FANOUT - 1)); 5298c2ecf20Sopenharmony_ci col >>= UBIFS_LPT_FANOUT_SHIFT; 5308c2ecf20Sopenharmony_ci num <<= UBIFS_LPT_FANOUT_SHIFT; 5318c2ecf20Sopenharmony_ci num |= bits; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci return num; 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci/** 5378c2ecf20Sopenharmony_ci * calc_nnode_num_from_parent - calculate nnode number. 5388c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 5398c2ecf20Sopenharmony_ci * @parent: parent nnode 5408c2ecf20Sopenharmony_ci * @iip: index in parent 5418c2ecf20Sopenharmony_ci * 5428c2ecf20Sopenharmony_ci * The nnode number is a number that uniquely identifies a nnode and can be used 5438c2ecf20Sopenharmony_ci * easily to traverse the tree from the root to that nnode. 5448c2ecf20Sopenharmony_ci * 5458c2ecf20Sopenharmony_ci * This function calculates and returns the nnode number based on the parent's 5468c2ecf20Sopenharmony_ci * nnode number and the index in parent. 5478c2ecf20Sopenharmony_ci */ 5488c2ecf20Sopenharmony_cistatic int calc_nnode_num_from_parent(const struct ubifs_info *c, 5498c2ecf20Sopenharmony_ci struct ubifs_nnode *parent, int iip) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci int num, shft; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (!parent) 5548c2ecf20Sopenharmony_ci return 1; 5558c2ecf20Sopenharmony_ci shft = (c->lpt_hght - parent->level) * UBIFS_LPT_FANOUT_SHIFT; 5568c2ecf20Sopenharmony_ci num = parent->num ^ (1 << shft); 5578c2ecf20Sopenharmony_ci num |= (UBIFS_LPT_FANOUT + iip) << shft; 5588c2ecf20Sopenharmony_ci return num; 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci/** 5628c2ecf20Sopenharmony_ci * calc_pnode_num_from_parent - calculate pnode number. 5638c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 5648c2ecf20Sopenharmony_ci * @parent: parent nnode 5658c2ecf20Sopenharmony_ci * @iip: index in parent 5668c2ecf20Sopenharmony_ci * 5678c2ecf20Sopenharmony_ci * The pnode number is a number that uniquely identifies a pnode and can be used 5688c2ecf20Sopenharmony_ci * easily to traverse the tree from the root to that pnode. 5698c2ecf20Sopenharmony_ci * 5708c2ecf20Sopenharmony_ci * This function calculates and returns the pnode number based on the parent's 5718c2ecf20Sopenharmony_ci * nnode number and the index in parent. 5728c2ecf20Sopenharmony_ci */ 5738c2ecf20Sopenharmony_cistatic int calc_pnode_num_from_parent(const struct ubifs_info *c, 5748c2ecf20Sopenharmony_ci struct ubifs_nnode *parent, int iip) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci int i, n = c->lpt_hght - 1, pnum = parent->num, num = 0; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) { 5798c2ecf20Sopenharmony_ci num <<= UBIFS_LPT_FANOUT_SHIFT; 5808c2ecf20Sopenharmony_ci num |= pnum & (UBIFS_LPT_FANOUT - 1); 5818c2ecf20Sopenharmony_ci pnum >>= UBIFS_LPT_FANOUT_SHIFT; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci num <<= UBIFS_LPT_FANOUT_SHIFT; 5848c2ecf20Sopenharmony_ci num |= iip; 5858c2ecf20Sopenharmony_ci return num; 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci/** 5898c2ecf20Sopenharmony_ci * ubifs_create_dflt_lpt - create default LPT. 5908c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 5918c2ecf20Sopenharmony_ci * @main_lebs: number of main area LEBs is passed and returned here 5928c2ecf20Sopenharmony_ci * @lpt_first: LEB number of first LPT LEB 5938c2ecf20Sopenharmony_ci * @lpt_lebs: number of LEBs for LPT is passed and returned here 5948c2ecf20Sopenharmony_ci * @big_lpt: use big LPT model is passed and returned here 5958c2ecf20Sopenharmony_ci * @hash: hash of the LPT is returned here 5968c2ecf20Sopenharmony_ci * 5978c2ecf20Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 5988c2ecf20Sopenharmony_ci */ 5998c2ecf20Sopenharmony_ciint ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first, 6008c2ecf20Sopenharmony_ci int *lpt_lebs, int *big_lpt, u8 *hash) 6018c2ecf20Sopenharmony_ci{ 6028c2ecf20Sopenharmony_ci int lnum, err = 0, node_sz, iopos, i, j, cnt, len, alen, row; 6038c2ecf20Sopenharmony_ci int blnum, boffs, bsz, bcnt; 6048c2ecf20Sopenharmony_ci struct ubifs_pnode *pnode = NULL; 6058c2ecf20Sopenharmony_ci struct ubifs_nnode *nnode = NULL; 6068c2ecf20Sopenharmony_ci void *buf = NULL, *p; 6078c2ecf20Sopenharmony_ci struct ubifs_lpt_lprops *ltab = NULL; 6088c2ecf20Sopenharmony_ci int *lsave = NULL; 6098c2ecf20Sopenharmony_ci struct shash_desc *desc; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci err = calc_dflt_lpt_geom(c, main_lebs, big_lpt); 6128c2ecf20Sopenharmony_ci if (err) 6138c2ecf20Sopenharmony_ci return err; 6148c2ecf20Sopenharmony_ci *lpt_lebs = c->lpt_lebs; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci /* Needed by 'ubifs_pack_nnode()' and 'set_ltab()' */ 6178c2ecf20Sopenharmony_ci c->lpt_first = lpt_first; 6188c2ecf20Sopenharmony_ci /* Needed by 'set_ltab()' */ 6198c2ecf20Sopenharmony_ci c->lpt_last = lpt_first + c->lpt_lebs - 1; 6208c2ecf20Sopenharmony_ci /* Needed by 'ubifs_pack_lsave()' */ 6218c2ecf20Sopenharmony_ci c->main_first = c->leb_cnt - *main_lebs; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci desc = ubifs_hash_get_desc(c); 6248c2ecf20Sopenharmony_ci if (IS_ERR(desc)) 6258c2ecf20Sopenharmony_ci return PTR_ERR(desc); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci lsave = kmalloc_array(c->lsave_cnt, sizeof(int), GFP_KERNEL); 6288c2ecf20Sopenharmony_ci pnode = kzalloc(sizeof(struct ubifs_pnode), GFP_KERNEL); 6298c2ecf20Sopenharmony_ci nnode = kzalloc(sizeof(struct ubifs_nnode), GFP_KERNEL); 6308c2ecf20Sopenharmony_ci buf = vmalloc(c->leb_size); 6318c2ecf20Sopenharmony_ci ltab = vmalloc(array_size(sizeof(struct ubifs_lpt_lprops), 6328c2ecf20Sopenharmony_ci c->lpt_lebs)); 6338c2ecf20Sopenharmony_ci if (!pnode || !nnode || !buf || !ltab || !lsave) { 6348c2ecf20Sopenharmony_ci err = -ENOMEM; 6358c2ecf20Sopenharmony_ci goto out; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci ubifs_assert(c, !c->ltab); 6398c2ecf20Sopenharmony_ci c->ltab = ltab; /* Needed by set_ltab */ 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci /* Initialize LPT's own lprops */ 6428c2ecf20Sopenharmony_ci for (i = 0; i < c->lpt_lebs; i++) { 6438c2ecf20Sopenharmony_ci ltab[i].free = c->leb_size; 6448c2ecf20Sopenharmony_ci ltab[i].dirty = 0; 6458c2ecf20Sopenharmony_ci ltab[i].tgc = 0; 6468c2ecf20Sopenharmony_ci ltab[i].cmt = 0; 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci lnum = lpt_first; 6508c2ecf20Sopenharmony_ci p = buf; 6518c2ecf20Sopenharmony_ci /* Number of leaf nodes (pnodes) */ 6528c2ecf20Sopenharmony_ci cnt = c->pnode_cnt; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* 6558c2ecf20Sopenharmony_ci * The first pnode contains the LEB properties for the LEBs that contain 6568c2ecf20Sopenharmony_ci * the root inode node and the root index node of the index tree. 6578c2ecf20Sopenharmony_ci */ 6588c2ecf20Sopenharmony_ci node_sz = ALIGN(ubifs_idx_node_sz(c, 1), 8); 6598c2ecf20Sopenharmony_ci iopos = ALIGN(node_sz, c->min_io_size); 6608c2ecf20Sopenharmony_ci pnode->lprops[0].free = c->leb_size - iopos; 6618c2ecf20Sopenharmony_ci pnode->lprops[0].dirty = iopos - node_sz; 6628c2ecf20Sopenharmony_ci pnode->lprops[0].flags = LPROPS_INDEX; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci node_sz = UBIFS_INO_NODE_SZ; 6658c2ecf20Sopenharmony_ci iopos = ALIGN(node_sz, c->min_io_size); 6668c2ecf20Sopenharmony_ci pnode->lprops[1].free = c->leb_size - iopos; 6678c2ecf20Sopenharmony_ci pnode->lprops[1].dirty = iopos - node_sz; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci for (i = 2; i < UBIFS_LPT_FANOUT; i++) 6708c2ecf20Sopenharmony_ci pnode->lprops[i].free = c->leb_size; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci /* Add first pnode */ 6738c2ecf20Sopenharmony_ci ubifs_pack_pnode(c, p, pnode); 6748c2ecf20Sopenharmony_ci err = ubifs_shash_update(c, desc, p, c->pnode_sz); 6758c2ecf20Sopenharmony_ci if (err) 6768c2ecf20Sopenharmony_ci goto out; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci p += c->pnode_sz; 6798c2ecf20Sopenharmony_ci len = c->pnode_sz; 6808c2ecf20Sopenharmony_ci pnode->num += 1; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci /* Reset pnode values for remaining pnodes */ 6838c2ecf20Sopenharmony_ci pnode->lprops[0].free = c->leb_size; 6848c2ecf20Sopenharmony_ci pnode->lprops[0].dirty = 0; 6858c2ecf20Sopenharmony_ci pnode->lprops[0].flags = 0; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci pnode->lprops[1].free = c->leb_size; 6888c2ecf20Sopenharmony_ci pnode->lprops[1].dirty = 0; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci /* 6918c2ecf20Sopenharmony_ci * To calculate the internal node branches, we keep information about 6928c2ecf20Sopenharmony_ci * the level below. 6938c2ecf20Sopenharmony_ci */ 6948c2ecf20Sopenharmony_ci blnum = lnum; /* LEB number of level below */ 6958c2ecf20Sopenharmony_ci boffs = 0; /* Offset of level below */ 6968c2ecf20Sopenharmony_ci bcnt = cnt; /* Number of nodes in level below */ 6978c2ecf20Sopenharmony_ci bsz = c->pnode_sz; /* Size of nodes in level below */ 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci /* Add all remaining pnodes */ 7008c2ecf20Sopenharmony_ci for (i = 1; i < cnt; i++) { 7018c2ecf20Sopenharmony_ci if (len + c->pnode_sz > c->leb_size) { 7028c2ecf20Sopenharmony_ci alen = ALIGN(len, c->min_io_size); 7038c2ecf20Sopenharmony_ci set_ltab(c, lnum, c->leb_size - alen, alen - len); 7048c2ecf20Sopenharmony_ci memset(p, 0xff, alen - len); 7058c2ecf20Sopenharmony_ci err = ubifs_leb_change(c, lnum++, buf, alen); 7068c2ecf20Sopenharmony_ci if (err) 7078c2ecf20Sopenharmony_ci goto out; 7088c2ecf20Sopenharmony_ci p = buf; 7098c2ecf20Sopenharmony_ci len = 0; 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci ubifs_pack_pnode(c, p, pnode); 7128c2ecf20Sopenharmony_ci err = ubifs_shash_update(c, desc, p, c->pnode_sz); 7138c2ecf20Sopenharmony_ci if (err) 7148c2ecf20Sopenharmony_ci goto out; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci p += c->pnode_sz; 7178c2ecf20Sopenharmony_ci len += c->pnode_sz; 7188c2ecf20Sopenharmony_ci /* 7198c2ecf20Sopenharmony_ci * pnodes are simply numbered left to right starting at zero, 7208c2ecf20Sopenharmony_ci * which means the pnode number can be used easily to traverse 7218c2ecf20Sopenharmony_ci * down the tree to the corresponding pnode. 7228c2ecf20Sopenharmony_ci */ 7238c2ecf20Sopenharmony_ci pnode->num += 1; 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci row = 0; 7278c2ecf20Sopenharmony_ci for (i = UBIFS_LPT_FANOUT; cnt > i; i <<= UBIFS_LPT_FANOUT_SHIFT) 7288c2ecf20Sopenharmony_ci row += 1; 7298c2ecf20Sopenharmony_ci /* Add all nnodes, one level at a time */ 7308c2ecf20Sopenharmony_ci while (1) { 7318c2ecf20Sopenharmony_ci /* Number of internal nodes (nnodes) at next level */ 7328c2ecf20Sopenharmony_ci cnt = DIV_ROUND_UP(cnt, UBIFS_LPT_FANOUT); 7338c2ecf20Sopenharmony_ci for (i = 0; i < cnt; i++) { 7348c2ecf20Sopenharmony_ci if (len + c->nnode_sz > c->leb_size) { 7358c2ecf20Sopenharmony_ci alen = ALIGN(len, c->min_io_size); 7368c2ecf20Sopenharmony_ci set_ltab(c, lnum, c->leb_size - alen, 7378c2ecf20Sopenharmony_ci alen - len); 7388c2ecf20Sopenharmony_ci memset(p, 0xff, alen - len); 7398c2ecf20Sopenharmony_ci err = ubifs_leb_change(c, lnum++, buf, alen); 7408c2ecf20Sopenharmony_ci if (err) 7418c2ecf20Sopenharmony_ci goto out; 7428c2ecf20Sopenharmony_ci p = buf; 7438c2ecf20Sopenharmony_ci len = 0; 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci /* Only 1 nnode at this level, so it is the root */ 7468c2ecf20Sopenharmony_ci if (cnt == 1) { 7478c2ecf20Sopenharmony_ci c->lpt_lnum = lnum; 7488c2ecf20Sopenharmony_ci c->lpt_offs = len; 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci /* Set branches to the level below */ 7518c2ecf20Sopenharmony_ci for (j = 0; j < UBIFS_LPT_FANOUT; j++) { 7528c2ecf20Sopenharmony_ci if (bcnt) { 7538c2ecf20Sopenharmony_ci if (boffs + bsz > c->leb_size) { 7548c2ecf20Sopenharmony_ci blnum += 1; 7558c2ecf20Sopenharmony_ci boffs = 0; 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci nnode->nbranch[j].lnum = blnum; 7588c2ecf20Sopenharmony_ci nnode->nbranch[j].offs = boffs; 7598c2ecf20Sopenharmony_ci boffs += bsz; 7608c2ecf20Sopenharmony_ci bcnt--; 7618c2ecf20Sopenharmony_ci } else { 7628c2ecf20Sopenharmony_ci nnode->nbranch[j].lnum = 0; 7638c2ecf20Sopenharmony_ci nnode->nbranch[j].offs = 0; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci nnode->num = calc_nnode_num(row, i); 7678c2ecf20Sopenharmony_ci ubifs_pack_nnode(c, p, nnode); 7688c2ecf20Sopenharmony_ci p += c->nnode_sz; 7698c2ecf20Sopenharmony_ci len += c->nnode_sz; 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci /* Only 1 nnode at this level, so it is the root */ 7728c2ecf20Sopenharmony_ci if (cnt == 1) 7738c2ecf20Sopenharmony_ci break; 7748c2ecf20Sopenharmony_ci /* Update the information about the level below */ 7758c2ecf20Sopenharmony_ci bcnt = cnt; 7768c2ecf20Sopenharmony_ci bsz = c->nnode_sz; 7778c2ecf20Sopenharmony_ci row -= 1; 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci if (*big_lpt) { 7818c2ecf20Sopenharmony_ci /* Need to add LPT's save table */ 7828c2ecf20Sopenharmony_ci if (len + c->lsave_sz > c->leb_size) { 7838c2ecf20Sopenharmony_ci alen = ALIGN(len, c->min_io_size); 7848c2ecf20Sopenharmony_ci set_ltab(c, lnum, c->leb_size - alen, alen - len); 7858c2ecf20Sopenharmony_ci memset(p, 0xff, alen - len); 7868c2ecf20Sopenharmony_ci err = ubifs_leb_change(c, lnum++, buf, alen); 7878c2ecf20Sopenharmony_ci if (err) 7888c2ecf20Sopenharmony_ci goto out; 7898c2ecf20Sopenharmony_ci p = buf; 7908c2ecf20Sopenharmony_ci len = 0; 7918c2ecf20Sopenharmony_ci } 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci c->lsave_lnum = lnum; 7948c2ecf20Sopenharmony_ci c->lsave_offs = len; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci for (i = 0; i < c->lsave_cnt && i < *main_lebs; i++) 7978c2ecf20Sopenharmony_ci lsave[i] = c->main_first + i; 7988c2ecf20Sopenharmony_ci for (; i < c->lsave_cnt; i++) 7998c2ecf20Sopenharmony_ci lsave[i] = c->main_first; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci ubifs_pack_lsave(c, p, lsave); 8028c2ecf20Sopenharmony_ci p += c->lsave_sz; 8038c2ecf20Sopenharmony_ci len += c->lsave_sz; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci /* Need to add LPT's own LEB properties table */ 8078c2ecf20Sopenharmony_ci if (len + c->ltab_sz > c->leb_size) { 8088c2ecf20Sopenharmony_ci alen = ALIGN(len, c->min_io_size); 8098c2ecf20Sopenharmony_ci set_ltab(c, lnum, c->leb_size - alen, alen - len); 8108c2ecf20Sopenharmony_ci memset(p, 0xff, alen - len); 8118c2ecf20Sopenharmony_ci err = ubifs_leb_change(c, lnum++, buf, alen); 8128c2ecf20Sopenharmony_ci if (err) 8138c2ecf20Sopenharmony_ci goto out; 8148c2ecf20Sopenharmony_ci p = buf; 8158c2ecf20Sopenharmony_ci len = 0; 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci c->ltab_lnum = lnum; 8198c2ecf20Sopenharmony_ci c->ltab_offs = len; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci /* Update ltab before packing it */ 8228c2ecf20Sopenharmony_ci len += c->ltab_sz; 8238c2ecf20Sopenharmony_ci alen = ALIGN(len, c->min_io_size); 8248c2ecf20Sopenharmony_ci set_ltab(c, lnum, c->leb_size - alen, alen - len); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci ubifs_pack_ltab(c, p, ltab); 8278c2ecf20Sopenharmony_ci p += c->ltab_sz; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci /* Write remaining buffer */ 8308c2ecf20Sopenharmony_ci memset(p, 0xff, alen - len); 8318c2ecf20Sopenharmony_ci err = ubifs_leb_change(c, lnum, buf, alen); 8328c2ecf20Sopenharmony_ci if (err) 8338c2ecf20Sopenharmony_ci goto out; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci err = ubifs_shash_final(c, desc, hash); 8368c2ecf20Sopenharmony_ci if (err) 8378c2ecf20Sopenharmony_ci goto out; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci c->nhead_lnum = lnum; 8408c2ecf20Sopenharmony_ci c->nhead_offs = ALIGN(len, c->min_io_size); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci dbg_lp("space_bits %d", c->space_bits); 8438c2ecf20Sopenharmony_ci dbg_lp("lpt_lnum_bits %d", c->lpt_lnum_bits); 8448c2ecf20Sopenharmony_ci dbg_lp("lpt_offs_bits %d", c->lpt_offs_bits); 8458c2ecf20Sopenharmony_ci dbg_lp("lpt_spc_bits %d", c->lpt_spc_bits); 8468c2ecf20Sopenharmony_ci dbg_lp("pcnt_bits %d", c->pcnt_bits); 8478c2ecf20Sopenharmony_ci dbg_lp("lnum_bits %d", c->lnum_bits); 8488c2ecf20Sopenharmony_ci dbg_lp("pnode_sz %d", c->pnode_sz); 8498c2ecf20Sopenharmony_ci dbg_lp("nnode_sz %d", c->nnode_sz); 8508c2ecf20Sopenharmony_ci dbg_lp("ltab_sz %d", c->ltab_sz); 8518c2ecf20Sopenharmony_ci dbg_lp("lsave_sz %d", c->lsave_sz); 8528c2ecf20Sopenharmony_ci dbg_lp("lsave_cnt %d", c->lsave_cnt); 8538c2ecf20Sopenharmony_ci dbg_lp("lpt_hght %d", c->lpt_hght); 8548c2ecf20Sopenharmony_ci dbg_lp("big_lpt %d", c->big_lpt); 8558c2ecf20Sopenharmony_ci dbg_lp("LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs); 8568c2ecf20Sopenharmony_ci dbg_lp("LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs); 8578c2ecf20Sopenharmony_ci dbg_lp("LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs); 8588c2ecf20Sopenharmony_ci if (c->big_lpt) 8598c2ecf20Sopenharmony_ci dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs); 8608c2ecf20Sopenharmony_ciout: 8618c2ecf20Sopenharmony_ci c->ltab = NULL; 8628c2ecf20Sopenharmony_ci kfree(desc); 8638c2ecf20Sopenharmony_ci kfree(lsave); 8648c2ecf20Sopenharmony_ci vfree(ltab); 8658c2ecf20Sopenharmony_ci vfree(buf); 8668c2ecf20Sopenharmony_ci kfree(nnode); 8678c2ecf20Sopenharmony_ci kfree(pnode); 8688c2ecf20Sopenharmony_ci return err; 8698c2ecf20Sopenharmony_ci} 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci/** 8728c2ecf20Sopenharmony_ci * update_cats - add LEB properties of a pnode to LEB category lists and heaps. 8738c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 8748c2ecf20Sopenharmony_ci * @pnode: pnode 8758c2ecf20Sopenharmony_ci * 8768c2ecf20Sopenharmony_ci * When a pnode is loaded into memory, the LEB properties it contains are added, 8778c2ecf20Sopenharmony_ci * by this function, to the LEB category lists and heaps. 8788c2ecf20Sopenharmony_ci */ 8798c2ecf20Sopenharmony_cistatic void update_cats(struct ubifs_info *c, struct ubifs_pnode *pnode) 8808c2ecf20Sopenharmony_ci{ 8818c2ecf20Sopenharmony_ci int i; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 8848c2ecf20Sopenharmony_ci int cat = pnode->lprops[i].flags & LPROPS_CAT_MASK; 8858c2ecf20Sopenharmony_ci int lnum = pnode->lprops[i].lnum; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci if (!lnum) 8888c2ecf20Sopenharmony_ci return; 8898c2ecf20Sopenharmony_ci ubifs_add_to_cat(c, &pnode->lprops[i], cat); 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci} 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci/** 8948c2ecf20Sopenharmony_ci * replace_cats - add LEB properties of a pnode to LEB category lists and heaps. 8958c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 8968c2ecf20Sopenharmony_ci * @old_pnode: pnode copied 8978c2ecf20Sopenharmony_ci * @new_pnode: pnode copy 8988c2ecf20Sopenharmony_ci * 8998c2ecf20Sopenharmony_ci * During commit it is sometimes necessary to copy a pnode 9008c2ecf20Sopenharmony_ci * (see dirty_cow_pnode). When that happens, references in 9018c2ecf20Sopenharmony_ci * category lists and heaps must be replaced. This function does that. 9028c2ecf20Sopenharmony_ci */ 9038c2ecf20Sopenharmony_cistatic void replace_cats(struct ubifs_info *c, struct ubifs_pnode *old_pnode, 9048c2ecf20Sopenharmony_ci struct ubifs_pnode *new_pnode) 9058c2ecf20Sopenharmony_ci{ 9068c2ecf20Sopenharmony_ci int i; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 9098c2ecf20Sopenharmony_ci if (!new_pnode->lprops[i].lnum) 9108c2ecf20Sopenharmony_ci return; 9118c2ecf20Sopenharmony_ci ubifs_replace_cat(c, &old_pnode->lprops[i], 9128c2ecf20Sopenharmony_ci &new_pnode->lprops[i]); 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci} 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci/** 9178c2ecf20Sopenharmony_ci * check_lpt_crc - check LPT node crc is correct. 9188c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 9198c2ecf20Sopenharmony_ci * @buf: buffer containing node 9208c2ecf20Sopenharmony_ci * @len: length of node 9218c2ecf20Sopenharmony_ci * 9228c2ecf20Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 9238c2ecf20Sopenharmony_ci */ 9248c2ecf20Sopenharmony_cistatic int check_lpt_crc(const struct ubifs_info *c, void *buf, int len) 9258c2ecf20Sopenharmony_ci{ 9268c2ecf20Sopenharmony_ci int pos = 0; 9278c2ecf20Sopenharmony_ci uint8_t *addr = buf; 9288c2ecf20Sopenharmony_ci uint16_t crc, calc_crc; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci crc = ubifs_unpack_bits(c, &addr, &pos, UBIFS_LPT_CRC_BITS); 9318c2ecf20Sopenharmony_ci calc_crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, 9328c2ecf20Sopenharmony_ci len - UBIFS_LPT_CRC_BYTES); 9338c2ecf20Sopenharmony_ci if (crc != calc_crc) { 9348c2ecf20Sopenharmony_ci ubifs_err(c, "invalid crc in LPT node: crc %hx calc %hx", 9358c2ecf20Sopenharmony_ci crc, calc_crc); 9368c2ecf20Sopenharmony_ci dump_stack(); 9378c2ecf20Sopenharmony_ci return -EINVAL; 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci return 0; 9408c2ecf20Sopenharmony_ci} 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci/** 9438c2ecf20Sopenharmony_ci * check_lpt_type - check LPT node type is correct. 9448c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 9458c2ecf20Sopenharmony_ci * @addr: address of type bit field is passed and returned updated here 9468c2ecf20Sopenharmony_ci * @pos: position of type bit field is passed and returned updated here 9478c2ecf20Sopenharmony_ci * @type: expected type 9488c2ecf20Sopenharmony_ci * 9498c2ecf20Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 9508c2ecf20Sopenharmony_ci */ 9518c2ecf20Sopenharmony_cistatic int check_lpt_type(const struct ubifs_info *c, uint8_t **addr, 9528c2ecf20Sopenharmony_ci int *pos, int type) 9538c2ecf20Sopenharmony_ci{ 9548c2ecf20Sopenharmony_ci int node_type; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci node_type = ubifs_unpack_bits(c, addr, pos, UBIFS_LPT_TYPE_BITS); 9578c2ecf20Sopenharmony_ci if (node_type != type) { 9588c2ecf20Sopenharmony_ci ubifs_err(c, "invalid type (%d) in LPT node type %d", 9598c2ecf20Sopenharmony_ci node_type, type); 9608c2ecf20Sopenharmony_ci dump_stack(); 9618c2ecf20Sopenharmony_ci return -EINVAL; 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci return 0; 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci/** 9678c2ecf20Sopenharmony_ci * unpack_pnode - unpack a pnode. 9688c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 9698c2ecf20Sopenharmony_ci * @buf: buffer containing packed pnode to unpack 9708c2ecf20Sopenharmony_ci * @pnode: pnode structure to fill 9718c2ecf20Sopenharmony_ci * 9728c2ecf20Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 9738c2ecf20Sopenharmony_ci */ 9748c2ecf20Sopenharmony_cistatic int unpack_pnode(const struct ubifs_info *c, void *buf, 9758c2ecf20Sopenharmony_ci struct ubifs_pnode *pnode) 9768c2ecf20Sopenharmony_ci{ 9778c2ecf20Sopenharmony_ci uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; 9788c2ecf20Sopenharmony_ci int i, pos = 0, err; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_PNODE); 9818c2ecf20Sopenharmony_ci if (err) 9828c2ecf20Sopenharmony_ci return err; 9838c2ecf20Sopenharmony_ci if (c->big_lpt) 9848c2ecf20Sopenharmony_ci pnode->num = ubifs_unpack_bits(c, &addr, &pos, c->pcnt_bits); 9858c2ecf20Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 9868c2ecf20Sopenharmony_ci struct ubifs_lprops * const lprops = &pnode->lprops[i]; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci lprops->free = ubifs_unpack_bits(c, &addr, &pos, c->space_bits); 9898c2ecf20Sopenharmony_ci lprops->free <<= 3; 9908c2ecf20Sopenharmony_ci lprops->dirty = ubifs_unpack_bits(c, &addr, &pos, c->space_bits); 9918c2ecf20Sopenharmony_ci lprops->dirty <<= 3; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci if (ubifs_unpack_bits(c, &addr, &pos, 1)) 9948c2ecf20Sopenharmony_ci lprops->flags = LPROPS_INDEX; 9958c2ecf20Sopenharmony_ci else 9968c2ecf20Sopenharmony_ci lprops->flags = 0; 9978c2ecf20Sopenharmony_ci lprops->flags |= ubifs_categorize_lprops(c, lprops); 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci err = check_lpt_crc(c, buf, c->pnode_sz); 10008c2ecf20Sopenharmony_ci return err; 10018c2ecf20Sopenharmony_ci} 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci/** 10048c2ecf20Sopenharmony_ci * ubifs_unpack_nnode - unpack a nnode. 10058c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 10068c2ecf20Sopenharmony_ci * @buf: buffer containing packed nnode to unpack 10078c2ecf20Sopenharmony_ci * @nnode: nnode structure to fill 10088c2ecf20Sopenharmony_ci * 10098c2ecf20Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 10108c2ecf20Sopenharmony_ci */ 10118c2ecf20Sopenharmony_ciint ubifs_unpack_nnode(const struct ubifs_info *c, void *buf, 10128c2ecf20Sopenharmony_ci struct ubifs_nnode *nnode) 10138c2ecf20Sopenharmony_ci{ 10148c2ecf20Sopenharmony_ci uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; 10158c2ecf20Sopenharmony_ci int i, pos = 0, err; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_NNODE); 10188c2ecf20Sopenharmony_ci if (err) 10198c2ecf20Sopenharmony_ci return err; 10208c2ecf20Sopenharmony_ci if (c->big_lpt) 10218c2ecf20Sopenharmony_ci nnode->num = ubifs_unpack_bits(c, &addr, &pos, c->pcnt_bits); 10228c2ecf20Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 10238c2ecf20Sopenharmony_ci int lnum; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci lnum = ubifs_unpack_bits(c, &addr, &pos, c->lpt_lnum_bits) + 10268c2ecf20Sopenharmony_ci c->lpt_first; 10278c2ecf20Sopenharmony_ci if (lnum == c->lpt_last + 1) 10288c2ecf20Sopenharmony_ci lnum = 0; 10298c2ecf20Sopenharmony_ci nnode->nbranch[i].lnum = lnum; 10308c2ecf20Sopenharmony_ci nnode->nbranch[i].offs = ubifs_unpack_bits(c, &addr, &pos, 10318c2ecf20Sopenharmony_ci c->lpt_offs_bits); 10328c2ecf20Sopenharmony_ci } 10338c2ecf20Sopenharmony_ci err = check_lpt_crc(c, buf, c->nnode_sz); 10348c2ecf20Sopenharmony_ci return err; 10358c2ecf20Sopenharmony_ci} 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci/** 10388c2ecf20Sopenharmony_ci * unpack_ltab - unpack the LPT's own lprops table. 10398c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 10408c2ecf20Sopenharmony_ci * @buf: buffer from which to unpack 10418c2ecf20Sopenharmony_ci * 10428c2ecf20Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 10438c2ecf20Sopenharmony_ci */ 10448c2ecf20Sopenharmony_cistatic int unpack_ltab(const struct ubifs_info *c, void *buf) 10458c2ecf20Sopenharmony_ci{ 10468c2ecf20Sopenharmony_ci uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; 10478c2ecf20Sopenharmony_ci int i, pos = 0, err; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_LTAB); 10508c2ecf20Sopenharmony_ci if (err) 10518c2ecf20Sopenharmony_ci return err; 10528c2ecf20Sopenharmony_ci for (i = 0; i < c->lpt_lebs; i++) { 10538c2ecf20Sopenharmony_ci int free = ubifs_unpack_bits(c, &addr, &pos, c->lpt_spc_bits); 10548c2ecf20Sopenharmony_ci int dirty = ubifs_unpack_bits(c, &addr, &pos, c->lpt_spc_bits); 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci if (free < 0 || free > c->leb_size || dirty < 0 || 10578c2ecf20Sopenharmony_ci dirty > c->leb_size || free + dirty > c->leb_size) 10588c2ecf20Sopenharmony_ci return -EINVAL; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci c->ltab[i].free = free; 10618c2ecf20Sopenharmony_ci c->ltab[i].dirty = dirty; 10628c2ecf20Sopenharmony_ci c->ltab[i].tgc = 0; 10638c2ecf20Sopenharmony_ci c->ltab[i].cmt = 0; 10648c2ecf20Sopenharmony_ci } 10658c2ecf20Sopenharmony_ci err = check_lpt_crc(c, buf, c->ltab_sz); 10668c2ecf20Sopenharmony_ci return err; 10678c2ecf20Sopenharmony_ci} 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci/** 10708c2ecf20Sopenharmony_ci * unpack_lsave - unpack the LPT's save table. 10718c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 10728c2ecf20Sopenharmony_ci * @buf: buffer from which to unpack 10738c2ecf20Sopenharmony_ci * 10748c2ecf20Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 10758c2ecf20Sopenharmony_ci */ 10768c2ecf20Sopenharmony_cistatic int unpack_lsave(const struct ubifs_info *c, void *buf) 10778c2ecf20Sopenharmony_ci{ 10788c2ecf20Sopenharmony_ci uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; 10798c2ecf20Sopenharmony_ci int i, pos = 0, err; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_LSAVE); 10828c2ecf20Sopenharmony_ci if (err) 10838c2ecf20Sopenharmony_ci return err; 10848c2ecf20Sopenharmony_ci for (i = 0; i < c->lsave_cnt; i++) { 10858c2ecf20Sopenharmony_ci int lnum = ubifs_unpack_bits(c, &addr, &pos, c->lnum_bits); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci if (lnum < c->main_first || lnum >= c->leb_cnt) 10888c2ecf20Sopenharmony_ci return -EINVAL; 10898c2ecf20Sopenharmony_ci c->lsave[i] = lnum; 10908c2ecf20Sopenharmony_ci } 10918c2ecf20Sopenharmony_ci err = check_lpt_crc(c, buf, c->lsave_sz); 10928c2ecf20Sopenharmony_ci return err; 10938c2ecf20Sopenharmony_ci} 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci/** 10968c2ecf20Sopenharmony_ci * validate_nnode - validate a nnode. 10978c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 10988c2ecf20Sopenharmony_ci * @nnode: nnode to validate 10998c2ecf20Sopenharmony_ci * @parent: parent nnode (or NULL for the root nnode) 11008c2ecf20Sopenharmony_ci * @iip: index in parent 11018c2ecf20Sopenharmony_ci * 11028c2ecf20Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 11038c2ecf20Sopenharmony_ci */ 11048c2ecf20Sopenharmony_cistatic int validate_nnode(const struct ubifs_info *c, struct ubifs_nnode *nnode, 11058c2ecf20Sopenharmony_ci struct ubifs_nnode *parent, int iip) 11068c2ecf20Sopenharmony_ci{ 11078c2ecf20Sopenharmony_ci int i, lvl, max_offs; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci if (c->big_lpt) { 11108c2ecf20Sopenharmony_ci int num = calc_nnode_num_from_parent(c, parent, iip); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci if (nnode->num != num) 11138c2ecf20Sopenharmony_ci return -EINVAL; 11148c2ecf20Sopenharmony_ci } 11158c2ecf20Sopenharmony_ci lvl = parent ? parent->level - 1 : c->lpt_hght; 11168c2ecf20Sopenharmony_ci if (lvl < 1) 11178c2ecf20Sopenharmony_ci return -EINVAL; 11188c2ecf20Sopenharmony_ci if (lvl == 1) 11198c2ecf20Sopenharmony_ci max_offs = c->leb_size - c->pnode_sz; 11208c2ecf20Sopenharmony_ci else 11218c2ecf20Sopenharmony_ci max_offs = c->leb_size - c->nnode_sz; 11228c2ecf20Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 11238c2ecf20Sopenharmony_ci int lnum = nnode->nbranch[i].lnum; 11248c2ecf20Sopenharmony_ci int offs = nnode->nbranch[i].offs; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci if (lnum == 0) { 11278c2ecf20Sopenharmony_ci if (offs != 0) 11288c2ecf20Sopenharmony_ci return -EINVAL; 11298c2ecf20Sopenharmony_ci continue; 11308c2ecf20Sopenharmony_ci } 11318c2ecf20Sopenharmony_ci if (lnum < c->lpt_first || lnum > c->lpt_last) 11328c2ecf20Sopenharmony_ci return -EINVAL; 11338c2ecf20Sopenharmony_ci if (offs < 0 || offs > max_offs) 11348c2ecf20Sopenharmony_ci return -EINVAL; 11358c2ecf20Sopenharmony_ci } 11368c2ecf20Sopenharmony_ci return 0; 11378c2ecf20Sopenharmony_ci} 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci/** 11408c2ecf20Sopenharmony_ci * validate_pnode - validate a pnode. 11418c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 11428c2ecf20Sopenharmony_ci * @pnode: pnode to validate 11438c2ecf20Sopenharmony_ci * @parent: parent nnode 11448c2ecf20Sopenharmony_ci * @iip: index in parent 11458c2ecf20Sopenharmony_ci * 11468c2ecf20Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 11478c2ecf20Sopenharmony_ci */ 11488c2ecf20Sopenharmony_cistatic int validate_pnode(const struct ubifs_info *c, struct ubifs_pnode *pnode, 11498c2ecf20Sopenharmony_ci struct ubifs_nnode *parent, int iip) 11508c2ecf20Sopenharmony_ci{ 11518c2ecf20Sopenharmony_ci int i; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci if (c->big_lpt) { 11548c2ecf20Sopenharmony_ci int num = calc_pnode_num_from_parent(c, parent, iip); 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci if (pnode->num != num) 11578c2ecf20Sopenharmony_ci return -EINVAL; 11588c2ecf20Sopenharmony_ci } 11598c2ecf20Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 11608c2ecf20Sopenharmony_ci int free = pnode->lprops[i].free; 11618c2ecf20Sopenharmony_ci int dirty = pnode->lprops[i].dirty; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci if (free < 0 || free > c->leb_size || free % c->min_io_size || 11648c2ecf20Sopenharmony_ci (free & 7)) 11658c2ecf20Sopenharmony_ci return -EINVAL; 11668c2ecf20Sopenharmony_ci if (dirty < 0 || dirty > c->leb_size || (dirty & 7)) 11678c2ecf20Sopenharmony_ci return -EINVAL; 11688c2ecf20Sopenharmony_ci if (dirty + free > c->leb_size) 11698c2ecf20Sopenharmony_ci return -EINVAL; 11708c2ecf20Sopenharmony_ci } 11718c2ecf20Sopenharmony_ci return 0; 11728c2ecf20Sopenharmony_ci} 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci/** 11758c2ecf20Sopenharmony_ci * set_pnode_lnum - set LEB numbers on a pnode. 11768c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 11778c2ecf20Sopenharmony_ci * @pnode: pnode to update 11788c2ecf20Sopenharmony_ci * 11798c2ecf20Sopenharmony_ci * This function calculates the LEB numbers for the LEB properties it contains 11808c2ecf20Sopenharmony_ci * based on the pnode number. 11818c2ecf20Sopenharmony_ci */ 11828c2ecf20Sopenharmony_cistatic void set_pnode_lnum(const struct ubifs_info *c, 11838c2ecf20Sopenharmony_ci struct ubifs_pnode *pnode) 11848c2ecf20Sopenharmony_ci{ 11858c2ecf20Sopenharmony_ci int i, lnum; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci lnum = (pnode->num << UBIFS_LPT_FANOUT_SHIFT) + c->main_first; 11888c2ecf20Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 11898c2ecf20Sopenharmony_ci if (lnum >= c->leb_cnt) 11908c2ecf20Sopenharmony_ci return; 11918c2ecf20Sopenharmony_ci pnode->lprops[i].lnum = lnum++; 11928c2ecf20Sopenharmony_ci } 11938c2ecf20Sopenharmony_ci} 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci/** 11968c2ecf20Sopenharmony_ci * ubifs_read_nnode - read a nnode from flash and link it to the tree in memory. 11978c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 11988c2ecf20Sopenharmony_ci * @parent: parent nnode (or NULL for the root) 11998c2ecf20Sopenharmony_ci * @iip: index in parent 12008c2ecf20Sopenharmony_ci * 12018c2ecf20Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 12028c2ecf20Sopenharmony_ci */ 12038c2ecf20Sopenharmony_ciint ubifs_read_nnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip) 12048c2ecf20Sopenharmony_ci{ 12058c2ecf20Sopenharmony_ci struct ubifs_nbranch *branch = NULL; 12068c2ecf20Sopenharmony_ci struct ubifs_nnode *nnode = NULL; 12078c2ecf20Sopenharmony_ci void *buf = c->lpt_nod_buf; 12088c2ecf20Sopenharmony_ci int err, lnum, offs; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci if (parent) { 12118c2ecf20Sopenharmony_ci branch = &parent->nbranch[iip]; 12128c2ecf20Sopenharmony_ci lnum = branch->lnum; 12138c2ecf20Sopenharmony_ci offs = branch->offs; 12148c2ecf20Sopenharmony_ci } else { 12158c2ecf20Sopenharmony_ci lnum = c->lpt_lnum; 12168c2ecf20Sopenharmony_ci offs = c->lpt_offs; 12178c2ecf20Sopenharmony_ci } 12188c2ecf20Sopenharmony_ci nnode = kzalloc(sizeof(struct ubifs_nnode), GFP_NOFS); 12198c2ecf20Sopenharmony_ci if (!nnode) { 12208c2ecf20Sopenharmony_ci err = -ENOMEM; 12218c2ecf20Sopenharmony_ci goto out; 12228c2ecf20Sopenharmony_ci } 12238c2ecf20Sopenharmony_ci if (lnum == 0) { 12248c2ecf20Sopenharmony_ci /* 12258c2ecf20Sopenharmony_ci * This nnode was not written which just means that the LEB 12268c2ecf20Sopenharmony_ci * properties in the subtree below it describe empty LEBs. We 12278c2ecf20Sopenharmony_ci * make the nnode as though we had read it, which in fact means 12288c2ecf20Sopenharmony_ci * doing almost nothing. 12298c2ecf20Sopenharmony_ci */ 12308c2ecf20Sopenharmony_ci if (c->big_lpt) 12318c2ecf20Sopenharmony_ci nnode->num = calc_nnode_num_from_parent(c, parent, iip); 12328c2ecf20Sopenharmony_ci } else { 12338c2ecf20Sopenharmony_ci err = ubifs_leb_read(c, lnum, buf, offs, c->nnode_sz, 1); 12348c2ecf20Sopenharmony_ci if (err) 12358c2ecf20Sopenharmony_ci goto out; 12368c2ecf20Sopenharmony_ci err = ubifs_unpack_nnode(c, buf, nnode); 12378c2ecf20Sopenharmony_ci if (err) 12388c2ecf20Sopenharmony_ci goto out; 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci err = validate_nnode(c, nnode, parent, iip); 12418c2ecf20Sopenharmony_ci if (err) 12428c2ecf20Sopenharmony_ci goto out; 12438c2ecf20Sopenharmony_ci if (!c->big_lpt) 12448c2ecf20Sopenharmony_ci nnode->num = calc_nnode_num_from_parent(c, parent, iip); 12458c2ecf20Sopenharmony_ci if (parent) { 12468c2ecf20Sopenharmony_ci branch->nnode = nnode; 12478c2ecf20Sopenharmony_ci nnode->level = parent->level - 1; 12488c2ecf20Sopenharmony_ci } else { 12498c2ecf20Sopenharmony_ci c->nroot = nnode; 12508c2ecf20Sopenharmony_ci nnode->level = c->lpt_hght; 12518c2ecf20Sopenharmony_ci } 12528c2ecf20Sopenharmony_ci nnode->parent = parent; 12538c2ecf20Sopenharmony_ci nnode->iip = iip; 12548c2ecf20Sopenharmony_ci return 0; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ciout: 12578c2ecf20Sopenharmony_ci ubifs_err(c, "error %d reading nnode at %d:%d", err, lnum, offs); 12588c2ecf20Sopenharmony_ci dump_stack(); 12598c2ecf20Sopenharmony_ci kfree(nnode); 12608c2ecf20Sopenharmony_ci return err; 12618c2ecf20Sopenharmony_ci} 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci/** 12648c2ecf20Sopenharmony_ci * read_pnode - read a pnode from flash and link it to the tree in memory. 12658c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 12668c2ecf20Sopenharmony_ci * @parent: parent nnode 12678c2ecf20Sopenharmony_ci * @iip: index in parent 12688c2ecf20Sopenharmony_ci * 12698c2ecf20Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 12708c2ecf20Sopenharmony_ci */ 12718c2ecf20Sopenharmony_cistatic int read_pnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip) 12728c2ecf20Sopenharmony_ci{ 12738c2ecf20Sopenharmony_ci struct ubifs_nbranch *branch; 12748c2ecf20Sopenharmony_ci struct ubifs_pnode *pnode = NULL; 12758c2ecf20Sopenharmony_ci void *buf = c->lpt_nod_buf; 12768c2ecf20Sopenharmony_ci int err, lnum, offs; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci branch = &parent->nbranch[iip]; 12798c2ecf20Sopenharmony_ci lnum = branch->lnum; 12808c2ecf20Sopenharmony_ci offs = branch->offs; 12818c2ecf20Sopenharmony_ci pnode = kzalloc(sizeof(struct ubifs_pnode), GFP_NOFS); 12828c2ecf20Sopenharmony_ci if (!pnode) 12838c2ecf20Sopenharmony_ci return -ENOMEM; 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci if (lnum == 0) { 12868c2ecf20Sopenharmony_ci /* 12878c2ecf20Sopenharmony_ci * This pnode was not written which just means that the LEB 12888c2ecf20Sopenharmony_ci * properties in it describe empty LEBs. We make the pnode as 12898c2ecf20Sopenharmony_ci * though we had read it. 12908c2ecf20Sopenharmony_ci */ 12918c2ecf20Sopenharmony_ci int i; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci if (c->big_lpt) 12948c2ecf20Sopenharmony_ci pnode->num = calc_pnode_num_from_parent(c, parent, iip); 12958c2ecf20Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 12968c2ecf20Sopenharmony_ci struct ubifs_lprops * const lprops = &pnode->lprops[i]; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci lprops->free = c->leb_size; 12998c2ecf20Sopenharmony_ci lprops->flags = ubifs_categorize_lprops(c, lprops); 13008c2ecf20Sopenharmony_ci } 13018c2ecf20Sopenharmony_ci } else { 13028c2ecf20Sopenharmony_ci err = ubifs_leb_read(c, lnum, buf, offs, c->pnode_sz, 1); 13038c2ecf20Sopenharmony_ci if (err) 13048c2ecf20Sopenharmony_ci goto out; 13058c2ecf20Sopenharmony_ci err = unpack_pnode(c, buf, pnode); 13068c2ecf20Sopenharmony_ci if (err) 13078c2ecf20Sopenharmony_ci goto out; 13088c2ecf20Sopenharmony_ci } 13098c2ecf20Sopenharmony_ci err = validate_pnode(c, pnode, parent, iip); 13108c2ecf20Sopenharmony_ci if (err) 13118c2ecf20Sopenharmony_ci goto out; 13128c2ecf20Sopenharmony_ci if (!c->big_lpt) 13138c2ecf20Sopenharmony_ci pnode->num = calc_pnode_num_from_parent(c, parent, iip); 13148c2ecf20Sopenharmony_ci branch->pnode = pnode; 13158c2ecf20Sopenharmony_ci pnode->parent = parent; 13168c2ecf20Sopenharmony_ci pnode->iip = iip; 13178c2ecf20Sopenharmony_ci set_pnode_lnum(c, pnode); 13188c2ecf20Sopenharmony_ci c->pnodes_have += 1; 13198c2ecf20Sopenharmony_ci return 0; 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ciout: 13228c2ecf20Sopenharmony_ci ubifs_err(c, "error %d reading pnode at %d:%d", err, lnum, offs); 13238c2ecf20Sopenharmony_ci ubifs_dump_pnode(c, pnode, parent, iip); 13248c2ecf20Sopenharmony_ci dump_stack(); 13258c2ecf20Sopenharmony_ci ubifs_err(c, "calc num: %d", calc_pnode_num_from_parent(c, parent, iip)); 13268c2ecf20Sopenharmony_ci kfree(pnode); 13278c2ecf20Sopenharmony_ci return err; 13288c2ecf20Sopenharmony_ci} 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci/** 13318c2ecf20Sopenharmony_ci * read_ltab - read LPT's own lprops table. 13328c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 13338c2ecf20Sopenharmony_ci * 13348c2ecf20Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 13358c2ecf20Sopenharmony_ci */ 13368c2ecf20Sopenharmony_cistatic int read_ltab(struct ubifs_info *c) 13378c2ecf20Sopenharmony_ci{ 13388c2ecf20Sopenharmony_ci int err; 13398c2ecf20Sopenharmony_ci void *buf; 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci buf = vmalloc(c->ltab_sz); 13428c2ecf20Sopenharmony_ci if (!buf) 13438c2ecf20Sopenharmony_ci return -ENOMEM; 13448c2ecf20Sopenharmony_ci err = ubifs_leb_read(c, c->ltab_lnum, buf, c->ltab_offs, c->ltab_sz, 1); 13458c2ecf20Sopenharmony_ci if (err) 13468c2ecf20Sopenharmony_ci goto out; 13478c2ecf20Sopenharmony_ci err = unpack_ltab(c, buf); 13488c2ecf20Sopenharmony_ciout: 13498c2ecf20Sopenharmony_ci vfree(buf); 13508c2ecf20Sopenharmony_ci return err; 13518c2ecf20Sopenharmony_ci} 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci/** 13548c2ecf20Sopenharmony_ci * read_lsave - read LPT's save table. 13558c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 13568c2ecf20Sopenharmony_ci * 13578c2ecf20Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 13588c2ecf20Sopenharmony_ci */ 13598c2ecf20Sopenharmony_cistatic int read_lsave(struct ubifs_info *c) 13608c2ecf20Sopenharmony_ci{ 13618c2ecf20Sopenharmony_ci int err, i; 13628c2ecf20Sopenharmony_ci void *buf; 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci buf = vmalloc(c->lsave_sz); 13658c2ecf20Sopenharmony_ci if (!buf) 13668c2ecf20Sopenharmony_ci return -ENOMEM; 13678c2ecf20Sopenharmony_ci err = ubifs_leb_read(c, c->lsave_lnum, buf, c->lsave_offs, 13688c2ecf20Sopenharmony_ci c->lsave_sz, 1); 13698c2ecf20Sopenharmony_ci if (err) 13708c2ecf20Sopenharmony_ci goto out; 13718c2ecf20Sopenharmony_ci err = unpack_lsave(c, buf); 13728c2ecf20Sopenharmony_ci if (err) 13738c2ecf20Sopenharmony_ci goto out; 13748c2ecf20Sopenharmony_ci for (i = 0; i < c->lsave_cnt; i++) { 13758c2ecf20Sopenharmony_ci int lnum = c->lsave[i]; 13768c2ecf20Sopenharmony_ci struct ubifs_lprops *lprops; 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci /* 13798c2ecf20Sopenharmony_ci * Due to automatic resizing, the values in the lsave table 13808c2ecf20Sopenharmony_ci * could be beyond the volume size - just ignore them. 13818c2ecf20Sopenharmony_ci */ 13828c2ecf20Sopenharmony_ci if (lnum >= c->leb_cnt) 13838c2ecf20Sopenharmony_ci continue; 13848c2ecf20Sopenharmony_ci lprops = ubifs_lpt_lookup(c, lnum); 13858c2ecf20Sopenharmony_ci if (IS_ERR(lprops)) { 13868c2ecf20Sopenharmony_ci err = PTR_ERR(lprops); 13878c2ecf20Sopenharmony_ci goto out; 13888c2ecf20Sopenharmony_ci } 13898c2ecf20Sopenharmony_ci } 13908c2ecf20Sopenharmony_ciout: 13918c2ecf20Sopenharmony_ci vfree(buf); 13928c2ecf20Sopenharmony_ci return err; 13938c2ecf20Sopenharmony_ci} 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci/** 13968c2ecf20Sopenharmony_ci * ubifs_get_nnode - get a nnode. 13978c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 13988c2ecf20Sopenharmony_ci * @parent: parent nnode (or NULL for the root) 13998c2ecf20Sopenharmony_ci * @iip: index in parent 14008c2ecf20Sopenharmony_ci * 14018c2ecf20Sopenharmony_ci * This function returns a pointer to the nnode on success or a negative error 14028c2ecf20Sopenharmony_ci * code on failure. 14038c2ecf20Sopenharmony_ci */ 14048c2ecf20Sopenharmony_cistruct ubifs_nnode *ubifs_get_nnode(struct ubifs_info *c, 14058c2ecf20Sopenharmony_ci struct ubifs_nnode *parent, int iip) 14068c2ecf20Sopenharmony_ci{ 14078c2ecf20Sopenharmony_ci struct ubifs_nbranch *branch; 14088c2ecf20Sopenharmony_ci struct ubifs_nnode *nnode; 14098c2ecf20Sopenharmony_ci int err; 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci branch = &parent->nbranch[iip]; 14128c2ecf20Sopenharmony_ci nnode = branch->nnode; 14138c2ecf20Sopenharmony_ci if (nnode) 14148c2ecf20Sopenharmony_ci return nnode; 14158c2ecf20Sopenharmony_ci err = ubifs_read_nnode(c, parent, iip); 14168c2ecf20Sopenharmony_ci if (err) 14178c2ecf20Sopenharmony_ci return ERR_PTR(err); 14188c2ecf20Sopenharmony_ci return branch->nnode; 14198c2ecf20Sopenharmony_ci} 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci/** 14228c2ecf20Sopenharmony_ci * ubifs_get_pnode - get a pnode. 14238c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 14248c2ecf20Sopenharmony_ci * @parent: parent nnode 14258c2ecf20Sopenharmony_ci * @iip: index in parent 14268c2ecf20Sopenharmony_ci * 14278c2ecf20Sopenharmony_ci * This function returns a pointer to the pnode on success or a negative error 14288c2ecf20Sopenharmony_ci * code on failure. 14298c2ecf20Sopenharmony_ci */ 14308c2ecf20Sopenharmony_cistruct ubifs_pnode *ubifs_get_pnode(struct ubifs_info *c, 14318c2ecf20Sopenharmony_ci struct ubifs_nnode *parent, int iip) 14328c2ecf20Sopenharmony_ci{ 14338c2ecf20Sopenharmony_ci struct ubifs_nbranch *branch; 14348c2ecf20Sopenharmony_ci struct ubifs_pnode *pnode; 14358c2ecf20Sopenharmony_ci int err; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci branch = &parent->nbranch[iip]; 14388c2ecf20Sopenharmony_ci pnode = branch->pnode; 14398c2ecf20Sopenharmony_ci if (pnode) 14408c2ecf20Sopenharmony_ci return pnode; 14418c2ecf20Sopenharmony_ci err = read_pnode(c, parent, iip); 14428c2ecf20Sopenharmony_ci if (err) 14438c2ecf20Sopenharmony_ci return ERR_PTR(err); 14448c2ecf20Sopenharmony_ci update_cats(c, branch->pnode); 14458c2ecf20Sopenharmony_ci return branch->pnode; 14468c2ecf20Sopenharmony_ci} 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci/** 14498c2ecf20Sopenharmony_ci * ubifs_pnode_lookup - lookup a pnode in the LPT. 14508c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 14518c2ecf20Sopenharmony_ci * @i: pnode number (0 to (main_lebs - 1) / UBIFS_LPT_FANOUT) 14528c2ecf20Sopenharmony_ci * 14538c2ecf20Sopenharmony_ci * This function returns a pointer to the pnode on success or a negative 14548c2ecf20Sopenharmony_ci * error code on failure. 14558c2ecf20Sopenharmony_ci */ 14568c2ecf20Sopenharmony_cistruct ubifs_pnode *ubifs_pnode_lookup(struct ubifs_info *c, int i) 14578c2ecf20Sopenharmony_ci{ 14588c2ecf20Sopenharmony_ci int err, h, iip, shft; 14598c2ecf20Sopenharmony_ci struct ubifs_nnode *nnode; 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci if (!c->nroot) { 14628c2ecf20Sopenharmony_ci err = ubifs_read_nnode(c, NULL, 0); 14638c2ecf20Sopenharmony_ci if (err) 14648c2ecf20Sopenharmony_ci return ERR_PTR(err); 14658c2ecf20Sopenharmony_ci } 14668c2ecf20Sopenharmony_ci i <<= UBIFS_LPT_FANOUT_SHIFT; 14678c2ecf20Sopenharmony_ci nnode = c->nroot; 14688c2ecf20Sopenharmony_ci shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT; 14698c2ecf20Sopenharmony_ci for (h = 1; h < c->lpt_hght; h++) { 14708c2ecf20Sopenharmony_ci iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); 14718c2ecf20Sopenharmony_ci shft -= UBIFS_LPT_FANOUT_SHIFT; 14728c2ecf20Sopenharmony_ci nnode = ubifs_get_nnode(c, nnode, iip); 14738c2ecf20Sopenharmony_ci if (IS_ERR(nnode)) 14748c2ecf20Sopenharmony_ci return ERR_CAST(nnode); 14758c2ecf20Sopenharmony_ci } 14768c2ecf20Sopenharmony_ci iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); 14778c2ecf20Sopenharmony_ci return ubifs_get_pnode(c, nnode, iip); 14788c2ecf20Sopenharmony_ci} 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci/** 14818c2ecf20Sopenharmony_ci * ubifs_lpt_lookup - lookup LEB properties in the LPT. 14828c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 14838c2ecf20Sopenharmony_ci * @lnum: LEB number to lookup 14848c2ecf20Sopenharmony_ci * 14858c2ecf20Sopenharmony_ci * This function returns a pointer to the LEB properties on success or a 14868c2ecf20Sopenharmony_ci * negative error code on failure. 14878c2ecf20Sopenharmony_ci */ 14888c2ecf20Sopenharmony_cistruct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum) 14898c2ecf20Sopenharmony_ci{ 14908c2ecf20Sopenharmony_ci int i, iip; 14918c2ecf20Sopenharmony_ci struct ubifs_pnode *pnode; 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci i = lnum - c->main_first; 14948c2ecf20Sopenharmony_ci pnode = ubifs_pnode_lookup(c, i >> UBIFS_LPT_FANOUT_SHIFT); 14958c2ecf20Sopenharmony_ci if (IS_ERR(pnode)) 14968c2ecf20Sopenharmony_ci return ERR_CAST(pnode); 14978c2ecf20Sopenharmony_ci iip = (i & (UBIFS_LPT_FANOUT - 1)); 14988c2ecf20Sopenharmony_ci dbg_lp("LEB %d, free %d, dirty %d, flags %d", lnum, 14998c2ecf20Sopenharmony_ci pnode->lprops[iip].free, pnode->lprops[iip].dirty, 15008c2ecf20Sopenharmony_ci pnode->lprops[iip].flags); 15018c2ecf20Sopenharmony_ci return &pnode->lprops[iip]; 15028c2ecf20Sopenharmony_ci} 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci/** 15058c2ecf20Sopenharmony_ci * dirty_cow_nnode - ensure a nnode is not being committed. 15068c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 15078c2ecf20Sopenharmony_ci * @nnode: nnode to check 15088c2ecf20Sopenharmony_ci * 15098c2ecf20Sopenharmony_ci * Returns dirtied nnode on success or negative error code on failure. 15108c2ecf20Sopenharmony_ci */ 15118c2ecf20Sopenharmony_cistatic struct ubifs_nnode *dirty_cow_nnode(struct ubifs_info *c, 15128c2ecf20Sopenharmony_ci struct ubifs_nnode *nnode) 15138c2ecf20Sopenharmony_ci{ 15148c2ecf20Sopenharmony_ci struct ubifs_nnode *n; 15158c2ecf20Sopenharmony_ci int i; 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci if (!test_bit(COW_CNODE, &nnode->flags)) { 15188c2ecf20Sopenharmony_ci /* nnode is not being committed */ 15198c2ecf20Sopenharmony_ci if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) { 15208c2ecf20Sopenharmony_ci c->dirty_nn_cnt += 1; 15218c2ecf20Sopenharmony_ci ubifs_add_nnode_dirt(c, nnode); 15228c2ecf20Sopenharmony_ci } 15238c2ecf20Sopenharmony_ci return nnode; 15248c2ecf20Sopenharmony_ci } 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci /* nnode is being committed, so copy it */ 15278c2ecf20Sopenharmony_ci n = kmemdup(nnode, sizeof(struct ubifs_nnode), GFP_NOFS); 15288c2ecf20Sopenharmony_ci if (unlikely(!n)) 15298c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci n->cnext = NULL; 15328c2ecf20Sopenharmony_ci __set_bit(DIRTY_CNODE, &n->flags); 15338c2ecf20Sopenharmony_ci __clear_bit(COW_CNODE, &n->flags); 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci /* The children now have new parent */ 15368c2ecf20Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 15378c2ecf20Sopenharmony_ci struct ubifs_nbranch *branch = &n->nbranch[i]; 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci if (branch->cnode) 15408c2ecf20Sopenharmony_ci branch->cnode->parent = n; 15418c2ecf20Sopenharmony_ci } 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci ubifs_assert(c, !test_bit(OBSOLETE_CNODE, &nnode->flags)); 15448c2ecf20Sopenharmony_ci __set_bit(OBSOLETE_CNODE, &nnode->flags); 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci c->dirty_nn_cnt += 1; 15478c2ecf20Sopenharmony_ci ubifs_add_nnode_dirt(c, nnode); 15488c2ecf20Sopenharmony_ci if (nnode->parent) 15498c2ecf20Sopenharmony_ci nnode->parent->nbranch[n->iip].nnode = n; 15508c2ecf20Sopenharmony_ci else 15518c2ecf20Sopenharmony_ci c->nroot = n; 15528c2ecf20Sopenharmony_ci return n; 15538c2ecf20Sopenharmony_ci} 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci/** 15568c2ecf20Sopenharmony_ci * dirty_cow_pnode - ensure a pnode is not being committed. 15578c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 15588c2ecf20Sopenharmony_ci * @pnode: pnode to check 15598c2ecf20Sopenharmony_ci * 15608c2ecf20Sopenharmony_ci * Returns dirtied pnode on success or negative error code on failure. 15618c2ecf20Sopenharmony_ci */ 15628c2ecf20Sopenharmony_cistatic struct ubifs_pnode *dirty_cow_pnode(struct ubifs_info *c, 15638c2ecf20Sopenharmony_ci struct ubifs_pnode *pnode) 15648c2ecf20Sopenharmony_ci{ 15658c2ecf20Sopenharmony_ci struct ubifs_pnode *p; 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci if (!test_bit(COW_CNODE, &pnode->flags)) { 15688c2ecf20Sopenharmony_ci /* pnode is not being committed */ 15698c2ecf20Sopenharmony_ci if (!test_and_set_bit(DIRTY_CNODE, &pnode->flags)) { 15708c2ecf20Sopenharmony_ci c->dirty_pn_cnt += 1; 15718c2ecf20Sopenharmony_ci add_pnode_dirt(c, pnode); 15728c2ecf20Sopenharmony_ci } 15738c2ecf20Sopenharmony_ci return pnode; 15748c2ecf20Sopenharmony_ci } 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci /* pnode is being committed, so copy it */ 15778c2ecf20Sopenharmony_ci p = kmemdup(pnode, sizeof(struct ubifs_pnode), GFP_NOFS); 15788c2ecf20Sopenharmony_ci if (unlikely(!p)) 15798c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci p->cnext = NULL; 15828c2ecf20Sopenharmony_ci __set_bit(DIRTY_CNODE, &p->flags); 15838c2ecf20Sopenharmony_ci __clear_bit(COW_CNODE, &p->flags); 15848c2ecf20Sopenharmony_ci replace_cats(c, pnode, p); 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci ubifs_assert(c, !test_bit(OBSOLETE_CNODE, &pnode->flags)); 15878c2ecf20Sopenharmony_ci __set_bit(OBSOLETE_CNODE, &pnode->flags); 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci c->dirty_pn_cnt += 1; 15908c2ecf20Sopenharmony_ci add_pnode_dirt(c, pnode); 15918c2ecf20Sopenharmony_ci pnode->parent->nbranch[p->iip].pnode = p; 15928c2ecf20Sopenharmony_ci return p; 15938c2ecf20Sopenharmony_ci} 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci/** 15968c2ecf20Sopenharmony_ci * ubifs_lpt_lookup_dirty - lookup LEB properties in the LPT. 15978c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 15988c2ecf20Sopenharmony_ci * @lnum: LEB number to lookup 15998c2ecf20Sopenharmony_ci * 16008c2ecf20Sopenharmony_ci * This function returns a pointer to the LEB properties on success or a 16018c2ecf20Sopenharmony_ci * negative error code on failure. 16028c2ecf20Sopenharmony_ci */ 16038c2ecf20Sopenharmony_cistruct ubifs_lprops *ubifs_lpt_lookup_dirty(struct ubifs_info *c, int lnum) 16048c2ecf20Sopenharmony_ci{ 16058c2ecf20Sopenharmony_ci int err, i, h, iip, shft; 16068c2ecf20Sopenharmony_ci struct ubifs_nnode *nnode; 16078c2ecf20Sopenharmony_ci struct ubifs_pnode *pnode; 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci if (!c->nroot) { 16108c2ecf20Sopenharmony_ci err = ubifs_read_nnode(c, NULL, 0); 16118c2ecf20Sopenharmony_ci if (err) 16128c2ecf20Sopenharmony_ci return ERR_PTR(err); 16138c2ecf20Sopenharmony_ci } 16148c2ecf20Sopenharmony_ci nnode = c->nroot; 16158c2ecf20Sopenharmony_ci nnode = dirty_cow_nnode(c, nnode); 16168c2ecf20Sopenharmony_ci if (IS_ERR(nnode)) 16178c2ecf20Sopenharmony_ci return ERR_CAST(nnode); 16188c2ecf20Sopenharmony_ci i = lnum - c->main_first; 16198c2ecf20Sopenharmony_ci shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT; 16208c2ecf20Sopenharmony_ci for (h = 1; h < c->lpt_hght; h++) { 16218c2ecf20Sopenharmony_ci iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); 16228c2ecf20Sopenharmony_ci shft -= UBIFS_LPT_FANOUT_SHIFT; 16238c2ecf20Sopenharmony_ci nnode = ubifs_get_nnode(c, nnode, iip); 16248c2ecf20Sopenharmony_ci if (IS_ERR(nnode)) 16258c2ecf20Sopenharmony_ci return ERR_CAST(nnode); 16268c2ecf20Sopenharmony_ci nnode = dirty_cow_nnode(c, nnode); 16278c2ecf20Sopenharmony_ci if (IS_ERR(nnode)) 16288c2ecf20Sopenharmony_ci return ERR_CAST(nnode); 16298c2ecf20Sopenharmony_ci } 16308c2ecf20Sopenharmony_ci iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); 16318c2ecf20Sopenharmony_ci pnode = ubifs_get_pnode(c, nnode, iip); 16328c2ecf20Sopenharmony_ci if (IS_ERR(pnode)) 16338c2ecf20Sopenharmony_ci return ERR_CAST(pnode); 16348c2ecf20Sopenharmony_ci pnode = dirty_cow_pnode(c, pnode); 16358c2ecf20Sopenharmony_ci if (IS_ERR(pnode)) 16368c2ecf20Sopenharmony_ci return ERR_CAST(pnode); 16378c2ecf20Sopenharmony_ci iip = (i & (UBIFS_LPT_FANOUT - 1)); 16388c2ecf20Sopenharmony_ci dbg_lp("LEB %d, free %d, dirty %d, flags %d", lnum, 16398c2ecf20Sopenharmony_ci pnode->lprops[iip].free, pnode->lprops[iip].dirty, 16408c2ecf20Sopenharmony_ci pnode->lprops[iip].flags); 16418c2ecf20Sopenharmony_ci ubifs_assert(c, test_bit(DIRTY_CNODE, &pnode->flags)); 16428c2ecf20Sopenharmony_ci return &pnode->lprops[iip]; 16438c2ecf20Sopenharmony_ci} 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci/** 16468c2ecf20Sopenharmony_ci * ubifs_lpt_calc_hash - Calculate hash of the LPT pnodes 16478c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 16488c2ecf20Sopenharmony_ci * @hash: the returned hash of the LPT pnodes 16498c2ecf20Sopenharmony_ci * 16508c2ecf20Sopenharmony_ci * This function iterates over the LPT pnodes and creates a hash over them. 16518c2ecf20Sopenharmony_ci * Returns 0 for success or a negative error code otherwise. 16528c2ecf20Sopenharmony_ci */ 16538c2ecf20Sopenharmony_ciint ubifs_lpt_calc_hash(struct ubifs_info *c, u8 *hash) 16548c2ecf20Sopenharmony_ci{ 16558c2ecf20Sopenharmony_ci struct ubifs_nnode *nnode, *nn; 16568c2ecf20Sopenharmony_ci struct ubifs_cnode *cnode; 16578c2ecf20Sopenharmony_ci struct shash_desc *desc; 16588c2ecf20Sopenharmony_ci int iip = 0, i; 16598c2ecf20Sopenharmony_ci int bufsiz = max_t(int, c->nnode_sz, c->pnode_sz); 16608c2ecf20Sopenharmony_ci void *buf; 16618c2ecf20Sopenharmony_ci int err; 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci if (!ubifs_authenticated(c)) 16648c2ecf20Sopenharmony_ci return 0; 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci if (!c->nroot) { 16678c2ecf20Sopenharmony_ci err = ubifs_read_nnode(c, NULL, 0); 16688c2ecf20Sopenharmony_ci if (err) 16698c2ecf20Sopenharmony_ci return err; 16708c2ecf20Sopenharmony_ci } 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci desc = ubifs_hash_get_desc(c); 16738c2ecf20Sopenharmony_ci if (IS_ERR(desc)) 16748c2ecf20Sopenharmony_ci return PTR_ERR(desc); 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci buf = kmalloc(bufsiz, GFP_NOFS); 16778c2ecf20Sopenharmony_ci if (!buf) { 16788c2ecf20Sopenharmony_ci err = -ENOMEM; 16798c2ecf20Sopenharmony_ci goto out; 16808c2ecf20Sopenharmony_ci } 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci cnode = (struct ubifs_cnode *)c->nroot; 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci while (cnode) { 16858c2ecf20Sopenharmony_ci nnode = cnode->parent; 16868c2ecf20Sopenharmony_ci nn = (struct ubifs_nnode *)cnode; 16878c2ecf20Sopenharmony_ci if (cnode->level > 1) { 16888c2ecf20Sopenharmony_ci while (iip < UBIFS_LPT_FANOUT) { 16898c2ecf20Sopenharmony_ci if (nn->nbranch[iip].lnum == 0) { 16908c2ecf20Sopenharmony_ci /* Go right */ 16918c2ecf20Sopenharmony_ci iip++; 16928c2ecf20Sopenharmony_ci continue; 16938c2ecf20Sopenharmony_ci } 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci nnode = ubifs_get_nnode(c, nn, iip); 16968c2ecf20Sopenharmony_ci if (IS_ERR(nnode)) { 16978c2ecf20Sopenharmony_ci err = PTR_ERR(nnode); 16988c2ecf20Sopenharmony_ci goto out; 16998c2ecf20Sopenharmony_ci } 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci /* Go down */ 17028c2ecf20Sopenharmony_ci iip = 0; 17038c2ecf20Sopenharmony_ci cnode = (struct ubifs_cnode *)nnode; 17048c2ecf20Sopenharmony_ci break; 17058c2ecf20Sopenharmony_ci } 17068c2ecf20Sopenharmony_ci if (iip < UBIFS_LPT_FANOUT) 17078c2ecf20Sopenharmony_ci continue; 17088c2ecf20Sopenharmony_ci } else { 17098c2ecf20Sopenharmony_ci struct ubifs_pnode *pnode; 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 17128c2ecf20Sopenharmony_ci if (nn->nbranch[i].lnum == 0) 17138c2ecf20Sopenharmony_ci continue; 17148c2ecf20Sopenharmony_ci pnode = ubifs_get_pnode(c, nn, i); 17158c2ecf20Sopenharmony_ci if (IS_ERR(pnode)) { 17168c2ecf20Sopenharmony_ci err = PTR_ERR(pnode); 17178c2ecf20Sopenharmony_ci goto out; 17188c2ecf20Sopenharmony_ci } 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci ubifs_pack_pnode(c, buf, pnode); 17218c2ecf20Sopenharmony_ci err = ubifs_shash_update(c, desc, buf, 17228c2ecf20Sopenharmony_ci c->pnode_sz); 17238c2ecf20Sopenharmony_ci if (err) 17248c2ecf20Sopenharmony_ci goto out; 17258c2ecf20Sopenharmony_ci } 17268c2ecf20Sopenharmony_ci } 17278c2ecf20Sopenharmony_ci /* Go up and to the right */ 17288c2ecf20Sopenharmony_ci iip = cnode->iip + 1; 17298c2ecf20Sopenharmony_ci cnode = (struct ubifs_cnode *)nnode; 17308c2ecf20Sopenharmony_ci } 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci err = ubifs_shash_final(c, desc, hash); 17338c2ecf20Sopenharmony_ciout: 17348c2ecf20Sopenharmony_ci kfree(desc); 17358c2ecf20Sopenharmony_ci kfree(buf); 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci return err; 17388c2ecf20Sopenharmony_ci} 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci/** 17418c2ecf20Sopenharmony_ci * lpt_check_hash - check the hash of the LPT. 17428c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 17438c2ecf20Sopenharmony_ci * 17448c2ecf20Sopenharmony_ci * This function calculates a hash over all pnodes in the LPT and compares it with 17458c2ecf20Sopenharmony_ci * the hash stored in the master node. Returns %0 on success and a negative error 17468c2ecf20Sopenharmony_ci * code on failure. 17478c2ecf20Sopenharmony_ci */ 17488c2ecf20Sopenharmony_cistatic int lpt_check_hash(struct ubifs_info *c) 17498c2ecf20Sopenharmony_ci{ 17508c2ecf20Sopenharmony_ci int err; 17518c2ecf20Sopenharmony_ci u8 hash[UBIFS_HASH_ARR_SZ]; 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci if (!ubifs_authenticated(c)) 17548c2ecf20Sopenharmony_ci return 0; 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci err = ubifs_lpt_calc_hash(c, hash); 17578c2ecf20Sopenharmony_ci if (err) 17588c2ecf20Sopenharmony_ci return err; 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci if (ubifs_check_hash(c, c->mst_node->hash_lpt, hash)) { 17618c2ecf20Sopenharmony_ci err = -EPERM; 17628c2ecf20Sopenharmony_ci ubifs_err(c, "Failed to authenticate LPT"); 17638c2ecf20Sopenharmony_ci } else { 17648c2ecf20Sopenharmony_ci err = 0; 17658c2ecf20Sopenharmony_ci } 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci return err; 17688c2ecf20Sopenharmony_ci} 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci/** 17718c2ecf20Sopenharmony_ci * lpt_init_rd - initialize the LPT for reading. 17728c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 17738c2ecf20Sopenharmony_ci * 17748c2ecf20Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 17758c2ecf20Sopenharmony_ci */ 17768c2ecf20Sopenharmony_cistatic int lpt_init_rd(struct ubifs_info *c) 17778c2ecf20Sopenharmony_ci{ 17788c2ecf20Sopenharmony_ci int err, i; 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci c->ltab = vmalloc(array_size(sizeof(struct ubifs_lpt_lprops), 17818c2ecf20Sopenharmony_ci c->lpt_lebs)); 17828c2ecf20Sopenharmony_ci if (!c->ltab) 17838c2ecf20Sopenharmony_ci return -ENOMEM; 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci i = max_t(int, c->nnode_sz, c->pnode_sz); 17868c2ecf20Sopenharmony_ci c->lpt_nod_buf = kmalloc(i, GFP_KERNEL); 17878c2ecf20Sopenharmony_ci if (!c->lpt_nod_buf) 17888c2ecf20Sopenharmony_ci return -ENOMEM; 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci for (i = 0; i < LPROPS_HEAP_CNT; i++) { 17918c2ecf20Sopenharmony_ci c->lpt_heap[i].arr = kmalloc_array(LPT_HEAP_SZ, 17928c2ecf20Sopenharmony_ci sizeof(void *), 17938c2ecf20Sopenharmony_ci GFP_KERNEL); 17948c2ecf20Sopenharmony_ci if (!c->lpt_heap[i].arr) 17958c2ecf20Sopenharmony_ci return -ENOMEM; 17968c2ecf20Sopenharmony_ci c->lpt_heap[i].cnt = 0; 17978c2ecf20Sopenharmony_ci c->lpt_heap[i].max_cnt = LPT_HEAP_SZ; 17988c2ecf20Sopenharmony_ci } 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci c->dirty_idx.arr = kmalloc_array(LPT_HEAP_SZ, sizeof(void *), 18018c2ecf20Sopenharmony_ci GFP_KERNEL); 18028c2ecf20Sopenharmony_ci if (!c->dirty_idx.arr) 18038c2ecf20Sopenharmony_ci return -ENOMEM; 18048c2ecf20Sopenharmony_ci c->dirty_idx.cnt = 0; 18058c2ecf20Sopenharmony_ci c->dirty_idx.max_cnt = LPT_HEAP_SZ; 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci err = read_ltab(c); 18088c2ecf20Sopenharmony_ci if (err) 18098c2ecf20Sopenharmony_ci return err; 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_ci err = lpt_check_hash(c); 18128c2ecf20Sopenharmony_ci if (err) 18138c2ecf20Sopenharmony_ci return err; 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci dbg_lp("space_bits %d", c->space_bits); 18168c2ecf20Sopenharmony_ci dbg_lp("lpt_lnum_bits %d", c->lpt_lnum_bits); 18178c2ecf20Sopenharmony_ci dbg_lp("lpt_offs_bits %d", c->lpt_offs_bits); 18188c2ecf20Sopenharmony_ci dbg_lp("lpt_spc_bits %d", c->lpt_spc_bits); 18198c2ecf20Sopenharmony_ci dbg_lp("pcnt_bits %d", c->pcnt_bits); 18208c2ecf20Sopenharmony_ci dbg_lp("lnum_bits %d", c->lnum_bits); 18218c2ecf20Sopenharmony_ci dbg_lp("pnode_sz %d", c->pnode_sz); 18228c2ecf20Sopenharmony_ci dbg_lp("nnode_sz %d", c->nnode_sz); 18238c2ecf20Sopenharmony_ci dbg_lp("ltab_sz %d", c->ltab_sz); 18248c2ecf20Sopenharmony_ci dbg_lp("lsave_sz %d", c->lsave_sz); 18258c2ecf20Sopenharmony_ci dbg_lp("lsave_cnt %d", c->lsave_cnt); 18268c2ecf20Sopenharmony_ci dbg_lp("lpt_hght %d", c->lpt_hght); 18278c2ecf20Sopenharmony_ci dbg_lp("big_lpt %d", c->big_lpt); 18288c2ecf20Sopenharmony_ci dbg_lp("LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs); 18298c2ecf20Sopenharmony_ci dbg_lp("LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs); 18308c2ecf20Sopenharmony_ci dbg_lp("LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs); 18318c2ecf20Sopenharmony_ci if (c->big_lpt) 18328c2ecf20Sopenharmony_ci dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs); 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci return 0; 18358c2ecf20Sopenharmony_ci} 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci/** 18388c2ecf20Sopenharmony_ci * lpt_init_wr - initialize the LPT for writing. 18398c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 18408c2ecf20Sopenharmony_ci * 18418c2ecf20Sopenharmony_ci * 'lpt_init_rd()' must have been called already. 18428c2ecf20Sopenharmony_ci * 18438c2ecf20Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 18448c2ecf20Sopenharmony_ci */ 18458c2ecf20Sopenharmony_cistatic int lpt_init_wr(struct ubifs_info *c) 18468c2ecf20Sopenharmony_ci{ 18478c2ecf20Sopenharmony_ci int err, i; 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci c->ltab_cmt = vmalloc(array_size(sizeof(struct ubifs_lpt_lprops), 18508c2ecf20Sopenharmony_ci c->lpt_lebs)); 18518c2ecf20Sopenharmony_ci if (!c->ltab_cmt) 18528c2ecf20Sopenharmony_ci return -ENOMEM; 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci c->lpt_buf = vmalloc(c->leb_size); 18558c2ecf20Sopenharmony_ci if (!c->lpt_buf) 18568c2ecf20Sopenharmony_ci return -ENOMEM; 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_ci if (c->big_lpt) { 18598c2ecf20Sopenharmony_ci c->lsave = kmalloc_array(c->lsave_cnt, sizeof(int), GFP_NOFS); 18608c2ecf20Sopenharmony_ci if (!c->lsave) 18618c2ecf20Sopenharmony_ci return -ENOMEM; 18628c2ecf20Sopenharmony_ci err = read_lsave(c); 18638c2ecf20Sopenharmony_ci if (err) 18648c2ecf20Sopenharmony_ci return err; 18658c2ecf20Sopenharmony_ci } 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci for (i = 0; i < c->lpt_lebs; i++) 18688c2ecf20Sopenharmony_ci if (c->ltab[i].free == c->leb_size) { 18698c2ecf20Sopenharmony_ci err = ubifs_leb_unmap(c, i + c->lpt_first); 18708c2ecf20Sopenharmony_ci if (err) 18718c2ecf20Sopenharmony_ci return err; 18728c2ecf20Sopenharmony_ci } 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci return 0; 18758c2ecf20Sopenharmony_ci} 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci/** 18788c2ecf20Sopenharmony_ci * ubifs_lpt_init - initialize the LPT. 18798c2ecf20Sopenharmony_ci * @c: UBIFS file-system description object 18808c2ecf20Sopenharmony_ci * @rd: whether to initialize lpt for reading 18818c2ecf20Sopenharmony_ci * @wr: whether to initialize lpt for writing 18828c2ecf20Sopenharmony_ci * 18838c2ecf20Sopenharmony_ci * For mounting 'rw', @rd and @wr are both true. For mounting 'ro', @rd is true 18848c2ecf20Sopenharmony_ci * and @wr is false. For mounting from 'ro' to 'rw', @rd is false and @wr is 18858c2ecf20Sopenharmony_ci * true. 18868c2ecf20Sopenharmony_ci * 18878c2ecf20Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 18888c2ecf20Sopenharmony_ci */ 18898c2ecf20Sopenharmony_ciint ubifs_lpt_init(struct ubifs_info *c, int rd, int wr) 18908c2ecf20Sopenharmony_ci{ 18918c2ecf20Sopenharmony_ci int err; 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci if (rd) { 18948c2ecf20Sopenharmony_ci err = lpt_init_rd(c); 18958c2ecf20Sopenharmony_ci if (err) 18968c2ecf20Sopenharmony_ci goto out_err; 18978c2ecf20Sopenharmony_ci } 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci if (wr) { 19008c2ecf20Sopenharmony_ci err = lpt_init_wr(c); 19018c2ecf20Sopenharmony_ci if (err) 19028c2ecf20Sopenharmony_ci goto out_err; 19038c2ecf20Sopenharmony_ci } 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci return 0; 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ciout_err: 19088c2ecf20Sopenharmony_ci if (wr) 19098c2ecf20Sopenharmony_ci ubifs_lpt_free(c, 1); 19108c2ecf20Sopenharmony_ci if (rd) 19118c2ecf20Sopenharmony_ci ubifs_lpt_free(c, 0); 19128c2ecf20Sopenharmony_ci return err; 19138c2ecf20Sopenharmony_ci} 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci/** 19168c2ecf20Sopenharmony_ci * struct lpt_scan_node - somewhere to put nodes while we scan LPT. 19178c2ecf20Sopenharmony_ci * @nnode: where to keep a nnode 19188c2ecf20Sopenharmony_ci * @pnode: where to keep a pnode 19198c2ecf20Sopenharmony_ci * @cnode: where to keep a cnode 19208c2ecf20Sopenharmony_ci * @in_tree: is the node in the tree in memory 19218c2ecf20Sopenharmony_ci * @ptr.nnode: pointer to the nnode (if it is an nnode) which may be here or in 19228c2ecf20Sopenharmony_ci * the tree 19238c2ecf20Sopenharmony_ci * @ptr.pnode: ditto for pnode 19248c2ecf20Sopenharmony_ci * @ptr.cnode: ditto for cnode 19258c2ecf20Sopenharmony_ci */ 19268c2ecf20Sopenharmony_cistruct lpt_scan_node { 19278c2ecf20Sopenharmony_ci union { 19288c2ecf20Sopenharmony_ci struct ubifs_nnode nnode; 19298c2ecf20Sopenharmony_ci struct ubifs_pnode pnode; 19308c2ecf20Sopenharmony_ci struct ubifs_cnode cnode; 19318c2ecf20Sopenharmony_ci }; 19328c2ecf20Sopenharmony_ci int in_tree; 19338c2ecf20Sopenharmony_ci union { 19348c2ecf20Sopenharmony_ci struct ubifs_nnode *nnode; 19358c2ecf20Sopenharmony_ci struct ubifs_pnode *pnode; 19368c2ecf20Sopenharmony_ci struct ubifs_cnode *cnode; 19378c2ecf20Sopenharmony_ci } ptr; 19388c2ecf20Sopenharmony_ci}; 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_ci/** 19418c2ecf20Sopenharmony_ci * scan_get_nnode - for the scan, get a nnode from either the tree or flash. 19428c2ecf20Sopenharmony_ci * @c: the UBIFS file-system description object 19438c2ecf20Sopenharmony_ci * @path: where to put the nnode 19448c2ecf20Sopenharmony_ci * @parent: parent of the nnode 19458c2ecf20Sopenharmony_ci * @iip: index in parent of the nnode 19468c2ecf20Sopenharmony_ci * 19478c2ecf20Sopenharmony_ci * This function returns a pointer to the nnode on success or a negative error 19488c2ecf20Sopenharmony_ci * code on failure. 19498c2ecf20Sopenharmony_ci */ 19508c2ecf20Sopenharmony_cistatic struct ubifs_nnode *scan_get_nnode(struct ubifs_info *c, 19518c2ecf20Sopenharmony_ci struct lpt_scan_node *path, 19528c2ecf20Sopenharmony_ci struct ubifs_nnode *parent, int iip) 19538c2ecf20Sopenharmony_ci{ 19548c2ecf20Sopenharmony_ci struct ubifs_nbranch *branch; 19558c2ecf20Sopenharmony_ci struct ubifs_nnode *nnode; 19568c2ecf20Sopenharmony_ci void *buf = c->lpt_nod_buf; 19578c2ecf20Sopenharmony_ci int err; 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci branch = &parent->nbranch[iip]; 19608c2ecf20Sopenharmony_ci nnode = branch->nnode; 19618c2ecf20Sopenharmony_ci if (nnode) { 19628c2ecf20Sopenharmony_ci path->in_tree = 1; 19638c2ecf20Sopenharmony_ci path->ptr.nnode = nnode; 19648c2ecf20Sopenharmony_ci return nnode; 19658c2ecf20Sopenharmony_ci } 19668c2ecf20Sopenharmony_ci nnode = &path->nnode; 19678c2ecf20Sopenharmony_ci path->in_tree = 0; 19688c2ecf20Sopenharmony_ci path->ptr.nnode = nnode; 19698c2ecf20Sopenharmony_ci memset(nnode, 0, sizeof(struct ubifs_nnode)); 19708c2ecf20Sopenharmony_ci if (branch->lnum == 0) { 19718c2ecf20Sopenharmony_ci /* 19728c2ecf20Sopenharmony_ci * This nnode was not written which just means that the LEB 19738c2ecf20Sopenharmony_ci * properties in the subtree below it describe empty LEBs. We 19748c2ecf20Sopenharmony_ci * make the nnode as though we had read it, which in fact means 19758c2ecf20Sopenharmony_ci * doing almost nothing. 19768c2ecf20Sopenharmony_ci */ 19778c2ecf20Sopenharmony_ci if (c->big_lpt) 19788c2ecf20Sopenharmony_ci nnode->num = calc_nnode_num_from_parent(c, parent, iip); 19798c2ecf20Sopenharmony_ci } else { 19808c2ecf20Sopenharmony_ci err = ubifs_leb_read(c, branch->lnum, buf, branch->offs, 19818c2ecf20Sopenharmony_ci c->nnode_sz, 1); 19828c2ecf20Sopenharmony_ci if (err) 19838c2ecf20Sopenharmony_ci return ERR_PTR(err); 19848c2ecf20Sopenharmony_ci err = ubifs_unpack_nnode(c, buf, nnode); 19858c2ecf20Sopenharmony_ci if (err) 19868c2ecf20Sopenharmony_ci return ERR_PTR(err); 19878c2ecf20Sopenharmony_ci } 19888c2ecf20Sopenharmony_ci err = validate_nnode(c, nnode, parent, iip); 19898c2ecf20Sopenharmony_ci if (err) 19908c2ecf20Sopenharmony_ci return ERR_PTR(err); 19918c2ecf20Sopenharmony_ci if (!c->big_lpt) 19928c2ecf20Sopenharmony_ci nnode->num = calc_nnode_num_from_parent(c, parent, iip); 19938c2ecf20Sopenharmony_ci nnode->level = parent->level - 1; 19948c2ecf20Sopenharmony_ci nnode->parent = parent; 19958c2ecf20Sopenharmony_ci nnode->iip = iip; 19968c2ecf20Sopenharmony_ci return nnode; 19978c2ecf20Sopenharmony_ci} 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_ci/** 20008c2ecf20Sopenharmony_ci * scan_get_pnode - for the scan, get a pnode from either the tree or flash. 20018c2ecf20Sopenharmony_ci * @c: the UBIFS file-system description object 20028c2ecf20Sopenharmony_ci * @path: where to put the pnode 20038c2ecf20Sopenharmony_ci * @parent: parent of the pnode 20048c2ecf20Sopenharmony_ci * @iip: index in parent of the pnode 20058c2ecf20Sopenharmony_ci * 20068c2ecf20Sopenharmony_ci * This function returns a pointer to the pnode on success or a negative error 20078c2ecf20Sopenharmony_ci * code on failure. 20088c2ecf20Sopenharmony_ci */ 20098c2ecf20Sopenharmony_cistatic struct ubifs_pnode *scan_get_pnode(struct ubifs_info *c, 20108c2ecf20Sopenharmony_ci struct lpt_scan_node *path, 20118c2ecf20Sopenharmony_ci struct ubifs_nnode *parent, int iip) 20128c2ecf20Sopenharmony_ci{ 20138c2ecf20Sopenharmony_ci struct ubifs_nbranch *branch; 20148c2ecf20Sopenharmony_ci struct ubifs_pnode *pnode; 20158c2ecf20Sopenharmony_ci void *buf = c->lpt_nod_buf; 20168c2ecf20Sopenharmony_ci int err; 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci branch = &parent->nbranch[iip]; 20198c2ecf20Sopenharmony_ci pnode = branch->pnode; 20208c2ecf20Sopenharmony_ci if (pnode) { 20218c2ecf20Sopenharmony_ci path->in_tree = 1; 20228c2ecf20Sopenharmony_ci path->ptr.pnode = pnode; 20238c2ecf20Sopenharmony_ci return pnode; 20248c2ecf20Sopenharmony_ci } 20258c2ecf20Sopenharmony_ci pnode = &path->pnode; 20268c2ecf20Sopenharmony_ci path->in_tree = 0; 20278c2ecf20Sopenharmony_ci path->ptr.pnode = pnode; 20288c2ecf20Sopenharmony_ci memset(pnode, 0, sizeof(struct ubifs_pnode)); 20298c2ecf20Sopenharmony_ci if (branch->lnum == 0) { 20308c2ecf20Sopenharmony_ci /* 20318c2ecf20Sopenharmony_ci * This pnode was not written which just means that the LEB 20328c2ecf20Sopenharmony_ci * properties in it describe empty LEBs. We make the pnode as 20338c2ecf20Sopenharmony_ci * though we had read it. 20348c2ecf20Sopenharmony_ci */ 20358c2ecf20Sopenharmony_ci int i; 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_ci if (c->big_lpt) 20388c2ecf20Sopenharmony_ci pnode->num = calc_pnode_num_from_parent(c, parent, iip); 20398c2ecf20Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 20408c2ecf20Sopenharmony_ci struct ubifs_lprops * const lprops = &pnode->lprops[i]; 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci lprops->free = c->leb_size; 20438c2ecf20Sopenharmony_ci lprops->flags = ubifs_categorize_lprops(c, lprops); 20448c2ecf20Sopenharmony_ci } 20458c2ecf20Sopenharmony_ci } else { 20468c2ecf20Sopenharmony_ci ubifs_assert(c, branch->lnum >= c->lpt_first && 20478c2ecf20Sopenharmony_ci branch->lnum <= c->lpt_last); 20488c2ecf20Sopenharmony_ci ubifs_assert(c, branch->offs >= 0 && branch->offs < c->leb_size); 20498c2ecf20Sopenharmony_ci err = ubifs_leb_read(c, branch->lnum, buf, branch->offs, 20508c2ecf20Sopenharmony_ci c->pnode_sz, 1); 20518c2ecf20Sopenharmony_ci if (err) 20528c2ecf20Sopenharmony_ci return ERR_PTR(err); 20538c2ecf20Sopenharmony_ci err = unpack_pnode(c, buf, pnode); 20548c2ecf20Sopenharmony_ci if (err) 20558c2ecf20Sopenharmony_ci return ERR_PTR(err); 20568c2ecf20Sopenharmony_ci } 20578c2ecf20Sopenharmony_ci err = validate_pnode(c, pnode, parent, iip); 20588c2ecf20Sopenharmony_ci if (err) 20598c2ecf20Sopenharmony_ci return ERR_PTR(err); 20608c2ecf20Sopenharmony_ci if (!c->big_lpt) 20618c2ecf20Sopenharmony_ci pnode->num = calc_pnode_num_from_parent(c, parent, iip); 20628c2ecf20Sopenharmony_ci pnode->parent = parent; 20638c2ecf20Sopenharmony_ci pnode->iip = iip; 20648c2ecf20Sopenharmony_ci set_pnode_lnum(c, pnode); 20658c2ecf20Sopenharmony_ci return pnode; 20668c2ecf20Sopenharmony_ci} 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci/** 20698c2ecf20Sopenharmony_ci * ubifs_lpt_scan_nolock - scan the LPT. 20708c2ecf20Sopenharmony_ci * @c: the UBIFS file-system description object 20718c2ecf20Sopenharmony_ci * @start_lnum: LEB number from which to start scanning 20728c2ecf20Sopenharmony_ci * @end_lnum: LEB number at which to stop scanning 20738c2ecf20Sopenharmony_ci * @scan_cb: callback function called for each lprops 20748c2ecf20Sopenharmony_ci * @data: data to be passed to the callback function 20758c2ecf20Sopenharmony_ci * 20768c2ecf20Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 20778c2ecf20Sopenharmony_ci */ 20788c2ecf20Sopenharmony_ciint ubifs_lpt_scan_nolock(struct ubifs_info *c, int start_lnum, int end_lnum, 20798c2ecf20Sopenharmony_ci ubifs_lpt_scan_callback scan_cb, void *data) 20808c2ecf20Sopenharmony_ci{ 20818c2ecf20Sopenharmony_ci int err = 0, i, h, iip, shft; 20828c2ecf20Sopenharmony_ci struct ubifs_nnode *nnode; 20838c2ecf20Sopenharmony_ci struct ubifs_pnode *pnode; 20848c2ecf20Sopenharmony_ci struct lpt_scan_node *path; 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci if (start_lnum == -1) { 20878c2ecf20Sopenharmony_ci start_lnum = end_lnum + 1; 20888c2ecf20Sopenharmony_ci if (start_lnum >= c->leb_cnt) 20898c2ecf20Sopenharmony_ci start_lnum = c->main_first; 20908c2ecf20Sopenharmony_ci } 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci ubifs_assert(c, start_lnum >= c->main_first && start_lnum < c->leb_cnt); 20938c2ecf20Sopenharmony_ci ubifs_assert(c, end_lnum >= c->main_first && end_lnum < c->leb_cnt); 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_ci if (!c->nroot) { 20968c2ecf20Sopenharmony_ci err = ubifs_read_nnode(c, NULL, 0); 20978c2ecf20Sopenharmony_ci if (err) 20988c2ecf20Sopenharmony_ci return err; 20998c2ecf20Sopenharmony_ci } 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci path = kmalloc_array(c->lpt_hght + 1, sizeof(struct lpt_scan_node), 21028c2ecf20Sopenharmony_ci GFP_NOFS); 21038c2ecf20Sopenharmony_ci if (!path) 21048c2ecf20Sopenharmony_ci return -ENOMEM; 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci path[0].ptr.nnode = c->nroot; 21078c2ecf20Sopenharmony_ci path[0].in_tree = 1; 21088c2ecf20Sopenharmony_ciagain: 21098c2ecf20Sopenharmony_ci /* Descend to the pnode containing start_lnum */ 21108c2ecf20Sopenharmony_ci nnode = c->nroot; 21118c2ecf20Sopenharmony_ci i = start_lnum - c->main_first; 21128c2ecf20Sopenharmony_ci shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT; 21138c2ecf20Sopenharmony_ci for (h = 1; h < c->lpt_hght; h++) { 21148c2ecf20Sopenharmony_ci iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); 21158c2ecf20Sopenharmony_ci shft -= UBIFS_LPT_FANOUT_SHIFT; 21168c2ecf20Sopenharmony_ci nnode = scan_get_nnode(c, path + h, nnode, iip); 21178c2ecf20Sopenharmony_ci if (IS_ERR(nnode)) { 21188c2ecf20Sopenharmony_ci err = PTR_ERR(nnode); 21198c2ecf20Sopenharmony_ci goto out; 21208c2ecf20Sopenharmony_ci } 21218c2ecf20Sopenharmony_ci } 21228c2ecf20Sopenharmony_ci iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); 21238c2ecf20Sopenharmony_ci pnode = scan_get_pnode(c, path + h, nnode, iip); 21248c2ecf20Sopenharmony_ci if (IS_ERR(pnode)) { 21258c2ecf20Sopenharmony_ci err = PTR_ERR(pnode); 21268c2ecf20Sopenharmony_ci goto out; 21278c2ecf20Sopenharmony_ci } 21288c2ecf20Sopenharmony_ci iip = (i & (UBIFS_LPT_FANOUT - 1)); 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_ci /* Loop for each lprops */ 21318c2ecf20Sopenharmony_ci while (1) { 21328c2ecf20Sopenharmony_ci struct ubifs_lprops *lprops = &pnode->lprops[iip]; 21338c2ecf20Sopenharmony_ci int ret, lnum = lprops->lnum; 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci ret = scan_cb(c, lprops, path[h].in_tree, data); 21368c2ecf20Sopenharmony_ci if (ret < 0) { 21378c2ecf20Sopenharmony_ci err = ret; 21388c2ecf20Sopenharmony_ci goto out; 21398c2ecf20Sopenharmony_ci } 21408c2ecf20Sopenharmony_ci if (ret & LPT_SCAN_ADD) { 21418c2ecf20Sopenharmony_ci /* Add all the nodes in path to the tree in memory */ 21428c2ecf20Sopenharmony_ci for (h = 1; h < c->lpt_hght; h++) { 21438c2ecf20Sopenharmony_ci const size_t sz = sizeof(struct ubifs_nnode); 21448c2ecf20Sopenharmony_ci struct ubifs_nnode *parent; 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_ci if (path[h].in_tree) 21478c2ecf20Sopenharmony_ci continue; 21488c2ecf20Sopenharmony_ci nnode = kmemdup(&path[h].nnode, sz, GFP_NOFS); 21498c2ecf20Sopenharmony_ci if (!nnode) { 21508c2ecf20Sopenharmony_ci err = -ENOMEM; 21518c2ecf20Sopenharmony_ci goto out; 21528c2ecf20Sopenharmony_ci } 21538c2ecf20Sopenharmony_ci parent = nnode->parent; 21548c2ecf20Sopenharmony_ci parent->nbranch[nnode->iip].nnode = nnode; 21558c2ecf20Sopenharmony_ci path[h].ptr.nnode = nnode; 21568c2ecf20Sopenharmony_ci path[h].in_tree = 1; 21578c2ecf20Sopenharmony_ci path[h + 1].cnode.parent = nnode; 21588c2ecf20Sopenharmony_ci } 21598c2ecf20Sopenharmony_ci if (path[h].in_tree) 21608c2ecf20Sopenharmony_ci ubifs_ensure_cat(c, lprops); 21618c2ecf20Sopenharmony_ci else { 21628c2ecf20Sopenharmony_ci const size_t sz = sizeof(struct ubifs_pnode); 21638c2ecf20Sopenharmony_ci struct ubifs_nnode *parent; 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_ci pnode = kmemdup(&path[h].pnode, sz, GFP_NOFS); 21668c2ecf20Sopenharmony_ci if (!pnode) { 21678c2ecf20Sopenharmony_ci err = -ENOMEM; 21688c2ecf20Sopenharmony_ci goto out; 21698c2ecf20Sopenharmony_ci } 21708c2ecf20Sopenharmony_ci parent = pnode->parent; 21718c2ecf20Sopenharmony_ci parent->nbranch[pnode->iip].pnode = pnode; 21728c2ecf20Sopenharmony_ci path[h].ptr.pnode = pnode; 21738c2ecf20Sopenharmony_ci path[h].in_tree = 1; 21748c2ecf20Sopenharmony_ci update_cats(c, pnode); 21758c2ecf20Sopenharmony_ci c->pnodes_have += 1; 21768c2ecf20Sopenharmony_ci } 21778c2ecf20Sopenharmony_ci err = dbg_check_lpt_nodes(c, (struct ubifs_cnode *) 21788c2ecf20Sopenharmony_ci c->nroot, 0, 0); 21798c2ecf20Sopenharmony_ci if (err) 21808c2ecf20Sopenharmony_ci goto out; 21818c2ecf20Sopenharmony_ci err = dbg_check_cats(c); 21828c2ecf20Sopenharmony_ci if (err) 21838c2ecf20Sopenharmony_ci goto out; 21848c2ecf20Sopenharmony_ci } 21858c2ecf20Sopenharmony_ci if (ret & LPT_SCAN_STOP) { 21868c2ecf20Sopenharmony_ci err = 0; 21878c2ecf20Sopenharmony_ci break; 21888c2ecf20Sopenharmony_ci } 21898c2ecf20Sopenharmony_ci /* Get the next lprops */ 21908c2ecf20Sopenharmony_ci if (lnum == end_lnum) { 21918c2ecf20Sopenharmony_ci /* 21928c2ecf20Sopenharmony_ci * We got to the end without finding what we were 21938c2ecf20Sopenharmony_ci * looking for 21948c2ecf20Sopenharmony_ci */ 21958c2ecf20Sopenharmony_ci err = -ENOSPC; 21968c2ecf20Sopenharmony_ci goto out; 21978c2ecf20Sopenharmony_ci } 21988c2ecf20Sopenharmony_ci if (lnum + 1 >= c->leb_cnt) { 21998c2ecf20Sopenharmony_ci /* Wrap-around to the beginning */ 22008c2ecf20Sopenharmony_ci start_lnum = c->main_first; 22018c2ecf20Sopenharmony_ci goto again; 22028c2ecf20Sopenharmony_ci } 22038c2ecf20Sopenharmony_ci if (iip + 1 < UBIFS_LPT_FANOUT) { 22048c2ecf20Sopenharmony_ci /* Next lprops is in the same pnode */ 22058c2ecf20Sopenharmony_ci iip += 1; 22068c2ecf20Sopenharmony_ci continue; 22078c2ecf20Sopenharmony_ci } 22088c2ecf20Sopenharmony_ci /* We need to get the next pnode. Go up until we can go right */ 22098c2ecf20Sopenharmony_ci iip = pnode->iip; 22108c2ecf20Sopenharmony_ci while (1) { 22118c2ecf20Sopenharmony_ci h -= 1; 22128c2ecf20Sopenharmony_ci ubifs_assert(c, h >= 0); 22138c2ecf20Sopenharmony_ci nnode = path[h].ptr.nnode; 22148c2ecf20Sopenharmony_ci if (iip + 1 < UBIFS_LPT_FANOUT) 22158c2ecf20Sopenharmony_ci break; 22168c2ecf20Sopenharmony_ci iip = nnode->iip; 22178c2ecf20Sopenharmony_ci } 22188c2ecf20Sopenharmony_ci /* Go right */ 22198c2ecf20Sopenharmony_ci iip += 1; 22208c2ecf20Sopenharmony_ci /* Descend to the pnode */ 22218c2ecf20Sopenharmony_ci h += 1; 22228c2ecf20Sopenharmony_ci for (; h < c->lpt_hght; h++) { 22238c2ecf20Sopenharmony_ci nnode = scan_get_nnode(c, path + h, nnode, iip); 22248c2ecf20Sopenharmony_ci if (IS_ERR(nnode)) { 22258c2ecf20Sopenharmony_ci err = PTR_ERR(nnode); 22268c2ecf20Sopenharmony_ci goto out; 22278c2ecf20Sopenharmony_ci } 22288c2ecf20Sopenharmony_ci iip = 0; 22298c2ecf20Sopenharmony_ci } 22308c2ecf20Sopenharmony_ci pnode = scan_get_pnode(c, path + h, nnode, iip); 22318c2ecf20Sopenharmony_ci if (IS_ERR(pnode)) { 22328c2ecf20Sopenharmony_ci err = PTR_ERR(pnode); 22338c2ecf20Sopenharmony_ci goto out; 22348c2ecf20Sopenharmony_ci } 22358c2ecf20Sopenharmony_ci iip = 0; 22368c2ecf20Sopenharmony_ci } 22378c2ecf20Sopenharmony_ciout: 22388c2ecf20Sopenharmony_ci kfree(path); 22398c2ecf20Sopenharmony_ci return err; 22408c2ecf20Sopenharmony_ci} 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_ci/** 22438c2ecf20Sopenharmony_ci * dbg_chk_pnode - check a pnode. 22448c2ecf20Sopenharmony_ci * @c: the UBIFS file-system description object 22458c2ecf20Sopenharmony_ci * @pnode: pnode to check 22468c2ecf20Sopenharmony_ci * @col: pnode column 22478c2ecf20Sopenharmony_ci * 22488c2ecf20Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 22498c2ecf20Sopenharmony_ci */ 22508c2ecf20Sopenharmony_cistatic int dbg_chk_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode, 22518c2ecf20Sopenharmony_ci int col) 22528c2ecf20Sopenharmony_ci{ 22538c2ecf20Sopenharmony_ci int i; 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci if (pnode->num != col) { 22568c2ecf20Sopenharmony_ci ubifs_err(c, "pnode num %d expected %d parent num %d iip %d", 22578c2ecf20Sopenharmony_ci pnode->num, col, pnode->parent->num, pnode->iip); 22588c2ecf20Sopenharmony_ci return -EINVAL; 22598c2ecf20Sopenharmony_ci } 22608c2ecf20Sopenharmony_ci for (i = 0; i < UBIFS_LPT_FANOUT; i++) { 22618c2ecf20Sopenharmony_ci struct ubifs_lprops *lp, *lprops = &pnode->lprops[i]; 22628c2ecf20Sopenharmony_ci int lnum = (pnode->num << UBIFS_LPT_FANOUT_SHIFT) + i + 22638c2ecf20Sopenharmony_ci c->main_first; 22648c2ecf20Sopenharmony_ci int found, cat = lprops->flags & LPROPS_CAT_MASK; 22658c2ecf20Sopenharmony_ci struct ubifs_lpt_heap *heap; 22668c2ecf20Sopenharmony_ci struct list_head *list = NULL; 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci if (lnum >= c->leb_cnt) 22698c2ecf20Sopenharmony_ci continue; 22708c2ecf20Sopenharmony_ci if (lprops->lnum != lnum) { 22718c2ecf20Sopenharmony_ci ubifs_err(c, "bad LEB number %d expected %d", 22728c2ecf20Sopenharmony_ci lprops->lnum, lnum); 22738c2ecf20Sopenharmony_ci return -EINVAL; 22748c2ecf20Sopenharmony_ci } 22758c2ecf20Sopenharmony_ci if (lprops->flags & LPROPS_TAKEN) { 22768c2ecf20Sopenharmony_ci if (cat != LPROPS_UNCAT) { 22778c2ecf20Sopenharmony_ci ubifs_err(c, "LEB %d taken but not uncat %d", 22788c2ecf20Sopenharmony_ci lprops->lnum, cat); 22798c2ecf20Sopenharmony_ci return -EINVAL; 22808c2ecf20Sopenharmony_ci } 22818c2ecf20Sopenharmony_ci continue; 22828c2ecf20Sopenharmony_ci } 22838c2ecf20Sopenharmony_ci if (lprops->flags & LPROPS_INDEX) { 22848c2ecf20Sopenharmony_ci switch (cat) { 22858c2ecf20Sopenharmony_ci case LPROPS_UNCAT: 22868c2ecf20Sopenharmony_ci case LPROPS_DIRTY_IDX: 22878c2ecf20Sopenharmony_ci case LPROPS_FRDI_IDX: 22888c2ecf20Sopenharmony_ci break; 22898c2ecf20Sopenharmony_ci default: 22908c2ecf20Sopenharmony_ci ubifs_err(c, "LEB %d index but cat %d", 22918c2ecf20Sopenharmony_ci lprops->lnum, cat); 22928c2ecf20Sopenharmony_ci return -EINVAL; 22938c2ecf20Sopenharmony_ci } 22948c2ecf20Sopenharmony_ci } else { 22958c2ecf20Sopenharmony_ci switch (cat) { 22968c2ecf20Sopenharmony_ci case LPROPS_UNCAT: 22978c2ecf20Sopenharmony_ci case LPROPS_DIRTY: 22988c2ecf20Sopenharmony_ci case LPROPS_FREE: 22998c2ecf20Sopenharmony_ci case LPROPS_EMPTY: 23008c2ecf20Sopenharmony_ci case LPROPS_FREEABLE: 23018c2ecf20Sopenharmony_ci break; 23028c2ecf20Sopenharmony_ci default: 23038c2ecf20Sopenharmony_ci ubifs_err(c, "LEB %d not index but cat %d", 23048c2ecf20Sopenharmony_ci lprops->lnum, cat); 23058c2ecf20Sopenharmony_ci return -EINVAL; 23068c2ecf20Sopenharmony_ci } 23078c2ecf20Sopenharmony_ci } 23088c2ecf20Sopenharmony_ci switch (cat) { 23098c2ecf20Sopenharmony_ci case LPROPS_UNCAT: 23108c2ecf20Sopenharmony_ci list = &c->uncat_list; 23118c2ecf20Sopenharmony_ci break; 23128c2ecf20Sopenharmony_ci case LPROPS_EMPTY: 23138c2ecf20Sopenharmony_ci list = &c->empty_list; 23148c2ecf20Sopenharmony_ci break; 23158c2ecf20Sopenharmony_ci case LPROPS_FREEABLE: 23168c2ecf20Sopenharmony_ci list = &c->freeable_list; 23178c2ecf20Sopenharmony_ci break; 23188c2ecf20Sopenharmony_ci case LPROPS_FRDI_IDX: 23198c2ecf20Sopenharmony_ci list = &c->frdi_idx_list; 23208c2ecf20Sopenharmony_ci break; 23218c2ecf20Sopenharmony_ci } 23228c2ecf20Sopenharmony_ci found = 0; 23238c2ecf20Sopenharmony_ci switch (cat) { 23248c2ecf20Sopenharmony_ci case LPROPS_DIRTY: 23258c2ecf20Sopenharmony_ci case LPROPS_DIRTY_IDX: 23268c2ecf20Sopenharmony_ci case LPROPS_FREE: 23278c2ecf20Sopenharmony_ci heap = &c->lpt_heap[cat - 1]; 23288c2ecf20Sopenharmony_ci if (lprops->hpos < heap->cnt && 23298c2ecf20Sopenharmony_ci heap->arr[lprops->hpos] == lprops) 23308c2ecf20Sopenharmony_ci found = 1; 23318c2ecf20Sopenharmony_ci break; 23328c2ecf20Sopenharmony_ci case LPROPS_UNCAT: 23338c2ecf20Sopenharmony_ci case LPROPS_EMPTY: 23348c2ecf20Sopenharmony_ci case LPROPS_FREEABLE: 23358c2ecf20Sopenharmony_ci case LPROPS_FRDI_IDX: 23368c2ecf20Sopenharmony_ci list_for_each_entry(lp, list, list) 23378c2ecf20Sopenharmony_ci if (lprops == lp) { 23388c2ecf20Sopenharmony_ci found = 1; 23398c2ecf20Sopenharmony_ci break; 23408c2ecf20Sopenharmony_ci } 23418c2ecf20Sopenharmony_ci break; 23428c2ecf20Sopenharmony_ci } 23438c2ecf20Sopenharmony_ci if (!found) { 23448c2ecf20Sopenharmony_ci ubifs_err(c, "LEB %d cat %d not found in cat heap/list", 23458c2ecf20Sopenharmony_ci lprops->lnum, cat); 23468c2ecf20Sopenharmony_ci return -EINVAL; 23478c2ecf20Sopenharmony_ci } 23488c2ecf20Sopenharmony_ci switch (cat) { 23498c2ecf20Sopenharmony_ci case LPROPS_EMPTY: 23508c2ecf20Sopenharmony_ci if (lprops->free != c->leb_size) { 23518c2ecf20Sopenharmony_ci ubifs_err(c, "LEB %d cat %d free %d dirty %d", 23528c2ecf20Sopenharmony_ci lprops->lnum, cat, lprops->free, 23538c2ecf20Sopenharmony_ci lprops->dirty); 23548c2ecf20Sopenharmony_ci return -EINVAL; 23558c2ecf20Sopenharmony_ci } 23568c2ecf20Sopenharmony_ci break; 23578c2ecf20Sopenharmony_ci case LPROPS_FREEABLE: 23588c2ecf20Sopenharmony_ci case LPROPS_FRDI_IDX: 23598c2ecf20Sopenharmony_ci if (lprops->free + lprops->dirty != c->leb_size) { 23608c2ecf20Sopenharmony_ci ubifs_err(c, "LEB %d cat %d free %d dirty %d", 23618c2ecf20Sopenharmony_ci lprops->lnum, cat, lprops->free, 23628c2ecf20Sopenharmony_ci lprops->dirty); 23638c2ecf20Sopenharmony_ci return -EINVAL; 23648c2ecf20Sopenharmony_ci } 23658c2ecf20Sopenharmony_ci break; 23668c2ecf20Sopenharmony_ci } 23678c2ecf20Sopenharmony_ci } 23688c2ecf20Sopenharmony_ci return 0; 23698c2ecf20Sopenharmony_ci} 23708c2ecf20Sopenharmony_ci 23718c2ecf20Sopenharmony_ci/** 23728c2ecf20Sopenharmony_ci * dbg_check_lpt_nodes - check nnodes and pnodes. 23738c2ecf20Sopenharmony_ci * @c: the UBIFS file-system description object 23748c2ecf20Sopenharmony_ci * @cnode: next cnode (nnode or pnode) to check 23758c2ecf20Sopenharmony_ci * @row: row of cnode (root is zero) 23768c2ecf20Sopenharmony_ci * @col: column of cnode (leftmost is zero) 23778c2ecf20Sopenharmony_ci * 23788c2ecf20Sopenharmony_ci * This function returns %0 on success and a negative error code on failure. 23798c2ecf20Sopenharmony_ci */ 23808c2ecf20Sopenharmony_ciint dbg_check_lpt_nodes(struct ubifs_info *c, struct ubifs_cnode *cnode, 23818c2ecf20Sopenharmony_ci int row, int col) 23828c2ecf20Sopenharmony_ci{ 23838c2ecf20Sopenharmony_ci struct ubifs_nnode *nnode, *nn; 23848c2ecf20Sopenharmony_ci struct ubifs_cnode *cn; 23858c2ecf20Sopenharmony_ci int num, iip = 0, err; 23868c2ecf20Sopenharmony_ci 23878c2ecf20Sopenharmony_ci if (!dbg_is_chk_lprops(c)) 23888c2ecf20Sopenharmony_ci return 0; 23898c2ecf20Sopenharmony_ci 23908c2ecf20Sopenharmony_ci while (cnode) { 23918c2ecf20Sopenharmony_ci ubifs_assert(c, row >= 0); 23928c2ecf20Sopenharmony_ci nnode = cnode->parent; 23938c2ecf20Sopenharmony_ci if (cnode->level) { 23948c2ecf20Sopenharmony_ci /* cnode is a nnode */ 23958c2ecf20Sopenharmony_ci num = calc_nnode_num(row, col); 23968c2ecf20Sopenharmony_ci if (cnode->num != num) { 23978c2ecf20Sopenharmony_ci ubifs_err(c, "nnode num %d expected %d parent num %d iip %d", 23988c2ecf20Sopenharmony_ci cnode->num, num, 23998c2ecf20Sopenharmony_ci (nnode ? nnode->num : 0), cnode->iip); 24008c2ecf20Sopenharmony_ci return -EINVAL; 24018c2ecf20Sopenharmony_ci } 24028c2ecf20Sopenharmony_ci nn = (struct ubifs_nnode *)cnode; 24038c2ecf20Sopenharmony_ci while (iip < UBIFS_LPT_FANOUT) { 24048c2ecf20Sopenharmony_ci cn = nn->nbranch[iip].cnode; 24058c2ecf20Sopenharmony_ci if (cn) { 24068c2ecf20Sopenharmony_ci /* Go down */ 24078c2ecf20Sopenharmony_ci row += 1; 24088c2ecf20Sopenharmony_ci col <<= UBIFS_LPT_FANOUT_SHIFT; 24098c2ecf20Sopenharmony_ci col += iip; 24108c2ecf20Sopenharmony_ci iip = 0; 24118c2ecf20Sopenharmony_ci cnode = cn; 24128c2ecf20Sopenharmony_ci break; 24138c2ecf20Sopenharmony_ci } 24148c2ecf20Sopenharmony_ci /* Go right */ 24158c2ecf20Sopenharmony_ci iip += 1; 24168c2ecf20Sopenharmony_ci } 24178c2ecf20Sopenharmony_ci if (iip < UBIFS_LPT_FANOUT) 24188c2ecf20Sopenharmony_ci continue; 24198c2ecf20Sopenharmony_ci } else { 24208c2ecf20Sopenharmony_ci struct ubifs_pnode *pnode; 24218c2ecf20Sopenharmony_ci 24228c2ecf20Sopenharmony_ci /* cnode is a pnode */ 24238c2ecf20Sopenharmony_ci pnode = (struct ubifs_pnode *)cnode; 24248c2ecf20Sopenharmony_ci err = dbg_chk_pnode(c, pnode, col); 24258c2ecf20Sopenharmony_ci if (err) 24268c2ecf20Sopenharmony_ci return err; 24278c2ecf20Sopenharmony_ci } 24288c2ecf20Sopenharmony_ci /* Go up and to the right */ 24298c2ecf20Sopenharmony_ci row -= 1; 24308c2ecf20Sopenharmony_ci col >>= UBIFS_LPT_FANOUT_SHIFT; 24318c2ecf20Sopenharmony_ci iip = cnode->iip + 1; 24328c2ecf20Sopenharmony_ci cnode = (struct ubifs_cnode *)nnode; 24338c2ecf20Sopenharmony_ci } 24348c2ecf20Sopenharmony_ci return 0; 24358c2ecf20Sopenharmony_ci} 2436