History

ext4: atomically set inode->i_flags in ext4_set_inode_flags()

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.

Theodore Ts'o committed 12 years ago

Commit 00a1a051 parent 981e8932 files · +24 −6Browse files

Unified · Split

2 files
  1. fs/ext4/inode.c +9 −6
  2. include/linux/bitops.h +15 −0
fs/ext4/inode.c+9−6
@@ -38,6 +38,7 @@ · 37 unmodified lines
3838 #include <linux/slab.h>
3939 #include <linux/ratelimit.h>
4040 #include <linux/aio.h>
41+#include <linux/bitops.h>
4142
4243 #include "ext4_jbd2.h"
4344 #include "xattr.h"
@@ -3921,18 +3922,20 @@ int ext4_get_inode_loc(struct inode *inode, struct ext4_iloc *iloc) · 3877 unmodified lines
39213922 void ext4_set_inode_flags(struct inode *inode)
39223923 {
39233924 unsigned int flags = EXT4_I(inode)->i_flags;
3925+ unsigned int new_fl = 0;
39243926
3925− inode->i_flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC);
39263927 if (flags & EXT4_SYNC_FL)
3927− inode->i_flags |= S_SYNC;
3928+ new_fl |= S_SYNC;
39283929 if (flags & EXT4_APPEND_FL)
3929− inode->i_flags |= S_APPEND;
3930+ new_fl |= S_APPEND;
39303931 if (flags & EXT4_IMMUTABLE_FL)
3931− inode->i_flags |= S_IMMUTABLE;
3932+ new_fl |= S_IMMUTABLE;
39323933 if (flags & EXT4_NOATIME_FL)
3933− inode->i_flags |= S_NOATIME;
3934+ new_fl |= S_NOATIME;
39343935 if (flags & EXT4_DIRSYNC_FL)
3935− inode->i_flags |= S_DIRSYNC;
3936+ new_fl |= S_DIRSYNC;
3937+ set_mask_bits(&inode->i_flags,
3938+ S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC, new_fl);
39363939 }
39373940
39383941 /* Propagate flags from i_flags to EXT4_I(inode)->i_flags */
include/linux/bitops.h+15−0
@@ -196,6 +196,21 @@ static inline unsigned long __ffs64(u64 word) · 195 unmodified lines
196196
197197 #ifdef __KERNEL__
198198
199+#ifndef set_mask_bits
200+#define set_mask_bits(ptr, _mask, _bits) \
201+({ \
202+ const typeof(*ptr) mask = (_mask), bits = (_bits); \
203+ typeof(*ptr) old, new; \
204+ \
205+ do { \
206+ old = ACCESS_ONCE(*ptr); \
207+ new = (old & ~mask) | bits; \
208+ } while (cmpxchg(ptr, old, new) != old); \
209+ \
210+ new; \
211+})
212+#endif
213+
199214 #ifndef find_last_bit
200215 /**
201216 * find_last_bit - find the last set bit in a memory region