diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/ext4.h | 1 | ||||
-rw-r--r-- | fs/ext4/file.c | 19 | ||||
-rw-r--r-- | fs/ext4/inode.c | 61 |
3 files changed, 80 insertions, 1 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 64edb09c481e..98760d14e2cd 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -1068,6 +1068,7 @@ extern void ext4_set_aops(struct inode *inode); | |||
1068 | extern int ext4_writepage_trans_blocks(struct inode *); | 1068 | extern int ext4_writepage_trans_blocks(struct inode *); |
1069 | extern int ext4_block_truncate_page(handle_t *handle, struct page *page, | 1069 | extern int ext4_block_truncate_page(handle_t *handle, struct page *page, |
1070 | struct address_space *mapping, loff_t from); | 1070 | struct address_space *mapping, loff_t from); |
1071 | extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct page *page); | ||
1071 | 1072 | ||
1072 | /* ioctl.c */ | 1073 | /* ioctl.c */ |
1073 | extern long ext4_ioctl(struct file *, unsigned int, unsigned long); | 1074 | extern long ext4_ioctl(struct file *, unsigned int, unsigned long); |
diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 4159be6366ab..b9510ba66a2d 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c | |||
@@ -123,6 +123,23 @@ force_commit: | |||
123 | return ret; | 123 | return ret; |
124 | } | 124 | } |
125 | 125 | ||
126 | static struct vm_operations_struct ext4_file_vm_ops = { | ||
127 | .fault = filemap_fault, | ||
128 | .page_mkwrite = ext4_page_mkwrite, | ||
129 | }; | ||
130 | |||
131 | static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma) | ||
132 | { | ||
133 | struct address_space *mapping = file->f_mapping; | ||
134 | |||
135 | if (!mapping->a_ops->readpage) | ||
136 | return -ENOEXEC; | ||
137 | file_accessed(file); | ||
138 | vma->vm_ops = &ext4_file_vm_ops; | ||
139 | vma->vm_flags |= VM_CAN_NONLINEAR; | ||
140 | return 0; | ||
141 | } | ||
142 | |||
126 | const struct file_operations ext4_file_operations = { | 143 | const struct file_operations ext4_file_operations = { |
127 | .llseek = generic_file_llseek, | 144 | .llseek = generic_file_llseek, |
128 | .read = do_sync_read, | 145 | .read = do_sync_read, |
@@ -133,7 +150,7 @@ const struct file_operations ext4_file_operations = { | |||
133 | #ifdef CONFIG_COMPAT | 150 | #ifdef CONFIG_COMPAT |
134 | .compat_ioctl = ext4_compat_ioctl, | 151 | .compat_ioctl = ext4_compat_ioctl, |
135 | #endif | 152 | #endif |
136 | .mmap = generic_file_mmap, | 153 | .mmap = ext4_file_mmap, |
137 | .open = generic_file_open, | 154 | .open = generic_file_open, |
138 | .release = ext4_release_file, | 155 | .release = ext4_release_file, |
139 | .fsync = ext4_sync_file, | 156 | .fsync = ext4_sync_file, |
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index c5d506dce8c5..034fc544aa66 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -3564,3 +3564,64 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val) | |||
3564 | 3564 | ||
3565 | return err; | 3565 | return err; |
3566 | } | 3566 | } |
3567 | |||
3568 | static int ext4_bh_unmapped(handle_t *handle, struct buffer_head *bh) | ||
3569 | { | ||
3570 | return !buffer_mapped(bh); | ||
3571 | } | ||
3572 | |||
3573 | int ext4_page_mkwrite(struct vm_area_struct *vma, struct page *page) | ||
3574 | { | ||
3575 | loff_t size; | ||
3576 | unsigned long len; | ||
3577 | int ret = -EINVAL; | ||
3578 | struct file *file = vma->vm_file; | ||
3579 | struct inode *inode = file->f_path.dentry->d_inode; | ||
3580 | struct address_space *mapping = inode->i_mapping; | ||
3581 | |||
3582 | /* | ||
3583 | * Get i_alloc_sem to stop truncates messing with the inode. We cannot | ||
3584 | * get i_mutex because we are already holding mmap_sem. | ||
3585 | */ | ||
3586 | down_read(&inode->i_alloc_sem); | ||
3587 | size = i_size_read(inode); | ||
3588 | if (page->mapping != mapping || size <= page_offset(page) | ||
3589 | || !PageUptodate(page)) { | ||
3590 | /* page got truncated from under us? */ | ||
3591 | goto out_unlock; | ||
3592 | } | ||
3593 | ret = 0; | ||
3594 | if (PageMappedToDisk(page)) | ||
3595 | goto out_unlock; | ||
3596 | |||
3597 | if (page->index == size >> PAGE_CACHE_SHIFT) | ||
3598 | len = size & ~PAGE_CACHE_MASK; | ||
3599 | else | ||
3600 | len = PAGE_CACHE_SIZE; | ||
3601 | |||
3602 | if (page_has_buffers(page)) { | ||
3603 | /* return if we have all the buffers mapped */ | ||
3604 | if (!walk_page_buffers(NULL, page_buffers(page), 0, len, NULL, | ||
3605 | ext4_bh_unmapped)) | ||
3606 | goto out_unlock; | ||
3607 | } | ||
3608 | /* | ||
3609 | * OK, we need to fill the hole... Do write_begin write_end | ||
3610 | * to do block allocation/reservation.We are not holding | ||
3611 | * inode.i__mutex here. That allow * parallel write_begin, | ||
3612 | * write_end call. lock_page prevent this from happening | ||
3613 | * on the same page though | ||
3614 | */ | ||
3615 | ret = mapping->a_ops->write_begin(file, mapping, page_offset(page), | ||
3616 | len, AOP_FLAG_UNINTERRUPTIBLE, &page, NULL); | ||
3617 | if (ret < 0) | ||
3618 | goto out_unlock; | ||
3619 | ret = mapping->a_ops->write_end(file, mapping, page_offset(page), | ||
3620 | len, len, page, NULL); | ||
3621 | if (ret < 0) | ||
3622 | goto out_unlock; | ||
3623 | ret = 0; | ||
3624 | out_unlock: | ||
3625 | up_read(&inode->i_alloc_sem); | ||
3626 | return ret; | ||
3627 | } | ||