|
@@ -4162,17 +4162,14 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length)
|
|
|
struct address_space *mapping = inode->i_mapping;
|
|
|
struct ext4_map_blocks map;
|
|
|
handle_t *handle;
|
|
|
- loff_t first_block_offset, last_block_offset, block_len;
|
|
|
- loff_t first_page, last_page, first_page_offset, last_page_offset;
|
|
|
+ loff_t first_page, last_page, page_len;
|
|
|
+ loff_t first_page_offset, last_page_offset;
|
|
|
int ret, credits, blocks_released, err = 0;
|
|
|
|
|
|
first_block = (offset + sb->s_blocksize - 1) >>
|
|
|
EXT4_BLOCK_SIZE_BITS(sb);
|
|
|
last_block = (offset + length) >> EXT4_BLOCK_SIZE_BITS(sb);
|
|
|
|
|
|
- first_block_offset = first_block << EXT4_BLOCK_SIZE_BITS(sb);
|
|
|
- last_block_offset = last_block << EXT4_BLOCK_SIZE_BITS(sb);
|
|
|
-
|
|
|
first_page = (offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
|
|
|
last_page = (offset + length) >> PAGE_CACHE_SHIFT;
|
|
|
|
|
@@ -4211,24 +4208,44 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length)
|
|
|
goto out;
|
|
|
|
|
|
/*
|
|
|
- * Now we need to zero out the un block aligned data.
|
|
|
- * If the file is smaller than a block, just
|
|
|
- * zero out the middle
|
|
|
+ * Now we need to zero out the non-page-aligned data in the
|
|
|
+ * pages at the start and tail of the hole, and unmap the buffer
|
|
|
+ * heads for the block aligned regions of the page that were
|
|
|
+ * completely zeroed.
|
|
|
*/
|
|
|
- if (first_block > last_block)
|
|
|
- ext4_block_zero_page_range(handle, mapping, offset, length);
|
|
|
- else {
|
|
|
- /* zero out the head of the hole before the first block */
|
|
|
- block_len = first_block_offset - offset;
|
|
|
- if (block_len > 0)
|
|
|
- ext4_block_zero_page_range(handle, mapping,
|
|
|
- offset, block_len);
|
|
|
-
|
|
|
- /* zero out the tail of the hole after the last block */
|
|
|
- block_len = offset + length - last_block_offset;
|
|
|
- if (block_len > 0) {
|
|
|
- ext4_block_zero_page_range(handle, mapping,
|
|
|
- last_block_offset, block_len);
|
|
|
+ if (first_page > last_page) {
|
|
|
+ /*
|
|
|
+ * If the file space being truncated is contained within a page
|
|
|
+ * just zero out and unmap the middle of that page
|
|
|
+ */
|
|
|
+ err = ext4_discard_partial_page_buffers(handle,
|
|
|
+ mapping, offset, length, 0);
|
|
|
+
|
|
|
+ if (err)
|
|
|
+ goto out;
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * zero out and unmap the partial page that contains
|
|
|
+ * the start of the hole
|
|
|
+ */
|
|
|
+ page_len = first_page_offset - offset;
|
|
|
+ if (page_len > 0) {
|
|
|
+ err = ext4_discard_partial_page_buffers(handle, mapping,
|
|
|
+ offset, page_len, 0);
|
|
|
+ if (err)
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * zero out and unmap the partial page that contains
|
|
|
+ * the end of the hole
|
|
|
+ */
|
|
|
+ page_len = offset + length - last_page_offset;
|
|
|
+ if (page_len > 0) {
|
|
|
+ err = ext4_discard_partial_page_buffers(handle, mapping,
|
|
|
+ last_page_offset, page_len, 0);
|
|
|
+ if (err)
|
|
|
+ goto out;
|
|
|
}
|
|
|
}
|
|
|
|