diff options
Diffstat (limited to 'mm/swapfile.c')
| -rw-r--r-- | mm/swapfile.c | 41 | 
1 files changed, 25 insertions, 16 deletions
diff --git a/mm/swapfile.c b/mm/swapfile.c index 0789a762ce2f..dab43523afdd 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -2368,9 +2368,8 @@ EXPORT_SYMBOL_GPL(add_swap_extent);   * requirements, they are simply tossed out - we will never use those blocks   * for swapping.   * - * For S_ISREG swapfiles we set S_SWAPFILE across the life of the swapon.  This - * prevents root from shooting her foot off by ftruncating an in-use swapfile, - * which will scribble on the fs. + * For all swap devices we set S_SWAPFILE across the life of the swapon.  This + * prevents users from writing to the swap device, which will corrupt memory.   *   * The amount of disk space which a single swap extent represents varies.   * Typically it is in the 1-4 megabyte range.  So we can have hundreds of @@ -2661,13 +2660,14 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)  	inode = mapping->host;  	if (S_ISBLK(inode->i_mode)) {  		struct block_device *bdev = I_BDEV(inode); +  		set_blocksize(bdev, old_block_size);  		blkdev_put(bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL); -	} else { -		inode_lock(inode); -		inode->i_flags &= ~S_SWAPFILE; -		inode_unlock(inode);  	} + +	inode_lock(inode); +	inode->i_flags &= ~S_SWAPFILE; +	inode_unlock(inode);  	filp_close(swap_file, NULL);  	/* @@ -2890,11 +2890,11 @@ static int claim_swapfile(struct swap_info_struct *p, struct inode *inode)  		p->flags |= SWP_BLKDEV;  	} else if (S_ISREG(inode->i_mode)) {  		p->bdev = inode->i_sb->s_bdev; -		inode_lock(inode); -		if (IS_SWAPFILE(inode)) -			return -EBUSY; -	} else -		return -EINVAL; +	} + +	inode_lock(inode); +	if (IS_SWAPFILE(inode)) +		return -EBUSY;  	return 0;  } @@ -3275,6 +3275,17 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)  	if (error)  		goto bad_swap; +	/* +	 * Flush any pending IO and dirty mappings before we start using this +	 * swap device. +	 */ +	inode->i_flags |= S_SWAPFILE; +	error = inode_drain_writes(inode); +	if (error) { +		inode->i_flags &= ~S_SWAPFILE; +		goto bad_swap; +	} +  	mutex_lock(&swapon_mutex);  	prio = -1;  	if (swap_flags & SWAP_FLAG_PREFER) @@ -3295,8 +3306,6 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)  	atomic_inc(&proc_poll_event);  	wake_up_interruptible(&proc_poll_wait); -	if (S_ISREG(inode->i_mode)) -		inode->i_flags |= S_SWAPFILE;  	error = 0;  	goto out;  bad_swap: @@ -3318,7 +3327,7 @@ bad_swap:  	if (inced_nr_rotate_swap)  		atomic_dec(&nr_rotate_swap);  	if (swap_file) { -		if (inode && S_ISREG(inode->i_mode)) { +		if (inode) {  			inode_unlock(inode);  			inode = NULL;  		} @@ -3331,7 +3340,7 @@ out:  	}  	if (name)  		putname(name); -	if (inode && S_ISREG(inode->i_mode)) +	if (inode)  		inode_unlock(inode);  	if (!error)  		enable_swap_slots_cache();  |