shithub: lwext4

Download patch

ref: b05eaa268d556f2fdb0ad212960ff64142d54bef
parent: 745c18c6062f49f83782537a06e3f5ffc04e40e1
author: ngkaho1234 <ngkaho1234@gmail.com>
date: Thu Jan 14 07:02:43 EST 2016

Numorous changes. See below:

  - ext4: ext4_trunc_inode now requires inode index instead of an
          existing inode_ref.
  - ext4: the support of removing directory recursively is temporarily
          dropped.
  - ext4: ext4_dir_rm temporarily relies on ext4_fs_truncate instead of
          ext4_trunc_inode to ensure directory inode not having 0
          length in case of power failure.

  - ext4_journal: connect transactions having reference to the same blocks
                  together.

--- a/lwext4/ext4.c
+++ b/lwext4/ext4.c
@@ -705,52 +705,73 @@
 }
 
 static int ext4_trunc_inode(struct ext4_mountpoint *mp,
-			    struct ext4_inode_ref *inode_ref, uint64_t new_size)
+			    uint32_t index, uint64_t new_size)
 {
 	int r = EOK;
 	struct ext4_fs *const fs = &mp->fs;
-	uint64_t inode_size = ext4_inode_get_size(&fs->sb, inode_ref->inode);
-	uint32_t index = inode_ref->index;
+	struct ext4_inode_ref inode_ref;
+	uint64_t inode_size;
+	bool has_trans = mp->fs.jbd_journal && mp->fs.curr_trans;
+	r = ext4_fs_get_inode_ref(fs, index, &inode_ref);
+	if (r != EOK)
+		return r;
 
+	inode_size = ext4_inode_get_size(&fs->sb, inode_ref.inode);
+	ext4_fs_put_inode_ref(&inode_ref);
+	if (has_trans)
+		ext4_trans_stop(mp);
 
 	while (inode_size > new_size + CONFIG_MAX_TRUNCATE_SIZE) {
 
 		inode_size -= CONFIG_MAX_TRUNCATE_SIZE;
+
 		ext4_trans_start(mp);
-		r = ext4_fs_truncate_inode(inode_ref, inode_size);
+		r = ext4_fs_get_inode_ref(fs, index, &inode_ref);
+		if (r != EOK) {
+			ext4_trans_abort(mp);
+			break;
+		}
+		r = ext4_fs_truncate_inode(&inode_ref, inode_size);
 		if (r != EOK)
-			return r;
+			ext4_fs_put_inode_ref(&inode_ref);
+		else
+			r = ext4_fs_put_inode_ref(&inode_ref);
 
-		r = ext4_fs_put_inode_ref(inode_ref);
-		if (r != EOK)
+		if (r != EOK) {
 			ext4_trans_abort(mp);
-		else
+			goto Finish;
+		} else
 			ext4_trans_stop(mp);
-
-		r = ext4_fs_get_inode_ref(fs, index, inode_ref);
-		if (r != EOK)
-			return r;
 	}
 
 	if (inode_size > new_size) {
 
 		inode_size = new_size;
+
 		ext4_trans_start(mp);
-		r = ext4_fs_truncate_inode(inode_ref, inode_size);
+		r = ext4_fs_get_inode_ref(fs, index, &inode_ref);
+		if (r != EOK) {
+			ext4_trans_abort(mp);
+			goto Finish;
+		}
+		r = ext4_fs_truncate_inode(&inode_ref, inode_size);
 		if (r != EOK)
-			return r;
+			ext4_fs_put_inode_ref(&inode_ref);
+		else
+			r = ext4_fs_put_inode_ref(&inode_ref);
 
-		r = ext4_fs_put_inode_ref(inode_ref);
 		if (r != EOK)
 			ext4_trans_abort(mp);
 		else
 			ext4_trans_stop(mp);
 
-		r = ext4_fs_get_inode_ref(fs, index, inode_ref);
-		if (r != EOK)
-			return r;
 	}
 
+Finish:
+
+	if (has_trans)
+		ext4_trans_start(mp);
+
 	return r;
 }
 
