Commit 699489bb authored by Jaegeuk Kim's avatar Jaegeuk Kim

f2fs: sync dir->i_size with its block allocation

If new dentry block is allocated and its i_size is updated, we should update
its inode block together in order to sync i_size and its block allocation.
Otherwise, we can loose additional dentry block due to the unconsistent i_size.

Errorneous Scenario
-------------------

In the recovery routine,
 - recovery_dentry
 | - __f2fs_add_link
 | | - get_new_data_page
 | | | - i_size_write(new_i_size)
 | | | - mark_inode_dirty_sync(dir)
 | | - update_parent_metadata
 | | | - mark_inode_dirty(dir)
 |
 - write_checkpoint
   - sync_dirty_dir_inodes
     - filemap_flush(dentry_blocks)
       - f2fs_write_data_page
         - skip to write the last dentry block due to index < i_size

In the above flow, new_i_size is not updated to its inode block so that the
last dentry block will be lost accordingly.
Signed-off-by: default avatarJaegeuk Kim <jaegeuk.kim@samsung.com>
parent 2d4d9fb5
...@@ -339,6 +339,8 @@ struct page *get_new_data_page(struct inode *inode, ...@@ -339,6 +339,8 @@ struct page *get_new_data_page(struct inode *inode,
if (new_i_size && if (new_i_size &&
i_size_read(inode) < ((index + 1) << PAGE_CACHE_SHIFT)) { i_size_read(inode) < ((index + 1) << PAGE_CACHE_SHIFT)) {
i_size_write(inode, ((index + 1) << PAGE_CACHE_SHIFT)); i_size_write(inode, ((index + 1) << PAGE_CACHE_SHIFT));
/* Only the directory inode sets new_i_size */
set_inode_flag(F2FS_I(inode), FI_UPDATE_DIR);
mark_inode_dirty_sync(inode); mark_inode_dirty_sync(inode);
} }
return page; return page;
......
...@@ -370,22 +370,20 @@ static struct page *init_inode_metadata(struct inode *inode, ...@@ -370,22 +370,20 @@ static struct page *init_inode_metadata(struct inode *inode,
static void update_parent_metadata(struct inode *dir, struct inode *inode, static void update_parent_metadata(struct inode *dir, struct inode *inode,
unsigned int current_depth) unsigned int current_depth)
{ {
bool need_dir_update = false;
if (is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) { if (is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) {
if (S_ISDIR(inode->i_mode)) { if (S_ISDIR(inode->i_mode)) {
inc_nlink(dir); inc_nlink(dir);
need_dir_update = true; set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR);
} }
clear_inode_flag(F2FS_I(inode), FI_NEW_INODE); clear_inode_flag(F2FS_I(inode), FI_NEW_INODE);
} }
dir->i_mtime = dir->i_ctime = CURRENT_TIME; dir->i_mtime = dir->i_ctime = CURRENT_TIME;
if (F2FS_I(dir)->i_current_depth != current_depth) { if (F2FS_I(dir)->i_current_depth != current_depth) {
F2FS_I(dir)->i_current_depth = current_depth; F2FS_I(dir)->i_current_depth = current_depth;
need_dir_update = true; set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR);
} }
if (need_dir_update) if (is_inode_flag_set(F2FS_I(dir), FI_UPDATE_DIR))
update_inode_page(dir); update_inode_page(dir);
else else
mark_inode_dirty(dir); mark_inode_dirty(dir);
...@@ -502,6 +500,7 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct inode *in ...@@ -502,6 +500,7 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct inode *in
update_parent_metadata(dir, inode, current_depth); update_parent_metadata(dir, inode, current_depth);
fail: fail:
clear_inode_flag(F2FS_I(dir), FI_UPDATE_DIR);
kunmap(dentry_page); kunmap(dentry_page);
f2fs_put_page(dentry_page, 1); f2fs_put_page(dentry_page, 1);
return err; return err;
......
...@@ -859,6 +859,7 @@ enum { ...@@ -859,6 +859,7 @@ enum {
FI_INC_LINK, /* need to increment i_nlink */ FI_INC_LINK, /* need to increment i_nlink */
FI_ACL_MODE, /* indicate acl mode */ FI_ACL_MODE, /* indicate acl mode */
FI_NO_ALLOC, /* should not allocate any blocks */ FI_NO_ALLOC, /* should not allocate any blocks */
FI_UPDATE_DIR, /* should update inode block for consistency */
FI_DELAY_IPUT, /* used for the recovery */ FI_DELAY_IPUT, /* used for the recovery */
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment