示例#1
0
/**
 * ubifs_write_sb_node - write superblock node.
 * @c: UBIFS file-system description object
 * @sup: superblock node read with 'ubifs_read_sb_node()'
 *
 * This function returns %0 on success and a negative error code on failure.
 */
int ubifs_write_sb_node(struct ubifs_info *c, struct ubifs_sb_node *sup)
{
	int len = ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size);

	ubifs_prepare_node(c, sup, UBIFS_SB_NODE_SZ, 1);
	return ubifs_leb_change(c, UBIFS_SB_LNUM, sup, len, UBI_LONGTERM);
}
示例#2
0
/**
 * ubifs_write_node - write node to the media.
 * @c: UBIFS file-system description object
 * @buf: the node to write
 * @len: node length
 * @lnum: logical eraseblock number
 * @offs: offset within the logical eraseblock
 * @dtype: node life-time hint (%UBI_LONGTERM, %UBI_SHORTTERM, %UBI_UNKNOWN)
 *
 * This function automatically fills node magic number, assigns sequence
 * number, and calculates node CRC checksum. The length of the @buf buffer has
 * to be aligned to the minimal I/O unit size. This function automatically
 * appends padding node and padding bytes if needed. Returns zero in case of
 * success and a negative error code in case of failure.
 */
int ubifs_write_node(struct ubifs_info *c, void *buf, int len, int lnum,
		     int offs, int dtype)
{
	int err, buf_len = ALIGN(len, c->min_io_size);

	dbg_io("LEB %d:%d, %s, length %d (aligned %d)",
	       lnum, offs, dbg_ntype(((struct ubifs_ch *)buf)->node_type), len,
	       buf_len);
	ubifs_assert(lnum >= 0 && lnum < c->leb_cnt && offs >= 0);
	ubifs_assert(offs % c->min_io_size == 0 && offs < c->leb_size);
	ubifs_assert(!c->ro_media && !c->ro_mount);
	ubifs_assert(!c->space_fixup);

	if (c->ro_error)
		return -EROFS;

	ubifs_prepare_node(c, buf, len, 1);
	err = ubi_leb_write(c->ubi, lnum, buf, offs, buf_len, dtype);
	if (err) {
		ubifs_err("cannot write %d bytes to LEB %d:%d, error %d",
			  buf_len, lnum, offs, err);
		dbg_dump_node(c, buf);
		dbg_dump_stack();
	}

	return err;
}
示例#3
0
/**
 * write_node - write node to a journal head.
 * @c: UBIFS file-system description object
 * @jhead: journal head
 * @node: node to write
 * @len: node length
 * @lnum: LEB number written is returned here
 * @offs: offset written is returned here
 *
 * This function writes a node to reserved space of journal head @jhead.
 * Returns zero in case of success and a negative error code in case of
 * failure.
 */
static int write_node(struct ubifs_info *c, int jhead, void *node, int len,
		      int *lnum, int *offs)
{
	struct ubifs_wbuf *wbuf = &c->jheads[jhead].wbuf;

	ubifs_assert(jhead != GCHD);

	*lnum = c->jheads[jhead].wbuf.lnum;
	*offs = c->jheads[jhead].wbuf.offs + c->jheads[jhead].wbuf.used;

	dbg_jrn("jhead %d, LEB %d:%d, len %d", jhead, *lnum, *offs, len);
	ubifs_prepare_node(c, node, len, 0);

	return ubifs_wbuf_write_nolock(wbuf, node, len);
}
示例#4
0
/**
 * ubifs_jrn_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_jrn_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 *dn;
	int err, dlen, len, lnum, offs, bit, sz;
	unsigned int blk;

	dbg_jrn("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);
	ubifs_prepare_node(c, trun, UBIFS_TRUN_NODE_SZ, 0);

	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_jrn_key(c, &key, "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);
				ubifs_prepare_node(c, dn, dlen, 0);
			}
		}
	}

	if (dlen)
		len = ALIGN(UBIFS_TRUN_NODE_SZ, 8) + dlen;
	else
		len = UBIFS_TRUN_NODE_SZ;

	err = make_reservation(c, BASEHD, len);
	if (err)
		goto out_free;

	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;
}
示例#5
0
/**
 * ubifs_log_start_commit - start commit.
 * @c: UBIFS file-system description object
 * @ltail_lnum: return new log tail LEB number
 *
 * The commit operation starts with writing "commit start" node to the log and
 * reference nodes for all journal heads which will define new journal after
 * the commit has been finished. The commit start and reference nodes are
 * written in one go to the nearest empty log LEB (hence, when commit is
 * finished UBIFS may safely unmap all the previous log LEBs). This function
 * returns zero in case of success and a negative error code in case of
 * failure.
 */
