/** * 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); }
/** * 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; }
/** * 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); }
/** * 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; }
/** * 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; }