// SPDX-License-Identifier: GPL-2.0-only /* * This file is part of UBIFS. * * Copyright (C) 2006-2008 Nokia Corporation. * * Authors: Adrian Hunter * Artem Bityutskiy (Битюцкий Артём)
*/
/* * This file implements the LEB properties tree (LPT) area. The LPT area * contains the LEB properties tree, a table of LPT area eraseblocks (ltab), and * (for the "big" model) a table of saved LEB numbers (lsave). The LPT area sits * between the log and the orphan area. * * The LPT area is like a miniature self-contained file system. It is required * that it never runs out of space, is fast to access and update, and scales * logarithmically. The LEB properties tree is implemented as a wandering tree * much like the TNC, and the LPT area has its own garbage collection. * * The LPT has two slightly different forms called the "small model" and the * "big model". The small model is used when the entire LEB properties table * can be written into a single eraseblock. In that case, garbage collection * consists of just writing the whole table, which therefore makes all other * eraseblocks reusable. In the case of the big model, dirty eraseblocks are * selected for garbage collection, which consists of marking the clean nodes in * that LEB as dirty, and then only the dirty nodes are written out. Also, in * the case of the big model, a table of LEB numbers is saved so that the entire * LPT does not to be scanned looking for empty eraseblocks when UBIFS is first * mounted.
*/
/** * do_calc_lpt_geom - calculate sizes for the LPT area. * @c: the UBIFS file-system description object * * Calculate the sizes of LPT bit fields, nodes, and tree, based on the * properties of the flash and whether LPT is "big" (c->big_lpt).
*/ staticvoid do_calc_lpt_geom(struct ubifs_info *c)
{ int i, n, bits, per_leb_wastage, max_pnode_cnt; longlong sz, tot_wastage;
n = DIV_ROUND_UP(c->pnode_cnt, UBIFS_LPT_FANOUT);
c->nnode_cnt = n; for (i = 1; i < c->lpt_hght; i++) {
n = DIV_ROUND_UP(n, UBIFS_LPT_FANOUT);
c->nnode_cnt += n;
}
/** * ubifs_calc_lpt_geom - calculate and check sizes for the LPT area. * @c: the UBIFS file-system description object * * This function returns %0 on success and a negative error code on failure.
*/ int ubifs_calc_lpt_geom(struct ubifs_info *c)
{ int lebs_needed; longlong sz;
do_calc_lpt_geom(c);
/* Verify that lpt_lebs is big enough */
sz = c->lpt_sz * 2; /* Must have at least 2 times the size */
lebs_needed = div_u64(sz + c->leb_size - 1, c->leb_size); if (lebs_needed > c->lpt_lebs) {
ubifs_err(c, "too few LPT LEBs"); return -EINVAL;
}
/* Verify that ltab fits in a single LEB (since ltab is a single node */ if (c->ltab_sz > c->leb_size) {
ubifs_err(c, "LPT ltab too big"); return -EINVAL;
}
c->check_lpt_free = c->big_lpt; return 0;
}
/** * calc_dflt_lpt_geom - calculate default LPT geometry. * @c: the UBIFS file-system description object * @main_lebs: number of main area LEBs is passed and returned here * @big_lpt: whether the LPT area is "big" is returned here * * The size of the LPT area depends on parameters that themselves are dependent * on the size of the LPT area. This function, successively recalculates the LPT * area geometry until the parameters and resultant geometry are consistent. * * This function returns %0 on success and a negative error code on failure.
*/ staticint calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, int *big_lpt)
{ int i, lebs_needed; longlong sz;
/* Start by assuming the minimum number of LPT LEBs */
c->lpt_lebs = UBIFS_MIN_LPT_LEBS;
c->main_lebs = *main_lebs - c->lpt_lebs; if (c->main_lebs <= 0) return -EINVAL;
/* And assume we will use the small LPT model */
c->big_lpt = 0;
/* * Calculate the geometry based on assumptions above and then see if it * makes sense
*/
do_calc_lpt_geom(c);
/* Small LPT model must have lpt_sz < leb_size */ if (c->lpt_sz > c->leb_size) { /* Nope, so try again using big LPT model */
c->big_lpt = 1;
do_calc_lpt_geom(c);
}
/* Now check there are enough LPT LEBs */ for (i = 0; i < 64 ; i++) {
sz = c->lpt_sz * 4; /* Allow 4 times the size */
lebs_needed = div_u64(sz + c->leb_size - 1, c->leb_size); if (lebs_needed > c->lpt_lebs) { /* Not enough LPT LEBs so try again with more */
c->lpt_lebs = lebs_needed;
c->main_lebs = *main_lebs - c->lpt_lebs; if (c->main_lebs <= 0) return -EINVAL;
do_calc_lpt_geom(c); continue;
} if (c->ltab_sz > c->leb_size) {
ubifs_err(c, "LPT ltab too big"); return -EINVAL;
}
*main_lebs = c->main_lebs;
*big_lpt = c->big_lpt; return 0;
} return -EINVAL;
}
/** * pack_bits - pack bit fields end-to-end. * @c: UBIFS file-system description object * @addr: address at which to pack (passed and next address returned) * @pos: bit position at which to pack (passed and next position returned) * @val: value to pack * @nrbits: number of bits of value to pack (1-32)
*/ staticvoid pack_bits(conststruct ubifs_info *c, uint8_t **addr, int *pos, uint32_t val, int nrbits)
{
uint8_t *p = *addr; int b = *pos;
/** * ubifs_unpack_bits - unpack bit fields. * @c: UBIFS file-system description object * @addr: address at which to unpack (passed and next address returned) * @pos: bit position at which to unpack (passed and next position returned) * @nrbits: number of bits of value to unpack (1-32) * * This functions returns the value unpacked.
*/
uint32_t ubifs_unpack_bits(conststruct ubifs_info *c, uint8_t **addr, int *pos, int nrbits)
{ constint k = 32 - nrbits;
uint8_t *p = *addr; int b = *pos;
uint32_t val; constint bytes = (nrbits + b + 7) >> 3;
ubifs_assert(c, nrbits > 0);
ubifs_assert(c, nrbits <= 32);
ubifs_assert(c, *pos >= 0);
ubifs_assert(c, *pos < 8); if (b) { switch (bytes) { case 2:
val = p[1]; break; case 3:
val = p[1] | ((uint32_t)p[2] << 8); break; case 4:
val = p[1] | ((uint32_t)p[2] << 8) |
((uint32_t)p[3] << 16); break; case 5:
val = p[1] | ((uint32_t)p[2] << 8) |
((uint32_t)p[3] << 16) |
((uint32_t)p[4] << 24);
}
val <<= (8 - b);
val |= *p >> b;
nrbits += b;
} else { switch (bytes) { case 1:
val = p[0]; break; case 2:
val = p[0] | ((uint32_t)p[1] << 8); break; case 3:
val = p[0] | ((uint32_t)p[1] << 8) |
((uint32_t)p[2] << 16); break; case 4:
val = p[0] | ((uint32_t)p[1] << 8) |
((uint32_t)p[2] << 16) |
((uint32_t)p[3] << 24); break;
}
}
val <<= k;
val >>= k;
b = nrbits & 7;
p += nrbits >> 3;
*addr = p;
*pos = b;
ubifs_assert(c, (val >> nrbits) == 0 || nrbits - b == 32); return val;
}
/** * ubifs_pack_pnode - pack all the bit fields of a pnode. * @c: UBIFS file-system description object * @buf: buffer into which to pack * @pnode: pnode to pack
*/ void ubifs_pack_pnode(struct ubifs_info *c, void *buf, struct ubifs_pnode *pnode)
{
uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; int i, pos = 0;
uint16_t crc;
/** * ubifs_pack_nnode - pack all the bit fields of a nnode. * @c: UBIFS file-system description object * @buf: buffer into which to pack * @nnode: nnode to pack
*/ void ubifs_pack_nnode(struct ubifs_info *c, void *buf, struct ubifs_nnode *nnode)
{
uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; int i, pos = 0;
uint16_t crc;
pack_bits(c, &addr, &pos, UBIFS_LPT_NNODE, UBIFS_LPT_TYPE_BITS); if (c->big_lpt)
pack_bits(c, &addr, &pos, nnode->num, c->pcnt_bits); for (i = 0; i < UBIFS_LPT_FANOUT; i++) { int lnum = nnode->nbranch[i].lnum;
/** * add_pnode_dirt - add dirty space to LPT LEB properties. * @c: UBIFS file-system description object * @pnode: pnode for which to add dirt
*/ staticvoid add_pnode_dirt(struct ubifs_info *c, struct ubifs_pnode *pnode)
{
ubifs_add_lpt_dirt(c, pnode->parent->nbranch[pnode->iip].lnum,
c->pnode_sz);
}
/** * calc_nnode_num - calculate nnode number. * @row: the row in the tree (root is zero) * @col: the column in the row (leftmost is zero) * * The nnode number is a number that uniquely identifies a nnode and can be used * easily to traverse the tree from the root to that nnode. * * This function calculates and returns the nnode number for the nnode at @row * and @col.
*/ staticint calc_nnode_num(int row, int col)
{ int num, bits;
num = 1; while (row--) {
bits = (col & (UBIFS_LPT_FANOUT - 1));
col >>= UBIFS_LPT_FANOUT_SHIFT;
num <<= UBIFS_LPT_FANOUT_SHIFT;
num |= bits;
} return num;
}
/** * calc_nnode_num_from_parent - calculate nnode number. * @c: UBIFS file-system description object * @parent: parent nnode * @iip: index in parent * * The nnode number is a number that uniquely identifies a nnode and can be used * easily to traverse the tree from the root to that nnode. * * This function calculates and returns the nnode number based on the parent's * nnode number and the index in parent.
*/ staticint calc_nnode_num_from_parent(conststruct ubifs_info *c, struct ubifs_nnode *parent, int iip)
{ int num, shft;
if (!parent) return 1;
shft = (c->lpt_hght - parent->level) * UBIFS_LPT_FANOUT_SHIFT;
num = parent->num ^ (1 << shft);
num |= (UBIFS_LPT_FANOUT + iip) << shft; return num;
}
/** * calc_pnode_num_from_parent - calculate pnode number. * @c: UBIFS file-system description object * @parent: parent nnode * @iip: index in parent * * The pnode number is a number that uniquely identifies a pnode and can be used * easily to traverse the tree from the root to that pnode. * * This function calculates and returns the pnode number based on the parent's * nnode number and the index in parent.
*/ staticint calc_pnode_num_from_parent(conststruct ubifs_info *c, struct ubifs_nnode *parent, int iip)
{ int i, n = c->lpt_hght - 1, pnum = parent->num, num = 0;
for (i = 0; i < n; i++) {
num <<= UBIFS_LPT_FANOUT_SHIFT;
num |= pnum & (UBIFS_LPT_FANOUT - 1);
pnum >>= UBIFS_LPT_FANOUT_SHIFT;
}
num <<= UBIFS_LPT_FANOUT_SHIFT;
num |= iip; return num;
}
/** * ubifs_create_dflt_lpt - create default LPT. * @c: UBIFS file-system description object * @main_lebs: number of main area LEBs is passed and returned here * @lpt_first: LEB number of first LPT LEB * @lpt_lebs: number of LEBs for LPT is passed and returned here * @big_lpt: use big LPT model is passed and returned here * @hash: hash of the LPT is returned here * * This function returns %0 on success and a negative error code on failure.
*/ int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first, int *lpt_lebs, int *big_lpt, u8 *hash)
{ int lnum, err = 0, node_sz, iopos, i, j, cnt, len, alen, row; int blnum, boffs, bsz, bcnt; struct ubifs_pnode *pnode = NULL; struct ubifs_nnode *nnode = NULL; void *buf = NULL, *p; struct ubifs_lpt_lprops *ltab = NULL; int *lsave = NULL; struct shash_desc *desc;
ubifs_assert(c, !c->ltab);
c->ltab = ltab; /* Needed by set_ltab */
/* Initialize LPT's own lprops */ for (i = 0; i < c->lpt_lebs; i++) {
ltab[i].free = c->leb_size;
ltab[i].dirty = 0;
ltab[i].tgc = 0;
ltab[i].cmt = 0;
}
lnum = lpt_first;
p = buf; /* Number of leaf nodes (pnodes) */
cnt = c->pnode_cnt;
/* * The first pnode contains the LEB properties for the LEBs that contain * the root inode node and the root index node of the index tree.
*/
node_sz = ALIGN(ubifs_idx_node_sz(c, 1), 8);
iopos = ALIGN(node_sz, c->min_io_size);
pnode->lprops[0].free = c->leb_size - iopos;
pnode->lprops[0].dirty = iopos - node_sz;
pnode->lprops[0].flags = LPROPS_INDEX;
/* * To calculate the internal node branches, we keep information about * the level below.
*/
blnum = lnum; /* LEB number of level below */
boffs = 0; /* Offset of level below */
bcnt = cnt; /* Number of nodes in level below */
bsz = c->pnode_sz; /* Size of nodes in level below */
/* Add all remaining pnodes */ for (i = 1; i < cnt; i++) { if (len + c->pnode_sz > c->leb_size) {
alen = ALIGN(len, c->min_io_size);
set_ltab(c, lnum, c->leb_size - alen, alen - len);
memset(p, 0xff, alen - len);
err = ubifs_leb_change(c, lnum++, buf, alen); if (err) goto out;
p = buf;
len = 0;
}
ubifs_pack_pnode(c, p, pnode);
err = ubifs_shash_update(c, desc, p, c->pnode_sz); if (err) goto out;
p += c->pnode_sz;
len += c->pnode_sz; /* * pnodes are simply numbered left to right starting at zero, * which means the pnode number can be used easily to traverse * down the tree to the corresponding pnode.
*/
pnode->num += 1;
}
row = 0; for (i = UBIFS_LPT_FANOUT; cnt > i; i <<= UBIFS_LPT_FANOUT_SHIFT)
row += 1; /* Add all nnodes, one level at a time */ while (1) { /* Number of internal nodes (nnodes) at next level */
cnt = DIV_ROUND_UP(cnt, UBIFS_LPT_FANOUT); for (i = 0; i < cnt; i++) { if (len + c->nnode_sz > c->leb_size) {
alen = ALIGN(len, c->min_io_size);
set_ltab(c, lnum, c->leb_size - alen,
alen - len);
memset(p, 0xff, alen - len);
err = ubifs_leb_change(c, lnum++, buf, alen); if (err) goto out;
p = buf;
len = 0;
} /* Only 1 nnode at this level, so it is the root */ if (cnt == 1) {
c->lpt_lnum = lnum;
c->lpt_offs = len;
} /* Set branches to the level below */ for (j = 0; j < UBIFS_LPT_FANOUT; j++) { if (bcnt) { if (boffs + bsz > c->leb_size) {
blnum += 1;
boffs = 0;
}
nnode->nbranch[j].lnum = blnum;
nnode->nbranch[j].offs = boffs;
boffs += bsz;
bcnt--;
} else {
nnode->nbranch[j].lnum = 0;
nnode->nbranch[j].offs = 0;
}
}
nnode->num = calc_nnode_num(row, i);
ubifs_pack_nnode(c, p, nnode);
p += c->nnode_sz;
len += c->nnode_sz;
} /* Only 1 nnode at this level, so it is the root */ if (cnt == 1) break; /* Update the information about the level below */
bcnt = cnt;
bsz = c->nnode_sz;
row -= 1;
}
if (*big_lpt) { /* Need to add LPT's save table */ if (len + c->lsave_sz > c->leb_size) {
alen = ALIGN(len, c->min_io_size);
set_ltab(c, lnum, c->leb_size - alen, alen - len);
memset(p, 0xff, alen - len);
err = ubifs_leb_change(c, lnum++, buf, alen); if (err) goto out;
p = buf;
len = 0;
}
c->lsave_lnum = lnum;
c->lsave_offs = len;
for (i = 0; i < c->lsave_cnt && i < *main_lebs; i++)
lsave[i] = c->main_first + i; for (; i < c->lsave_cnt; i++)
lsave[i] = c->main_first;
ubifs_pack_lsave(c, p, lsave);
p += c->lsave_sz;
len += c->lsave_sz;
}
/* Need to add LPT's own LEB properties table */ if (len + c->ltab_sz > c->leb_size) {
alen = ALIGN(len, c->min_io_size);
set_ltab(c, lnum, c->leb_size - alen, alen - len);
memset(p, 0xff, alen - len);
err = ubifs_leb_change(c, lnum++, buf, alen); if (err) goto out;
p = buf;
len = 0;
}
c->ltab_lnum = lnum;
c->ltab_offs = len;
/* Update ltab before packing it */
len += c->ltab_sz;
alen = ALIGN(len, c->min_io_size);
set_ltab(c, lnum, c->leb_size - alen, alen - len);
dbg_lp("space_bits %d", c->space_bits);
dbg_lp("lpt_lnum_bits %d", c->lpt_lnum_bits);
dbg_lp("lpt_offs_bits %d", c->lpt_offs_bits);
dbg_lp("lpt_spc_bits %d", c->lpt_spc_bits);
dbg_lp("pcnt_bits %d", c->pcnt_bits);
dbg_lp("lnum_bits %d", c->lnum_bits);
dbg_lp("pnode_sz %d", c->pnode_sz);
dbg_lp("nnode_sz %d", c->nnode_sz);
dbg_lp("ltab_sz %d", c->ltab_sz);
dbg_lp("lsave_sz %d", c->lsave_sz);
dbg_lp("lsave_cnt %d", c->lsave_cnt);
dbg_lp("lpt_hght %d", c->lpt_hght);
dbg_lp("big_lpt %u", c->big_lpt);
dbg_lp("LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs);
dbg_lp("LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs);
dbg_lp("LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs); if (c->big_lpt)
dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs);
out:
c->ltab = NULL;
kfree(desc);
kfree(lsave);
vfree(ltab);
vfree(buf);
kfree(nnode);
kfree(pnode); return err;
}
/** * update_cats - add LEB properties of a pnode to LEB category lists and heaps. * @c: UBIFS file-system description object * @pnode: pnode * * When a pnode is loaded into memory, the LEB properties it contains are added, * by this function, to the LEB category lists and heaps.
*/ staticvoid update_cats(struct ubifs_info *c, struct ubifs_pnode *pnode)
{ int i;
for (i = 0; i < UBIFS_LPT_FANOUT; i++) { int cat = pnode->lprops[i].flags & LPROPS_CAT_MASK; int lnum = pnode->lprops[i].lnum;
if (!lnum) return;
ubifs_add_to_cat(c, &pnode->lprops[i], cat);
}
}
/** * replace_cats - add LEB properties of a pnode to LEB category lists and heaps. * @c: UBIFS file-system description object * @old_pnode: pnode copied * @new_pnode: pnode copy * * During commit it is sometimes necessary to copy a pnode * (see dirty_cow_pnode). When that happens, references in * category lists and heaps must be replaced. This function does that.
*/ staticvoid replace_cats(struct ubifs_info *c, struct ubifs_pnode *old_pnode, struct ubifs_pnode *new_pnode)
{ int i;
for (i = 0; i < UBIFS_LPT_FANOUT; i++) { if (!new_pnode->lprops[i].lnum) return;
ubifs_replace_cat(c, &old_pnode->lprops[i],
&new_pnode->lprops[i]);
}
}
/** * check_lpt_crc - check LPT node crc is correct. * @c: UBIFS file-system description object * @buf: buffer containing node * @len: length of node * * This function returns %0 on success and a negative error code on failure.
*/ staticint check_lpt_crc(conststruct ubifs_info *c, void *buf, int len)
{ int pos = 0;
uint8_t *addr = buf;
uint16_t crc, calc_crc;
/** * check_lpt_type - check LPT node type is correct. * @c: UBIFS file-system description object * @addr: address of type bit field is passed and returned updated here * @pos: position of type bit field is passed and returned updated here * @type: expected type * * This function returns %0 on success and a negative error code on failure.
*/ staticint check_lpt_type(conststruct ubifs_info *c, uint8_t **addr, int *pos, int type)
{ int node_type;
node_type = ubifs_unpack_bits(c, addr, pos, UBIFS_LPT_TYPE_BITS); if (node_type != type) {
ubifs_err(c, "invalid type (%d) in LPT node type %d",
node_type, type);
dump_stack(); return -EINVAL;
} return 0;
}
/** * unpack_pnode - unpack a pnode. * @c: UBIFS file-system description object * @buf: buffer containing packed pnode to unpack * @pnode: pnode structure to fill * * This function returns %0 on success and a negative error code on failure.
*/ staticint unpack_pnode(conststruct ubifs_info *c, void *buf, struct ubifs_pnode *pnode)
{
uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; int i, pos = 0, err;
err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_PNODE); if (err) return err; if (c->big_lpt)
pnode->num = ubifs_unpack_bits(c, &addr, &pos, c->pcnt_bits); for (i = 0; i < UBIFS_LPT_FANOUT; i++) { struct ubifs_lprops * const lprops = &pnode->lprops[i];
/** * ubifs_unpack_nnode - unpack a nnode. * @c: UBIFS file-system description object * @buf: buffer containing packed nnode to unpack * @nnode: nnode structure to fill * * This function returns %0 on success and a negative error code on failure.
*/ int ubifs_unpack_nnode(conststruct ubifs_info *c, void *buf, struct ubifs_nnode *nnode)
{
uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; int i, pos = 0, err;
err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_NNODE); if (err) return err; if (c->big_lpt)
nnode->num = ubifs_unpack_bits(c, &addr, &pos, c->pcnt_bits); for (i = 0; i < UBIFS_LPT_FANOUT; i++) { int lnum;
/** * unpack_ltab - unpack the LPT's own lprops table. * @c: UBIFS file-system description object * @buf: buffer from which to unpack * * This function returns %0 on success and a negative error code on failure.
*/ staticint unpack_ltab(conststruct ubifs_info *c, void *buf)
{
uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; int i, pos = 0, err;
err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_LTAB); if (err) return err; for (i = 0; i < c->lpt_lebs; i++) { int free = ubifs_unpack_bits(c, &addr, &pos, c->lpt_spc_bits); int dirty = ubifs_unpack_bits(c, &addr, &pos, c->lpt_spc_bits);
/** * unpack_lsave - unpack the LPT's save table. * @c: UBIFS file-system description object * @buf: buffer from which to unpack * * This function returns %0 on success and a negative error code on failure.
*/ staticint unpack_lsave(conststruct ubifs_info *c, void *buf)
{
uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; int i, pos = 0, err;
err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_LSAVE); if (err) return err; for (i = 0; i < c->lsave_cnt; i++) { int lnum = ubifs_unpack_bits(c, &addr, &pos, c->lnum_bits);
/** * validate_nnode - validate a nnode. * @c: UBIFS file-system description object * @nnode: nnode to validate * @parent: parent nnode (or NULL for the root nnode) * @iip: index in parent * * This function returns %0 on success and a negative error code on failure.
*/ staticint validate_nnode(conststruct ubifs_info *c, struct ubifs_nnode *nnode, struct ubifs_nnode *parent, int iip)
{ int i, lvl, max_offs;
if (c->big_lpt) { int num = calc_nnode_num_from_parent(c, parent, iip);
if (nnode->num != num) return -EINVAL;
}
lvl = parent ? parent->level - 1 : c->lpt_hght; if (lvl < 1) return -EINVAL; if (lvl == 1)
max_offs = c->leb_size - c->pnode_sz; else
max_offs = c->leb_size - c->nnode_sz; for (i = 0; i < UBIFS_LPT_FANOUT; i++) { int lnum = nnode->nbranch[i].lnum; int offs = nnode->nbranch[i].offs;
if (lnum == 0) { if (offs != 0) return -EINVAL; continue;
} if (lnum < c->lpt_first || lnum > c->lpt_last) return -EINVAL; if (offs < 0 || offs > max_offs) return -EINVAL;
} return 0;
}
/** * validate_pnode - validate a pnode. * @c: UBIFS file-system description object * @pnode: pnode to validate * @parent: parent nnode * @iip: index in parent * * This function returns %0 on success and a negative error code on failure.
*/ staticint validate_pnode(conststruct ubifs_info *c, struct ubifs_pnode *pnode, struct ubifs_nnode *parent, int iip)
{ int i;
if (c->big_lpt) { int num = calc_pnode_num_from_parent(c, parent, iip);
if (pnode->num != num) return -EINVAL;
} for (i = 0; i < UBIFS_LPT_FANOUT; i++) { int free = pnode->lprops[i].free; int dirty = pnode->lprops[i].dirty;
/** * set_pnode_lnum - set LEB numbers on a pnode. * @c: UBIFS file-system description object * @pnode: pnode to update * * This function calculates the LEB numbers for the LEB properties it contains * based on the pnode number.
*/ staticvoid set_pnode_lnum(conststruct ubifs_info *c, struct ubifs_pnode *pnode)
{ int i, lnum;
lnum = (pnode->num << UBIFS_LPT_FANOUT_SHIFT) + c->main_first; for (i = 0; i < UBIFS_LPT_FANOUT; i++) { if (lnum >= c->leb_cnt) return;
pnode->lprops[i].lnum = lnum++;
}
}
/** * ubifs_read_nnode - read a nnode from flash and link it to the tree in memory. * @c: UBIFS file-system description object * @parent: parent nnode (or NULL for the root) * @iip: index in parent * * This function returns %0 on success and a negative error code on failure.
*/ int ubifs_read_nnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip)
{ struct ubifs_nbranch *branch = NULL; struct ubifs_nnode *nnode = NULL; void *buf = c->lpt_nod_buf; int err, lnum, offs;
if (parent) {
branch = &parent->nbranch[iip];
lnum = branch->lnum;
offs = branch->offs;
} else {
lnum = c->lpt_lnum;
offs = c->lpt_offs;
}
nnode = kzalloc(sizeof(struct ubifs_nnode), GFP_NOFS); if (!nnode) {
err = -ENOMEM; goto out;
} if (lnum == 0) { /* * This nnode was not written which just means that the LEB * properties in the subtree below it describe empty LEBs. We * make the nnode as though we had read it, which in fact means * doing almost nothing.
*/ if (c->big_lpt)
nnode->num = calc_nnode_num_from_parent(c, parent, iip);
} else {
err = ubifs_leb_read(c, lnum, buf, offs, c->nnode_sz, 1); if (err) goto out;
err = ubifs_unpack_nnode(c, buf, nnode); if (err) goto out;
}
err = validate_nnode(c, nnode, parent, iip); if (err) goto out; if (!c->big_lpt)
nnode->num = calc_nnode_num_from_parent(c, parent, iip); if (parent) {
branch->nnode = nnode;
nnode->level = parent->level - 1;
} else {
c->nroot = nnode;
nnode->level = c->lpt_hght;
}
nnode->parent = parent;
nnode->iip = iip; return 0;
/** * read_pnode - read a pnode from flash and link it to the tree in memory. * @c: UBIFS file-system description object * @parent: parent nnode * @iip: index in parent * * This function returns %0 on success and a negative error code on failure.
*/ staticint read_pnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip)
{ struct ubifs_nbranch *branch; struct ubifs_pnode *pnode = NULL; void *buf = c->lpt_nod_buf; int err, lnum, offs;
if (lnum == 0) { /* * This pnode was not written which just means that the LEB * properties in it describe empty LEBs. We make the pnode as * though we had read it.
*/ int i;
if (c->big_lpt)
pnode->num = calc_pnode_num_from_parent(c, parent, iip); for (i = 0; i < UBIFS_LPT_FANOUT; i++) { struct ubifs_lprops * const lprops = &pnode->lprops[i];
/** * read_ltab - read LPT's own lprops table. * @c: UBIFS file-system description object * * This function returns %0 on success and a negative error code on failure.
*/ staticint read_ltab(struct ubifs_info *c)
{ int err; void *buf;
/** * read_lsave - read LPT's save table. * @c: UBIFS file-system description object * * This function returns %0 on success and a negative error code on failure.
*/ staticint read_lsave(struct ubifs_info *c)
{ int err, i; void *buf;
buf = vmalloc(c->lsave_sz); if (!buf) return -ENOMEM;
err = ubifs_leb_read(c, c->lsave_lnum, buf, c->lsave_offs,
c->lsave_sz, 1); if (err) goto out;
err = unpack_lsave(c, buf); if (err) goto out; for (i = 0; i < c->lsave_cnt; i++) { int lnum = c->lsave[i]; struct ubifs_lprops *lprops;
/* * Due to automatic resizing, the values in the lsave table * could be beyond the volume size - just ignore them.
*/ if (lnum >= c->leb_cnt) continue;
lprops = ubifs_lpt_lookup(c, lnum); if (IS_ERR(lprops)) {
err = PTR_ERR(lprops); goto out;
}
}
out:
vfree(buf); return err;
}
/** * ubifs_get_nnode - get a nnode. * @c: UBIFS file-system description object * @parent: parent nnode (or NULL for the root) * @iip: index in parent * * This function returns a pointer to the nnode on success or a negative error * code on failure.
*/ struct ubifs_nnode *ubifs_get_nnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip)
{ struct ubifs_nbranch *branch; struct ubifs_nnode *nnode; int err;
/** * ubifs_get_pnode - get a pnode. * @c: UBIFS file-system description object * @parent: parent nnode * @iip: index in parent * * This function returns a pointer to the pnode on success or a negative error * code on failure.
*/ struct ubifs_pnode *ubifs_get_pnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip)
{ struct ubifs_nbranch *branch; struct ubifs_pnode *pnode; int err;
/** * ubifs_pnode_lookup - lookup a pnode in the LPT. * @c: UBIFS file-system description object * @i: pnode number (0 to (main_lebs - 1) / UBIFS_LPT_FANOUT) * * This function returns a pointer to the pnode on success or a negative * error code on failure.
*/ struct ubifs_pnode *ubifs_pnode_lookup(struct ubifs_info *c, int i)
{ int err, h, iip, shft; struct ubifs_nnode *nnode;
/** * ubifs_lpt_lookup - lookup LEB properties in the LPT. * @c: UBIFS file-system description object * @lnum: LEB number to lookup * * This function returns a pointer to the LEB properties on success or a * negative error code on failure.
*/ struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum)
{ int i, iip; struct ubifs_pnode *pnode;
i = lnum - c->main_first;
pnode = ubifs_pnode_lookup(c, i >> UBIFS_LPT_FANOUT_SHIFT); if (IS_ERR(pnode)) return ERR_CAST(pnode);
iip = (i & (UBIFS_LPT_FANOUT - 1));
dbg_lp("LEB %d, free %d, dirty %d, flags %d", lnum,
pnode->lprops[iip].free, pnode->lprops[iip].dirty,
pnode->lprops[iip].flags); return &pnode->lprops[iip];
}
/** * dirty_cow_nnode - ensure a nnode is not being committed. * @c: UBIFS file-system description object * @nnode: nnode to check * * Returns dirtied nnode on success or negative error code on failure.
*/ staticstruct ubifs_nnode *dirty_cow_nnode(struct ubifs_info *c, struct ubifs_nnode *nnode)
{ struct ubifs_nnode *n; int i;
if (!test_bit(COW_CNODE, &nnode->flags)) { /* nnode is not being committed */ if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) {
c->dirty_nn_cnt += 1;
ubifs_add_nnode_dirt(c, nnode);
} return nnode;
}
/* nnode is being committed, so copy it */
n = kmemdup(nnode, sizeof(struct ubifs_nnode), GFP_NOFS); if (unlikely(!n)) return ERR_PTR(-ENOMEM);
/** * dirty_cow_pnode - ensure a pnode is not being committed. * @c: UBIFS file-system description object * @pnode: pnode to check * * Returns dirtied pnode on success or negative error code on failure.
*/ staticstruct ubifs_pnode *dirty_cow_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode)
{ struct ubifs_pnode *p;
if (!test_bit(COW_CNODE, &pnode->flags)) { /* pnode is not being committed */ if (!test_and_set_bit(DIRTY_CNODE, &pnode->flags)) {
c->dirty_pn_cnt += 1;
add_pnode_dirt(c, pnode);
} return pnode;
}
/* pnode is being committed, so copy it */
p = kmemdup(pnode, sizeof(struct ubifs_pnode), GFP_NOFS); if (unlikely(!p)) return ERR_PTR(-ENOMEM);
/** * ubifs_lpt_lookup_dirty - lookup LEB properties in the LPT. * @c: UBIFS file-system description object * @lnum: LEB number to lookup * * This function returns a pointer to the LEB properties on success or a * negative error code on failure.
*/ struct ubifs_lprops *ubifs_lpt_lookup_dirty(struct ubifs_info *c, int lnum)
{ int err, i, h, iip, shft; struct ubifs_nnode *nnode; struct ubifs_pnode *pnode;
/** * ubifs_lpt_calc_hash - Calculate hash of the LPT pnodes * @c: UBIFS file-system description object * @hash: the returned hash of the LPT pnodes * * This function iterates over the LPT pnodes and creates a hash over them. * Returns 0 for success or a negative error code otherwise.
*/ int ubifs_lpt_calc_hash(struct ubifs_info *c, u8 *hash)
{ struct ubifs_nnode *nnode, *nn; struct ubifs_cnode *cnode; struct shash_desc *desc; int iip = 0, i; int bufsiz = max_t(int, c->nnode_sz, c->pnode_sz); void *buf; int err;
if (!ubifs_authenticated(c)) return 0;
if (!c->nroot) {
err = ubifs_read_nnode(c, NULL, 0); if (err) return err;
}
desc = ubifs_hash_get_desc(c); if (IS_ERR(desc)) return PTR_ERR(desc);
while (cnode) {
nnode = cnode->parent;
nn = (struct ubifs_nnode *)cnode; if (cnode->level > 1) { while (iip < UBIFS_LPT_FANOUT) { if (nn->nbranch[iip].lnum == 0) { /* Go right */
iip++; continue;
}
/* Go down */
iip = 0;
cnode = (struct ubifs_cnode *)nnode; break;
} if (iip < UBIFS_LPT_FANOUT) continue;
} else { struct ubifs_pnode *pnode;
for (i = 0; i < UBIFS_LPT_FANOUT; i++) { if (nn->nbranch[i].lnum == 0) continue;
pnode = ubifs_get_pnode(c, nn, i); if (IS_ERR(pnode)) {
err = PTR_ERR(pnode); goto out;
}
ubifs_pack_pnode(c, buf, pnode);
err = ubifs_shash_update(c, desc, buf,
c->pnode_sz); if (err) goto out;
}
} /* Go up and to the right */
iip = cnode->iip + 1;
cnode = (struct ubifs_cnode *)nnode;
}
/** * lpt_check_hash - check the hash of the LPT. * @c: UBIFS file-system description object * * This function calculates a hash over all pnodes in the LPT and compares it with * the hash stored in the master node. Returns %0 on success and a negative error * code on failure.
*/ staticint lpt_check_hash(struct ubifs_info *c)
{ int err;
u8 hash[UBIFS_HASH_ARR_SZ];
if (!ubifs_authenticated(c)) return 0;
err = ubifs_lpt_calc_hash(c, hash); if (err) return err;
if (ubifs_check_hash(c, c->mst_node->hash_lpt, hash)) {
err = -EPERM;
ubifs_err(c, "Failed to authenticate LPT");
} else {
err = 0;
}
return err;
}
/** * lpt_init_rd - initialize the LPT for reading. * @c: UBIFS file-system description object * * This function returns %0 on success and a negative error code on failure.
*/ staticint lpt_init_rd(struct ubifs_info *c)
{ int err, i;
c->ltab = vmalloc(array_size(sizeof(struct ubifs_lpt_lprops),
c->lpt_lebs)); if (!c->ltab) return -ENOMEM;
i = max_t(int, c->nnode_sz, c->pnode_sz);
c->lpt_nod_buf = kmalloc(i, GFP_KERNEL); if (!c->lpt_nod_buf) return -ENOMEM;
for (i = 0; i < LPROPS_HEAP_CNT; i++) {
c->lpt_heap[i].arr = kmalloc_array(LPT_HEAP_SZ, sizeof(void *),
GFP_KERNEL); if (!c->lpt_heap[i].arr) return -ENOMEM;
c->lpt_heap[i].cnt = 0;
c->lpt_heap[i].max_cnt = LPT_HEAP_SZ;
}
dbg_lp("space_bits %d", c->space_bits);
dbg_lp("lpt_lnum_bits %d", c->lpt_lnum_bits);
dbg_lp("lpt_offs_bits %d", c->lpt_offs_bits);
dbg_lp("lpt_spc_bits %d", c->lpt_spc_bits);
dbg_lp("pcnt_bits %d", c->pcnt_bits);
dbg_lp("lnum_bits %d", c->lnum_bits);
dbg_lp("pnode_sz %d", c->pnode_sz);
dbg_lp("nnode_sz %d", c->nnode_sz);
dbg_lp("ltab_sz %d", c->ltab_sz);
dbg_lp("lsave_sz %d", c->lsave_sz);
dbg_lp("lsave_cnt %d", c->lsave_cnt);
dbg_lp("lpt_hght %d", c->lpt_hght);
dbg_lp("big_lpt %u", c->big_lpt);
dbg_lp("LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs);
dbg_lp("LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs);
dbg_lp("LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs); if (c->big_lpt)
dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs);
return 0;
}
/** * lpt_init_wr - initialize the LPT for writing. * @c: UBIFS file-system description object * * 'lpt_init_rd()' must have been called already. * * This function returns %0 on success and a negative error code on failure.
*/ staticint lpt_init_wr(struct ubifs_info *c)
{ int err, i;
c->ltab_cmt = vmalloc(array_size(sizeof(struct ubifs_lpt_lprops),
c->lpt_lebs)); if (!c->ltab_cmt) return -ENOMEM;
c->lpt_buf = vmalloc(c->leb_size); if (!c->lpt_buf) return -ENOMEM;
if (c->big_lpt) {
c->lsave = kmalloc_array(c->lsave_cnt, sizeof(int), GFP_NOFS); if (!c->lsave) return -ENOMEM;
err = read_lsave(c); if (err) return err;
}
for (i = 0; i < c->lpt_lebs; i++) if (c->ltab[i].free == c->leb_size) {
err = ubifs_leb_unmap(c, i + c->lpt_first); if (err) return err;
}
return 0;
}
/** * ubifs_lpt_init - initialize the LPT. * @c: UBIFS file-system description object * @rd: whether to initialize lpt for reading * @wr: whether to initialize lpt for writing * * For mounting 'rw', @rd and @wr are both true. For mounting 'ro', @rd is true * and @wr is false. For mounting from 'ro' to 'rw', @rd is false and @wr is * true. * * This function returns %0 on success and a negative error code on failure.
*/ int ubifs_lpt_init(struct ubifs_info *c, int rd, int wr)
{ int err;
if (rd) {
err = lpt_init_rd(c); if (err) goto out_err;
}
if (wr) {
err = lpt_init_wr(c); if (err) goto out_err;
}
return 0;
out_err: if (wr)
ubifs_lpt_free(c, 1); if (rd)
ubifs_lpt_free(c, 0); return err;
}
/** * struct lpt_scan_node - somewhere to put nodes while we scan LPT. * @nnode: where to keep a nnode * @pnode: where to keep a pnode * @cnode: where to keep a cnode * @in_tree: is the node in the tree in memory * @ptr: union of node pointers * @ptr.nnode: pointer to the nnode (if it is an nnode) which may be here or in * the tree * @ptr.pnode: ditto for pnode * @ptr.cnode: ditto for cnode
*/ struct lpt_scan_node { union { struct ubifs_nnode nnode; struct ubifs_pnode pnode; struct ubifs_cnode cnode;
}; int in_tree; union { struct ubifs_nnode *nnode; struct ubifs_pnode *pnode; struct ubifs_cnode *cnode;
} ptr;
};
/** * scan_get_nnode - for the scan, get a nnode from either the tree or flash. * @c: the UBIFS file-system description object * @path: where to put the nnode * @parent: parent of the nnode * @iip: index in parent of the nnode * * This function returns a pointer to the nnode on success or a negative error * code on failure.
*/ staticstruct ubifs_nnode *scan_get_nnode(struct ubifs_info *c, struct lpt_scan_node *path, struct ubifs_nnode *parent, int iip)
{ struct ubifs_nbranch *branch; struct ubifs_nnode *nnode; void *buf = c->lpt_nod_buf; int err;
branch = &parent->nbranch[iip];
nnode = branch->nnode; if (nnode) {
path->in_tree = 1;
path->ptr.nnode = nnode; return nnode;
}
nnode = &path->nnode;
path->in_tree = 0;
path->ptr.nnode = nnode;
memset(nnode, 0, sizeof(struct ubifs_nnode)); if (branch->lnum == 0) { /* * This nnode was not written which just means that the LEB * properties in the subtree below it describe empty LEBs. We * make the nnode as though we had read it, which in fact means * doing almost nothing.
*/ if (c->big_lpt)
nnode->num = calc_nnode_num_from_parent(c, parent, iip);
} else {
err = ubifs_leb_read(c, branch->lnum, buf, branch->offs,
c->nnode_sz, 1); if (err) return ERR_PTR(err);
err = ubifs_unpack_nnode(c, buf, nnode); if (err) return ERR_PTR(err);
}
err = validate_nnode(c, nnode, parent, iip); if (err) return ERR_PTR(err); if (!c->big_lpt)
nnode->num = calc_nnode_num_from_parent(c, parent, iip);
nnode->level = parent->level - 1;
nnode->parent = parent;
nnode->iip = iip; return nnode;
}
/** * scan_get_pnode - for the scan, get a pnode from either the tree or flash. * @c: the UBIFS file-system description object * @path: where to put the pnode * @parent: parent of the pnode * @iip: index in parent of the pnode * * This function returns a pointer to the pnode on success or a negative error * code on failure.
*/ staticstruct ubifs_pnode *scan_get_pnode(struct ubifs_info *c, struct lpt_scan_node *path, struct ubifs_nnode *parent, int iip)
{ struct ubifs_nbranch *branch; struct ubifs_pnode *pnode; void *buf = c->lpt_nod_buf; int err;
branch = &parent->nbranch[iip];
pnode = branch->pnode; if (pnode) {
path->in_tree = 1;
path->ptr.pnode = pnode; return pnode;
}
pnode = &path->pnode;
path->in_tree = 0;
path->ptr.pnode = pnode;
memset(pnode, 0, sizeof(struct ubifs_pnode)); if (branch->lnum == 0) { /* * This pnode was not written which just means that the LEB * properties in it describe empty LEBs. We make the pnode as * though we had read it.
*/ int i;
if (c->big_lpt)
pnode->num = calc_pnode_num_from_parent(c, parent, iip); for (i = 0; i < UBIFS_LPT_FANOUT; i++) { struct ubifs_lprops * const lprops = &pnode->lprops[i];
/** * ubifs_lpt_scan_nolock - scan the LPT. * @c: the UBIFS file-system description object * @start_lnum: LEB number from which to start scanning * @end_lnum: LEB number at which to stop scanning * @scan_cb: callback function called for each lprops * @data: data to be passed to the callback function * * This function returns %0 on success and a negative error code on failure.
*/ int ubifs_lpt_scan_nolock(struct ubifs_info *c, int start_lnum, int end_lnum,
ubifs_lpt_scan_callback scan_cb, void *data)
{ int err = 0, i, h, iip, shft; struct ubifs_nnode *nnode; struct ubifs_pnode *pnode; struct lpt_scan_node *path;
if (start_lnum == -1) {
start_lnum = end_lnum + 1; if (start_lnum >= c->leb_cnt)
start_lnum = c->main_first;
}
/* Loop for each lprops */ while (1) { struct ubifs_lprops *lprops = &pnode->lprops[iip]; int ret, lnum = lprops->lnum;
ret = scan_cb(c, lprops, path[h].in_tree, data); if (ret < 0) {
err = ret; goto out;
} if (ret & LPT_SCAN_ADD) { /* Add all the nodes in path to the tree in memory */ for (h = 1; h < c->lpt_hght; h++) { const size_t sz = sizeof(struct ubifs_nnode); struct ubifs_nnode *parent;
pnode = kmemdup(&path[h].pnode, sz, GFP_NOFS); if (!pnode) {
err = -ENOMEM; goto out;
}
parent = pnode->parent;
parent->nbranch[pnode->iip].pnode = pnode;
path[h].ptr.pnode = pnode;
path[h].in_tree = 1;
update_cats(c, pnode);
c->pnodes_have += 1;
}
err = dbg_check_lpt_nodes(c, (struct ubifs_cnode *)
c->nroot, 0, 0); if (err) goto out;
err = dbg_check_cats(c); if (err) goto out;
} if (ret & LPT_SCAN_STOP) {
err = 0; break;
} /* Get the next lprops */ if (lnum == end_lnum) { /* * We got to the end without finding what we were * looking for
*/
err = -ENOSPC; goto out;
} if (lnum + 1 >= c->leb_cnt) { /* Wrap-around to the beginning */
start_lnum = c->main_first; goto again;
} if (iip + 1 < UBIFS_LPT_FANOUT) { /* Next lprops is in the same pnode */
iip += 1; continue;
} /* We need to get the next pnode. Go up until we can go right */
iip = pnode->iip; while (1) {
h -= 1;
ubifs_assert(c, h >= 0);
nnode = path[h].ptr.nnode; if (iip + 1 < UBIFS_LPT_FANOUT) break;
iip = nnode->iip;
} /* Go right */
iip += 1; /* Descend to the pnode */
h += 1; for (; h < c->lpt_hght; h++) {
nnode = scan_get_nnode(c, path + h, nnode, iip); if (IS_ERR(nnode)) {
err = PTR_ERR(nnode); goto out;
}
iip = 0;
}
pnode = scan_get_pnode(c, path + h, nnode, iip); if (IS_ERR(pnode)) {
err = PTR_ERR(pnode); goto out;
}
iip = 0;
}
out:
kfree(path); return err;
}
/** * dbg_chk_pnode - check a pnode. * @c: the UBIFS file-system description object * @pnode: pnode to check * @col: pnode column * * This function returns %0 on success and a negative error code on failure.
*/ staticint dbg_chk_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode, int col)
{ int i;
if (pnode->num != col) {
ubifs_err(c, "pnode num %d expected %d parent num %d iip %d",
pnode->num, col, pnode->parent->num, pnode->iip); return -EINVAL;
} for (i = 0; i < UBIFS_LPT_FANOUT; i++) { struct ubifs_lprops *lp, *lprops = &pnode->lprops[i]; int lnum = (pnode->num << UBIFS_LPT_FANOUT_SHIFT) + i +
c->main_first; int found, cat = lprops->flags & LPROPS_CAT_MASK; struct ubifs_lpt_heap *heap; struct list_head *list = NULL;
if (lnum >= c->leb_cnt) continue; if (lprops->lnum != lnum) {
ubifs_err(c, "bad LEB number %d expected %d",
lprops->lnum, lnum); return -EINVAL;
} if (lprops->flags & LPROPS_TAKEN) { if (cat != LPROPS_UNCAT) {
ubifs_err(c, "LEB %d taken but not uncat %d",
lprops->lnum, cat); return -EINVAL;
} continue;
} if (lprops->flags & LPROPS_INDEX) { switch (cat) { case LPROPS_UNCAT: case LPROPS_DIRTY_IDX: case LPROPS_FRDI_IDX: break; default:
ubifs_err(c, "LEB %d index but cat %d",
lprops->lnum, cat); return -EINVAL;
}
} else { switch (cat) { case LPROPS_UNCAT: case LPROPS_DIRTY: case LPROPS_FREE: case LPROPS_EMPTY: case LPROPS_FREEABLE: break; default:
ubifs_err(c, "LEB %d not index but cat %d",
lprops->lnum, cat); return -EINVAL;
}
} switch (cat) { case LPROPS_UNCAT:
list = &c->uncat_list; break; case LPROPS_EMPTY:
list = &c->empty_list; break; case LPROPS_FREEABLE:
list = &c->freeable_list; break; case LPROPS_FRDI_IDX:
list = &c->frdi_idx_list; break;
}
found = 0; switch (cat) { case LPROPS_DIRTY: case LPROPS_DIRTY_IDX: case LPROPS_FREE:
heap = &c->lpt_heap[cat - 1]; if (lprops->hpos < heap->cnt &&
heap->arr[lprops->hpos] == lprops)
found = 1; break; case LPROPS_UNCAT: case LPROPS_EMPTY: case LPROPS_FREEABLE: case LPROPS_FRDI_IDX:
list_for_each_entry(lp, list, list) if (lprops == lp) {
found = 1; break;
} break;
} if (!found) {
ubifs_err(c, "LEB %d cat %d not found in cat heap/list",
lprops->lnum, cat); return -EINVAL;
} switch (cat) { case LPROPS_EMPTY: if (lprops->free != c->leb_size) {
ubifs_err(c, "LEB %d cat %d free %d dirty %d",
lprops->lnum, cat, lprops->free,
lprops->dirty); return -EINVAL;
} break; case LPROPS_FREEABLE: case LPROPS_FRDI_IDX: if (lprops->free + lprops->dirty != c->leb_size) {
ubifs_err(c, "LEB %d cat %d free %d dirty %d",
lprops->lnum, cat, lprops->free,
lprops->dirty); return -EINVAL;
} break;
}
} return 0;
}
/** * dbg_check_lpt_nodes - check nnodes and pnodes. * @c: the UBIFS file-system description object * @cnode: next cnode (nnode or pnode) to check * @row: row of cnode (root is zero) * @col: column of cnode (leftmost is zero) * * This function returns %0 on success and a negative error code on failure.
*/ int dbg_check_lpt_nodes(struct ubifs_info *c, struct ubifs_cnode *cnode, int row, int col)
{ struct ubifs_nnode *nnode, *nn; struct ubifs_cnode *cn; int num, iip = 0, err;
if (!dbg_is_chk_lprops(c)) return 0;
while (cnode) {
ubifs_assert(c, row >= 0);
nnode = cnode->parent; if (cnode->level) { /* cnode is a nnode */
num = calc_nnode_num(row, col); if (cnode->num != num) {
ubifs_err(c, "nnode num %d expected %d parent num %d iip %d",
cnode->num, num,
(nnode ? nnode->num : 0), cnode->iip); return -EINVAL;
}
nn = (struct ubifs_nnode *)cnode; while (iip < UBIFS_LPT_FANOUT) {
cn = nn->nbranch[iip].cnode; if (cn) { /* Go down */
row += 1;
col <<= UBIFS_LPT_FANOUT_SHIFT;
col += iip;
iip = 0;
cnode = cn; break;
} /* Go right */
iip += 1;
} if (iip < UBIFS_LPT_FANOUT) continue;
} else { struct ubifs_pnode *pnode;
/* cnode is a pnode */
pnode = (struct ubifs_pnode *)cnode;
err = dbg_chk_pnode(c, pnode, col); if (err) return err;
} /* Go up and to the right */
row -= 1;
col >>= UBIFS_LPT_FANOUT_SHIFT;
iip = cnode->iip + 1;
cnode = (struct ubifs_cnode *)nnode;
} return 0;
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.