@@ -3570,6 +3570,126 @@ static int ext4_discard_partial_page_buffers_no_lock(handle_t *handle,
3570
3570
return err ;
3571
3571
}
3572
3572
3573
+ /*
3574
+ * ext4_block_truncate_page() zeroes out a mapping from file offset `from'
3575
+ * up to the end of the block which corresponds to `from'.
3576
+ * This required during truncate. We need to physically zero the tail end
3577
+ * of that block so it doesn't yield old data if the file is later grown.
3578
+ */
3579
+ int ext4_block_truncate_page (handle_t * handle ,
3580
+ struct address_space * mapping , loff_t from )
3581
+ {
3582
+ unsigned offset = from & (PAGE_CACHE_SIZE - 1 );
3583
+ unsigned length ;
3584
+ unsigned blocksize ;
3585
+ struct inode * inode = mapping -> host ;
3586
+
3587
+ blocksize = inode -> i_sb -> s_blocksize ;
3588
+ length = blocksize - (offset & (blocksize - 1 ));
3589
+
3590
+ return ext4_block_zero_page_range (handle , mapping , from , length );
3591
+ }
3592
+
3593
+ /*
3594
+ * ext4_block_zero_page_range() zeros out a mapping of length 'length'
3595
+ * starting from file offset 'from'. The range to be zero'd must
3596
+ * be contained with in one block. If the specified range exceeds
3597
+ * the end of the block it will be shortened to end of the block
3598
+ * that cooresponds to 'from'
3599
+ */
3600
+ int ext4_block_zero_page_range (handle_t * handle ,
3601
+ struct address_space * mapping , loff_t from , loff_t length )
3602
+ {
3603
+ ext4_fsblk_t index = from >> PAGE_CACHE_SHIFT ;
3604
+ unsigned offset = from & (PAGE_CACHE_SIZE - 1 );
3605
+ unsigned blocksize , max , pos ;
3606
+ ext4_lblk_t iblock ;
3607
+ struct inode * inode = mapping -> host ;
3608
+ struct buffer_head * bh ;
3609
+ struct page * page ;
3610
+ int err = 0 ;
3611
+
3612
+ page = find_or_create_page (mapping , from >> PAGE_CACHE_SHIFT ,
3613
+ mapping_gfp_mask (mapping ) & ~__GFP_FS );
3614
+ if (!page )
3615
+ return - ENOMEM ;
3616
+
3617
+ blocksize = inode -> i_sb -> s_blocksize ;
3618
+ max = blocksize - (offset & (blocksize - 1 ));
3619
+
3620
+ /*
3621
+ * correct length if it does not fall between
3622
+ * 'from' and the end of the block
3623
+ */
3624
+ if (length > max || length < 0 )
3625
+ length = max ;
3626
+
3627
+ iblock = index << (PAGE_CACHE_SHIFT - inode -> i_sb -> s_blocksize_bits );
3628
+
3629
+ if (!page_has_buffers (page ))
3630
+ create_empty_buffers (page , blocksize , 0 );
3631
+
3632
+ /* Find the buffer that contains "offset" */
3633
+ bh = page_buffers (page );
3634
+ pos = blocksize ;
3635
+ while (offset >= pos ) {
3636
+ bh = bh -> b_this_page ;
3637
+ iblock ++ ;
3638
+ pos += blocksize ;
3639
+ }
3640
+
3641
+ err = 0 ;
3642
+ if (buffer_freed (bh )) {
3643
+ BUFFER_TRACE (bh , "freed: skip" );
3644
+ goto unlock ;
3645
+ }
3646
+
3647
+ if (!buffer_mapped (bh )) {
3648
+ BUFFER_TRACE (bh , "unmapped" );
3649
+ ext4_get_block (inode , iblock , bh , 0 );
3650
+ /* unmapped? It's a hole - nothing to do */
3651
+ if (!buffer_mapped (bh )) {
3652
+ BUFFER_TRACE (bh , "still unmapped" );
3653
+ goto unlock ;
3654
+ }
3655
+ }
3656
+
3657
+ /* Ok, it's mapped. Make sure it's up-to-date */
3658
+ if (PageUptodate (page ))
3659
+ set_buffer_uptodate (bh );
3660
+
3661
+ if (!buffer_uptodate (bh )) {
3662
+ err = - EIO ;
3663
+ ll_rw_block (READ , 1 , & bh );
3664
+ wait_on_buffer (bh );
3665
+ /* Uhhuh. Read error. Complain and punt. */
3666
+ if (!buffer_uptodate (bh ))
3667
+ goto unlock ;
3668
+ }
3669
+
3670
+ if (ext4_should_journal_data (inode )) {
3671
+ BUFFER_TRACE (bh , "get write access" );
3672
+ err = ext4_journal_get_write_access (handle , bh );
3673
+ if (err )
3674
+ goto unlock ;
3675
+ }
3676
+
3677
+ zero_user (page , offset , length );
3678
+
3679
+ BUFFER_TRACE (bh , "zeroed end of block" );
3680
+
3681
+ err = 0 ;
3682
+ if (ext4_should_journal_data (inode )) {
3683
+ err = ext4_handle_dirty_metadata (handle , inode , bh );
3684
+ } else
3685
+ mark_buffer_dirty (bh );
3686
+
3687
+ unlock :
3688
+ unlock_page (page );
3689
+ page_cache_release (page );
3690
+ return err ;
3691
+ }
3692
+
3573
3693
int ext4_can_truncate (struct inode * inode )
3574
3694
{
3575
3695
if (S_ISREG (inode -> i_mode ))
0 commit comments