在ext3文件系统中,如果处于日志模式,间接块、双间接块和三间接块会被视为元数据块并被日志记录吗?
使用源代码Luke-来自Linux fs/ext3/inode.c
(链接的行号根据本文正确,但URL是Linus的头树,因此最终可能会更改):
/*
* Note that we always start a transaction even if we're not journalling
* data. This is to preserve ordering: any hole instantiation within
* __block_write_full_page -> ext3_get_block() should be journalled
* along with the data so we don't crash and then get metadata which
* refers to old data.
[ ... ]
static int ext3_ordered_writepage(struct page *page,
struct writeback_control *wbc)
{
struct inode *inode = page->mapping->host;
struct buffer_head *page_bufs;
handle_t *handle = NULL;
int ret = 0;
int err;
[ ... ]
handle = ext3_journal_start(inode, ext3_writepage_trans_blocks(inode));
[ ... ]
ret = block_write_full_page(page, ext3_get_block, wbc);
[ ... ]
/*
* And attach them to the current transaction. But only if
* block_write_full_page() succeeded. Otherwise they are unmapped,
* and generally junk.
*/
if (ret == 0) {
err = walk_page_buffers(handle, page_bufs, 0, PAGE_CACHE_SIZE,
NULL, journal_dirty_data_fn);
if (!ret)
ret = err;
}
walk_page_buffers(handle, page_bufs, 0,
PAGE_CACHE_SIZE, NULL, bput_one);
err = ext3_journal_stop(handle);
if (!ret)
ret = err;
[ ... ]
return ret;
}
[ ... ]
/*
* ext3_truncate()
*
* We block out ext3_get_block() block instantiations across the entire
* transaction, and VFS/VM ensures that ext3_truncate() cannot run
* simultaneously on behalf of the same inode.
*
* As we work through the truncate and commit bits of it to the journal there
* is one core, guiding principle: the file's tree must always be consistent on
* disk. We must be able to restart the truncate after a crash.
*
[ ... ]
*/
void ext3_truncate(struct inode *inode)
{
handle_t *handle;
[ ... ]
handle = start_transaction(inode);
[ ... ]
/*
* OK. This truncate is going to happen. We add the inode to the
* orphan list, so that if this truncate spans multiple transactions,
* and we crash, we will resume the truncate when the filesystem
* recovers. It also marks the inode dirty, to catch the new size.
*
* Implication: the file must always be in a sane, consistent
* truncatable state while each transaction commits.
*/
if (ext3_orphan_add(handle, inode))
goto out_stop;
[ ... ]
if (n == 1) { /* direct blocks */
ext3_free_data(handle, inode, NULL, i_data+offsets[0],
i_data + EXT3_NDIR_BLOCKS);
goto do_indirects;
}
[ ... ]
do_indirects:
/* Kill the remaining (whole) subtrees */
switch (offsets[0]) {
default:
nr = i_data[EXT3_IND_BLOCK];
if (nr) {
ext3_free_branches(handle, inode, NULL, &nr, &nr+1, 1);
i_data[EXT3_IND_BLOCK] = 0;
}
case EXT3_IND_BLOCK:
nr = i_data[EXT3_DIND_BLOCK];
if (nr) {
ext3_free_branches(handle, inode, NULL, &nr, &nr+1, 2);
i_data[EXT3_DIND_BLOCK] = 0;
}
case EXT3_DIND_BLOCK:
nr = i_data[EXT3_TIND_BLOCK];
if (nr) {
ext3_free_branches(handle, inode, NULL, &nr, &nr+1, 3);
i_data[EXT3_TIND_BLOCK] = 0;
}
case EXT3_TIND_BLOCK:
;
}
[ ... ]
ext3_journal_stop(handle);
[ ... ]
}
这清楚地表明,文件扩展名/截断(即任何级别的直接/间接块的分配和/或释放)始终是事务性的。还有ext3_get_blocks()
本身中的代码,即使在之前没有创建事务的情况下进行调用,它也会打开事务——用于直接I/O写入。
简而言之,是的,只要ext3有一个日志,那么任何级别的直接/间接块的修改都会被处理。