int ubifs_log_start_commit(struct ubifs_info *c, int *ltail_lnum)
{
	void *buf;
	struct ubifs_cs_node *cs;
	struct ubifs_ref_node *ref;
	int err, i, max_len, len;

	err = dbg_check_bud_bytes(c);
	if (err)
		return err;

	max_len = UBIFS_CS_NODE_SZ + c->jhead_cnt * UBIFS_REF_NODE_SZ;
	max_len = ALIGN(max_len, c->min_io_size);
	buf = cs = kmalloc(max_len, GFP_NOFS);
	if (!buf)
		return -ENOMEM;

	cs->ch.node_type = UBIFS_CS_NODE;
	cs->cmt_no = cpu_to_le64(c->cmt_no);
	ubifs_prepare_node(c, cs, UBIFS_CS_NODE_SZ, 0);

	/*
	 * Note, we do not lock 'c->log_mutex' because this is the commit start
	 * phase and we are exclusively using the log. And we do not lock
	 * write-buffer because nobody can write to the file-system at this
	 * phase.
	 */

	len = UBIFS_CS_NODE_SZ;
	for (i = 0; i < c->jhead_cnt; i++) {
		int lnum = c->jheads[i].wbuf.lnum;
		int offs = c->jheads[i].wbuf.offs;

		if (lnum == -1 || offs == c->leb_size)
			continue;

		dbg_log("add ref to LEB %d:%d for jhead %s",
			lnum, offs, dbg_jhead(i));
		ref = buf + len;
		ref->ch.node_type = UBIFS_REF_NODE;
		ref->lnum = cpu_to_le32(lnum);
		ref->offs = cpu_to_le32(offs);
		ref->jhead = cpu_to_le32(i);

		ubifs_prepare_node(c, ref, UBIFS_REF_NODE_SZ, 0);
		len += UBIFS_REF_NODE_SZ;
	}

	ubifs_pad(c, buf + len, ALIGN(len, c->min_io_size) - len);

#ifdef CONFIG_UBIFS_FS_FULL_USE_LOG
	/* Not Switch to next log LEB, programming next available page in the same log LEB continuously*/

	/* if available page is in the end of the LEB, switch to next LEB*/
	if(c->lhead_offs >= (c->leb_size - (c->min_io_size * 4)) )
	{
		int old_lnum = c->lhead_lnum;
		int old_offs = c->lhead_offs;
		c->lhead_lnum = ubifs_next_log_lnum(c, c->lhead_lnum);
		c->lhead_offs = 0;
		ubifs_msg("switch log LEB %d:%d to %d:%d\n", old_lnum, old_offs, c->lhead_lnum, c->lhead_offs);
	}
#else
	/* Switch to the next log LEB */
	if (c->lhead_offs) {
		int old_lnum = c->lhead_lnum;
		int old_offs = c->lhead_offs;
		c->lhead_lnum = ubifs_next_log_lnum(c, c->lhead_lnum);
		c->lhead_offs = 0;
		ubifs_msg("switch log LEB %d:%d to %d:%d\n", old_lnum, old_offs, c->lhead_lnum, c->lhead_offs);
	}
#endif

	if (c->lhead_offs == 0) {
		/* Must ensure next LEB has been unmapped */
		err = ubifs_leb_unmap(c, c->lhead_lnum);
		if (err)
			goto out;
	}

	len = ALIGN(len, c->min_io_size);
	dbg_log("writing commit start at LEB %d:0, len %d", c->lhead_lnum, len);
	err = ubifs_leb_write(c, c->lhead_lnum, cs, c->lhead_offs, len); //MTK, modify offset 0 -> c->lhead_offs
	if (err)
		goto out;

	*ltail_lnum = c->lhead_lnum;

	c->lhead_offs += len;
	if (c->lhead_offs == c->leb_size) {
		c->lhead_lnum = ubifs_next_log_lnum(c, c->lhead_lnum);
		c->lhead_offs = 0;
	}

	remove_buds(c);

	/*
	 * We have started the commit and now users may use the rest of the log
	 * for new writes.
	 */
	c->min_log_bytes = 0;

out:
	kfree(buf);
	return err;
}