Commit 3bdfab20 authored by Andrew Morton's avatar Andrew Morton Committed by Dave Jones

[PATCH] Ext2/3 noatime and dirsync fixes

Patch from "Theodore Ts'o" <tytso@mit.edu>

I recently noticed a bug in ext2/3; newly created inodes which inherit
the noatime flag from their containing directory do not respect noatime
until the inode is flushed from the inode cache and then re-read later.
This is because the code which checks the ext2 no-atime attribute and
then sets the S_NOATIME in inode->i_flags is present in
ext2_read_inode(), but not in ext2_new_inode().

I fixed this in 2.4, and then found an even worse bug in the 2.5 code;
the DIRSYNC flag is completely ignored *except* in the case where a
directory is newly created using mkdir and its parent directory has the
DIRSYNC flag.  S_DIRSYNC doesn't get set in the ext2_new_inode() or the
ext2_ioctl() paths (which is used by chattr).

This patch centralizes the code which translates the ext2 flags in the
raw ext2 inode to the appropriate flag values in inode->i_flags in a
single location.  This fixes the bug, makes things cleaner, and also
removes 30 lines of code and 128 bytes of compiled x86 text in the
bargain.
parent 5577ba7d
...@@ -112,6 +112,7 @@ extern int ext2_sync_inode (struct inode *); ...@@ -112,6 +112,7 @@ extern int ext2_sync_inode (struct inode *);
extern void ext2_discard_prealloc (struct inode *); extern void ext2_discard_prealloc (struct inode *);
extern void ext2_truncate (struct inode *); extern void ext2_truncate (struct inode *);
extern int ext2_setattr (struct dentry *, struct iattr *); extern int ext2_setattr (struct dentry *, struct iattr *);
extern void ext2_set_inode_flags(struct inode *inode);
/* ioctl.c */ /* ioctl.c */
extern int ext2_ioctl (struct inode *, struct file *, unsigned int, extern int ext2_ioctl (struct inode *, struct file *, unsigned int,
......
...@@ -545,10 +545,7 @@ struct inode * ext2_new_inode(struct inode * dir, int mode) ...@@ -545,10 +545,7 @@ struct inode * ext2_new_inode(struct inode * dir, int mode)
ei->i_prealloc_count = 0; ei->i_prealloc_count = 0;
ei->i_dir_start_lookup = 0; ei->i_dir_start_lookup = 0;
ei->i_state = EXT2_STATE_NEW; ei->i_state = EXT2_STATE_NEW;
if (ei->i_flags & EXT2_SYNC_FL) ext2_set_inode_flags(inode);
inode->i_flags |= S_SYNC;
if (ei->i_flags & EXT2_DIRSYNC_FL)
inode->i_flags |= S_DIRSYNC;
inode->i_generation = EXT2_SB(sb)->s_next_generation++; inode->i_generation = EXT2_SB(sb)->s_next_generation++;
insert_inode_hash(inode); insert_inode_hash(inode);
......
...@@ -1011,6 +1011,23 @@ static struct ext2_inode *ext2_get_inode(struct super_block *sb, ino_t ino, ...@@ -1011,6 +1011,23 @@ static struct ext2_inode *ext2_get_inode(struct super_block *sb, ino_t ino,
return ERR_PTR(-EIO); return ERR_PTR(-EIO);
} }
void ext2_set_inode_flags(struct inode *inode)
{
unsigned int flags = EXT2_I(inode)->i_flags;
inode->i_flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC);
if (flags & EXT2_SYNC_FL)
inode->i_flags |= S_SYNC;
if (flags & EXT2_APPEND_FL)
inode->i_flags |= S_APPEND;
if (flags & EXT2_IMMUTABLE_FL)
inode->i_flags |= S_IMMUTABLE;
if (flags & EXT2_NOATIME_FL)
inode->i_flags |= S_NOATIME;
if (flags & EXT2_DIRSYNC_FL)
inode->i_flags |= S_DIRSYNC;
}
void ext2_read_inode (struct inode * inode) void ext2_read_inode (struct inode * inode)
{ {
struct ext2_inode_info *ei = EXT2_I(inode); struct ext2_inode_info *ei = EXT2_I(inode);
...@@ -1108,14 +1125,7 @@ void ext2_read_inode (struct inode * inode) ...@@ -1108,14 +1125,7 @@ void ext2_read_inode (struct inode * inode)
le32_to_cpu(raw_inode->i_block[0])); le32_to_cpu(raw_inode->i_block[0]));
} }
brelse (bh); brelse (bh);
if (ei->i_flags & EXT2_SYNC_FL) ext2_set_inode_flags(inode);
inode->i_flags |= S_SYNC;
if (ei->i_flags & EXT2_APPEND_FL)
inode->i_flags |= S_APPEND;
if (ei->i_flags & EXT2_IMMUTABLE_FL)
inode->i_flags |= S_IMMUTABLE;
if (ei->i_flags & EXT2_NOATIME_FL)
inode->i_flags |= S_NOATIME;
return; return;
bad_inode: bad_inode:
......
...@@ -58,22 +58,7 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, ...@@ -58,22 +58,7 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE; flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE;
ei->i_flags = flags; ei->i_flags = flags;
if (flags & EXT2_SYNC_FL) ext2_set_inode_flags(inode);
inode->i_flags |= S_SYNC;
else
inode->i_flags &= ~S_SYNC;
if (flags & EXT2_APPEND_FL)
inode->i_flags |= S_APPEND;
else
inode->i_flags &= ~S_APPEND;
if (flags & EXT2_IMMUTABLE_FL)
inode->i_flags |= S_IMMUTABLE;
else
inode->i_flags &= ~S_IMMUTABLE;
if (flags & EXT2_NOATIME_FL)
inode->i_flags |= S_NOATIME;
else
inode->i_flags &= ~S_NOATIME;
inode->i_ctime = CURRENT_TIME; inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(inode); mark_inode_dirty(inode);
return 0; return 0;
......
...@@ -568,10 +568,7 @@ struct inode *ext3_new_inode(handle_t *handle, struct inode * dir, int mode) ...@@ -568,10 +568,7 @@ struct inode *ext3_new_inode(handle_t *handle, struct inode * dir, int mode)
#endif #endif
ei->i_block_group = group; ei->i_block_group = group;
if (ei->i_flags & EXT3_SYNC_FL) ext3_set_inode_flags(inode);
inode->i_flags |= S_SYNC;
if (ei->i_flags & EXT3_DIRSYNC_FL)
inode->i_flags |= S_DIRSYNC;
if (IS_DIRSYNC(inode)) if (IS_DIRSYNC(inode))
handle->h_sync = 1; handle->h_sync = 1;
insert_inode_hash(inode); insert_inode_hash(inode);
......
...@@ -2209,6 +2209,24 @@ int ext3_get_inode_loc (struct inode *inode, struct ext3_iloc *iloc) ...@@ -2209,6 +2209,24 @@ int ext3_get_inode_loc (struct inode *inode, struct ext3_iloc *iloc)
return -EIO; return -EIO;
} }
void ext3_set_inode_flags(struct inode *inode)
{
unsigned int flags = EXT3_I(inode)->i_flags;
inode->i_flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC);
if (flags & EXT3_SYNC_FL)
inode->i_flags |= S_SYNC;
if (flags & EXT3_APPEND_FL)
inode->i_flags |= S_APPEND;
if (flags & EXT3_IMMUTABLE_FL)
inode->i_flags |= S_IMMUTABLE;
if (flags & EXT3_NOATIME_FL)
inode->i_flags |= S_NOATIME;
if (flags & EXT3_DIRSYNC_FL)
inode->i_flags |= S_DIRSYNC;
}
void ext3_read_inode(struct inode * inode) void ext3_read_inode(struct inode * inode)
{ {
struct ext3_iloc iloc; struct ext3_iloc iloc;
...@@ -2320,14 +2338,7 @@ void ext3_read_inode(struct inode * inode) ...@@ -2320,14 +2338,7 @@ void ext3_read_inode(struct inode * inode)
init_special_inode(inode, inode->i_mode, init_special_inode(inode, inode->i_mode,
le32_to_cpu(iloc.raw_inode->i_block[0])); le32_to_cpu(iloc.raw_inode->i_block[0]));
} }
if (ei->i_flags & EXT3_SYNC_FL) ext3_set_inode_flags(inode);
inode->i_flags |= S_SYNC;
if (ei->i_flags & EXT3_APPEND_FL)
inode->i_flags |= S_APPEND;
if (ei->i_flags & EXT3_IMMUTABLE_FL)
inode->i_flags |= S_IMMUTABLE;
if (ei->i_flags & EXT3_NOATIME_FL)
inode->i_flags |= S_NOATIME;
return; return;
bad_inode: bad_inode:
......
...@@ -85,22 +85,7 @@ int ext3_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, ...@@ -85,22 +85,7 @@ int ext3_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
flags |= oldflags & ~EXT3_FL_USER_MODIFIABLE; flags |= oldflags & ~EXT3_FL_USER_MODIFIABLE;
ei->i_flags = flags; ei->i_flags = flags;
if (flags & EXT3_SYNC_FL) ext3_set_inode_flags(inode);
inode->i_flags |= S_SYNC;
else
inode->i_flags &= ~S_SYNC;
if (flags & EXT3_APPEND_FL)
inode->i_flags |= S_APPEND;
else
inode->i_flags &= ~S_APPEND;
if (flags & EXT3_IMMUTABLE_FL)
inode->i_flags |= S_IMMUTABLE;
else
inode->i_flags &= ~S_IMMUTABLE;
if (flags & EXT3_NOATIME_FL)
inode->i_flags |= S_NOATIME;
else
inode->i_flags &= ~S_NOATIME;
inode->i_ctime = CURRENT_TIME; inode->i_ctime = CURRENT_TIME;
err = ext3_mark_iloc_dirty(handle, inode, &iloc); err = ext3_mark_iloc_dirty(handle, inode, &iloc);
......
...@@ -730,6 +730,7 @@ extern void ext3_discard_prealloc (struct inode *); ...@@ -730,6 +730,7 @@ extern void ext3_discard_prealloc (struct inode *);
extern void ext3_dirty_inode(struct inode *); extern void ext3_dirty_inode(struct inode *);
extern int ext3_change_inode_journal_flag(struct inode *, int); extern int ext3_change_inode_journal_flag(struct inode *, int);
extern void ext3_truncate (struct inode *); extern void ext3_truncate (struct inode *);
extern void ext3_set_inode_flags(struct inode *);
/* ioctl.c */ /* ioctl.c */
extern int ext3_ioctl (struct inode *, struct file *, unsigned int, extern int ext3_ioctl (struct inode *, struct file *, unsigned int,
......
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