/** * ubifs_tnc_read_node - read a leaf node from the flash media. * @c: UBIFS file-system description object * @zbr: key and position of the node * @node: node is returned here * * This function reads a node defined by @zbr from the flash media. Returns * zero in case of success or a negative negative error code in case of * failure. */ int ubifs_tnc_read_node(struct ubifs_info *c, struct ubifs_zbranch *zbr, void *node) { union ubifs_key key1, *key = &zbr->key; int err, type = key_type(c, key); struct ubifs_wbuf *wbuf; /* * 'zbr' has to point to on-flash node. The node may sit in a bud and * may even be in a write buffer, so we have to take care about this. */ wbuf = ubifs_get_wbuf(c, zbr->lnum); if (wbuf) err = ubifs_read_node_wbuf(wbuf, node, type, zbr->len, zbr->lnum, zbr->offs); else err = ubifs_read_node(c, node, type, zbr->len, zbr->lnum, zbr->offs); if (err) { dbg_tnc("key %s", DBGKEY(key)); return err; } /* Make sure the key of the read node is correct */ key_read(c, node + UBIFS_KEY_OFFSET, &key1); if (!keys_eq(c, key, &key1)) { ubifs_err("bad key in node at LEB %d:%d", zbr->lnum, zbr->offs); dbg_tnc("looked for key %s found node's key %s", DBGKEY(key), DBGKEY1(&key1)); dbg_dump_node(c, node); return -EINVAL; } return 0; }
/** * ubifs_jnl_truncate - update the journal for a truncation. * @c: UBIFS file-system description object * @inum: inode number of inode being truncated * @old_size: old size * @new_size: new size * * When the size of a file decreases due to truncation, a truncation node is * written, the journal tree is updated, and the last data block is re-written * if it has been affected. * * This function returns %0 in the case of success, and a negative error code in * case of failure. */ int ubifs_jnl_truncate(struct ubifs_info *c, ino_t inum, loff_t old_size, loff_t new_size) { union ubifs_key key, to_key; struct ubifs_trun_node *trun; struct ubifs_data_node *uninitialized_var(dn); int err, dlen, len, lnum, offs, bit, sz; unsigned int blk; dbg_jnl("ino %lu, size %lld -> %lld", inum, old_size, new_size); sz = UBIFS_TRUN_NODE_SZ + UBIFS_MAX_DATA_NODE_SZ * WORST_COMPR_FACTOR; trun = kmalloc(sz, GFP_NOFS); if (!trun) return -ENOMEM; trun->ch.node_type = UBIFS_TRUN_NODE; trun_key_init_flash(c, &trun->key, inum); trun->old_size = cpu_to_le64(old_size); trun->new_size = cpu_to_le64(new_size); dlen = new_size & (UBIFS_BLOCK_SIZE - 1); if (dlen) { /* Get last data block so it can be truncated */ dn = (void *)trun + ALIGN(UBIFS_TRUN_NODE_SZ, 8); blk = new_size / UBIFS_BLOCK_SIZE; data_key_init(c, &key, inum, blk); dbg_jnl("last block key %s", DBGKEY(&key)); err = ubifs_tnc_lookup(c, &key, dn); if (err == -ENOENT) dlen = 0; /* Not found (so it is a hole) */ else if (err) goto out_free; else { if (le32_to_cpu(dn->size) <= dlen) dlen = 0; /* Nothing to do */ else { int compr_type = le16_to_cpu(dn->compr_type); if (compr_type != UBIFS_COMPR_NONE) { err = recomp_data_node(dn, &dlen); if (err) goto out_free; } else { dn->size = cpu_to_le32(dlen); dlen += UBIFS_DATA_NODE_SZ; } zero_data_node_unused(dn); } } } if (dlen) len = ALIGN(UBIFS_TRUN_NODE_SZ, 8) + dlen; else len = UBIFS_TRUN_NODE_SZ; /* Must make reservation before allocating sequence numbers */ err = make_reservation(c, BASEHD, len); if (err) goto out_free; ubifs_prepare_node(c, trun, UBIFS_TRUN_NODE_SZ, 0); if (dlen) ubifs_prepare_node(c, dn, dlen, 0); err = write_head(c, BASEHD, trun, len, &lnum, &offs, 0); if (!err) ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf, inum); release_head(c, BASEHD); if (err) goto out_ro; if (dlen) { offs += ALIGN(UBIFS_TRUN_NODE_SZ, 8); err = ubifs_tnc_add(c, &key, lnum, offs, dlen); if (err) goto out_ro; } err = ubifs_add_dirt(c, lnum, UBIFS_TRUN_NODE_SZ); if (err) goto out_ro; bit = new_size & (UBIFS_BLOCK_SIZE - 1); blk = new_size / UBIFS_BLOCK_SIZE + (bit ? 1 : 0); data_key_init(c, &key, inum, blk); bit = old_size & (UBIFS_BLOCK_SIZE - 1); blk = old_size / UBIFS_BLOCK_SIZE - (bit ? 0: 1); data_key_init(c, &to_key, inum, blk); err = ubifs_tnc_remove_range(c, &key, &to_key); if (err) goto out_ro; finish_reservation(c); kfree(trun); return 0; out_ro: ubifs_ro_mode(c, err); finish_reservation(c); out_free: kfree(trun); return err; }
/** * read_znode - read an indexing node from flash and fill znode. * @c: UBIFS file-system description object * @lnum: LEB of the indexing node to read * @offs: node offset * @len: node length * @znode: znode to read to * * This function reads an indexing node from the flash media and fills znode * with the read data. Returns zero in case of success and a negative error * code in case of failure. The read indexing node is validated and if anything * is wrong with it, this function prints complaint messages and returns * %-EINVAL. */ static int read_znode(struct ubifs_info *c, int lnum, int offs, int len, struct ubifs_znode *znode) { int i, err, type, cmp; struct ubifs_idx_node *idx; idx = kmalloc(c->max_idx_node_sz, GFP_NOFS); if (!idx) return -ENOMEM; err = ubifs_read_node(c, idx, UBIFS_IDX_NODE, len, lnum, offs); if (err < 0) { kfree(idx); return err; } znode->child_cnt = le16_to_cpu(idx->child_cnt); znode->level = le16_to_cpu(idx->level); dbg_tnc("LEB %d:%d, level %d, %d branch", lnum, offs, znode->level, znode->child_cnt); if (znode->child_cnt > c->fanout || znode->level > UBIFS_MAX_LEVELS) { dbg_err("current fanout %d, branch count %d", c->fanout, znode->child_cnt); dbg_err("max levels %d, znode level %d", UBIFS_MAX_LEVELS, znode->level); err = 1; goto out_dump; } for (i = 0; i < znode->child_cnt; i++) { const struct ubifs_branch *br = ubifs_idx_branch(c, idx, i); struct ubifs_zbranch *zbr = &znode->zbranch[i]; key_read(c, &br->key, &zbr->key); zbr->lnum = le32_to_cpu(br->lnum); zbr->offs = le32_to_cpu(br->offs); zbr->len = le32_to_cpu(br->len); zbr->znode = NULL; /* Validate branch */ if (zbr->lnum < c->main_first || zbr->lnum >= c->leb_cnt || zbr->offs < 0 || zbr->offs + zbr->len > c->leb_size || zbr->offs & 7) { dbg_err("bad branch %d", i); err = 2; goto out_dump; } switch (key_type(c, &zbr->key)) { case UBIFS_INO_KEY: case UBIFS_DATA_KEY: case UBIFS_DENT_KEY: case UBIFS_XENT_KEY: break; default: dbg_msg("bad key type at slot %d: %s", i, DBGKEY(&zbr->key)); err = 3; goto out_dump; } if (znode->level) continue; type = key_type(c, &zbr->key); if (c->ranges[type].max_len == 0) { if (zbr->len != c->ranges[type].len) { dbg_err("bad target node (type %d) length (%d)", type, zbr->len); dbg_err("have to be %d", c->ranges[type].len); err = 4; goto out_dump; } } else if (zbr->len < c->ranges[type].min_len || zbr->len > c->ranges[type].max_len) { dbg_err("bad target node (type %d) length (%d)", type, zbr->len); dbg_err("have to be in range of %d-%d", c->ranges[type].min_len, c->ranges[type].max_len); err = 5; goto out_dump; } } /* * Ensure that the next key is greater or equivalent to the * previous one. */ for (i = 0; i < znode->child_cnt - 1; i++) { const union ubifs_key *key1, *key2; key1 = &znode->zbranch[i].key; key2 = &znode->zbranch[i + 1].key; cmp = keys_cmp(c, key1, key2); if (cmp > 0) { dbg_err("bad key order (keys %d and %d)", i, i + 1); err = 6; goto out_dump; } else if (cmp == 0 && !is_hash_key(c, key1)) { /* These can only be keys with colliding hash */ dbg_err("keys %d and %d are not hashed but equivalent", i, i + 1); err = 7; goto out_dump; } } kfree(idx); return 0; out_dump: ubifs_err("bad indexing node at LEB %d:%d, error %d", lnum, offs, err); dbg_dump_node(c, idx); kfree(idx); return -EINVAL; }
/** * ubifs_jnl_write_data - write a data node to the journal. * @c: UBIFS file-system description object * @inode: inode the data node belongs to * @key: node key * @buf: buffer to write * @len: data length (must not exceed %UBIFS_BLOCK_SIZE) * * This function writes a data node to the journal. Returns %0 if the data node * was successfully written, and a negative error code in case of failure. */ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode, const union ubifs_key *key, const void *buf, int len) { int err, lnum, offs, compr_type, out_len; int dlen = UBIFS_DATA_NODE_SZ + UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR; const struct ubifs_inode *ui = ubifs_inode(inode); struct ubifs_data_node *data; dbg_jnl("ino %lu, blk %u, len %d, key %s", key_ino(c, key), key_block(c, key), len, DBGKEY(key)); ubifs_assert(len <= UBIFS_BLOCK_SIZE); data = kmalloc(dlen, GFP_NOFS); if (!data) return -ENOMEM; data->ch.node_type = UBIFS_DATA_NODE; key_write(c, key, &data->key); data->size = cpu_to_le32(len); zero_data_node_unused(data); if (!(ui->flags && UBIFS_COMPR_FL)) /* Compression is disabled for this inode */ compr_type = UBIFS_COMPR_NONE; else compr_type = ui->compr_type; out_len = dlen - UBIFS_DATA_NODE_SZ; ubifs_compress(buf, len, &data->data, &out_len, &compr_type); ubifs_assert(out_len <= UBIFS_BLOCK_SIZE); dlen = UBIFS_DATA_NODE_SZ + out_len; data->compr_type = cpu_to_le16(compr_type); /* Make reservation before allocating sequence numbers */ err = make_reservation(c, DATAHD, dlen); if (err) goto out_free; err = write_node(c, DATAHD, data, dlen, &lnum, &offs); if (!err) ubifs_wbuf_add_ino_nolock(&c->jheads[DATAHD].wbuf, key_ino(c, key)); release_head(c, DATAHD); if (err) goto out_ro; err = ubifs_tnc_add(c, key, lnum, offs, dlen); if (err) goto out_ro; finish_reservation(c); kfree(data); return 0; out_ro: ubifs_ro_mode(c, err); finish_reservation(c); out_free: kfree(data); return err; }