diff options
author | Lukas Czerner <lczerner@redhat.com> | 2015-04-03 10:46:58 -0400 |
---|---|---|
committer | Ben Hutchings <ben@decadent.org.uk> | 2015-08-07 00:32:02 +0100 |
commit | 64bd2394daf84a6ec29fa5a4b6d39ef45625df81 (patch) | |
tree | 6d5a25334db3591eb4611e91bb69aea38480c8db /fs | |
parent | 738f1509ae86591d59092f9942b059ce09716504 (diff) | |
download | lwn-64bd2394daf84a6ec29fa5a4b6d39ef45625df81.tar.gz lwn-64bd2394daf84a6ec29fa5a4b6d39ef45625df81.zip |
ext4: make fsync to sync parent dir in no-journal for real this time
commit e12fb97222fc41e8442896934f76d39ef99b590a upstream.
Previously commit 14ece1028b3ed53ffec1b1213ffc6acaf79ad77c added a
support for for syncing parent directory of newly created inodes to
make sure that the inode is not lost after a power failure in
no-journal mode.
However this does not work in majority of cases, namely:
- if the directory has inline data
- if the directory is already indexed
- if the directory already has at least one block and:
- the new entry fits into it
- or we've successfully converted it to indexed
So in those cases we might lose the inode entirely even after fsync in
the no-journal mode. This also includes ext2 default mode obviously.
I've noticed this while running xfstest generic/321 and even though the
test should fail (we need to run fsck after a crash in no-journal mode)
I could not find a newly created entries even when if it was fsynced
before.
Fix this by adjusting the ext4_add_entry() successful exit paths to set
the inode EXT4_STATE_NEWENTRY so that fsync has the chance to fsync the
parent directory as well.
Signed-off-by: Lukas Czerner <lczerner@redhat.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Reviewed-by: Jan Kara <jack@suse.cz>
Cc: Frank Mayhar <fmayhar@google.com>
[bwh: Backported to 3.2: inline data is not supported]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/namei.c | 18 |
1 files changed, 10 insertions, 8 deletions
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index c9f2e3d04a14..cd1dd498297e 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1456,7 +1456,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, struct inode *inode) { struct inode *dir = dentry->d_parent->d_inode; - struct buffer_head *bh; + struct buffer_head *bh = NULL; struct ext4_dir_entry_2 *de; struct super_block *sb; int retval; @@ -1471,7 +1471,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, if (is_dx(dir)) { retval = ext4_dx_add_entry(handle, dentry, inode); if (!retval || (retval != ERR_BAD_DX_DIR)) - return retval; + goto out; ext4_clear_inode_flag(dir, EXT4_INODE_INDEX); dx_fallback++; ext4_mark_inode_dirty(handle, dir); @@ -1482,14 +1482,15 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, if(!bh) return retval; retval = add_dirent_to_buf(handle, dentry, inode, NULL, bh); - if (retval != -ENOSPC) { - brelse(bh); - return retval; - } + if (retval != -ENOSPC) + goto out; if (blocks == 1 && !dx_fallback && - EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_DIR_INDEX)) - return make_indexed_dir(handle, dentry, inode, bh); + EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_DIR_INDEX)) { + retval = make_indexed_dir(handle, dentry, inode, bh); + bh = NULL; /* make_indexed_dir releases bh */ + goto out; + } brelse(bh); } bh = ext4_append(handle, dir, &block, &retval); @@ -1499,6 +1500,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, de->inode = 0; de->rec_len = ext4_rec_len_to_disk(blocksize, blocksize); retval = add_dirent_to_buf(handle, dentry, inode, de, bh); +out: brelse(bh); if (retval == 0) ext4_set_inode_state(inode, EXT4_STATE_NEWENTRY); |