@@ -796,6 +817,9 @@
 	if (parent_inode)
 		*parent_inode = ref.index;
 
+	if (flags & O_CREAT)
+		ext4_trans_start(mp);
+
 	len = ext4_path_check(path, &is_goal);
 	while (1) {
 
@@ -905,7 +929,7 @@
 	if (is_goal) {
 
 		if ((f->flags & O_TRUNC) && (imode == EXT4_INODE_MODE_FILE)) {
-			r = ext4_trunc_inode(mp, &ref, 0);
+			r = ext4_trunc_inode(mp, ref.index, 0);
 			if (r != EOK) {
 				ext4_fs_put_inode_ref(&ref);
 				return r;
@@ -923,6 +947,14 @@
 	}
 
 	r = ext4_fs_put_inode_ref(&ref);
+	if (flags & O_CREAT) {
+		if (r == EOK)
+			ext4_trans_stop(mp);
+		else
+			ext4_trans_abort(mp);
+
+	}
+
 	return r;
 }
 
@@ -1270,7 +1302,30 @@
 		EXT4_MP_UNLOCK(mp);
 		return r;
 	}
+	/* We do not allow opening files here. */
+	if (ext4_inode_type(&mp->fs.sb, child.inode) ==
+	    EXT4_INODE_MODE_DIRECTORY) {
+		ext4_fs_put_inode_ref(&parent);
+		ext4_fs_put_inode_ref(&child);
+		ext4_trans_abort(mp);
+		EXT4_MP_UNLOCK(mp);
+		return r;
+	}
 
+	/*Link count will be zero, the inode should be freed. */
+	if (ext4_inode_get_links_cnt(child.inode) == 1) {
+		ext4_block_cache_write_back(mp->fs.bdev, 1);
+		r = ext4_trunc_inode(mp, child.index, 0);
+		if (r != EOK) {
+			ext4_fs_put_inode_ref(&parent);
+			ext4_fs_put_inode_ref(&child);
+			ext4_trans_abort(mp);
+			EXT4_MP_UNLOCK(mp);
+			return r;
+		}
+		ext4_block_cache_write_back(mp->fs.bdev, 0);
+	}
+
 	/*Set path*/
 	path += name_off;
 
@@ -1284,15 +1339,7 @@
 	/*Link count is zero, the inode should be freed. */
 	if (!ext4_inode_get_links_cnt(child.inode)) {
 		ext4_inode_set_del_time(child.inode, -1L);
-		/*Turncate*/
-		ext4_block_cache_write_back(mp->fs.bdev, 1);
-		/*Truncate may be IO heavy. Do it writeback cache mode.*/
-		r = ext4_trunc_inode(mp, &child, 0);
-		ext4_block_cache_write_back(mp->fs.bdev, 0);
 
-		if (r != EOK)
-			goto Finish;
-
 		r = ext4_fs_free_inode(&child);
 		if (r != EOK)
 			goto Finish;
@@ -1360,17 +1407,11 @@
 		return ENOENT;
 
 	EXT4_MP_LOCK(mp);
-	ext4_trans_start(mp);
 
 	ext4_block_cache_write_back(mp->fs.bdev, 1);
 	r = ext4_generic_open(f, path, flags, true, 0, 0);
 	ext4_block_cache_write_back(mp->fs.bdev, 0);
 
-	if (r != EOK)
-		ext4_trans_abort(mp);
-	else
-		ext4_trans_stop(mp);
-
 	EXT4_MP_UNLOCK(mp);
 	return r;
 }
@@ -1387,17 +1428,11 @@
         filetype = EXT4_DE_REG_FILE;
 
 	EXT4_MP_LOCK(mp);
-	ext4_trans_start(mp);
 
 	ext4_block_cache_write_back(mp->fs.bdev, 1);
 	r = ext4_generic_open2(f, path, flags, filetype, NULL, NULL);
 	ext4_block_cache_write_back(mp->fs.bdev, 0);
 
-	if (r != EOK)
-		ext4_trans_abort(mp);
-	else
-		ext4_trans_stop(mp);
-
 	EXT4_MP_UNLOCK(mp);
 	return r;
 }
