/* * This file is part of UBIFS. * * Copyright (C) 2006, 2007 Nokia Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: Adrian Hunter * Artem Bityutskiy */ #ifdef WITH_CRYPTO #include #endif #include "lpt.h" #include "bitops.h" #include "defs.h" #include "ubifs.h" #include "crc16.h" #include "sign.h" /** * 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). */ static void do_calc_lpt_geom(struct ubifs_info *c) { int n, bits, per_leb_wastage; long long sz, tot_wastage; c->pnode_cnt = (c->main_lebs + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; n = (c->pnode_cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; c->nnode_cnt = n; while (n > 1) { n = (n + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; c->nnode_cnt += n; } c->lpt_hght = 1; n = UBIFS_LPT_FANOUT; while (n < c->pnode_cnt) { c->lpt_hght += 1; n <<= UBIFS_LPT_FANOUT_SHIFT; } c->space_bits = fls(c->leb_size) - 3; c->lpt_lnum_bits = fls(c->lpt_lebs); c->lpt_offs_bits = fls(c->leb_size - 1); c->lpt_spc_bits = fls(c->leb_size); n = (c->max_leb_cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; c->pcnt_bits = fls(n - 1); c->lnum_bits = fls(c->max_leb_cnt - 1); bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + (c->big_lpt ? c->pcnt_bits : 0) + (c->space_bits * 2 + 1) * UBIFS_LPT_FANOUT; c->pnode_sz = (bits + 7) / 8; bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + (c->big_lpt ? c->pcnt_bits : 0) + (c->lpt_lnum_bits + c->lpt_offs_bits) * UBIFS_LPT_FANOUT; c->nnode_sz = (bits + 7) / 8; bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + c->lpt_lebs * c->lpt_spc_bits * 2; c->ltab_sz = (bits + 7) / 8; bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + c->lnum_bits * c->lsave_cnt; c->lsave_sz = (bits + 7) / 8; /* Calculate the minimum LPT size */ c->lpt_sz = (long long)c->pnode_cnt * c->pnode_sz; c->lpt_sz += (long long)c->nnode_cnt * c->nnode_sz; c->lpt_sz += c->ltab_sz; c->lpt_sz += c->lsave_sz; /* Add wastage */ sz = c->lpt_sz; per_leb_wastage = max_t(int, c->pnode_sz, c->nnode_sz); sz += per_leb_wastage; tot_wastage = per_leb_wastage; while (sz > c->leb_size) { sz += per_leb_wastage; sz -= c->leb_size; tot_wastage += per_leb_wastage; } tot_wastage += ALIGN(sz, c->min_io_size) - sz; c->lpt_sz += tot_wastage; } /** * 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. */ int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, int *big_lpt) { int i, lebs_needed; long long 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 */ sz += c->leb_size - 1; do_div(sz, c->leb_size); lebs_needed = sz; 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) { errmsg("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. * @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) */ static void pack_bits(uint8_t **addr, int *pos, uint32_t val, int nrbits) { uint8_t *p = *addr; int b = *pos; if (b) { *p |= ((uint8_t)val) << b; nrbits += b; if (nrbits > 8) { *++p = (uint8_t)(val >>= (8 - b)); if (nrbits > 16) { *++p = (uint8_t)(val >>= 8); if (nrbits > 24) { *++p = (uint8_t)(val >>= 8); if (nrbits > 32) *++p = (uint8_t)(val >>= 8); } } } } else { *p = (uint8_t)val; if (nrbits > 8) { *++p = (uint8_t)(val >>= 8); if (nrbits > 16) { *++p = (uint8_t)(val >>= 8); if (nrbits > 24) *++p = (uint8_t)(val >>= 8); } } } b = nrbits & 7; if (b == 0) p++; *addr = p; *pos = b; } /** * 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 */ static void 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; pack_bits(&addr, &pos, UBIFS_LPT_PNODE, UBIFS_LPT_TYPE_BITS); if (c->big_lpt) pack_bits(&addr, &pos, pnode->num, c->pcnt_bits); for (i = 0; i < UBIFS_LPT_FANOUT; i++) { pack_bits(&addr, &pos, pnode->lprops[i].free >> 3, c->space_bits); pack_bits(&addr, &pos, pnode->lprops[i].dirty >> 3, c->space_bits); if (pnode->lprops[i].flags & LPROPS_INDEX) pack_bits(&addr, &pos, 1, 1); else pack_bits(&addr, &pos, 0, 1); } crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, c->pnode_sz - UBIFS_LPT_CRC_BYTES); addr = buf; pos = 0; pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); } /** * 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 */ static void 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(&addr, &pos, UBIFS_LPT_NNODE, UBIFS_LPT_TYPE_BITS); if (c->big_lpt) pack_bits(&addr, &pos, nnode->num, c->pcnt_bits); for (i = 0; i < UBIFS_LPT_FANOUT; i++) { int lnum = nnode->nbranch[i].lnum; if (lnum == 0) lnum = c->lpt_last + 1; pack_bits(&addr, &pos, lnum - c->lpt_first, c->lpt_lnum_bits); pack_bits(&addr, &pos, nnode->nbranch[i].offs, c->lpt_offs_bits); } crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, c->nnode_sz - UBIFS_LPT_CRC_BYTES); addr = buf; pos = 0; pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); } /** * pack_ltab - pack the LPT's own lprops table. * @c: UBIFS file-system description object * @buf: buffer into which to pack * @ltab: LPT's own lprops table to pack */ static void pack_ltab(struct ubifs_info *c, void *buf, struct ubifs_lpt_lprops *ltab) { uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; int i, pos = 0; uint16_t crc; pack_bits(&addr, &pos, UBIFS_LPT_LTAB, UBIFS_LPT_TYPE_BITS); for (i = 0; i < c->lpt_lebs; i++) { pack_bits(&addr, &pos, ltab[i].free, c->lpt_spc_bits); pack_bits(&addr, &pos, ltab[i].dirty, c->lpt_spc_bits); } crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, c->ltab_sz - UBIFS_LPT_CRC_BYTES); addr = buf; pos = 0; pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); } /** * pack_lsave - pack the LPT's save table. * @c: UBIFS file-system description object * @buf: buffer into which to pack * @lsave: LPT's save table to pack */ static void pack_lsave(struct ubifs_info *c, void *buf, int *lsave) { uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; int i, pos = 0; uint16_t crc; pack_bits(&addr, &pos, UBIFS_LPT_LSAVE, UBIFS_LPT_TYPE_BITS); for (i = 0; i < c->lsave_cnt; i++) pack_bits(&addr, &pos, lsave[i], c->lnum_bits); crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, c->lsave_sz - UBIFS_LPT_CRC_BYTES); addr = buf; pos = 0; pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); } /** * set_ltab - set LPT LEB properties. * @c: UBIFS file-system description object * @lnum: LEB number * @free: amount of free space * @dirty: amount of dirty space */ static void set_ltab(struct ubifs_info *c, int lnum, int free, int dirty) { pr_debug("LEB %d free %d dirty %d to %d %d\n", lnum, c->ltab[lnum - c->lpt_first].free, c->ltab[lnum - c->lpt_first].dirty, free, dirty); c->ltab[lnum - c->lpt_first].free = free; c->ltab[lnum - c->lpt_first].dirty = dirty; } /** * 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. */ static int 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; } /** * create_lpt - create LPT. * @c: UBIFS file-system description object * * This function returns %0 on success and a negative error code on failure. */ int create_lpt(struct ubifs_info *c) { int lnum, err = 0, 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; int *lsave = NULL; unsigned int md_len; pnode = malloc(sizeof(struct ubifs_pnode)); nnode = malloc(sizeof(struct ubifs_nnode)); buf = malloc(c->leb_size); lsave = malloc(sizeof(int) * c->lsave_cnt); if (!pnode || !nnode || !buf || !lsave) { err = -ENOMEM; goto out; } memset(pnode, 0 , sizeof(struct ubifs_pnode)); memset(nnode, 0 , sizeof(struct ubifs_nnode)); hash_digest_init(); c->lscan_lnum = c->main_first; lnum = c->lpt_first; p = buf; len = 0; /* Number of leaf nodes (pnodes) */ cnt = (c->main_lebs + UBIFS_LPT_FANOUT - 1) >> UBIFS_LPT_FANOUT_SHIFT; //printf("pnode_cnt=%d\n",cnt); /* * 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 pnodes */ for (i = 0; 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 = write_leb(c, lnum++, alen, buf); if (err) goto out; p = buf; len = 0; } /* Fill in the pnode */ for (j = 0; j < UBIFS_LPT_FANOUT; j++) { int k = (i << UBIFS_LPT_FANOUT_SHIFT) + j; if (k < c->main_lebs) pnode->lprops[j] = c->lpt[k]; else { pnode->lprops[j].free = c->leb_size; pnode->lprops[j].dirty = 0; pnode->lprops[j].flags = 0; } } pack_pnode(c, p, pnode); hash_digest_update(p, c->pnode_sz); 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; } hash_digest_final(c->lpt_hash, &md_len); row = c->lpt_hght - 1; /* Add all nnodes, one level at a time */ while (1) { /* Number of internal nodes (nnodes) at next level */ cnt = (cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; if (cnt == 0) cnt = 1; 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 = write_leb(c, lnum++, alen, buf); if (err) goto out; p = buf; len = 0; } /* The root is on row zero */ if (row == 0) { 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); pack_nnode(c, p, nnode); p += c->nnode_sz; len += c->nnode_sz; } /* Row zero is the top row */ if (row == 0) break; /* Update the information about the level below */ bcnt = cnt; bsz = c->nnode_sz; row -= 1; } if (c->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 = write_leb(c, lnum++, alen, buf); if (err) goto out; p = buf; len = 0; } c->lsave_lnum = lnum; c->lsave_offs = len; for (i = 0; i < c->lsave_cnt; i++) lsave[i] = c->main_first + i; 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 = write_leb(c, lnum++, alen, buf); 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); pack_ltab(c, p, c->ltab); p += c->ltab_sz; /* Write remaining buffer */ memset(p, 0xff, alen - len); err = write_leb(c, lnum, alen, buf); if (err) goto out; c->nhead_lnum = lnum; c->nhead_offs = ALIGN(len, c->min_io_size); pr_debug("lpt_sz: %lld\n", c->lpt_sz); pr_debug("space_bits: %d\n", c->space_bits); pr_debug("lpt_lnum_bits: %d\n", c->lpt_lnum_bits); pr_debug("lpt_offs_bits: %d\n", c->lpt_offs_bits); pr_debug("lpt_spc_bits: %d\n", c->lpt_spc_bits); pr_debug("pcnt_bits: %d\n", c->pcnt_bits); pr_debug("lnum_bits: %d\n", c->lnum_bits); pr_debug("pnode_sz: %d\n", c->pnode_sz); pr_debug("nnode_sz: %d\n", c->nnode_sz); pr_debug("ltab_sz: %d\n", c->ltab_sz); pr_debug("lsave_sz: %d\n", c->lsave_sz); pr_debug("lsave_cnt: %d\n", c->lsave_cnt); pr_debug("lpt_hght: %d\n", c->lpt_hght); pr_debug("big_lpt: %d\n", c->big_lpt); pr_debug("LPT root is at %d:%d\n", c->lpt_lnum, c->lpt_offs); pr_debug("LPT head is at %d:%d\n", c->nhead_lnum, c->nhead_offs); pr_debug("LPT ltab is at %d:%d\n", c->ltab_lnum, c->ltab_offs); if (c->big_lpt) pr_debug("LPT lsave is at %d:%d\n", c->lsave_lnum, c->lsave_offs); out: free(lsave); free(buf); free(nnode); free(pnode); return err; }