forked from mirrors/linux
		
	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. Reported-by: John Sullivan <jsrhbz@kanargh.force9.co.uk> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> Cc: stable@kernel.org Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
		
							parent
							
								
									981e893ed5
								
							
						
					
					
						commit
						00a1a053eb
					
				
					 2 changed files with 24 additions and 6 deletions
				
			
		|  | @ -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" | ||||
|  | @ -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 */ | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Theodore Ts'o
						Theodore Ts'o