Commit 136963d1 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] hugetlbfs: fix truncate

- Opening a hugetlbfs file O_TRUNC calls the generic vmtruncate() functions
  and nukes the kernel.

  Give S_ISREG hugetlbfs files a inode_operations, and hence a setattr
  which know how to handle these files.

- Don't permit the user to truncate hugetlbfs files to sizes which are not
  a multiple of HPAGE_SIZE.

- We don't support expanding in ftruncate(), so remove that code.
parent 8ca8cd5b
...@@ -34,6 +34,7 @@ static struct super_operations hugetlbfs_ops; ...@@ -34,6 +34,7 @@ static struct super_operations hugetlbfs_ops;
static struct address_space_operations hugetlbfs_aops; static struct address_space_operations hugetlbfs_aops;
struct file_operations hugetlbfs_file_operations; struct file_operations hugetlbfs_file_operations;
static struct inode_operations hugetlbfs_dir_inode_operations; static struct inode_operations hugetlbfs_dir_inode_operations;
static struct inode_operations hugetlbfs_inode_operations;
static struct backing_dev_info hugetlbfs_backing_dev_info = { static struct backing_dev_info hugetlbfs_backing_dev_info = {
.ra_pages = 0, /* No readahead */ .ra_pages = 0, /* No readahead */
...@@ -326,44 +327,29 @@ static void hugetlb_vmtruncate_list(struct list_head *list, unsigned long pgoff) ...@@ -326,44 +327,29 @@ static void hugetlb_vmtruncate_list(struct list_head *list, unsigned long pgoff)
} }
} }
/*
* Expanding truncates are not allowed.
*/
static int hugetlb_vmtruncate(struct inode *inode, loff_t offset) static int hugetlb_vmtruncate(struct inode *inode, loff_t offset)
{ {
unsigned long pgoff; unsigned long pgoff;
struct address_space *mapping = inode->i_mapping; struct address_space *mapping = inode->i_mapping;
unsigned long limit;
pgoff = (offset + HPAGE_SIZE - 1) >> HPAGE_SHIFT; if (offset > inode->i_size)
return -EINVAL;
if (inode->i_size < offset) BUG_ON(offset & ~HPAGE_MASK);
goto do_expand; pgoff = offset >> HPAGE_SHIFT;
inode->i_size = offset; inode->i_size = offset;
down(&mapping->i_shared_sem); down(&mapping->i_shared_sem);
if (list_empty(&mapping->i_mmap) && list_empty(&mapping->i_mmap_shared))
goto out_unlock;
if (!list_empty(&mapping->i_mmap)) if (!list_empty(&mapping->i_mmap))
hugetlb_vmtruncate_list(&mapping->i_mmap, pgoff); hugetlb_vmtruncate_list(&mapping->i_mmap, pgoff);
if (!list_empty(&mapping->i_mmap_shared)) if (!list_empty(&mapping->i_mmap_shared))
hugetlb_vmtruncate_list(&mapping->i_mmap_shared, pgoff); hugetlb_vmtruncate_list(&mapping->i_mmap_shared, pgoff);
out_unlock:
up(&mapping->i_shared_sem); up(&mapping->i_shared_sem);
truncate_hugepages(mapping, offset); truncate_hugepages(mapping, offset);
return 0; return 0;
do_expand:
limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
if (limit != RLIM_INFINITY && offset > limit)
goto out_sig;
if (offset > inode->i_sb->s_maxbytes)
goto out;
inode->i_size = offset;
return 0;
out_sig:
send_sig(SIGXFSZ, current, 0);
out:
return -EFBIG;
} }
static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr) static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr)
...@@ -390,7 +376,9 @@ static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -390,7 +376,9 @@ static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr)
goto out; goto out;
if (ia_valid & ATTR_SIZE) { if (ia_valid & ATTR_SIZE) {
error = hugetlb_vmtruncate(inode, attr->ia_size); error = -EINVAL;
if (!(attr->ia_size & ~HPAGE_MASK))
error = hugetlb_vmtruncate(inode, attr->ia_size);
if (error) if (error)
goto out; goto out;
attr->ia_valid &= ~ATTR_SIZE; attr->ia_valid &= ~ATTR_SIZE;
...@@ -425,6 +413,7 @@ hugetlbfs_get_inode(struct super_block *sb, int mode, dev_t dev) ...@@ -425,6 +413,7 @@ hugetlbfs_get_inode(struct super_block *sb, int mode, dev_t dev)
init_special_inode(inode, mode, dev); init_special_inode(inode, mode, dev);
break; break;
case S_IFREG: case S_IFREG:
inode->i_op = &hugetlbfs_inode_operations;
inode->i_fop = &hugetlbfs_file_operations; inode->i_fop = &hugetlbfs_file_operations;
break; break;
case S_IFDIR: case S_IFDIR:
...@@ -525,6 +514,10 @@ static struct inode_operations hugetlbfs_dir_inode_operations = { ...@@ -525,6 +514,10 @@ static struct inode_operations hugetlbfs_dir_inode_operations = {
.setattr = hugetlbfs_setattr, .setattr = hugetlbfs_setattr,
}; };
static struct inode_operations hugetlbfs_inode_operations = {
.setattr = hugetlbfs_setattr,
};
static struct super_operations hugetlbfs_ops = { static struct super_operations hugetlbfs_ops = {
.statfs = simple_statfs, .statfs = simple_statfs,
.drop_inode = hugetlbfs_drop_inode, .drop_inode = hugetlbfs_drop_inode,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment