aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorHugh Dickins <hugh@veritas.com>2004-06-26 21:03:11 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2004-06-26 21:03:11 -0700
commit7a35e30c2194c7de5514a6fb1678cd2da9421843 (patch)
tree3672ba2d7eb7d6f2ee9bfc454054b6467ed08087 /mm
parent53d4b32b482da634f2c6fee04046330e2865643f (diff)
downloadhistory-7a35e30c2194c7de5514a6fb1678cd2da9421843.tar.gz
[PATCH] Don't hold i_sem on swapfiles
We permanently hold the i_sem of swapfiles so that nobody can addidentally ftruncate them, causing subsequent filesystem destruction. Problem is, it's fairly easy for things like backup applications to get stuck onthe swapfile, sleeping until someone does a swapoff. So take all that out again and add a new S_SWAPFILE inode flag. Test that in the truncate path and refuse to truncate an in-use swapfile. Synchronisation between swapon and truncate is via i_sem. Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'mm')
-rw-r--r--mm/memory.c12
-rw-r--r--mm/swapfile.c19
2 files changed, 25 insertions, 6 deletions
diff --git a/mm/memory.c b/mm/memory.c
index e1fafbb4deed71..c511c41943f24b 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -1223,6 +1223,12 @@ int vmtruncate(struct inode * inode, loff_t offset)
if (inode->i_size < offset)
goto do_expand;
+ /*
+ * truncation of in-use swapfiles is disallowed - it would cause
+ * subsequent swapout to scribble on the now-freed blocks.
+ */
+ if (IS_SWAPFILE(inode))
+ goto out_busy;
i_size_write(inode, offset);
unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
truncate_inode_pages(mapping, offset);
@@ -1233,7 +1239,7 @@ do_expand:
if (limit != RLIM_INFINITY && offset > limit)
goto out_sig;
if (offset > inode->i_sb->s_maxbytes)
- goto out;
+ goto out_big;
i_size_write(inode, offset);
out_truncate:
@@ -1242,8 +1248,10 @@ out_truncate:
return 0;
out_sig:
send_sig(SIGXFSZ, current, 0);
-out:
+out_big:
return -EFBIG;
+out_busy:
+ return -ETXTBSY;
}
EXPORT_SYMBOL(vmtruncate);
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 714c8339c5f21b..ef4045b4204e3b 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -1072,6 +1072,7 @@ asmlinkage long sys_swapoff(const char __user * specialfile)
unsigned short *swap_map;
struct file *swap_file, *victim;
struct address_space *mapping;
+ struct inode *inode;
char * pathname;
int i, type, prev;
int err;
@@ -1165,12 +1166,15 @@ asmlinkage long sys_swapoff(const char __user * specialfile)
swap_list_unlock();
up(&swapon_sem);
vfree(swap_map);
- if (S_ISBLK(mapping->host->i_mode)) {
- struct block_device *bdev = I_BDEV(mapping->host);
+ inode = mapping->host;
+ if (S_ISBLK(inode->i_mode)) {
+ struct block_device *bdev = I_BDEV(inode);
set_blocksize(bdev, p->old_block_size);
bd_release(bdev);
} else {
- up(&mapping->host->i_sem);
+ down(&inode->i_sem);
+ inode->i_flags &= ~S_SWAPFILE;
+ up(&inode->i_sem);
}
filp_close(swap_file, NULL);
err = 0;
@@ -1388,6 +1392,10 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags)
p->bdev = inode->i_sb->s_bdev;
down(&inode->i_sem);
did_down = 1;
+ if (IS_SWAPFILE(inode)) {
+ error = -EBUSY;
+ goto bad_swap;
+ }
} else {
goto bad_swap;
}
@@ -1560,8 +1568,11 @@ out:
}
if (name)
putname(name);
- if (error && did_down)
+ if (did_down) {
+ if (!error)
+ inode->i_flags |= S_SWAPFILE;
up(&inode->i_sem);
+ }
return error;
}