@@ -1438,7 +1473,7 @@
 	if (r != EOK)
 		goto Finish;
 
-	r = ext4_trunc_inode(f->mp, &ref, size);
+	r = ext4_trunc_inode(f->mp, ref.index, size);
 	if (r != EOK)
 		goto Finish;
 
@@ -2463,18 +2498,13 @@
 	ext4_file f;
 
 	struct ext4_mountpoint *mp = ext4_get_mount(path);
-	struct ext4_inode_ref act;
-	struct ext4_inode_ref child;
-	struct ext4_dir_iter it;
+	struct ext4_inode_ref act, parent;
 
 	uint32_t name_off;
-	uint32_t inode_up;
-	uint32_t inode_current;
-	uint32_t depth = 1;
+	uint32_t inode_up, inode_current;
 
-	bool has_children;
+	bool has_children = false;
 	bool is_goal;
-	bool dir_end;
 
 	if (!mp)
 		return ENOENT;
@@ -2481,8 +2511,6 @@
 
 	EXT4_MP_LOCK(mp);
 
-	struct ext4_fs *const fs = &mp->fs;
-
 	/*Check if exist.*/
 	r = ext4_generic_open(&f, path, "r", false, &inode_up, &name_off);
 	if (r != EOK) {
@@ -2498,188 +2526,74 @@
 
 	ext4_block_cache_write_back(mp->fs.bdev, 1);
 
-	do {
+	/*Load parent.*/
+	r = ext4_fs_get_inode_ref(&f.mp->fs, inode_up,
+			&parent);
+	if (r != EOK)
+		goto Finish;
+	r = ext4_fs_get_inode_ref(&f.mp->fs, inode_current,
+			&act);
+	if (r != EOK) {
+		ext4_fs_put_inode_ref(&parent);
+		goto Finish;
+	}
+	r = ext4_has_children(&has_children, &act);
+	if (r != EOK) {
+		ext4_fs_put_inode_ref(&parent);
+		ext4_fs_put_inode_ref(&act);
+		goto Finish;
+	}
+	if (has_children) {
+		r = ENOTEMPTY;
+		ext4_fs_put_inode_ref(&parent);
+		ext4_fs_put_inode_ref(&act);
+		goto Finish;
+	}
 
-		uint64_t act_curr_pos = 0;
-		has_children = false;
-		dir_end = false;
+	ext4_trans_start(mp);
 
-		while (r == EOK && !has_children && !dir_end) {
-
-			/*Load directory node.*/
-			r = ext4_fs_get_inode_ref(fs, inode_current, &act);
-			if (r != EOK) {
-				break;
-			}
-
-			/*Initialize iterator.*/
-			r = ext4_dir_iterator_init(&it, &act, act_curr_pos);
-			if (r != EOK) {
-				ext4_fs_put_inode_ref(&act);
-				break;
-			}
-
-			if (!it.curr) {
-				dir_end = true;
-				goto End;
-			}
-
-			ext4_trans_start(mp);
-
-			/*Get up directory inode when ".." entry*/
-			if ((it.curr->name_len == 2) &&
-			    ext4_is_dots(it.curr->name, it.curr->name_len)) {
-				inode_up = ext4_dir_en_get_inode(it.curr);
-			}
-
-			/*If directory or file entry,  but not "." ".." entry*/
-			if (!ext4_is_dots(it.curr->name, it.curr->name_len)) {
-
-				/*Get child inode reference do unlink
-				 * directory/file.*/
-				uint32_t cinode;
-				cinode = ext4_dir_en_get_inode(it.curr);
-				r = ext4_fs_get_inode_ref(fs, cinode, &child);
-				if (r != EOK)
-					goto End;
-
-				/*If directory with no leaf children*/
-				r = ext4_has_children(&has_children, &child);
-				if (r != EOK) {
-					ext4_fs_put_inode_ref(&child);
-					goto End;
-				}
-
-				if (has_children) {
-					/*Has directory children. Go into this
-					 * directory.*/
-					inode_up = inode_current;
-					inode_current = cinode;
-					depth++;
-					ext4_fs_put_inode_ref(&child);
-					goto End;
-				}
-
-				/* Truncate */
-				r = ext4_trunc_inode(mp, &child, 0);
-				if (r != EOK) {
-					ext4_fs_put_inode_ref(&child);
-					goto End;
-				}
-
-				/*No children in child directory or file. Just
-				 * unlink.*/
-				r = ext4_unlink(f.mp, &act, &child,
-						(char *)it.curr->name,
-						it.curr->name_len);
-				if (r != EOK) {
-					ext4_fs_put_inode_ref(&child);
-					goto End;
-				}
-
-				ext4_inode_set_del_time(child.inode, -1L);
-				ext4_inode_set_links_cnt(child.inode, 0);
-				child.dirty = true;
-
-				r = ext4_fs_free_inode(&child);
-				if (r != EOK) {
-					ext4_fs_put_inode_ref(&child);
-					goto End;
-				}
-
-				r = ext4_fs_put_inode_ref(&child);
-				if (r != EOK)
-					goto End;
-
-			}
-
-			r = ext4_dir_iterator_next(&it);
-			if (r != EOK)
-				goto End;
-
-			act_curr_pos = it.curr_off;
-End:
-			ext4_dir_iterator_fini(&it);
-			if (r == EOK)
-				r = ext4_fs_put_inode_ref(&act);
-			else
-				ext4_fs_put_inode_ref(&act);
-
-			if (r != EOK)
-				ext4_trans_abort(mp);
-			else
-				ext4_trans_stop(mp);
-		}
-
-		if (dir_end) {
-			/*Directory iterator reached last entry*/
-			depth--;
-			if (depth)
-				inode_current = inode_up;
-
-		}
-
-	} while (depth);
-
-	/*Last unlink*/
-	if (r == EOK && !depth) {
-		/*Load parent.*/
-		struct ext4_inode_ref parent;
-		r = ext4_fs_get_inode_ref(&f.mp->fs, inode_up,
-				&parent);
-		if (r != EOK)
-			goto Finish;
-		r = ext4_fs_get_inode_ref(&f.mp->fs, inode_current,
-				&act);
+	if (ext4_inode_get_links_cnt(act.inode) == 2) {
+		/*Truncate*/
+		r = ext4_fs_truncate_inode(&act, 0);
 		if (r != EOK) {
+			ext4_fs_put_inode_ref(&parent);
 			ext4_fs_put_inode_ref(&act);
 			goto Finish;
 		}
+	}
 
-		ext4_trans_start(mp);
+	 /* Unlink from root of current directory*/
+	r = ext4_unlink(f.mp, &parent, &act,
+			(char *)path, len);
+	if (r != EOK) {
+		ext4_fs_put_inode_ref(&parent);
+		ext4_fs_put_inode_ref(&act);
+		goto Finish;
+	}
 
-		/* In this place all directories should be
-		 * unlinked.
-		 * Last unlink from root of current directory*/
-		r = ext4_unlink(f.mp, &parent, &act,
-				(char *)path, len);
+	if (ext4_inode_get_links_cnt(act.inode) == 2) {
+		ext4_inode_set_del_time(act.inode, -1L);
+		ext4_inode_set_links_cnt(act.inode, 0);
+		act.dirty = true;
+
+		r = ext4_fs_free_inode(&act);
 		if (r != EOK) {
 			ext4_fs_put_inode_ref(&parent);
 			ext4_fs_put_inode_ref(&act);
 			goto Finish;
 		}
+	}
 
-		if (ext4_inode_get_links_cnt(act.inode) == 2) {
-			ext4_inode_set_del_time(act.inode, -1L);
-			ext4_inode_set_links_cnt(act.inode, 0);
-			act.dirty = true;
-			/*Turncate*/
-			r = ext4_trunc_inode(mp, &act, 0);
-			if (r != EOK) {
-				ext4_fs_put_inode_ref(&parent);
-				ext4_fs_put_inode_ref(&act);
-				goto Finish;
-			}
+	r = ext4_fs_put_inode_ref(&parent);
+	if (r != EOK)
+		goto Finish;
 
-			r = ext4_fs_free_inode(&act);
-			if (r != EOK) {
-				ext4_fs_put_inode_ref(&parent);
-				ext4_fs_put_inode_ref(&act);
-				goto Finish;
-			}
-		}
-
-		r = ext4_fs_put_inode_ref(&parent);
-		if (r != EOK)
-			goto Finish;
-
-		r = ext4_fs_put_inode_ref(&act);
-	Finish:
-		if (r != EOK)
-			ext4_trans_abort(mp);
-		else
-			ext4_trans_stop(mp);
-	}
+	r = ext4_fs_put_inode_ref(&act);
+Finish:
+	if (r != EOK)
+		ext4_trans_abort(mp);
+	else
+		ext4_trans_stop(mp);
 
 	ext4_block_cache_write_back(mp->fs.bdev, 0);
 	EXT4_MP_UNLOCK(mp);
--- a/lwext4/ext4_journal.c
+++ b/lwext4/ext4_journal.c
@@ -1273,8 +1273,9 @@
 	struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
 	TAILQ_FOREACH_SAFE(jbd_buf, &trans->buf_queue, buf_node,
 			tmp) {
-		struct ext4_block block = jbd_buf->block;
-		ext4_block_flush_buf(fs->bdev, block.buf);
+		struct ext4_buf *buf = jbd_buf->block_rec->buf;
+		if (buf)
+			ext4_block_flush_buf(fs->bdev, buf);
 	}
 }
 
