diff options
author | Lukas Czerner <lczerner@redhat.com> | 2015-04-03 10:46:58 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2015-04-03 10:46:58 -0400 |
commit | e12fb97222fc41e8442896934f76d39ef99b590a (patch) | |
tree | 5325c812dee98b42b4e5bdc7e3f3e827d024f5b9 /fs | |
parent | 9d21c9fa2cc24e2a195a79c27b6550e1a96051a4 (diff) |
ext4: make fsync to sync parent dir in no-journal for real this time
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>
Cc: stable@vger.kernel.org
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/namei.c | 20 |
1 files changed, 11 insertions, 9 deletions
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index efb64aee5b9e..23a0b9bf822d 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c | |||
@@ -1864,7 +1864,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, | |||
1864 | struct inode *inode) | 1864 | struct inode *inode) |
1865 | { | 1865 | { |
1866 | struct inode *dir = dentry->d_parent->d_inode; | 1866 | struct inode *dir = dentry->d_parent->d_inode; |
1867 | struct buffer_head *bh; | 1867 | struct buffer_head *bh = NULL; |
1868 | struct ext4_dir_entry_2 *de; | 1868 | struct ext4_dir_entry_2 *de; |
1869 | struct ext4_dir_entry_tail *t; | 1869 | struct ext4_dir_entry_tail *t; |
1870 | struct super_block *sb; | 1870 | struct super_block *sb; |
@@ -1888,14 +1888,14 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, | |||
1888 | return retval; | 1888 | return retval; |
1889 | if (retval == 1) { | 1889 | if (retval == 1) { |
1890 | retval = 0; | 1890 | retval = 0; |
1891 | return retval; | 1891 | goto out; |
1892 | } | 1892 | } |
1893 | } | 1893 | } |
1894 | 1894 | ||
1895 | if (is_dx(dir)) { | 1895 | if (is_dx(dir)) { |
1896 | retval = ext4_dx_add_entry(handle, dentry, inode); | 1896 | retval = ext4_dx_add_entry(handle, dentry, inode); |
1897 | if (!retval || (retval != ERR_BAD_DX_DIR)) | 1897 | if (!retval || (retval != ERR_BAD_DX_DIR)) |
1898 | return retval; | 1898 | goto out; |
1899 | ext4_clear_inode_flag(dir, EXT4_INODE_INDEX); | 1899 | ext4_clear_inode_flag(dir, EXT4_INODE_INDEX); |
1900 | dx_fallback++; | 1900 | dx_fallback++; |
1901 | ext4_mark_inode_dirty(handle, dir); | 1901 | ext4_mark_inode_dirty(handle, dir); |
@@ -1907,14 +1907,15 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, | |||
1907 | return PTR_ERR(bh); | 1907 | return PTR_ERR(bh); |
1908 | 1908 | ||
1909 | retval = add_dirent_to_buf(handle, dentry, inode, NULL, bh); | 1909 | retval = add_dirent_to_buf(handle, dentry, inode, NULL, bh); |
1910 | if (retval != -ENOSPC) { | 1910 | if (retval != -ENOSPC) |
1911 | brelse(bh); | 1911 | goto out; |
1912 | return retval; | ||
1913 | } | ||
1914 | 1912 | ||
1915 | if (blocks == 1 && !dx_fallback && | 1913 | if (blocks == 1 && !dx_fallback && |
1916 | EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_DIR_INDEX)) | 1914 | EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_DIR_INDEX)) { |
1917 | return make_indexed_dir(handle, dentry, inode, bh); | 1915 | retval = make_indexed_dir(handle, dentry, inode, bh); |
1916 | bh = NULL; /* make_indexed_dir releases bh */ | ||
1917 | goto out; | ||
1918 | } | ||
1918 | brelse(bh); | 1919 | brelse(bh); |
1919 | } | 1920 | } |
1920 | bh = ext4_append(handle, dir, &block); | 1921 | bh = ext4_append(handle, dir, &block); |
@@ -1930,6 +1931,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, | |||
1930 | } | 1931 | } |
1931 | 1932 | ||
1932 | retval = add_dirent_to_buf(handle, dentry, inode, de, bh); | 1933 | retval = add_dirent_to_buf(handle, dentry, inode, de, bh); |
1934 | out: | ||
1933 | brelse(bh); | 1935 | brelse(bh); |
1934 | if (retval == 0) | 1936 | if (retval == 0) |
1935 | ext4_set_inode_state(inode, EXT4_STATE_NEWENTRY); | 1937 | ext4_set_inode_state(inode, EXT4_STATE_NEWENTRY); |