Skip to content

Commit

Permalink
ext4: atomically set inode->i_flags in ext4_set_inode_flags()
Browse files Browse the repository at this point in the history
Use cmpxchg() to atomically set i_flags instead of clearing out the
S_IMMUTABLE, S_APPEND, etc. flags and then setting them from the
EXT4_IMMUTABLE_FL, EXT4_APPEND_FL flags, since this opens up a race
where an immutable file has the immutable flag cleared for a brief
window of time.

Reported-by: John Sullivan <[email protected]>
Signed-off-by: "Theodore Ts'o" <[email protected]>
Cc: [email protected]
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
tytso authored and torvalds committed Mar 31, 2014
1 parent 981e893 commit 00a1a05
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 6 deletions.
15 changes: 9 additions & 6 deletions fs/ext4/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include <linux/slab.h>
#include <linux/ratelimit.h>
#include <linux/aio.h>
#include <linux/bitops.h>

#include "ext4_jbd2.h"
#include "xattr.h"
Expand Down Expand Up @@ -3921,18 +3922,20 @@ int ext4_get_inode_loc(struct inode *inode, struct ext4_iloc *iloc)
void ext4_set_inode_flags(struct inode *inode)
{
unsigned int flags = EXT4_I(inode)->i_flags;
unsigned int new_fl = 0;

inode->i_flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC);
if (flags & EXT4_SYNC_FL)
inode->i_flags |= S_SYNC;
new_fl |= S_SYNC;
if (flags & EXT4_APPEND_FL)
inode->i_flags |= S_APPEND;
new_fl |= S_APPEND;
if (flags & EXT4_IMMUTABLE_FL)
inode->i_flags |= S_IMMUTABLE;
new_fl |= S_IMMUTABLE;
if (flags & EXT4_NOATIME_FL)
inode->i_flags |= S_NOATIME;
new_fl |= S_NOATIME;
if (flags & EXT4_DIRSYNC_FL)
inode->i_flags |= S_DIRSYNC;
new_fl |= S_DIRSYNC;
set_mask_bits(&inode->i_flags,
S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC, new_fl);
}

/* Propagate flags from i_flags to EXT4_I(inode)->i_flags */
Expand Down
15 changes: 15 additions & 0 deletions include/linux/bitops.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,21 @@ static inline unsigned long __ffs64(u64 word)

#ifdef __KERNEL__

#ifndef set_mask_bits
#define set_mask_bits(ptr, _mask, _bits) \
({ \
const typeof(*ptr) mask = (_mask), bits = (_bits); \
typeof(*ptr) old, new; \
\
do { \
old = ACCESS_ONCE(*ptr); \
new = (old & ~mask) | bits; \
} while (cmpxchg(ptr, old, new) != old); \
\
new; \
})
#endif

#ifndef find_last_bit
/**
* find_last_bit - find the last set bit in a memory region
Expand Down

0 comments on commit 00a1a05

Please sign in to comment.