@@ -1458,6 +1459,18 @@
 		       &tmp);
 }
 
+static void
+jbd_trans_change_ownership(struct jbd_block_rec *block_rec,
+			   struct jbd_trans *new_trans,
+			   struct ext4_buf *new_buf)
+{
+	LIST_REMOVE(block_rec, tbrec_node);
+	/* Now this block record belongs to this transaction. */
+	LIST_INSERT_HEAD(&new_trans->tbrec_list, block_rec, tbrec_node);
+	block_rec->trans = new_trans;
+	block_rec->buf = new_buf;
+}
+
 static inline struct jbd_block_rec *
 jbd_trans_insert_block_rec(struct jbd_trans *trans,
 			   ext4_fsblk_t lba,
@@ -1466,12 +1479,7 @@
 	struct jbd_block_rec *block_rec;
 	block_rec = jbd_trans_block_rec_lookup(trans->journal, lba);
 	if (block_rec) {
-		LIST_REMOVE(block_rec, tbrec_node);
-		/* Data should be flushed to disk already. */
-		ext4_assert(!block_rec->buf);
-		/* Now this block record belongs to this transaction. */
-		LIST_INSERT_HEAD(&trans->tbrec_list, block_rec, tbrec_node);
-		block_rec->trans = trans;
+		jbd_trans_change_ownership(block_rec, trans, buf);
 		return block_rec;
 	}
 	block_rec = calloc(1, sizeof(struct jbd_block_rec));
@@ -1481,11 +1489,68 @@
 	block_rec->lba = lba;
 	block_rec->buf = buf;
 	block_rec->trans = trans;
+	TAILQ_INIT(&block_rec->dirty_buf_queue);
 	LIST_INSERT_HEAD(&trans->tbrec_list, block_rec, tbrec_node);
 	RB_INSERT(jbd_block, &trans->journal->block_rec_root, block_rec);
 	return block_rec;
 }
 
