diff --git a/lwext4/ext4.c b/lwext4/ext4.c index 16e50a0..1684edb 100644 --- a/lwext4/ext4.c +++ b/lwext4/ext4.c @@ -705,52 +705,73 @@ static bool ext4_parse_flags(const char *flags, uint32_t *file_flags) } 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 @@ static int ext4_generic_open2(ext4_file *f, const char *path, int flags, 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 @@ static int ext4_generic_open2(ext4_file *f, const char *path, int flags, 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 @@ static int ext4_generic_open2(ext4_file *f, const char *path, int flags, } 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,6 +1302,29 @@ int ext4_fremove(const char *path) 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,14 +1339,6 @@ int ext4_fremove(const char *path) /*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) @@ -1360,17 +1407,11 @@ int ext4_fopen(ext4_file *f, const char *path, const char *flags) 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 @@ int ext4_fopen2(ext4_file *f, const char *path, int flags) 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 @@ static int ext4_ftruncate_no_lock(ext4_file *f, uint64_t size) 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,26 +2498,19 @@ int ext4_dir_rm(const char *path) 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; 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 @@ int ext4_dir_rm(const char *path) ext4_block_cache_write_back(mp->fs.bdev, 1); - do { - - uint64_t act_curr_pos = 0; - has_children = false; - dir_end = false; - - 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; - - } + /*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; + } - } while (depth); + ext4_trans_start(mp); - /*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; + } + + 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; - /* 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); + 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_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(&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); diff --git a/lwext4/ext4_journal.c b/lwext4/ext4_journal.c index dca62d1..4f5ca59 100644 --- a/lwext4/ext4_journal.c +++ b/lwext4/ext4_journal.c @@ -1273,8 +1273,9 @@ static void jbd_journal_flush_trans(struct jbd_trans *trans) 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 @@ jbd_trans_block_rec_lookup(struct jbd_journal *journal, &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 @@ jbd_trans_insert_block_rec(struct jbd_trans *trans, 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 @@ jbd_trans_insert_block_rec(struct jbd_trans *trans, 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 @@ int jbd_trans_set_block_dirty(struct jbd_trans *trans, { 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 @@ void jbd_journal_free_trans(struct jbd_journal *journal, 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 @@ void jbd_journal_free_trans(struct jbd_journal *journal, 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,6 +1770,15 @@ static int jbd_journal_prepare(struct jbd_journal *journal, 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. */ @@ -1710,6 +1798,15 @@ static int jbd_journal_prepare(struct jbd_journal *journal, 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 @@ again: 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 @@ static void jbd_trans_end_write(struct ext4_bcache *bc __unused, 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); diff --git a/lwext4/ext4_types.h b/lwext4/ext4_types.h index 51036f9..03d680f 100644 --- a/lwext4/ext4_types.h +++ b/lwext4/ext4_types.h @@ -1098,10 +1098,12 @@ struct jbd_fs { }; 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_block_rec { 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 {