+static void
+jbd_trans_finish_callback(struct jbd_journal *journal,
+			  const struct jbd_trans *trans,
+			  struct jbd_block_rec *block_rec,
+			  bool abort)
+{
+	struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
+	if (block_rec->trans != trans)
+		return;
+
+	if (!abort) {
+		struct jbd_buf *jbd_buf, *tmp;
+		TAILQ_FOREACH_SAFE(jbd_buf,
+				&block_rec->dirty_buf_queue,
+				dirty_buf_node,
+				tmp) {
+			/* All we need is a fake ext4_buf. */
+			struct ext4_buf buf;
+
+			jbd_trans_end_write(fs->bdev->bc,
+					&buf,
+					EOK,
+					jbd_buf);
+		}
+	} else {
+		struct jbd_buf *jbd_buf;
+		struct ext4_block jbd_block = EXT4_BLOCK_ZERO(),
+				  block = EXT4_BLOCK_ZERO();
+		jbd_buf = TAILQ_LAST(&block_rec->dirty_buf_queue,
+				jbd_buf_dirty);
+		if (jbd_buf) {
+			ext4_assert(ext4_block_get(fs->bdev,
+						&jbd_block,
+						jbd_buf->jbd_lba) == EOK);
+			ext4_assert(ext4_block_get_noread(fs->bdev,
+						&block,
+						block_rec->lba) == EOK);
+			memcpy(block.data, jbd_block.data,
+					journal->block_size);
+
+			jbd_trans_change_ownership(block_rec,
+					jbd_buf->trans, block.buf);
+
+			block.buf->end_write = jbd_trans_end_write;
+			block.buf->end_write_arg = jbd_buf;
+
+			ext4_bcache_set_flag(jbd_block.buf, BC_TMP);
+			ext4_bcache_set_dirty(block.buf);
+
+			ext4_block_set(fs->bdev, &jbd_block);
+			ext4_block_set(fs->bdev, &block);
+			return;
+		}
+	}
+}
+
 static inline void
 jbd_trans_remove_block_rec(struct jbd_journal *journal,
 			   struct jbd_block_rec *block_rec,
@@ -1511,35 +1576,41 @@
 {
 	struct jbd_buf *buf;
 
-	if (!ext4_bcache_test_flag(block->buf, BC_DIRTY) &&
-	    block->buf->end_write != jbd_trans_end_write) {
-		struct jbd_block_rec *block_rec;
-		buf = calloc(1, sizeof(struct jbd_buf));
-		if (!buf)
-			return ENOMEM;
+	struct jbd_block_rec *block_rec;
+	if (block->buf->end_write == jbd_trans_end_write) {
+		buf = block->buf->end_write_arg;
+		if (buf && buf->trans == trans)
+			return EOK;
+	}
+	buf = calloc(1, sizeof(struct jbd_buf));
+	if (!buf)
+		return ENOMEM;
 
-		if ((block_rec = jbd_trans_insert_block_rec(trans,
+	if ((block_rec = jbd_trans_insert_block_rec(trans,
 					block->lb_id,
 					block->buf)) == NULL) {
-			free(buf);
-			return ENOMEM;
-		}
+		free(buf);
+		return ENOMEM;
+	}
 
-		buf->block_rec = block_rec;
-		buf->trans = trans;
-		buf->block = *block;
-		ext4_bcache_inc_ref(block->buf);
+	TAILQ_INSERT_TAIL(&block_rec->dirty_buf_queue,
+			buf,
+			dirty_buf_node);
 
-		/* If the content reach the disk, notify us
-		 * so that we may do a checkpoint. */
-		block->buf->end_write = jbd_trans_end_write;
-		block->buf->end_write_arg = buf;
+	buf->block_rec = block_rec;
+	buf->trans = trans;
+	buf->block = *block;
+	ext4_bcache_inc_ref(block->buf);
 
-		trans->data_cnt++;
-		TAILQ_INSERT_HEAD(&trans->buf_queue, buf, buf_node);
+	/* If the content reach the disk, notify us
+	 * so that we may do a checkpoint. */
+	block->buf->end_write = jbd_trans_end_write;
+	block->buf->end_write_arg = buf;
 
-		ext4_bcache_set_dirty(block->buf);
-	}
+	trans->data_cnt++;
+	TAILQ_INSERT_HEAD(&trans->buf_queue, buf, buf_node);
+
+	ext4_bcache_set_dirty(block->buf);
 	return EOK;
 }
 
@@ -1606,6 +1677,7 @@
 	struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
 	TAILQ_FOREACH_SAFE(jbd_buf, &trans->buf_queue, buf_node,
 			  tmp) {
+		block_rec = jbd_buf->block_rec;
 		if (abort) {
 			jbd_buf->block.buf->end_write = NULL;
 			jbd_buf->block.buf->end_write_arg = NULL;
@@ -1613,6 +1685,13 @@
 			ext4_block_set(fs->bdev, &jbd_buf->block);
 		}
 
+		TAILQ_REMOVE(&jbd_buf->block_rec->dirty_buf_queue,
+			jbd_buf,
+			dirty_buf_node);
+		jbd_trans_finish_callback(journal,
+				trans,
+				block_rec,
+				abort);
 		TAILQ_REMOVE(&trans->buf_queue, jbd_buf, buf_node);
 		free(jbd_buf);
 	}
@@ -1691,7 +1770,16 @@
 		if (ext4_bcache_test_flag(jbd_buf->block.buf,
 					BC_DIRTY))
 			break;
+	
+		TAILQ_REMOVE(&jbd_buf->block_rec->dirty_buf_queue,
+			jbd_buf,
+			dirty_buf_node);
 
+		jbd_trans_finish_callback(journal,
+				trans,
+				jbd_buf->block_rec,
+				false);
+
 		/* The buffer has not been modified, just release
 		 * that jbd_buf. */
 		jbd_trans_remove_block_rec(journal,
@@ -1710,6 +1798,15 @@
 		bool uuid_exist = false;
 		if (!ext4_bcache_test_flag(jbd_buf->block.buf,
 					   BC_DIRTY)) {
+			TAILQ_REMOVE(&jbd_buf->block_rec->dirty_buf_queue,
+					jbd_buf,
+					dirty_buf_node);
+
+			jbd_trans_finish_callback(journal,
+					trans,
+					jbd_buf->block_rec,
+					false);
+
 			/* The buffer has not been modified, just release
 			 * that jbd_buf. */
 			jbd_trans_remove_block_rec(journal,
@@ -1791,6 +1888,7 @@
 
 		memcpy(data_block.data, jbd_buf->block.data,
 			journal->block_size);
+		jbd_buf->jbd_lba = data_block.lb_id;
 
 		rc = jbd_block_set(journal->jbd_fs, &data_block);
 		if (rc != EOK)
@@ -1943,6 +2041,13 @@
 		trans->error = res;
 
 	TAILQ_REMOVE(&trans->buf_queue, jbd_buf, buf_node);
+	TAILQ_REMOVE(&jbd_buf->block_rec->dirty_buf_queue,
+			jbd_buf,
+			dirty_buf_node);
+	jbd_trans_finish_callback(journal,
+			trans,
+			jbd_buf->block_rec,
+			false);
 	jbd_buf->block_rec->buf = NULL;
 	free(jbd_buf);
 
--- a/lwext4/ext4_types.h
+++ b/lwext4/ext4_types.h
@@ -1098,10 +1098,12 @@
 };
 
 struct jbd_buf {
+	uint64_t jbd_lba;
 	struct ext4_block block;
 	struct jbd_trans *trans;
 	struct jbd_block_rec *block_rec;
 	TAILQ_ENTRY(jbd_buf) buf_node;
+	TAILQ_ENTRY(jbd_buf) dirty_buf_node;
 };
 
 struct jbd_revoke_rec {
@@ -1115,6 +1117,7 @@
 	struct jbd_trans *trans;
 	RB_ENTRY(jbd_block_rec) block_rec_node;
 	LIST_ENTRY(jbd_block_rec) tbrec_node;
+	TAILQ_HEAD(jbd_buf_dirty, jbd_buf) dirty_buf_queue;
 };
 
 struct jbd_trans {